Sans Pareil Technologies, Inc.

Key To Your Business

Lesson 8



Bug Fix



The exercises we worked on from Lab 5 onwards have a serious bug.  If you attempt to long click on any grid cell that is displayed after you scroll outside the initial list of cells your application will crash.  The issue is in the SelectionListener class.

public void onItemCheckedStateChanged( ActionMode mode, int position, long id, boolean checked )
{
  if ( defaultColor == -1 ) defaultColor = gridView.getChildAt( position ).getDrawingCacheBackgroundColor();
  if ( checked )
  {
    indices.add( position );
    gridView.getChildAt( position ).setBackgroundColor( Color.GRAY );
  }
  else
  {
    indices.remove( position );
    if ( defaultColor != -1 ) gridView.getChildAt( position ).setBackgroundColor( defaultColor );
  }
}



Fragment



A fragment is an independent Android component which can be used by an activity. A fragment encapsulates behaviour so that it is easier to reuse within activities and layouts.

A fragment runs in the context of an activity, but has its own life cycle and typically its own user interface. It is also possible to define fragments without an user interface, i.e., headless fragments.

Note: Fragments do not subclass the Context class. Therefore you have to use the getActivity() method to get the parent activity.

Advantages



Fragments simplify the reuse of components in different layouts, e.g., you can build single-pane layouts for handsets (phones) and multi-pane layouts for tablets. This is not limited to tablets; for example, you can use fragments to support different layouts for landscape and portrait orientation on a smartphone.

Fragments may be dynamically added or removed from an activity. Using fragments enable development very flexible user interfaces.

The typical example is a list of items in an activity. On a tablet you see the details immediately on the same screen on the right hand side if you click on item. On a smartphone you jump to a new detail screen.
xfragmentsusage10.png.pagespeed.ic.klPuIUtmsaxfragmentsusage20.png.pagespeed.ic.PQNhLJI8-B

Support different screensizes



It is possible to define the fragments used in the layout file of an activity (static definition) or to modify the fragments of an activity at runtime (dynamic definition).

To display different fragments in your activities based on the available screen space you can:
  • Use one activity, which displays two fragments for tablets and one on handset devices. In this case change at runtime the fragments displayed by the activity whenever necessary. In this scenario you typically define instances of the FrameLayout class as placeholder in your layout and add the fragments at runtime to them.
  • Use separate activities to host each fragment on a handset. For example, when the tablet UI uses two fragments in an activity, use the same activity for handsets, but supply an alternative layout that includes just one fragment. If the detailed fragment is present, the main activity updates the fragment. If the detail fragment is not available, the main activity starts the detailed activity.

Which option to select depends on the use case, the dynamic method is more flexible but a bit harder to implement.

Defining fragments



To define a new fragment you either extend the Fragment class or one of its subclasses, for example, ListFragment, DialogFragment, PreferenceFragment or WebViewFragment.

Screen Shot 2016-02-21 at 11.34.54

Interactions



Fragments should not directly communicate with each other.  Communication between fragments should be done via the host activity.

A fragment should define an interface (as an inner type) and require that the activity, which hosts it implement this interface. This way you decouple the fragment from details of the activity which uses it. In its onAttach() method it can check if the activity correctly implements this interface.

public class ListFragment extends Fragment
{
  public interface OnItemSelectedListener
  {
    public void onItemSelected( final Object item );
  }
  private OnItemSelectedListener listener;
  @Override
  public void onAttach( final Context context )
  {
    super.onAttach( context );
    if ( context instanceof OnItemSelectedListener )
    {
      listener = (OnItemSelectedListener) context;
    }
    else
    {
      throw new ClassCastException( format( "%s must implement ListFragment.OnItemSelectedListener", context.getClass().getName() ) );
    }
  }
  @Override
  public void onDetach()
  {
    super.onDetach();
    listener = null;
  }
}


Life-cycle



The life-cycle of a fragment is connected to the life-cycle of its hosting activity.

A fragment has its own life cycle, but it is always connected to the life cycle of the activity which hosts the fragment.

If an activity stops, its fragments are also stopped; if an activity is destroyed, its fragments are also destroyed.

xfragmentlifecycle10.png.pagespeed.ic.enq8LZV2uu

  • onAttach - The fragment instance is associated with an activity instance. The fragment and the activity is not fully initialised. Typically you get in this method a reference to the activity which uses the fragment for further initialisation work.
  • onCreate - Fragment is created. This method is called after the onCreate() method of the activity but before the onCreateView() method of the fragment.
  • onCreateView - The fragment instance creates its user interface. Here you can inflate a layout via the inflate() method on the Inflator object passed as a parameter. The inflated views become part of the view hierarchy of its containing activity.
    In this method you should not interact with the activity, which is not yet fully initialised.
    There is no need to implement this method for headless fragments.
  • onActivityCreated - This is called after the onCreateView() method when the host activity is created. Activity and fragment instances have been created as well as the view hierarchy of the activity. At this point, views can be accessed with the findViewById() method.
    In this method you can instantiate objects which require a Context object.
  • onStart - This method is called once the fragment becomes visible.
  • onResume - Fragment becomes active.
  • onPause - Fragment is visible but becomes in-active, e.g., if another activity is animating on top of the activity which contains the fragment.
  • onStop - Fragment becomes in-visible.
  • onDestroyView - Destroys the view of the fragment. If the fragment is recreated from the backstack this method is called and afterwards the onCreateView method.
  • onDestroy - Not guaranteed to be called by the Android platform.

Add statically



To use your new fragment, you can statically add it to an XML layout. In this case the android:name attribute points to the corresponding class as demonstrated by the following code snippet.

Using this scenario makes sense in case you have different static layout files for different device configurations.
Screen Shot 2016-02-23 at 15.58.01

Add Dynamically



The FragmentManager which can be accessed in the activity via the getFragmentManager() method allows you to add, remove and replace fragments in the layout of your activity.

The modifications must be performed in a transaction via the FragmentTransaction class.

FrameLayout placeholder in the layout file is typically used to hold fragments that are dynamically swapped in/out.
Screen Shot 2016-02-21 at 12.58.29

Use the FragmentManager to replace the container with a fragment.
Screen Shot 2016-02-21 at 13.01.11

A new fragment replaces an existing fragment in this container.

If you want to add the transaction to the backstack of Android, you use the addToBackStack() method. This will add the action to the history stack of the activity, i.e., this will allow to revert the Fragment changes via the back button.

Check if a fragment is present in the layout



To check if the fragment is part of your layout you can use the FragmentManager.isInLayout().

As the logic in the activity depends on the scenario (single/multi pane), you typically write a check after setting the layout in the activity to check the mode. There are several approaches to perform this. One way is to define a configuration file in the values resource folder of you project with a key-value pair defaulting to false and additional configuration files for the different configurations setting this value to true.

For example this is a possible  values/config.xml configuration file.

<resources>
    <item type="bool" name="dual_pane">false</item>
</resources> 



The values-land/config.xml configuration file with a different value.

<resources>
    <item type="bool" name="dual_pane">true</item>
</resources> 



In your code you can access the state via:

getResources().getBoolean(R.bool.dual_pane);



FragmentTransaction



FragmentTransaction is used to manage transitions of fragments into and out of activities.  Fragments are added to and removed from hosting activities within the bounds of a transaction.  Each transaction may optionally be added to the back stack.

During a fragment transaction you can define animations which should be used based on the Property Animation API via the setCustomAnimations() method.

You can also use several standard animations provided by Android via the setTransition() method call. These are defined via the constants starting with FragmentTransaction.TRANSIT_FRAGMENT_*.

Both methods allow you to define an entry animation and an existing animation.

Background processing



Fragments can be used without defining a user interface.

To implement a headless fragment, return null from the onCreateView() method of your fragment.

Headless fragments are typically used to encapsulate some state across configuration changes or for a background processing task. For this purpose you would set your headless fragment to be retained. A retained fragment is not destroyed during configuration changes.
xretainedfragmentlifecyle10.png.pagespeed.ic.7-q2ddz2ze

Work on Lab7