Working with Fragments on Tablet Applications

You create a new activity for your table app, and now you have to add fragments to it. There’s no need to write new fragments to use with the tablet activity. However, you need to make some changes to your fragments to make sure they work properly in this new environment.

Communicating between fragments

If you try to add a task with your tablet application, it doesn’t show the edit fragment next to the list fragment as you expect, but instead opens a new activity to add a task.

The reason that your app opens the edit fragment in a new activity, rather than adjacent to the list fragment, is that it’s still doing exactly what it was told. It’s still executing the old phone behavior, which is to start a new activity for each fragment.

The code that does it is this line in ReminderListFragment:

startActivity(new Intent(this, ReminderEditActivity.class).putExtra(

ReminderProvider.COLUMN_ROWID, id));

It should be easy to change, right? Not so fast. If you change this line to make it work for tablets, you “break” your existing phone app. The problem is that you want one behavior (the existing one) for phones and another behavior for tablets.

To solve this problem, you create an abstract method named edit Reminder() that does one thing for phones and another thing for tablets. You then replace the existing call to startActivity() with this updated method.

remember.eps Don’t be so quick to put the new editReminder() method in the Reminder ListFragment. The fragment doesn’t know whether it’s running on a phone or a tablet. It has a method that it can call — editReminder() — that lets the user edit a reminder.

The key is to realize that because you need one version of editReminder() for phones and one for tablets, you need to put the editReminder() in two places — one that runs on phones and one that runs on tablets. Where does one class run on phones and another class run on tablets? In the activity, of course. So you put editReminder() in the phone and tablet Activity classes.

To create the editReminder() callback for your fragment to call, follow these steps:

1. Create a new OnEditReminder.java interface, and put the edit Reminder() method in it, like this:

package com.dummies.android.taskreminder;

public interface OnEditReminder {

public void editReminder(long id);

}

2. Implement this method in the ReminderListActivity for the phones.

Modify ReminderListActivity by adding the bits in bold:

package com.dummies.android.taskreminder;

import android.content.Intent;

import android.os.Bundle;

import android.support.v4.app.FragmentActivity;

public class ReminderListActivity extends FragmentActivity implements

OnEditReminder {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.reminder_list);

// Switch to tablet activity and finish this one if user is on a tablet.

if (isTablet()) {

startActivity(new Intent(this, ReminderListAndEditorActivity.class));

finish();

return;

}

}

@Override

public void editReminder(long id) {

startActivity(new Intent(this, ReminderEditActivity.class).putExtra(

ReminderProvider.COLUMN_ROWID, id));

}

}

The editReminder() call is identical, line-for-line, to what you had in the ReminderListFragment, except that now it’s in the ReminderListActivity instead.

3. Call this method from the fragment:

Visit ReminderListFragment and modify the onOptionsItem Selected() and onListItemClick() methods as follows:

@Override

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

caseR.id.menu_insert:

((OnEditReminder) getActivity()).editReminder(0);

return true;

caseR.id.menu_settings:

startActivity(new Intent(getActivity(), TaskPreferences.class));

return true;

}

return super.onOptionsItemSelected(item);

}

@Override

public void onListItemClick(ListView l, View v, int position, long id) {

super.onListItemClick(l, v, position, id);

((OnEditReminder) getActivity()).editReminder(id);

}

Now, rather than call startActivity() directly in each method, the app calls getActivity() to get the activity, casting it to an OnEditReminder and then calling editReminder().

technicalstuff.eps Casting is normally frowned on in Java because it’s often unsafe. However, it’s safe to cast the result of getActivity() to an OnEdit Reminder because the ReminderListFragment will always be in either ReminderListActivity or ReminderListAndEditor Activity, and both implement OnEditReminder. If ever you want to add the fragment to another activity, ensure that it, too, implements OnEditReminder.

4. Implement the same OnEditReminder interface in your ReminderListAndEditorActivity for tablets.

Add the code in bold to your ReminderListAndEditorActivity:

public class ReminderListAndEditorActivity extends FragmentActivity implements OnEditReminder {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.reminder_list_and_editor);

}

@Override

public void editReminder(long id) {

// TBD

}

}

Check out the next section to see exactly how to implement edit Reminder() for the tablet app.

Before you do that, you need to address one more subtle interaction between fragments. Take a look at the OnClickListener for the mConfirmButton in ReminderEditFragment and you’ll see this line:

getActivity().finish();

No good. Calling getActivity().finish() from the phone app returns the user to the list activity, but calling it from the tablet app closes the app. Clearly, from the tablet app you want to remove the edit fragment, not finish the entire activity.

To fix this problem, follow these steps:

1. Create a new interface named OnFinishEditor , and make it look like this:

package com.dummies.android.taskreminder;

public interface OnFinishEditor {

public void finishEditor();

}

2. Modify the ReminderEditFragment to call this interface instead of calling finish() directly.

Replace both instances of getActivity().finish() with the following (there should be two):

((OnFinishEditor) getActivity()).finishEditor();

3. Modify the two activities of ReminderEditFragment to add this interface and implement the finishEditor() method.

Add the following to your ReminderEditActivity:

public class ReminderEditActivity extends FragmentActivity implements

OnFinishEditor {

@Override

public void finishEditor() {

finish();

}

}

This is the same code that used to be called in your OnClickListener and onLoadFinished().

4. Add the following code to ReminderListAndEditorActivity :

public class ReminderListAndEditorActivity extends FragmentActivity implements

OnEditReminder, OnFinishEditor {

@Override

public void finishEditor() {

FragmentManager fragmentManager = getSupportFragmentManager(); 6

FragmentTransaction transaction = fragmentManager.beginTransaction(); 7

Fragment previousFragment = fragmentManager

.findFragmentByTag(ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG); 9

transaction.remove(previousFragment); 10

transaction.commit(); 11

}

}

As before, you’re using the FragmentManager and a Fragment Transaction to manage the adding and removing of fragments from the activity. Here’s what the code does:

6 Asks the FragmentActivity for the FragmentManager.

7 Starts the FragmentTransaction by calling begin Transaction().

9 Asks the FragmentManager to find the previous fragment named DEFAULT_EDIT_FRAGMENT_TAG, if any. This name must agree with the name that you used when you initially added the fragment in editReminder().

10 Removes the fragment. If previousFragment was null, this line does nothing.

11 Commits the transaction. Remember that every call to begin Transaction() must end with a call to commit().

Adding fragment transactions

Fundamentally, editReminder() should show an edit fragment for the task that the user tapped. Or it should show an empty edit fragment if the user tapped the Add button on the action bar so that the user can add a new task. If a user taps several tasks in a row, editReminder() should replace the existing edit fragment with a new one representing the last item.

You’ve already put fragments into activities using XML in your reminder_list.xml layout. Because you want to dynamically add and remove fragments, this time you use Java instead of XML. The process isn’t hard; it’s just a little different from XML.

When you want to add or remove fragments in Java, you need to use the FragmentManager to begin a FragmentTransaction. You then make your changes and call commit() on the FragmentTransaction, much like you might do when interacting with a database transaction.

Inside editReminder(), add the following code:

/**

* Set the edit fragment, replacing the existing fragment if there’s one

* already there.

*/

@Override

public void editReminder(long id) {

ReminderEditFragment fragment = new ReminderEditFragment(); →7

Bundle arguments = new Bundle(); →8

arguments.putLong(ReminderProvider.COLUMN_ROWID, id); →9

fragment.setArguments(arguments); →10

FragmentTransaction transaction = getSupportFragmentManager()

.beginTransaction(); →13

transaction.replace(R.id.edit_container, fragment,

ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG); →15

transaction.addToBackStack(null); →16

transaction.commit(); →17

}

Here’s how the code works:

7 Creates a new ReminderEditFragment fragment.

remember.eps Fragments must have no-argument constructors. All arguments go into a bundle.

8–10 Tells the fragment which task is being edited. An ID of 0 indicates that a new fragment is being created.

13 Gets the FragmentManager by calling FragmentActivity.getSupportFragmentManager(), and then calls begin Transaction() to start a new fragment transaction.

15 Calls FragmentTransaction.replace() to replace the existing fragment with the new fragment. The edit_container view tells you where to place the fragment, fragment tells you which fragment to use, and ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG reveals the fragment name.

16 Calls addToBackStack() and passes null for the optional state name.

This line requires a little explanation. Think about what happens whenever you start a new activity — it is added to the activity stack and, if users tap the Back button, they return to the previous activity. This standard interaction is expected by users for almost all activities.

The default behavior for fragments, though, is the opposite. By default, when you add a fragment to an activity, it doesn’t go on the back stack. So a user who taps the Back button exits the activity rather than removes the fragment you just added. This may not be what you want to happen. If you want the Back button to remove the fragment, you need to call addToBackStack().

When adding fragments dynamically, think about what your users are most likely to expect the Back button to do. In this case, when the user taps a list item to display an edit fragment, it’s reasonable for him to expect to be able to tap the Back button to close the edit fragment.

17 Every call to beginTransaction() must be accompanied by a call to commit(). This is where Android does the actual work to add fragments to, or remove them from, your activity.

Congratulations — you should now have a fully implemented version of your phone application running on your tablet.

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

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