During the rewrite of my old app Moustachify Everything (made in the days of Android 1.6), I decided to switch it to use a new-old thing called Fragments. They help you persist information during screen orientation changes when activities are destroyed and recreated.

Depending on what the fragments are used for, we'll be needing different files. And of course, you'll probably want to include the v4 support library as well.

Hidden fragments

Hidden fragments allow AsyncTasks and AlertDialogs to persist across rotations. Anyone who has worked with this before will understand that it's tedious, horrible and generally error prone.

Since it's hidden, you won't need to create/edit as many files.

Create: A class to extend whichever fragment type you need (eg. DialogFragment, regular Fragment or SherlockFragment)

Modify: Your Activity to extend FragmentActivity or SherlockFragmentActivity

Modify: Implement the dialog response in your activity from the Interface

Modify: Use snippet to create fragment when needed, show it using a unique "tag" string

The following is an example which allows you to instantiate a fragment (with arguments), display a custom dialog of choice and then keep itself displayed during rotations.

All you really need to do is make sure the interface suits your needs and the dialog shows the right content.

ExampleDialogFragment.java (click to view):

public class ExampleDialogFragment extends DialogFragment {

private static final String KEY_ARG_A = "argumentA";

private static final String KEY_ARG_B = "argumentB";



/**

* Interface to link back to the activity.

*/

public interface ExampleDialogResponses {

public void doPositiveClick(long timestamp);

}





/**

* Pass in variables, store for later use.

*/

public static ExampleDialogFragment newInstance(int argA, String argB) {

ExampleDialogFragment f = new ExampleDialogFragment();



Bundle args = new Bundle();

args.putInt(KEY_ARG_A, argA);

args.putString(KEY_ARG_B, argB);

f.setArguments(args);



return f;

}



/**

* Override the creation of the dialog.

*/

@Override

public Dialog onCreateDialog(Bundle savedInstanceState) {

// Fetch the information set earlier

int argA = getArguments().getInt(KEY_ARG_A);

String argB = getArguments().getString(KEY_ARG_B);



// Generate your dialog view or layout here

// view = ...

TextView view = new TextView(getActivity());

view.setText(argB + String.valueOf(argA));



// Display the dialog

return new AlertDialog.Builder(getActivity())

.setTitle("Choose OK or Cancel")

.setView(view)



// Cancel

.setNegativeButton(android.R.string.cancel, null)



// Click "OK" button handler

.setPositiveButton(android.R.string.ok,

new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int whichButton) {

long timestamp = System.currentTimeMillis();

((ExampleDialogResponses) getActivity()).doPositiveClick(timestamp);

}

}

)

.create();

}

}

Changing the base class of the activity is easy. While we're at it, implement the fragment response ExampleDialogFragment.ExampleDialogResponses.

Old:

public class YourActivity extends Activity

New:

public class YourActivity extends FragmentActivity implements ExampleDialogFragment.ExampleDialogResponses

// or if you're using ActionBarSherlock

public class YourActivity extends SherlockFragmentActivity implements ExampleDialogFragment.ExampleDialogResponses

Implementing the dialog response helps keep the code clean. This goes into your Activity class.

@Override

public void doPositiveClick(long timestamp) {

Log.d("doPositiveClick", String.valueOf(timestamp));

}

And finally, this is how you display the fragment. First we initialise the dialog with newInstance() and the given arguments, and then we display it with show().

DialogFragment newFragment = ExampleDialogFragment.newInstance(12345, "ABCDE");

newFragment.show(getFragmentManager(), "dialog");

For ActionBarSherlock user, replace getFragmentManager() with getSupportFragmentManager().

The "dialog" string is simply a unique name for this fragment within the context of the activity.

Layout fragments

Normal layout fragments contain a layout and are treated like a View, except it retains non-view information during rotations. This is something which has bothered many developers for a long time.

Disclaimer! Something to keep in mind is that view information is not retained as they are destroyed along with the activity.

Normal activity fragments which display a layout will require a little more work than hidden fragments.

Create: A new layout for the fragment

Create: A class for the new fragment extending Fragment or SherlockFragment

Modify: The Activity layout to include a fragment

Modify: Activity to extend FragmentActivity or SherlockFragmentActivity

There isn't anything special about creating a layout for your new fragment. Just like any other layout, you can put it in /res/layout. I won't bother pasting the code to this post. Check out the sample code for fragment_example.xml (click to view) if you're really that curious.

The main attraction is the fragment class ExampleFragment.java.

public class ExampleFragment extends SherlockFragment {

// These values are automatically retained

private int buy = 0;

private int sell = 0;



// Views are destroyed each time the activity is rotated.

// These are NOT automatically retained by the fragment.

private Button btnBuy;

private Button btnSell;

private TextView tvLabel;







@Override

public void onCreate(Bundle savedInstanceState) {



super.onCreate(savedInstanceState);



setRetainInstance(true);

}





@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_example, container, true);



// Store handles to the controls

btnBuy = (Button) view.findViewById(R.id.btnBuy);

btnSell = (Button) view.findViewById(R.id.btnSell);

tvLabel = (TextView) view.findViewById(R.id.tvLabel);



// Event handlers

btnBuy.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

buy++;

updateLabel();

}

});



btnSell.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

sell++;

updateLabel();

}

});



return view;

}





@Override

public void onViewCreated(View view, Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);



updateLabel();

}





private void updateLabel() {

StringBuilder sb = new StringBuilder();

sb.append("Buy: ");

sb.append(buy);

sb.append(", sell: ");

sb.append(sell);



tvLabel.setText(sb.toString());

}

}

Now to include the fragment in your activity layout. You'll need to add the "tools" namespace and add the fragment element to where it should be created.

To add the tools namespace, open the layouts file and go to the top. Add in the tools line:

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

<RelativeLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools" <!-- This one! -->

The fragment itself should be treated like a normal view, bound by height and width rules of the parent. Most of this can be done via the WYSIWYG editor.

<fragment

android:id="@+id/fragmentExample"

android:name="twig.nguyen.mustachify2.activities.fragments.ExampleFragment"

tools:layout="@layout/fragment_example"

android:layout_width="wrap_content"

android:layout_height="match_parent" />

The important attributes are:

android:id: the fragment an ID so you can refer to it via code

android:name: maps it to the given fragment class

tools:layout: fill the fragment with elements from the given layout

That's all the setup done! By now you should have a fully working preview in the layout editor.

Accessing fragment via code

Lastly in order to make use of your new toys, you'll have to be able to retrieve them.

FragmentManager fm = getSupportFragmentManager();

// or getSupportFragmentManager() for ActionBarSherlock users

fragment = (ExampleFragment) fm.findFragmentById(R.id.fragmentExample);

Some key points to remember:

having the fragment defined in the XML means initialisation is already done for you

onCreate() tells the fragment it'll be retained.

tells the fragment it'll be retained. onCreateView() is when you inflate the layout and initialise stuff.

is when you inflate the layout and initialise stuff. onViewCreated() is where you fill in the initialised views and the retained variables.

And that's it! Your fragment will handle the instantiation pains so you don't have to worry about them as much.

Sources