10
Using Fragment Arguments

In this chapter, you will get the list and the detail parts of CriminalIntent working together. When a user presses an item in the list of crimes, a new CrimeActivity hosting a CrimeFragment will appear and display the details for that instance of Crime (Figure 10.1).

Figure 10.1  Starting CrimeActivity from CrimeListActivity

Figure shows Starting of CrimeActivity from CrimeListActivity.

In GeoQuiz, you had one activity (QuizActivity) start another activity (CheatActivity). In CriminalIntent, you are going to start the CrimeActivity from a fragment. In particular, you will have CrimeListFragment start an instance of CrimeActivity.

Starting an Activity from a Fragment

Starting an activity from a fragment works nearly the same as starting an activity from another activity. You call the Fragment.startActivity(Intent) method, which calls the corresponding Activity method behind the scenes.

In CrimeListFragment’s CrimeHolder, begin by replacing the toast with code that starts an instance of CrimeActivity.

Listing 10.1  Starting CrimeActivity (CrimeListFragment.java)

private class CrimeHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener {
    ...
    @Override
    public void onClick(View view) {
        Toast.makeText(getActivity(),
                mCrime.getTitle() + " clicked!", Toast.LENGTH_SHORT)
                .show();
        Intent intent = new Intent(getActivity(), CrimeActivity.class);
        startActivity(intent);
    }
}

Here CrimeListFragment creates an explicit intent that names the CrimeActivity class. CrimeListFragment uses the getActivity() method to pass its hosting activity as the Context object that the Intent constructor requires.

Run CriminalIntent. Press any list item, and you will see a new CrimeActivity hosting a CrimeFragment (Figure 10.2).

Figure 10.2  A blank CrimeFragment

A blank CrimeFragment

The CrimeFragment does not yet display the data for a specific Crime, because you have not told it which Crime to display.

Putting an extra

You can tell CrimeFragment which Crime to display by passing the crime ID as an Intent extra when CrimeActivity is started.

Start by creating a newIntent method in CrimeActivity.

Listing 10.2  Creating a newIntent method (CrimeActivity.java)

public class CrimeActivity extends SingleFragmentActivity {

    public static final String EXTRA_CRIME_ID =
            "com.bignerdranch.android.criminalintent.crime_id";

    public static Intent newIntent(Context packageContext, UUID crimeId) {
        Intent intent = new Intent(packageContext, CrimeActivity.class);
        intent.putExtra(EXTRA_CRIME_ID, crimeId);
        return intent;
    }
    ...
}

After creating an explicit intent, you call putExtra(…) and pass in a string key and the value the key maps to (the crimeId). In this case, you are calling putExtra(String, Serializable) because UUID is a Serializable object.

Now, update the CrimeHolder to use the newIntent method while passing in the crime ID.

Listing 10.3  Stashing and passing a Crime (CrimeListFragment.java)

private class CrimeHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener {
    ...
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(getActivity(), CrimeActivity.class);
        Intent intent = CrimeActivity.newIntent(getActivity(), mCrime.getId());
        startActivity(intent);
    }
}

Retrieving an extra

The crime ID is now safely stashed in the intent that belongs to CrimeActivity. However, it is the CrimeFragment class that needs to retrieve and use that data.

There are two ways a fragment can access data in its activity’s intent: an easy, direct shortcut and a complex, flexible implementation. First, you are going to try out the shortcut. Then you will implement the complex and flexible solution.

In the shortcut, CrimeFragment will simply use the getActivity() method to access the CrimeActivity’s intent directly. In CrimeFragment.java, retrieve the extra from CrimeActivity’s intent and use it to fetch the Crime.

Listing 10.4  Retrieving the extra and fetching the Crime (CrimeFragment.java)

public class CrimeFragment extends Fragment {
    ...
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCrime = new Crime();
        UUID crimeId = (UUID) getActivity().getIntent()
                .getSerializableExtra(CrimeActivity.EXTRA_CRIME_ID);
        mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
    }
    ...
}

In Listing 10.4, other than the call to getActivity(), the code is the same as if you were retrieving the extra from the activity’s code. The getIntent() method returns the Intent that was used to start CrimeActivity. You call getSerializableExtra(String) on the Intent to pull the UUID out into a variable.

After you have retrieved the ID, you use it to fetch the Crime from CrimeLab.

Updating CrimeFragment’s view with Crime data

Now that CrimeFragment fetches a Crime, its view can display that Crime’s data. Update onCreateView(…) to display the Crime’s title and solved status. (The code for displaying the date is already in place.)

Listing 10.5  Updating view objects (CrimeFragment.java)

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    ...
    mTitleField = (EditText)v.findViewById(R.id.crime_title);
    mTitleField.setText(mCrime.getTitle());
    mTitleField.addTextChangedListener(new TextWatcher() {
        ...
    });
    ...
    mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
    mSolvedCheckBox.setChecked(mCrime.isSolved());
    mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
        ...
    });
    ...
    return v;
}

Run CriminalIntent. Select Crime #4 and watch a CrimeFragment instance with the correct crime data appear (Figure 10.3).

Figure 10.3  The crime that you wanted to see

Screenshot shows the CriminalIntent app in Android. The Title reads, Crime #4. Details show an inactive button. The solved box is shown checked.

The downside to direct retrieval

Having the fragment access the intent that belongs to the hosting activity makes for simple code. However, it costs you the encapsulation of your fragment. CrimeFragment is no longer a reusable building block because it expects that it will always be hosted by an activity whose Intent defines an extra named com.bignerdranch.android.criminalintent.crime_id.

This may be a reasonable expectation on CrimeFragment’s part, but it means that CrimeFragment, as currently written, cannot be used with just any activity.

A better solution is to stash the crime ID someplace that belongs to CrimeFragment rather than keeping it in CrimeActivity’s personal space. The CrimeFragment could then retrieve this data without relying on the presence of a particular extra in the activity’s intent. The someplace that belongs to a fragment is known as its arguments bundle.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.252.8