Your Second Activity

In this chapter, you will add a second activity to GeoQuiz.

CheatActivity offers the chance to peek at the answer

If the user chooses to view the answer and then returns to the QuizActivity and answers the question, he or she will get a new message.

QuizActivity knows if you’ve been cheating

Why is this a good Android programming exercise? You will learn how to:

  • Create a new activity and a new layout for it.

  • Start an activity from another activity. Starting an activity means asking the OS to create an activity instance and call its onCreate(Bundle) method.

  • Pass data between the parent (starting) activity and the child (started) activity.

Setting Up a Second Activity

But before you invoke the magic, open strings.xml and add all the strings you will need for this chapter.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    ...
    <string name="question_asia">Lake Baikal is the world\'s oldest and deepest
    freshwater lake.</string>
    <string name="warning_text">Are you sure you want to do this?</string>
    <string name="show_answer_button">Show Answer</string>
    <string name="cheat_button">Cheat!</string>
    <string name="judgment_toast">Cheating is wrong.</string>
</resources>

Creating a new activity

Creating an activity typically involves touching at least three files: the Java class file, an XML layout, and the application manifest. If you touch those files in the wrong ways, Android can get mad. To ensure that you do it right, you should use Android Studio’s New Activity wizard.

Set Activity Name to CheatActivity. This is the name of your Activity subclass. Layout Name should be automatically set to activity_cheat. This will be the base name of the layout file the wizard creates. Title will be set to “CheatActivity” for you, but since this is a string the user will see, change it to simply “Cheat”.

The New Blank Activity wizard

Diagram of layout for CheatActivity

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center"
 android:orientation="vertical"
 tools:context="com.bignerdranch.android.geoquiz.CheatActivity">
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="24dp"
    android:text="@string/warning_text"/>
  <TextView
      android:id="@+id/answer_text_view"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:padding="24dp"
      tools:text="Answer"/>
  <Button
    android:id="@+id/show_answer_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/show_answer_button"/>
</LinearLayout>

A new activity subclass

In the Project tool window, find the com.bignerdranch.android.geoquiz Java package and open the CheatActivity class, which is in the CheatActivity.java file.

Declaring activities in the manifest

The manifest is an XML file containing metadata that describes your application to the Android OS. The file is always named AndroidManifest.xml, and it lives in the app/manifests directory of your project.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.geoquiz" >
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".QuizActivity"
        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=".CheatActivity"
        android:label="@string/title_activity_cheat" >
    </activity>
</application>
</manifest>

Adding a Cheat! button to QuizActivity

The plan is for the user to press a button in QuizActivity to get an instance of CheatActivity on screen. So you need new buttons in layout/activity_quiz.xml and layout-land/ activity_quiz.xml.

 </LinearLayout>

  <Button
  android:id="@+id/cheat_button"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@string/cheat_button"/>

  <Button
 android:id="@+id/next_button"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@string/next_button"
 android:drawableRight="@drawable/arrow_right"
 android:drawablePadding="4dp"/>
 
</LinearLayout>

In the landscape layout, have the new button appear at the bottom and center of the root FrameLayout.

 ...
 </LinearLayout>
 <Button
 android:id="@+id/cheat_button"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="bottom|center"
 android:text="@string/cheat_button" />
 <Button
 android:id="@+id/next_button"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="bottom|right"
 android:text="@string/next_button"
 android:drawableRight="@drawable/arrow_right"
 android:drawablePadding="4dp" />
</FrameLayout>

Wiring up the Cheat! button (QuizActivity.java)

public class QuizActivity extends AppCompatActivity {
 ...
    private Button mNextButton;
    private Button mCheatButton;
 ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
 ...
        mCheatButton = (Button)findViewById(R.id.cheat_button);
        mCheatButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Start CheatActivity
            }
        });
        if (savedInstanceState != null) {
            mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
        }
        updateQuestion();
    }
...

Starting an Activity

The simplest way one activity can start another is with the Activity method:

public void startActivity(Intent intent)

You might guess that startActivity(…) is a static method that you call on the Activity subclass that you want to start. But it is not. When an activity calls startActivity(…), this call is sent to the OS.

Starting an activity

How does the ActivityManager know which Activity to start? That information is in the Intent parameter.

Communicating with intents

An intent is an object that a component can use to communicate with the OS. The only components you have seen so far are activities, but there are also services, broadcast receivers, and content providers.

Intents are multi-purpose communication tools, and the Intent class provides different constructors depending on what you are using the intent to do.

In this case, you are using an intent to tell the ActivityManager which activity to start, so you will use this constructor:

public Intent(Context packageContext, Class<?> cls)

The intent: telling ActivityManager what to do

Starting CheatActivity (QuizActivity.java)

Starting CheatActivity (QuizActivity.java)


...

        mCheatButton = (Button)findViewById(R.id.cheat_button);
        mCheatButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        // Start CheatActivity
                Intent i = new Intent(QuizActivity.this, CheatActivity.class);
                startActivity(i);
                }
        });

        ...

Explicit and implicit intents

When you create an Intent with a Context and a Class object, you are creating an explicit intent. You use explicit intents to start activities within your application.

It may seem strange that two activities within your application must communicate via the ActivityManager, which is outside of your application. However, this pattern makes it easy for an activity in one application to work with an activity in another application.

Passing Data Between Activities

Now that you have a QuizActivity and a CheatActivity, you can think about passing data between them.

The conversation between QuizActivity and CheatActivity

The QuizActivity will inform the CheatActivity of the answer to the current question when the CheatActivity is started.

When the user presses the Back button to return to the QuizActivity, the CheatActivity will be destroyed. In its last gasp, it will send data to the QuizActivity about whether the user cheated.

Using intent extras

To inform the CheatActivity of the answer to the current question, you will pass it the value of

 mQuestionBank[mCurrentIndex].isAnswerTrue()

You will send this value as an extra on the Intent that is passed into startActivity(Intent).

Intent extras: communicating with other activities

An extra is structured as a key-value pair, like the one you used to save out the value of mCurrentIndex in QuizActivity.onSaveInstanceState(Bundle).

To add an extra to an intent, you use Intent.putExtra(…). In particular, you will be calling

public Intent putExtra(String name, boolean value)

Intent.putExtra(…) comes in many flavors, but it always has two arguments. The first argument is always a String key, and the second argument is the value, whose type will vary. It returns the Intent itself, so you can chain multiple calls if you need to

Adding extra constant (CheatActivity.java)

public class CheatActivity extends AppCompatActivity { 
    private static final String EXTRA_ANSWER_IS_TRUE = 
    "com.bignerdranch.android.geoquiz.answer_is_true";
...

An activity may be started from several different places, so you should define keys for extras on the activities that retrieve and use them. Using your package name as a qualifier for your extra, as shown, prevents name collisions with extras from other apps.

A newIntent(…) method for CheatActivity (CheatActivity.java)

public class CheatActivity extends AppCompatActivity {
    private static final String EXTRA_ANSWER_IS_TRUE =
            "com.bignerdranch.android.geoquiz.answer_is_true";
    public static Intent newIntent(Context packageContext, boolean answerIsTrue) {
        Intent i = new Intent(packageContext, CheatActivity.class);
        i.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
        return i;
    }

 ...

Using an extra (CheatActivity.java)

public class CheatActivity extends AppCompatActivity {
    private static final String EXTRA_ANSWER_IS_TRUE =
            "com.bignerdranch.android.geoquiz.answer_is_true";
    private boolean mAnswerIsTrue;
 ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cheat);
        mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
    }
 ...
}

Enabling cheating (CheatActivity.java)

public class CheatActivity extends AppCompatActivity {
 ...
    private boolean mAnswerIsTrue;
    private TextView mAnswerTextView;
    private Button mShowAnswer;

 ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cheat);
        mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
        mAnswerTextView = (TextView) findViewById(R.id.answer_text_view);
        mShowAnswer = (Button) findViewById(R.id.show_answer_button);
        mShowAnswer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mAnswerIsTrue) {
                    mAnswerTextView.setText(R.string.true_button);
                } else {
                    mAnswerTextView.setText(R.string.false_button);
                }
            }
        });
    }
}

Full Example

This goes in the MainActivity should be a unique message ID

public static final String EXTRA_MESSAGE = "com.example.geoquiz.MESSAGE";

in click event to go to the new activity

  mCheatButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Logger.w("Cheat Button Clicked");


                Intent i = new Intent(MainActivity.this, CheatActivty.class);
                String message = "Hello from MainActivity";
                i.putExtra(EXTRA_MESSAGE, message);
                startActivity(i);

            }

get the message in the new activity

public class CheatActivty extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cheat_activty);
        Logger.addLogAdapter(new AndroidLogAdapter());


        Intent i = getIntent();
        String message = getIntent().getStringExtra(MainActivity.EXTRA_MESSAGE);
        Logger.d(message);

    }
}

Getting a result back from a child activity

At this point, the user can cheat with impunity. Let’s fix that by having the CheatActivity tell the QuizActivity whether the user chose to view the answer.

When you want to hear back from the child activity, you call the following Activity method:

public void startActivityForResult(Intent intent, int requestCode)
 private static final int REQUEST_CODE_CHEAT = 0;
 .......
 mCheatButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Logger.w("Cheat Button Clicked");


                Intent i = new Intent(MainActivity.this, CheatActivty.class);
                String message = "Hello from MainActivity";
                i.putExtra(EXTRA_MESSAGE, message);
                startActivityForResult(i, REQUEST_CODE_CHEAT);

            }
        });

in your second activity:

public class CheatActivty extends AppCompatActivity {
    private Button mShowAnswer;
......
mShowAnswer = (Button) findViewById(R.id.show_answer_button);
        mShowAnswer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent returnIntent = new Intent();
                returnIntent.putExtra("result", "Hello from Cheat Activity");
                setResult(Activity.RESULT_OK, returnIntent);
                finish();
            }
        });

Please note Setting a result

There are two methods you can call in the child activity to send data back to the parent:

public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent data)

Typically, the result code is one of two predefined constants: Activity.RESULT_OK or Activity.RESULT_CANCELED. (You can use another constant, RESULT_FIRST_USER, as an offset when defining your own result codes.)

Setting result codes is useful when the parent needs to take different action depending on how the child activity finished. For example, if a child activity had an OK button and a Cancel button, the child activity would set a different result code depending on which button was pressed. Then the parent activity would take different action depending on the result code.

Now you need To override the onActivityResult in your first activity

 @Override
    protected void onActivityResult(int requestCode, int resultCode, 
                                    @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_CHEAT){
            Logger.d(requestCode);
        }

    }

Last updated

Was this helpful?