16 June 2014

Making a Fragment Utility

Android's fragments bring much versatility to UI development. But if you want to show and hide multiple fragments with options, this can take a few coding lines. Repeat this throughout and your code can become ugly and difficult to manage.

A simple solution is to create a small Fragment Utility class. This class should handle the showing and hiding of a fragment with some simple method calls. It should allow you to add the fragment to the back stack, or not, to add a tag for the fragment and to decide on your entrance and exit animations.

To do this, we first create the animations. We usually want slide in and out animations, mostly from the right hand side or from the bottom.
To create the slide in animation from the right hand side, we use a simple translate animation.

<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate 
     android:fromXDelta="100%p" 
     android:toXDelta="0"
        android:duration="@integer/slide_out_animation_duration"/>
</set>
 
Here we are translating from xDelta of 100% to 0. For the slide out animation we simply reverse this.

<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate 
     android:fromXDelta="0" 
     android:toXDelta="100%p"
        android:duration="@integer/slide_out_animation_duration"/>
</set>

We can repeat these type of animations for slide entrance and exit from differing sides.

To create the utility we make a class named FragmentUtil.

We first will create an enum called FragmentSlideAnimation to contain our entrance and exit animations. We then add a function to return the chosen animations.

public enum FragmentSlideAnimation {fromtop, frombottom, fromright, 
fromleft};
 
/**
 * I get the slide in and out animations based on the FragmentSlideAnimation
 * @param animation
 * @return int array of size 2 of animations resource id's
 */
 public static int[] getSlideAnimations(FragmentSlideAnimation animation){
 int [] animations = new int [2];
 if (animation.equals(FragmentSlideAnimation.fromtop)){
  animations[0] = R.anim.slide_in_from_top;
  animations[1] = R.anim.slide_out_to_top;
 }
 else if (animation.equals(FragmentSlideAnimation.frombottom)){
  animations[0] = R.anim.slide_in_from_bottom;
  animations[1] = R.anim.slide_out_to_bottom;
 }
 else if (animation.equals(FragmentSlideAnimation.fromleft)){
  animations[0] = R.anim.slide_in_from_left;
  animations[1] = R.anim.slide_out_to_left;
 }
 else if (animation.equals(FragmentSlideAnimation.fromright)){
  animations[0] = R.anim.slide_in_from_right;
  animations[1] = R.anim.slide_out_to_right;
     }
 return animations;
}

We also want this class to handle fragments using the latest versions of Android, but also to support older versions using the v4 support jar.

This means that for each method we need to have a similar method for the v4 fragments.

Firstly we create a hide method. The function will take in the fragment to hide and the fragment manager or the fragment transaction. We can use overloading to create two methods with the same name but differing method signatures.

/**
 * I hide a fragment
 * @param frag
 * @param fragmentManager
 */
 public static void hideFragment(android.app.Fragment frag, 
         android.app.FragmentManager fragmentManager){
 android.app.FragmentTransaction ft = fragmentManager.beginTransaction();
 hideFragment(frag, ft);
 }
 
/**
 * I hide a fragment
 * @param frag
 * @param fragmentTransaction
 */
public static void hideFragment(android.app.Fragment frag, 
       android.app.FragmentTransaction fragmentTransaction){
      if (android.os.Build.VERSION.SDK_INT 
             >= android.os.Build.VERSION_CODES.HONEYCOMB){
   if (frag != null){
              fragmentTransaction.hide(frag);
              fragmentTransaction.commit();
 }
     }
 }

So next we want show the fragment. We are going to show all fragments on the content. We have a few options when showing a fragment.
  • To use a entrance/exit animation
  • To add to the back stack
  • To set a tag for the fragment
So, we simply use overloading to present methods with these options and without

/**
 * I show a fragment on content
 * @param fragmentManager
 * @param frag
 * @param backStackArg
 */
public static void showFragmentOnContent
  (android.app.FragmentManager fragmentManager, android.app.Fragment frag, 
  String backStackArg){
 showFragmentOnContent(fragmentManager, frag, backStackArg, null);
  }
 
/**
 * I show a fragment on content
 * @param fragmentManager
 * @param frag
 * @param backStackArg
 * @param animation
 */
public static void showFragmentOnContent
 (android.app.FragmentManager fragmentManager, 
  android.app.Fragment frag, String backStackArg,
  FragmentSlideAnimation animation){ 
        if (android.os.Build.VERSION.SDK_INT 
          >= android.os.Build.VERSION_CODES.HONEYCOMB){
 android.app.FragmentTransaction ft = fragmentManager.beginTransaction();
 showFragmentOnContent(ft, frag, backStackArg, animation);
 }
 }
 
 
/**
 * I show a fragment on content
 * @param fragmentTransaction
 * @param frag
 * @param backStackArg
 * @param animation
 */
public static void showFragmentOnContent
 (android.app.FragmentTransaction fragmentTransaction, 
  android.app.Fragment frag, String backStackArg, 
  FragmentSlideAnimation animation){
 if (android.os.Build.VERSION.SDK_INT 
         >= android.os.Build.VERSION_CODES.HONEYCOMB){
  fragmentTransaction.add(android.R.id.content, frag);
             if (backStackArg != null)
                    fragmentTransaction.addToBackStack(backStackArg);
             if (animation != null){
          int [] animations = getSlideAnimations(animation);
          fragmentTransaction.setCustomAnimations(animations[0], 
                              animations[1], animations[0], animations[1]);
             }
  fragmentTransaction.show(frag);
  fragmentTransaction.commit();
 }
 }

If you notice, we didn't get the option for adding a tag. Well we have to do this with extra methods as Android uses overloading on the fragmentTransaction.add method to achieve this.
/**
 * I show a fragment on content with a tag
 * @param fragmentManager
 * @param frag
 * @param tag
 */
public static void showFragmentOnContentWithTag
(android.app.FragmentManager fragmentManager, 
  android.app.Fragment frag, String tag){
    showFragmentOnContentWithTag(fragmentManager, frag, null, tag);
}
 
/**
 * I show a fragment on content with a tag
 * @param fragmentManager
 * @param frag
 * @param backStackArg
 * @param tag
 */
public static void showFragmentOnContentWithTag
   (android.app.FragmentManager fragmentManager,
    android.app.Fragment frag, String backStackArg, 
    String tag){
 android.app.FragmentTransaction ft = fragmentManager.beginTransaction();
     showFragmentOnContentWithTag(ft, frag, backStackArg, tag);
} 
/**
 * I show a fragment on content with a tag
 * @param fragmentTransaction
 * @param frag
 * @param tag
 */
public static void showFragmentOnContentWithTag
(android.app.FragmentTransaction fragmentTransaction, 
   android.app.Fragment frag, String tag){
 showFragmentOnContentWithTag(fragmentTransaction, frag, null, tag);
}
 
/**
 * I show a fragment on content with a tag
 * @param fragmentTransaction
 * @param frag
 * @param backStackArg
 * @param tag
 */
public static void showFragmentOnContentWithTag
(android.app.FragmentTransaction fragmentTransaction, 
   android.app.Fragment frag, String backStackArg, String tag){
 showFragmentOnContentWithTag(fragmentTransaction, frag, 
         backStackArg, tag, null);
}
 
/**
 * I show a fragment on content with a tag
 * @param fragmentTransaction
 * @param frag
 * @param backStackArg
 * @param tag
 * @param animation
 */
public static void showFragmentOnContentWithTag
  (android.app.FragmentTransaction fragmentTransaction, 
  android.app.Fragment frag, String backStackArg, 
  String tag, FragmentSlideAnimation animation){ 
 
    fragmentTransaction.add(android.R.id.content, frag, tag);
    if (backStackArg != null)fragmentTransaction.addToBackStack(backStackArg);
    if (animation != null){
     int [] animations = getSlideAnimations(animation);
     fragmentTransaction.setCustomAnimations(animations[0], 
                 animations[1], animations[0], animations[1]);
    }
    fragmentTransaction.show(frag);
    fragmentTransaction.commit();
}


And that's it. We need to repeat this for v4 fragments and we're done. We can then extend the utility to handle showing fragments on differing layouts instead of just on the content.

You can find the code for this on my github.



No comments:

Post a Comment