Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do organize an app using fragments?

I am currently re-coding most of the back end of my android app in order to follow the design guidelines more closely. Currently I am using all activities and zero fragments. I am trying to switch to fragments in order to use the slide out navigation draw and eventually some sliding tabs.

For navigation right now I have this drop down menu which when an item is clicked launches a new activity:

enter image description here

The "Your Statistics" activity is kind of like the home page, where the user will enter the app too. I also want the user to be able to get back to that "page" from anywhere in the app.

My activity that I plan to run the draw from I have a draw layout called fragment_main:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <FrameLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </FrameLayout>

    <ListView
        android:id="@+id/drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#FFF"
        android:choiceMode="singleChoice"/>

</android.support.v4.widget.DrawerLayout>

and my activity which loads the drawer layout is:

public class MainDraw extends FragmentActivity {
    final String[] data ={"one","two","three"};
    final String[] fragments ={
            "com.beerportfolio.beerportfoliopro.FragmentOne",
            "com.beerportfolio.beerportfoliopro.FragmentTwo",
            "com.beerportfolio.beerportfoliopro.FragmentThree"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_main);

        //todo: load statistics

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActionBar().getThemedContext(), android.R.layout.simple_list_item_1, data);

        final DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
        final ListView navList = (ListView) findViewById(R.id.drawer);
        navList.setAdapter(adapter);
        navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){

            @Override
            public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
                drawer.setDrawerListener( new DrawerLayout.SimpleDrawerListener(){
                    @Override
                    public void onDrawerClosed(View drawerView){
                        super.onDrawerClosed(drawerView);
                        FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
                        tx.replace(R.id.main, Fragment.instantiate(MainDraw.this, fragments[pos]));
                        tx.commit();
                    }
                });
                drawer.closeDrawer(navList);
            }
        });
        FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
        tx.replace(R.id.main,Fragment.instantiate(MainDraw.this, fragments[0]));
        tx.commit();
    }

}

IN my //todo: comment I should load my first "home" fragment there which is my statistics "page" ? And then all the other fragments will be transitioned in and out based on the draw clicks?

Thanks for your help in advance, I want to make sure I am doing this right, I used to code just to get things working which is why I am now re doing a huge chunk of my code. Please share any other fragment tips to that I might need!

like image 885
Mike Avatar asked Nov 02 '22 08:11

Mike


1 Answers

First of all read the well written documentation, it answers to your doubts.

I would share my personal pattern to convert existing Activity to Fragment

  • Create your on abstract Fragment class from which derive all drawer fragments, this can help to group common attributes
  • Use a method like selectItem() on docs, it helps to explicit do a call at first run (showing the "home" fragment) and then from onItemClick
  • move inflating XML layout from Activity.onCreate() code to Fragment.onCreateView() (ie setContentView to inflater.inflate(R.layout.my_layout, container, false), in many cases you can copy all code from onCreate() to onCreateView
  • move initialization code from Activity.onCreate() to Fragment.onActivityCreated(), this is very useful when both Activity (including fragment) and the direct Fragment exist, for example if your app exposes a "Share with" action you continue to have the Activity that inside the XML includes a <fragment/> and the fragment can be created from the drawer, too
  • if you need to communicate from Activity to Fragment and viceversa I suggest to create an interface and store it inside the 'onAttach()' (see google example)
  • Action bar items must be hidden when drawer is open, again take a look at example used in doc, here is very useful the interface to communicate from activity to fragment, the main activity can tell if drawer is open and the fragment can call the interface

    public interface FragmentActivityStatus {
        public boolean isDrawerOpen();
    }
    

The activity

    public class MainActivity extends Activity implements FragmentActivityStatus {
        @Override
        public boolean isDrawerOpen() {
            return drawerLayout.isDrawerOpen(drawerList);
        }
    }

The fragment

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        fragmentActivityStatus = (FragmentActivityStatus)activity;
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        boolean isMenuVisible = !fragmentActivityStatus.isDrawerOpen();
        menu.findItem(R.id.my_menu).setVisible(isMenuVisible);
        super.onPrepareOptionsMenu(menu);
    }

Not related to fragment, in your code you declare class names as string, consider to create a Class array if you refactor packages the code continue to work, then you can call the Class.getName() to obtain the string to pass to Fragment.instantiate()

   final Class<?>[] fragments = {
        FragmentOne.class,
        FragmentTwo.class,
        FragmentThree.class};

Then

    FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
    tx.replace(R.id.main, Fragment.instantiate(MainDraw.this,
         fragments[pos].getName()));
    tx.commit();
like image 128
dafi Avatar answered Nov 14 '22 05:11

dafi