One way to think of a fragment is as a sub-activity. And in fact, the semantics of a fragment are a lot like an activity. A fragment can have a view hierarchy associated with it, and it has a life cycle much like an activity’s life cycle.

Fragments can even respond to the Back button like activities do. If you were thinking, “If only I could put multiple activities together on a tablet’s screen at the same time,” then you’re on the right track. But because it would be too messy to have more than one activity of an application active at the same time on a tablet screen, fragments were created to implement basically that thought. This means fragments are contained within an activity. Fragments can only exist within the context of an activity; you can’t use a fragment without an activity.

Fragments can coexist with other elements of an activity, which means you do not need to convert the entire user interface of your activity to use fragments. You can create an activity’s layout as before and only use a fragment for one piece of the user interface. Fragments are not like activities, however, when it comes to saving state and restoring it later. The fragments framework provides several features to make saving and restoring fragments much simpler than the work you need to do on activities

fragments
Fragments used for a tablet UI and for a smartphone UI

 

 

fragments_lifecycle
Fragment’s Life Cycle

Example 1: Shakespere Instrucment

android_fragment_landscape android_fragment_portait

/manifests/AndroidManifest.xml

Config DetailActivity action and category. This allows you to start the details activity from code but will not show the details activity as an app in the App list.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.trankyphat.app.fragmens_shakespeareinstrucment">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!--
        Take a moment to view this application’s manifest file. In it you find the main activity with
        a category of LAUNCHER so that it will appear in the device’s list of apps. Then you have the
        separate DetailsActivity with a category of DEFAULT. This allows you to start the details
        activity from code but will not show the details activity as an app in the App list.
        -->
        <activity android:name=".DetailsActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>

</manifest>

/res/layout/activity_main.xml

activity_main.xml for all view (in this context only portait)

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is res/layout/main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--
    The other thing to
    keep in mind is that the <fragment> tag is just a placeholder in this layout. You should not
    put child tags under <fragment> in a layout XML file

    The fragment tag’s class attribute specifies your extended class for the titles of your
application. That is, you must extend one of the Android Fragment classes to implement
your logic, and the <fragment> tag must know the name of your extended class.
    -->
    <fragment class="com.trankyphat.app.fragmens_shakespeareinstrucment.TitlesFragment"
        android:id="@+id/titles"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

/res/layout-land/activity_main.xml

activity_main.xml for landscape view

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is res/layout-land/main.xml -->
<!--landscape mode, orientation horizontal-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--
    The other thing to
    keep in mind is that the <fragment> tag is just a placeholder in this layout. You should not
    put child tags under <fragment> in a layout XML file

    The fragment tag’s class attribute specifies your extended class for the titles of your
application. That is, you must extend one of the Android Fragment classes to implement
your logic, and the <fragment> tag must know the name of your extended class.
    -->
    <fragment class="com.trankyphat.app.fragmens_shakespeareinstrucment.TitlesFragment"
        android:id="@+id/titles" android:layout_weight="1"
        android:layout_width="0px"
        android:layout_height="match_parent" />

    <FrameLayout
        android:id="@+id/details" android:layout_weight="2"
        android:layout_width="0px"
        android:layout_height="match_parent" />
</LinearLayout>

/res/layout/detail.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is res/layout/details.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ScrollView android:id="@+id/scroller"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:id="@+id/text1"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </ScrollView>
</LinearLayout>

MainActivity.java

package com.trankyphat.app.fragmens_shakespeareinstrucment;

import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.res.Configuration;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    public static final String TAG = "MyMessage";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public boolean isMultiPane() {
        return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
    }

    /**
     * Helper function to show the details of a selected item, either by
     * displaying a fragment in-place in the current UI, or starting a
     * whole new activity in which it is displayed.
     */
    public void showDetails(int index) {
        Log.v(TAG, "in MainActivity showDetails(" + index + ")");

        if (isMultiPane()) {
            // Check what fragment is shown, replace if needed.
            /*to find out if there’s a details fragment in the layout, you can ask
            the fragment manager using findFragmentById(). This will return null if the frame layout is
            empty or will give you the current details fragment*/
            DetailsFragment details = (DetailsFragment) getFragmentManager().findFragmentById(R.id.details);
            if (details == null || details.getShownIndex() != index) {
                // Make new fragment to show this selection.
                details = DetailsFragment.newInstance(index);

                // Execute a transaction, replacing any existing
                // fragment inside the frame with the new one.
                Log.v(TAG, "about to run FragmentTransaction...");


                /* FragmentManager
                * The FragmentManager is a component that takes care of the fragments belonging to an
                    activity. This includes fragments on the back stack and fragments that may just be hanging
                    around

                    Obviously, you can’t use the getFragmentManager() method on a fragment that has not been
                    attached to an activity yet.
                * */

                /*To ensure that
                each fragment properly participates in the rollback, a FragmentTransaction is created and
                managed to perform that coordination*/
                FragmentTransaction ft = getFragmentManager().beginTransaction();

                //Custom animation
                //ft.setCustomAnimations(R.animator.fragment_open_enter,
                		//R.animator.fragment_open_exit);
                ft.setCustomAnimations(R.animator.bounce_in_down,
                        R.animator.slide_out_right);

                //which will cause the new fragment to fade in as the old fragment fades out.
                //ft.setCustomAnimations(R.animator.fade_in,
                  //      R.animator.fade_out);

                //ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                ft.replace(R.id.details, details);

                /*pressing the Back button will pop
                the current activity off the stack and return the user to whatever was underneath. If you
                choose to take advantage of the back stack for fragments, you will want to uncomment*/
                //ft.addToBackStack(TAG);
                ft.commit();
            }

        } else {
            // Otherwise we need to launch a new activity to display
            // the dialog fragment with selected text.
            Intent intent = new Intent();
            intent.setClass(this, DetailsActivity.class);

            intent.putExtra("index", index);
            startActivity(intent);
        }
    }
}

DetailsFragment.java

Fragment

package com.trankyphat.app.fragmens_shakespeareinstrucment;

import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class DetailsFragment extends Fragment {
    private int mIndex = 0;

    /*
    *You might wonder why you didn’t simply set the mIndex value in newInstance(). The reason
    is that Android will, behind the scenes, re-create your fragment using the default constructor.
    Then it sets the arguments bundle to what it was before. Android won’t use your newInstance()
    method, so the only reliable way to ensure that mIndex is set is to read the value from the
    arguments bundle and set it in onCreate(). The convenience method getShownIndex()
    retrieves the value of that index. Now the only method left to describe in the details fragment is
    onCreateView(). And this is very simple, too
    * */
    public static DetailsFragment newInstance(int index) {
        Log.v(MainActivity.TAG, "in DetailsFragment newInstance(" +
                index + ")");
        DetailsFragment df = new DetailsFragment();
        // Supply index input as an argument.
        Bundle args = new Bundle();
        args.putInt("index", index);
        df.setArguments(args);
        return df;
    }

    /*
    * This is why
        it is so important not to do anything fancy in the newInstance() method: when the fragment
        gets re-created, it won’t do it through newInstance().
    * */
    public static DetailsFragment newInstance(Bundle bundle) {
        int index = bundle.getInt("index", 0);
        return newInstance(index);
    }
    @Override
    public void onCreate(Bundle myBundle) {
        Log.v(MainActivity.TAG, "in DetailsFragment onCreate. Bundle contains:");
        if(myBundle != null) {
            for(String key : myBundle.keySet()) {
                Log.v(MainActivity.TAG, " " + key);
            }
        }
        else {
            Log.v(MainActivity.TAG, " myBundle is null");
        }
        super.onCreate(myBundle);
        mIndex = getArguments().getInt("index", 0);
    }
    public int getShownIndex() {
        return mIndex;
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.v(MainActivity.TAG,  "in DetailsFragment onCreateView. container = " +  container);
        // Don't tie this fragment to anything through the inflater.
        // Android takes care of attaching fragments for us. The
        // container is only passed in so you can know about the
        // container where this View hierarchy is going to go.
        View v = inflater.inflate(R.layout.details, container, false);
        TextView text1 = (TextView) v.findViewById(R.id.text1);
        text1.setText(Shakespeare.DIALOGUE[ mIndex ] );
        return v;
    }
}

DetailsActivity.java

Use only for Portait view

package com.trankyphat.app.fragmens_shakespeareinstrucment;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;


//Invoking a Separate Activity When Needed
/*You may wonder why you would
ever launch this activity if you’re in landscape mode, and the answer is, you wouldn’t.
However, once this activity has been started in portrait mode, if the user rotates the device
to landscape mode, this details activity will get restarted due to the configuration change. So
now the activity is starting up, and it’s in landscape mode. At that moment, it makes sense to
finish this activity and let the MainActivity take over and do all the work.*/

/*
* Must be config in Manifests file
* */
public class DetailsActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.v(MainActivity.TAG, "in DetailsActivity onCreate");
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, it means
            // that our MainActivity is being shown with both
            // the titles and the text, so this activity is
            // no longer needed. Bail out and let the MainActivity
            // do all the work.
            finish();
            /*The other choice for the user is to rotate the device to get back to landscape mode. Then your details
            activity will call finish() and go away, revealing the also-rotated main activity underneath*/
            return;
        }

       if(getIntent() != null) {
            // This is another way to instantiate a details
            // fragment.
            DetailsFragment details = DetailsFragment.newInstance(getIntent().getExtras());

            /*Another interesting aspect about this details activity is that you never set the root content
             view using setContentView().
             So how does the user interface get created? If you look
            carefully at the add() method call on the fragment transaction, you will see that the view
            container to which you add the fragment is specified as the resource android.R.id.content.
            This is the top-level view container for an activity
             */

           /*
           * android.R.id.content.
            This is the top-level view container for an activity, and therefore when you attach your
            fragment view hierarchy to this container, your fragment view hierarchy becomes the only
            view hierarchy for the activity. You
           * */

           /*
           * From the user’s point of view, they are now looking at just the details fragment view, which is the
            text from the Shakespearean play. If the user wants to select a different title, they press the Back
            button, which pops this activity to reveal your main activity (with the titles fragment only). The
            other choice for the user is to rotate the device to get back to landscape mode. Then your details
            activity will call finish() and go away, revealing the also-rotated main activity underneath.
           * */
            getFragmentManager().beginTransaction()
                    .add(android.R.id.content, details)
                    .commit();
        }
    }
}

TilesFragment.java

List fragment

package com.trankyphat.app.fragmens_shakespeareinstrucment;

import android.app.Activity;
import android.app.ListFragment;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

//Extend List to show list item
public class TitlesFragment extends ListFragment {

    private MainActivity myActivity = null;
    int mCurCheckPosition = 0;

    @Override
    public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
        Log.v(MainActivity.TAG,
                "in TitlesFragment onInflate. AttributeSet contains:");
        for(int i=0; i<attrs.getAttributeCount(); i++) {
            Log.v(MainActivity.TAG, "    " + attrs.getAttributeName(i) +
                    " = " + attrs.getAttributeValue(i));
        }
        super.onInflate(context, attrs, savedInstanceState);
    }

    @Override
    public void onAttach(Context context) {
        Log.v(MainActivity.TAG, "in TitlesFragment onAttach; activity is: " + myActivity);
        super.onAttach(context);
        //this fragment will attch to MainActivity
        this.myActivity = (MainActivity)context;
    }

    @Override
    public void onCreate(Bundle icicle) {
        Log.v(MainActivity.TAG, "in TitlesFragment onCreate. Bundle contains:");
        if(icicle != null) {
            for(String key : icicle.keySet()) {
                Log.v(MainActivity.TAG, "    " + key);
            }
        }
        else {
            Log.v(MainActivity.TAG, "    myBundle is null");
        }
        super.onCreate(icicle);
        if (icicle != null) {
            // Restore last state for checked position.
            mCurCheckPosition = icicle.getInt("curChoice", 0);
        }
    }

    @Override
    public View onCreateView(LayoutInflater myInflater, ViewGroup container, Bundle icicle) {
        Log.v(MainActivity.TAG, "in TitlesFragment onCreateView. container is " + container);
        return super.onCreateView(myInflater, container, icicle);
    }

    @Override
    public void onActivityCreated(Bundle icicle) {
        Log.v(MainActivity.TAG, "in TitlesFragment onActivityCreated. icicle contains:");
        if(icicle != null) {
            for(String key : icicle.keySet()) {
                Log.v(MainActivity.TAG, "    " + key);
            }
        }
        else {
            Log.v(MainActivity.TAG, "    icicle is null");
        }
        super.onActivityCreated(icicle);

        // Populate list with our static array of titles.
        /*Because ListFragment manages the ListView, do not attach the
        adapter to the ListView directly. You must use the ListFragment’s setListAdapter() method
        instead. The activity’s view hierarchy is now set up, so you’re safe going back into the
        activity to do the showDetails() call*/
        setListAdapter(new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_1,
                Shakespeare.TITLES));

        //Get ListView from List Fragment
        ListView lv = getListView();
        lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        lv.setSelection(mCurCheckPosition);

        myActivity.showDetails(mCurCheckPosition);
    }

    @Override
    public void onStart() {
        Log.v(MainActivity.TAG, "in TitlesFragment onStart");
        super.onStart();
    }

    @Override
    public void onResume() {
        Log.v(MainActivity.TAG, "in TitlesFragment onResume");
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.v(MainActivity.TAG, "in TitlesFragment onPause");
        super.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle icicle) {
        Log.v(MainActivity.TAG, "in TitlesFragment onSaveInstanceState");
        super.onSaveInstanceState(icicle);
        icicle.putInt("curChoice", mCurCheckPosition);
    }

    @Override
    public void onListItemClick(ListView l, View v, int pos, long id) {
        Log.v(MainActivity.TAG, "in TitlesFragment onListItemClick. pos = "
                + pos);
        myActivity.showDetails(pos);
        mCurCheckPosition = pos;
    }

    @Override
    public void onStop() {
        Log.v(MainActivity.TAG, "in TitlesFragment onStop");
        super.onStop();
    }

    @Override
    public void onDestroyView() {
        Log.v(MainActivity.TAG, "in TitlesFragment onDestroyView");
        super.onDestroyView();
    }

    @Override
    public void onDestroy() {
        Log.v(MainActivity.TAG, "in TitlesFragment onDestroy");
        super.onDestroy();
    }

    @Override
    public void onDetach() {
        Log.v(MainActivity.TAG, "in TitlesFragment onDetach");
        super.onDetach();
        myActivity = null;
    }
}

Shakespere.java

Static data for demo

package com.trankyphat.app.fragmens_shakespeareinstrucment;

public class Shakespeare {
    public static String TITLES[] = {
            "Henry IV (1)",
            "Henry V",
            "Henry VIII",
            "Romeo and Juliet",
            "Hamlet",
            "The Merchant of Venice",
            "Othello"
    };
    public static String DIALOGUE[] = {
            "Text 1",
            "Text 2",
            "Text 3",
            "Text 4",
            "Text 5",
            "Text 6",
            "Text 7"
    };
}

That although fragments can be instantiated with a static factory method such as newInstance(), you must always have a default constructor and a way to save initialization values into an initialization arguments bundle.

Using fragment transactions to change what’s displayed to a user, and animating those transitions using cool effects.

References

http://developer.android.com/guide/components/fragments.html: The Android Developer’s Guide page to fragments.
http://developer.android.com/design/patterns/multi-pane-layouts.html: Android design guidelines for multipane layouts.
http://developer.android.com/training/basics/fragments/index.html: Android training page for fragments.

 

Android 6 – Fragments

Category: Uncategorized
0
2230 views

Join the discussion

Your email address will not be published. Required fields are marked *