A master/detail flow is an interface design concept whereby a list of items (referred to as the master list) is displayed to the user.
WHAT IS A MASTER DETAIL LAYOUT:
A master-detail layout is a responsive layout for tablets and phones, that allows users to view a list of items (the master view), and drill down into each item for more details (the detail view).
Or
A master/detail flow is an interface design concept whereby a list of items (referred to as the master list) is displayed to the user. On selecting an item from the list, additional information relating to that item is then presented to the user within a details panel.
On small devices (i.e. phones), the control should behave similarly to a navigation control, where the user only sees one screen at a time and has the ability to navigate forwards backwards between the master and detail views. On larger devices (i.e. tablets), the control should be a split-screen view so the user sees the master view down the left edge of the screen with the detail view filling the remaining space.
But here I am showing, how to make a master-detail view for small devices. The following screenshot shows the view on mobile-phone:
Step 1- Create a first Basic Activity
The first Basic Activity, ItemListActivity, is the starting point for loading fragments for the screen.
ItemListActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package example.masterdetails; import android.app.Activity; import android.os.Bundle; public class ItemListActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_item_twopane); if (findViewById(R.id.item_detail_container) != null) { ((ItemListFragment) getFragmentManager() .findFragmentById(R.id.item_list)) .setActivateOnItemClick(true); } } } |
Step 2: Defining the Screen Layout
We need one layout that will display both the list fragment and the detail fragment.
activity_item_twopane.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<LinearLayout 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:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:baselineAligned="false" android:divider="?android:attr/dividerHorizontal" android:orientation="horizontal" android:showDividers="middle" tools:context="example.masterdetails.ItemListActivity" > <fragment android:id="@+id/item_list" android:name="example.masterdetails.ItemListFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" tools:layout="@android:layout/list_content" /> <FrameLayout android:id="@+id/item_detail_container" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout> |
Layouts can use a <fragment> tag to indicate a specific fragment to load. However, this only works for fixed fragments. For dynamic fragments, such as the detail fragment that will display different information based on what was chosen, we use a container view to indicate where the fragment should go in the layout. The most common container to use is a <FrameLayout>, as it only contains one child element and can then be positioned as a block anywhere in the containing layout.
FrameLayout is one of the useful layout provided by android system, which allows User Interface widgets to be overlapped with each other.
Step 3: Defining the List Fragment
The list fragment handles displaying our array of things. The list needs to contain all of the items from the data source and it also needs to handle taps on specific items to show the details. However, since a fragment isn’t an activity, it should pass up events that might potentially change the screen to the controlling Activity class.
A tap that occurs in dual pane mode will load a fragment in the other pane on the same screen.
Note: Most commonly, this communication is managed through an interface class defined in the fragment.
Here is the entire code for the ItemListFragment class, including a safe callback mechanism:
ItemListFragment.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
package example.masterdetails; import android.app.Activity; import android.app.ListFragment; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import example.masterdetails.dummy.DummyContent; public class ItemListFragment extends ListFragment { private static final String STATE_ACTIVATED_POSITION = "activated_position"; /** * The fragment's current callback object, which is notified of list item * clicks. */ private Callbacks mCallbacks = sDummyCallbacks; /** * The current activated item position. Only used on tablets. */ private int mActivatedPosition = ListView.INVALID_POSITION; public interface Callbacks { public void onItemSelected(String id); } /** * A dummy implementation of the {@link Callbacks} interface that does * nothing. Used only when this fragment is not attached to an activity. */ private static Callbacks sDummyCallbacks = new Callbacks() { @Override public void onItemSelected(String id) { } }; /** * Mandatory empty constructor for the fragment manager to instantiate the * fragment (e.g. upon screen orientation changes). */ public ItemListFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // TODO: replace with a real list adapter. setListAdapter(new ArrayAdapter<DummyContent.DummyItem>( getActivity(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, DummyContent.ITEMS)); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Restore the previously serialized activated item position. if (savedInstanceState != null && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) { setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION)); } } @Override public void onStart() { super.onStart(); //set first item activated by default onListItemClick(getListView(), getView(), 0, 0); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // Activities containing this fragment must implement its callbacks. if (!(activity instanceof Callbacks)) { throw new IllegalStateException("Activity must implement fragment's callbacks."); } mCallbacks = (Callbacks) activity; } @Override public void onDetach() { super.onDetach(); // Reset the active callbacks interface to the dummy implementation. mCallbacks = sDummyCallbacks; } @Override public void onListItemClick(ListView listView, View view, int position, long id) { super.onListItemClick(listView, view, position, id); // Notify the active callbacks interface (the activity, if the // fragment is attached to one) that an item has been selected. mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mActivatedPosition != ListView.INVALID_POSITION) { // Serialize and persist the activated item position. outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition); } } /** * Turns on activate-on-click mode. When this mode is on, list items will be * given the 'activated' state when touched. */ public void setActivateOnItemClick(boolean activateOnItemClick) { // When setting CHOICE_MODE_SINGLE, ListView will automatically // give items the 'activated' state when touched. getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE); } private void setActivatedPosition(int position) { if (position == ListView.INVALID_POSITION) { getListView().setItemChecked(mActivatedPosition, false); } else { getListView().setItemChecked(position, true); } mActivatedPosition = position; } } |
Now we need to return to the controlling Activity(means ItemListActivity.java) class so that it candle handle the callback.
Step 4: Updating the first Basic Activity
Add the callback support to the first Activity class, ItemListActivity, by having it implement the ItemListFragment. Callbacks interface created earlier. Then implement the onItemSelected() method. Here is the fully updated class:
ItemListActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package example.masterdetails; import android.app.Activity; import android.os.Bundle; public class ItemListActivity extends Activity implements ItemListFragment.Callbacks { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_item_twopane); if (findViewById(R.id.item_detail_container) != null) { ((ItemListFragment) getFragmentManager().findFragmentById( R.id.item_list)).setActivateOnItemClick(true); } } @Override public void onItemSelected(String id) { Bundle arguments = new Bundle(); arguments.putString(ItemDetailFragment.ARG_ITEM_ID, id); ItemDetailFragment fragment = new ItemDetailFragment(); fragment.setArguments(arguments); getFragmentManager().beginTransaction() .replace(R.id.item_detail_container, fragment).commit(); } } |
Step 5: Create the dummy Activity
The content for the example as it currently stands is defined by the DummyContent class file. Begin, therefore, by selecting the DummyContent.java file and reviewing the code. At the bottom of the file is a declaration for a class named DummyItem which is currently able to store two String objects representing a content string and an ID. DummyItem as follows:
DummyContent.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package example.masterdetails.dummy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class DummyContent { public static List<DummyItem> ITEMS = new ArrayList<DummyItem>(); // Note that the class currently adds three items in the form of strings that read “Item 1”, “Item 2” and “Item 3”: public static Map<String, DummyItem> ITEM_MAP = new HashMap<String, DummyItem>(); static { // Add 3 sample items. addItem(new DummyItem("1", "Item 1")); addItem(new DummyItem("2", "Item 2")); addItem(new DummyItem("3", "Item 3")); } private static void addItem(DummyItem item) { ITEMS.add(item); ITEM_MAP.put(item.id, item); } public static class DummyItem { public String id; public String content; public DummyItem(String id, String content) { this.id = id; this.content = content; } @Override public String toString() { return content; } } } |
Step 6: Updating the Android Manifest File
Now we need to add the fragments to the manifest file, right? Actually, no. Unlike Activity classes, Fragments are not registered in the manifest file. So, simply add the activity classes to the manifest file and you’re good to go.
Here’s the sample manifest file:
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="example.masterdetails" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="23" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".ItemListActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ItemDetailActivity" android:label="@string/title_item_detail" android:parentActivityName=".ItemListActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="example.masterdetails.ItemListActivity" /> </activity> </application> </manifest> |
After following above steps, you can run this app.
And can implement master-detail flow on your application.
A master/detail user interface consists of a master list of items which, when selected, displays additional information about that selection within a detail panel.