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

App Crashing - Can't Find Reason

After completing the "Build a Simple Android App" project, I'm attempting to create a similar, but a bit more complicated, app before I move on, so that I get used to the language more. My app is Doctor Who-themed, and while eventually it will be a shake-to-activate app similar to the sample app in the project on here, right now it still works with the click of a button because I need to be able to test it on an emulator.

The end goal for the app is to, when the device is shaken, choose one random number out of three (each corresponds to one of the New Who Doctors' Characters) and read that number to fade in an image of that Doctor. Directly after, it fades in a random quote from an array of quotes from that specific Doctor.

Currently, I'd gotten the app working to the point where, when I clicked the button, the app chose a Doctor, and a quote from the array for that Doctor, and was able to fade the quote in. In trying to implement the image fading in, I've done something that makes the app crash when I try to run it.

I'm sure it'll be some stupid mistake, but it would be fantastic if it could be found.

The project has four class files: MainActivity.java, DoctorWho.java, Nine.java, Ten.java, and Eleven.java. Nine, Ten, and Eleven are all nearly identical, just using different quotes. I'm not sure how much this crash encompasses, so I'll put most of the code here just in case.

MainActivity.java

private DoctorWho mDoctorWho = new DoctorWho();
private TextView mQuoteLabel;
private Button mGetQuoteButton;
private ImageView mImageView1;
private ImageView mImageView2;
private ImageView mImageView3;

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

    mQuoteLabel = (TextView) findViewById(R.id.quotePlace);
    mGetQuoteButton = (Button) findViewById(R.id.button1);
    mGetQuoteButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View arg0) {
            String quote = mDoctorWho.getDoctorQuote();
            mDoctorWho.animateDoctor();
            mQuoteLabel.setText(quote);
            animateQuoteIn();

        }
    });
}

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

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

DoctorWho.java

private Nine mNine = new Nine();
private Ten mTen = new Ten();
private Eleven mEleven = new Eleven();
private ImageView mImageView1;

int randomNumber = 0;

public String getDoctorQuote() {
    String quote = "";

    // Choose a Random number out of three values
    Random randomGenerator = new Random();
    int randomNumber = randomGenerator.nextInt(3);

    // Use that value to choose which of the Doctors to get a quote from
    if (randomNumber == 0) {
        // Quote from Nine
        quote = mNine.getQuote();
    }
    else if (randomNumber == 1) {
        // Quote from Ten
        quote = mTen.getQuote();
    }
    else if (randomNumber == 2) {
        // Quote from Eleven
        quote = mEleven.getQuote();
    }
    else {
        quote = "Error";
    }
    return quote;
}

public void animateDoctor() {
    if (randomNumber == 0) {
        animateNinthDoctor();
    }
    else if (randomNumber == 1) {
        animateTenthDoctor();
    }
    else if (randomNumber == 2) {
        animateEleventhDoctor();
    }
}

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

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

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

Nine.java (I've removed the quotes to make it a bit more condensed)

public String[] mNineQuotes = {
        "Quote",
        "Quote",
        "Quote" };

public String getQuote() {

    String quote = "";

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

    quote = mNineQuotes[randomNumber];

    return quote;
}

2 Answers

There are a bunch of ways of going about getting it to work, so this is just one way that you could try.

In the MainActivity.onCreate method:

// ... Your other code ...

@Override
public void onClick(View arg0) {
      // Get a random quote number
      int quoteNumber = mDoctorWho.getQuoteNumber();

      // Display the quote and image that correspond to the quote number that we got above
      mQuoteLabel.setText(mDoctorWho.getDoctorQuote(quoteNumber));
      animateDoctor(mDoctorWho.getDoctorImage(quoteNumber));
   }

public void animateDoctor(Drawable doctorImage) {
   // Assign the image that we passed into this method to the image view
   mImageView1.setImageDrawable(doctorImage);

   // Do the animation
   AlphaAnimation fadeInAnimation = new AlphaAnimation(0, 1);
   fadeInAnimation.setDuration(1500);
   fadeInAnimation.setFillAfter(true);
   mImageView1.setAnimation(fadeInAnimation);
}

// ... Your other code ...

Then in the DoctorWho class:

// ...Your other code ...

// This method returns a random quote number between 0 and 2
public int getQuoteNumber() {
   Random randomGenerator = new Random();
   return randomGenerator.nextInt(3);
}

// This method returns the Doctor Who quote that corresponds to the specified number
public String getDoctorQuote(int quoteNumber) {
   // Use that value to choose which of the Doctors to get a quote from
    if (quoteNumber == 0) {
        // Quote from Nine
        quote = mNine.getQuote();
    }
    else if (quoteNumber == 1) {
        // Quote from Ten
        quote = mTen.getQuote();
    }
    else if (quoteNumber == 2) {
        // Quote from Eleven
        quote = mEleven.getQuote();
    }
    else {
        quote = "Error";
    }
    return quote;
}

// This method returns the Doctor Who image that corresponds to the specified quote number
public Drawable getDoctorImage(int quoteNumber) {
   if (quoteNumber == 0) {
      return R.drawable.ninthdoctor;
   }
   else if (quoteNumber == 1) {
      return R.drawable.tenthdoctor;
   }
   else if (quoteNumber == 2) {
      return R.drawable.eleventhdoctor;
   }
   else {
      // The program should never get here, but you have to return something or throw an exception, or else your program won't compile
      return null;
   }
}
// ... Your other code ...

If you do it like this, you'll only need one animateDoctor method. Since the only difference between the different doctor animations is the image, it'll be easier to pass the image in as a parameter to a single method, rather than have several different methods which are all the same except for the doctor image. My code may not be entirely correct, but that's kind of an overview of one way to do it.

It looks like your mImageView1 property in your DoctorWho class never gets assigned a value, so when you call mImageView1.setAnimation in your animate methods, you're calling a method on a null value. You should assign the DoctorWho.mImageView1 property before you start calling methods on your DoctorWho class.

If you look at the Console window in Eclipse, it'll show you which line of code is causing the crash.

Ben Jakuben
Ben Jakuben
Treehouse Teacher

Can't wait to see this one published! Please let us know when it's in Google Play. :smile:

I think Ben Rubin is on the right track here. It looks like you're mixing some elements between your MainActivity and DoctorWho classes. Views like ImageViews will almost always be within Activity classes since we usually associate them with layout elements using the findViewById() method. It seems like you should move animateDoctor and the three animateXDoctor methods into MainActivity as they are associated with your view elements, not your DoctorWho object. They are acting on elements of the view.

There's also a chance for some refactoring here if you're up for it! There are a couple of places where you have repeated code, which is a good sign that you can refactor. The animateXDoctor methods are all doing the same thing, so those can just be one method. I'm guessing you just want to change the image in the ImageView when the button is clicked. You could do that in the onClick() method with mImageView1.setImageDrawable(R.drawable.new_doctor_image_name);

*Don't worry about this next part if it's more than you're comfortable with!

If you really want to get crazy, you could make a Doctor base class that is the same as Nine.java. You could add a "name" or "number" property that you set with the quotes each time you initialize it. Something like:

Doctor ninthDoctor = new Doctor();
ninthDoctor.number = 9;
ninthDoctor.mQuotes = new String[]{ "quote1", "quote2", "quote3" };

You could also make this simpler with a custom constructor:

Doctor ninthDoctor = new Doctor(9, new String[]{ "quote1", "quote2", "quote3" });

I'd like to make all of those changes, but since I'm still not as familiar with Java as I'd like to be, I'm probably going to have to take it one step at a time. (And I apologize for the delayed response - I don't have as much time to work on coding as I'd like.)

I did move animateDoctor and the three animateXDoctor methods into MainActivity, and I'm not understanding how to access in MainActivity the randomNumber integer that DoctorWho generates on-click - the methods I've moved need that same number to run properly, but the method that the number is generated in returns only a string - do I need to change that to something else?

And on refactoring the animateXDoctor methods into one method, it looks like that example doesn't take into account that when the button is clicked, I'm fading in a specific one of three images corresponding to the quote -- the reason I need to access the randomNumber in MainActivity, so that I can match the quote to the picture. Would it work to refactor the animateXDoctor methods all into one method, and then call that method in the if/else if animateDoctor method (that is now inside MainActivity along with the line you suggested? Like this?

    public void animateDoctor() {
        if (randomNumber == 0) {
            mImageView1.setImageDrawable(R.drawable.ninthdoctor);
            animateXDoctor();
        }
        else if (randomNumber == 1) {
            mImageView1.setImageDrawable(R.drawable.tenthdoctor);
            animateXDoctor();
        }
        else if (randomNumber == 2) {
            mImageView1.setImageDrawable(R.drawable.eleventh);
            animateXDoctor();
        }
    }

Or am I going in the complete wring direction here?

As for publishing it on Google Play, I'd like to, but I wasn't planning on it. Because I'm using quotes and pictures from a show, I don't want to accidentally step over intellectual property lines by publishing the app - I'm mostly just making it to get used to the language.

Ben Jakuben
Ben Jakuben
Treehouse Teacher

I think Ben Rubin's comment below answers some of these questions. Hope that helps you out, and be sure to upvote him and/or mark it as Best Answer! :)