Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Richard Luick
Richard Luick
10,955 Points

String Everywhere! Extra Credit - Rebuilding from Scratch

Hello, I decided to tackle the extra credit called Strings Everywhere! after the Rebuilding from Scratch lesson for the Blog Reader which asks to redo the Crystal Ball app using String resources. In my strings.xml file I created the appropriate string array and then called it in my CrystalBall.java file as shown in the lessons. However, I am getting an error "The method getResources() is undefined for the type CrystalBall". If anyone could help me understand the reasoning for this error and how I can fix it that would be great. My code is below.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Crystal Ball</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string-array name="ball_answers">
        <item>It is certain</item>
        <item>It is decidedly so</item>
        <item>All signs say YES</item>
        <item>The stars are not aligned</item>
        <item>My reply is no</item>
        <item>It is doubtful</item>
        <item>Better not tell you now</item>
        <item>Concentrate and ask again</item>
        <item>Unable to answer now</item>
    </string-array>

</resources>
package com.richluick.crystalball;

import java.util.Random;

import android.content.res.Resources;


public class CrystalBall {

     Resources resources = getResources();
     public String[] mAnswers = resources.getStringArray(R.array.ball_answers);

     public String getAnAnswer() {
        String answer = "";

        Random randomGenerator = new Random();
        int randomNumber = randomGenerator.nextInt(mAnswers.length);

        answer = mAnswers[randomNumber];
        return answer;
    }
}

Thanks!

1 Answer

John McDonald
John McDonald
13,958 Points

Try

public class CrystalBall extends Activity {
Richard Luick
Richard Luick
10,955 Points

So that clears up the error in the code but when I launch the app on the emulator it crashes immediately. The LogCat is posted below.

08-06 13:07:16.979: E/AndroidRuntime(1258): Process: com.richluick.crystalball, PID: 1258
08-06 13:07:16.979: E/AndroidRuntime(1258): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.richluick.crystalball/com.richluick.crystalball.MainActivity}: java.lang.NullPointerException
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2121)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.app.ActivityThread.access$800(ActivityThread.java:135)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.os.Handler.dispatchMessage(Handler.java:102)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.os.Looper.loop(Looper.java:136)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.app.ActivityThread.main(ActivityThread.java:5017)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at java.lang.reflect.Method.invokeNative(Native Method)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at java.lang.reflect.Method.invoke(Method.java:515)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at dalvik.system.NativeStart.main(Native Method)
08-06 13:07:16.979: E/AndroidRuntime(1258): Caused by: java.lang.NullPointerException
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.content.ContextWrapper.getResources(ContextWrapper.java:89)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.view.ContextThemeWrapper.getResources(ContextThemeWrapper.java:78)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at com.richluick.crystalball.CrystalBall.<init>(CrystalBall.java:11)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at com.richluick.crystalball.MainActivity.<init>(MainActivity.java:24)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at java.lang.Class.newInstanceImpl(Native Method)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at java.lang.Class.newInstance(Class.java:1208)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.app.Instrumentation.newActivity(Instrumentation.java:1061)
08-06 13:07:16.979: E/AndroidRuntime(1258):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2112)
08-06 13:07:16.979: E/AndroidRuntime(1258):     ... 11 more
John McDonald
John McDonald
13,958 Points

Sorry, try addng the new activity to the manifest and wrap this code:

Resources resources = getResources();
     public String[] mAnswers = resources.getStringArray(R.array.ball_answers);

In an onCreate() method

Richard Luick
Richard Luick
10,955 Points

Okay so in addition to adding the activity to the manifest, here is my CrystalBall.java code

package com.richluick.crystalball;

import java.util.Random;

import android.app.Activity;
import android.content.res.Resources;


public class CrystalBall extends Activity {

    protected String[] mAnswers;

    public void onCreate() { 
        Resources resources = getResources();
        mAnswers = resources.getStringArray(R.array.ball_answers);
    }

     public String getAnAnswer() {
        String answer = "";

        Random randomGenerator = new Random();
        int randomNumber = randomGenerator.nextInt(mAnswers.length);

        answer = mAnswers[randomNumber];
        return answer;
    }
}

The app now launches on my phone but crashes as soon as I shake it to get an answer. I am not sure what the issue is. Does it have something to do with me having two onCreate methods? I already had one in my MainActivity. Would it be better practice to put this code there?

package com.richluick.crystalball;

import android.app.Activity;
import android.content.res.Resources;
import android.graphics.drawable.AnimationDrawable;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;
import android.widget.TextView;

import com.richluick.crystalball.ShakeDetector.OnShakeListener;


public class MainActivity extends Activity {

    public static final String TAG = MainActivity.class.getSimpleName();

    private CrystalBall mCrystalBall = new CrystalBall();
    private TextView mAnswerLabel;
    private ImageView mCrystalBallImage;
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private ShakeDetector mShakeDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mAnswerLabel = (TextView) findViewById(R.id.textView1);
        mCrystalBallImage = (ImageView) findViewById(R.id.imageView1);

        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mShakeDetector = new ShakeDetector(new OnShakeListener() {
            public void onShake() {
                handleNewAnswer();
            }
        });

        //Toast.makeText(this, "Yay! Our activity was created!", Toast.LENGTH_LONG).show();
        Log.d(TAG, "We're logging from the onCreate Method");
    }

    @Override
    public void onResume() {
        super.onResume();
        mSensorManager.registerListener(mShakeDetector, mAccelerometer, 
                SensorManager.SENSOR_DELAY_UI);
    }

    @Override
    public void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(mShakeDetector);
    }

    private void animateCrystalBall() {
        mCrystalBallImage.setImageResource(R.drawable.ball_animation);
        AnimationDrawable ballAnimation = (AnimationDrawable) mCrystalBallImage.getDrawable();
        if (ballAnimation.isRunning()) {
            ballAnimation.stop();
        }
        ballAnimation.start();
    }

    private void animateAnswer() {
        AlphaAnimation fadeInAnimation = new AlphaAnimation(0,1);
        fadeInAnimation.setDuration(1500);
        fadeInAnimation.setFillAfter(true);
        mAnswerLabel.setAnimation(fadeInAnimation);
    }

    private void playSound() {
        MediaPlayer player = MediaPlayer.create(this, R.raw.crystal_ball);
        player.start();
        player.setOnCompletionListener(new OnCompletionListener() {
            public void onCompletion(MediaPlayer mp) {
                mp.release();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }   


    private void handleNewAnswer() {
        String answer = mCrystalBall.getAnAnswer();
        mAnswerLabel.setText(answer);
        animateCrystalBall();
        animateAnswer();
        playSound();
    }
}
John McDonald
John McDonald
13,958 Points

Can you add the manifest code as well?

Richard Luick
Richard Luick
10,955 Points
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.richluick.crystalball"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="Crystal Ball"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.richluick.crystalball.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="CrystalBall" android:exported="true" android:immersive="true"></activity>
    </application>

</manifest>
John McDonald
John McDonald
13,958 Points

I'm sorry, I thought that might have fixed your problem but apparently not :(

Richard Luick
Richard Luick
10,955 Points

Its okay I appreciate the help! After thinking about it, It seems to make sense to me that this code would get the job done. Maybe someone else has some insight on why it crashes when I shake the phone.