OverView of Fragment(Part2)
Best Practices
1.Loose Coupling of Activities and Fragments
Avoid tight coupling between activities and fragments.
- Especially if we make the fragment “too aware” of its hosting activity and the other fragments that might (or might not) be present in the activity, we increase its complexity and reduce its reuse.
- On the other hand, activities typical need more awareness of the fragments they and other activities host.
2.Define Fragment Interfaces to Invoke Activity Behavior Define interfaces in fragments to call back to their hosting activities.
- Rather than having a fragment directly access the internals of a hosting activity, create a nested interface within the fragment defining a set of callback methods.
- The hosting activity can then implement the interface, or we can provide another listener object that implements the interface.
- If we plan always to use the activity as an event listener, we can automatically “register” it in the fragment’s onAttach() method. Otherwise, we can provide a listener registration method. For example:
Fragment source code:
public class TFragment extends ListFragment{
private OnTitleSelectedListener listener;
public interface OnTitleSelectedListener{
public void OnTitleSelectedListener(int index);
}
public void setOnTitleSelectedListener(OnTitleSelectedListener listener){
this.listener=listener;
}
@Override
public void onListItemClick(ListView l,View v,int position,long id){
listener.OnTitleSelected(position);
}
//...
}
Activity source code:
public class MyActivity extends Activity
implements TitlesFragment.OnTitleSelectedLIstener
{
@Override
public boolean onCreate(Bundle bundle){
TitlesFragment titles=new TitlesFragment();
titles.OnTitleSelectedLIstener(this);
}
public void onTitleSelected(int index){
//..
}
//..
}
3.The Activity as a Switchboard
Let the activity serve as an intermediary between fragments.
- Fragments don’t have intent filters; an Intent can’t directly trigger a fragment.
- Let the activity respond to intents and fragment callbacks.
- The activity will know if it can “route” a event to a fragment it contains, or if it needs to start another activity to handle the event.
Advanced Fragment Initialization
Every fragment must have a default empty constructor.
- The system invokes the default constructor to re-instantiate the fragment when restoring its activity’s state.
Fragments generally should not implement additional constructors or override the default constructor.
- The first place application code can run where the fragment is ready to be used is in onAttach().
Advanced Fragment Initialization: Arguments
-
Immediately after instantiating a fragment, can can provide it with a set of arguments.
Create a Bundle object, and put in it any values to want to provide as arguments to the fragment. Invoke setArguments(Bundle) on the fragment, passing the Bundle. -
Within the fragment, invoke getArguments() when you’re ready to retrieve the Bundle of arguments.
-
Often, especially in the case of a fragment supporting only one or two arguments, it’s easiest to define a static factory method to instantiate the fragment and supply the arguments.
Fragment source code:
public static class DetailsFragment extends Fragment{
public static DetailsFragment newInstance(int index){
DetailsFragment f=new DetailsFragment();
//supply index input as an argument
Bundle args=new Bundle();
args.putInt("index",index);
f.setArguments(args);
return f;
}
public int getShownIndex(){
return getArguments().getInt("index",0);
}
//...
}
Activity Source Code:
DetailsFragment detail=DetailsFragment.newInstance(2);
Managing Fragment Backstack
A record of all Fragment transactions is kept for each Activity by the FragmentManager. When used properly, this allows the user to hit the device’s back button to remove previously added Fragments (not unlike how the back button removes an Activity). Simply call addToBackstack on each FragmentTransaction that should be recorded:
// create the transaction
FragmentTransaction fts = getSupportFragmentManager().beginTransaction();
// Replace the content of the container
fts.replace(R.id.flContainer, new FirstFragment());
// Append this transaction to the backstack
fts.addToBackStack("optional tag");
// Commit the changes
fts.commit();
Programmatically, we can also pop from the back stack at any time through the manager:
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
}
Nesting Fragments within Fragments
Inevitably in certain cases we will want to embed a fragment within another fragment. Since Android 4.2, we have the ability to embed a fragment within another fragment. This nested fragment is known as a child fragment. A common situation where we might want to nest fragments is when we are using a sliding drawer for top-level navigation and one of the fragments needs to display tabs.
Note that one limitation is that nested (or child) fragments must be dynamically added at runtime to their parent fragment and cannot be statically added using the <fragment> tag. To nest a fragment in another fragment, first we need a <FrameLayout> or alternatively a ViewPager to contain the dynamic child fragment in the res/layout/fragment_parent.xml layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I am the parent fragment" />
<FrameLayout
android:id="@+id/child_fragment_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Notice that there's a FrameLayout with the id of @+id/child_fragment_container in which the child fragment will be inserted. Inflation of the ParentFragment view is within the onCreateView method, just as was outlined in earlier sections. In addition, we would also define a ChildFragment that would have its own distinct layout file:
public class ParentFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_parent, container, false);
}
}
public class ChildFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_child, container, false);
}
}
Now we can add the child fragment to the parent at runtime using the getChildFragmentManager method:
public class ParentFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_parent, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
insertNestedFragment();
}
// Embeds the child fragment dynamically
private void insertNestedFragment() {
Fragment childFragment = new ChildFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.child_fragment_container, childFragment).commit();
}
}
Note : we must always use getChildFragmentManager when interacting with nested fragments instead of using getSupportFragmentManager.In the child fragment, we can use getParentFragment() to get the reference to the parent fragment, similar to a fragment's getActivity() method that gives reference to the parent Activity.
Implementing Dialogs Using Fragments
As of Honeycomb (API 11), the Activity class’s “managed dialog” methods are deprecated in favor of fragments.
- The DialogFragment class serves as a base class for fragment-based dialog management. DialogFragment implements a fragment that displays a dialog window, floating on top of its activity’s window.
- This fragment contains a Dialog object, which it displays as appropriate based on the fragment’s state.
- we should control dialog (showing and dismissing it) through the DialogFragment methods, not with direct calls on the dialog.
we can implement a DialogFragment so that it can be used only as a dialog, or so that it can be displayed as either a dialog or as a “normal” fragment managed by a ViewGroup within the activity.
- If we plan to use the fragment only as a dialog, override the onCreateDialog() method and return an instance of Dialog or one of its subclasses.
- If we want to use the fragment as either a dialog or a normal fragment, override onCreateView() and return a view hierarchy.
Using a Fragment-Based Dialog
The DialogFragment class provides an overloaded show() method, which posts the dialog as part of a transaction.
- If provided with a FragmentManager reference, the show() method creates a transaction, adds the fragment, and then commits. When the fragment is dismissed, a new transaction is executed automatically to remove it from the activity.
- If provided with a FragmentTransaction reference, the show() method adds the fragment and commits the transaction. In this case, we have the option of adding the transaction to the back stack prior to invoking show()
- In either version, the second argument is a String tag to identify the dialog fragment.
The DialogFragment class provides a dismiss() method to dismiss the fragment explicitly.
- If the fragment was added to the back stack, all back stack state up to and including this entry is popped. Otherwise, a new transaction is committed to remove the fragment.
- In the typical use case, the fragment dismisses itself with this method when its dialog is dismissed. Example: A Simple Confirmation Dialog Fragment source code:
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.sharma.hello.MainActivity">
<textview
android:id="@+id/tv_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="20dp"
android:text="@string/str_tv_about">
<button
android:id="@+id/btn_quit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str_btn_quit">
</button>
</textview>
</RelativeLayout>
MyAlertDialogWIndow.java
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.widget.Toast;
public class MyAlertDialogWIndow extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
OnClickListener positiveClick = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity().getBaseContext(), "Application finishing ...", Toast.LENGTH_SHORT).show();
getActivity().finish();
}
};
OnClickListener negativeClick = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity().getBaseContext(), "No option selecting", Toast.LENGTH_SHORT).show();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Do you want Yes or No ?");
builder.setNegativeButton("No", negativeClick);
builder.setPositiveButton("Yes", positiveClick);
builder.setTitle("Confirmation");
Dialog dialog = builder.create();
return dialog;
}
}
MainActivity.java
import com.sunil.alert.R;
import android.app.Activity;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button click = (Button) findViewById(R.id.btn_quit);
click.setOnClickListener(this);
}
@Override
public void onClick(View v) {
FragmentManager fm = getFragmentManager();
MyAlertDialogWIndow alert = new MyAlertDialogWIndow();
alert.show(fm, "Alert_Dialog");
}
}
Saving Fragment State
There are various situations such as when the screen orientation is rotated where the Activity can actually be destroyed and removed from memory and then re-created from scratch again. In these situations, the best practice is to prepare for cases where the Activity is re-created by properly saving and restoring the state.
Fragments have a onSaveInstanceState() method which is called when their state needs to be saved:
public class MySim extends Fragment{
private int value;
pirvate final String SOME_VALUE_KEY = "someValueToSave";
//fires when a configuration change occurs and fragment need to save state
@Override
protected void onSaveInstanceState(Bundle outState){
outState.putInt(SOME_VALUE_KEY,value);
super.onSaveInstanceState(outState);
}
}
Then we can pull data out of this saved state in onCreateView:
public class MySim extends Fragment{
//...
//inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedINstanceState){
View view=inflater.inflate(R.layout.mySim,container,false);
if(savedINstanceState != null){
value=savedINstanceState.getInt(value);
}
return view;
}
}
For the fragment state to be saved properly, we need to be sure that we aren't unnecessarily recreating the fragment on configuration changes. This means being careful not to reinitialize existing fragments when they already exist. Any fragments being initialized in an Activity need to be looked up by tag after a configuration change.This requires us to be careful to include a tag for lookup whenever putting a fragment into the activity within a transaction:
public class ParentActivity extends AppCompatActivity {
private MySimpleFragmetn fragmentSimple;
private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";
@Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
fragmentSimple = (MySimpleFragmetn) getSupportFragmentManager().findFragmentByTag(SIMPLE_FRAGMENT_TAG);
} else if (fragmentSimple == null) {
fragmentSimple = new MySimpleFragmetn
}
}
}
With this simple pattern, we can properly reuse fragments and restore their state across configuration changes.
public class ParentActivity extends AppCompatActivity {
private MySimpleFragmetn fragmentSimple;
private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";
@Override
protected void onCreate(Bundle savedInstanceState) {
if (!fragmentSimple.isInLayout()) {
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragmetnSimple,
SIMPLE_FRAGMENT_TAG).commit();
}
}
}
Retaining Fragments
In many cases, we can avoid problems when an Activity is re-created by simply using fragments. If our views and state are within a fragment, we can easily have the fragment be retained when the activity is re-created:
public class RetainedFragment extends Fragment {
private MyDataObject data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
This approach keeps the fragment from being destroyed during the activity lifecycle. They are instead retained inside the Fragment Manager.
References:
- http://developer.android.com/reference/android/app/Fragment.html
- http://developer.android.com/training/basics/fragments/creating.html
- http://developer.android.com/guide/components/fragments.html
- http://www.vogella.com/articles/AndroidFragments/article.html
- http://xperiment-andro.blogspot.com/2013/02/nested-fragments.html
- http://www.truiton.com/2015/06/android-tabs-example-fragments-viewpager/
- https://android.googlesource.com/platform/frameworks/support.git/+/master/design/src/android/support/design/widget/TabLayout.java
- https://android.googlesource.com/platform/frameworks/support.git/+/master/design/res/values/styles.xml
- http://architects.dzone.com/articles/android-tutorial-using
- http://developer.android.com/training/animation/screen-slide.html
- http://developer.android.com/reference/android/support/v4/view/ViewPager.html
- http://android-developers.blogspot.com/2011/08/horizontal-view-swiping-with-viewpager.html
- http://viewpagerindicator.com/
- https://newcircle.com/s/post/1250/android_fragments_tutorial
- http://mobile.tutsplus.com/tutorials/android/android-user-interface-design-horizontal-view-paging/
- http://tamsler.blogspot.com/2011/10/android-viewpager-and-fragments.html
- http://www.truiton.com/2013/05/android-fragmentpageradapter-example/
- http://www.tutorialspoint.com/android/android_fragments.htm
- http://tutorials.jenkov.com/android/fragment.html
- https://www.learn2crack.com/2014/05/android-working-with-fragments.html
- https://www.learn2crack.com/2014/05/android-working-with-fragments.html
- http://www.easyinfogeek.com/2013/07/full-example-of-using-fragment-in.html
- http://www.survivingwithandroid.com/2013/03/android-fragment-tutorial-webview-example.html
- http://www.mysamplecode.com/2012/08/android-fragment-example.html
- http://cyrilmottier.com/2014/05/20/custom-animations-with-fragments/
All rights reserved