• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Implementing Adaptative UI Flows
2parent.title=Designing for Multiple Screens
3parent.link=index.html
4
5trainingnavtop=true
6previous.title=Supporting Different Screen Densities
7previous.link=screendensities.html
8
9@jd:body
10
11
12<!-- This is the training bar -->
13<div id="tb-wrapper">
14<div id="tb">
15
16<h2>This lesson teaches you to</h2>
17
18<ol>
19  <li><a href="#TaskDetermineCurLayout">Determine the Current Layout</a></li>
20  <li><a href="#TaskReactToLayout">React According to Current Layout</a></li>
21  <li><a href="#TaskReuseFrag">Reuse Fragments in Other Activities</a></li>
22  <li><a href="#TaskHandleConfigChanges">Handle Screen Configuration Changes</a></li>
23</ol>
24
25<h2>You should also read</h2>
26
27<ul>
28  <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Supporting Tablets and
29Handsets</a></li>
30</ul>
31
32<h2>Try it out</h2>
33
34<div class="download-box">
35<a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Download
36  the sample app</a>
37<p class="filename">NewsReader.zip</p>
38</div>
39
40
41</div>
42</div>
43
44<p>Depending on the layout that your application is currently showing, the UI
45flow may be different. For example, if your application is in the dual-pane
46mode, clicking on an item on the left pane will simply display the content on
47the right pane; if it is in single-pane mode, the content should be displayed
48on its own (in a different activity).</p>
49
50
51<h2 id="TaskDetermineCurLayout">Determine the Current Layout</h2>
52
53<p>Since your implementation of each layout will be a little different, one of
54the first things you will probably have to do is determine what layout the user is currently
55viewing. For example, you might want to know whether the user is in "single
56pane" mode or "dual pane" mode. You can do that by querying if a given view
57exists and is visible:</p>
58
59<pre class="prettyprint">
60public class NewsReaderActivity extends FragmentActivity {
61    boolean mIsDualPane;
62
63    &#64;Override
64    public void onCreate(Bundle savedInstanceState) {
65        super.onCreate(savedInstanceState);
66        setContentView(R.layout.main_layout);
67
68        View articleView = findViewById(R.id.article);
69        mIsDualPane = articleView != null &amp;&amp;
70                        articleView.getVisibility() == View.VISIBLE;
71    }
72}
73</pre>
74
75<p>Notice that this code queries whether the "article" pane is available or not,
76which is much more flexible than hard-coding a query for a specific layout.</p>
77
78<p>Another example of how you can adapt to the existence of different
79components is to check whether they are available before performing an operation on
80them. For example, in the News Reader sample app, there is a button that opens a
81menu, but that button only exists when running on versions older than Android 3.0 (because it's
82function is taken over by the {@link android.app.ActionBar} on API level 11+). So, to add the event
83listener for this button, you can do:</p>
84
85<pre class="prettyprint">
86Button catButton = (Button) findViewById(R.id.categorybutton);
87OnClickListener listener = /* create your listener here */;
88if (catButton != null) {
89    catButton.setOnClickListener(listener);
90}
91</pre>
92
93
94<h2 id="TaskReactToLayout">React According to Current Layout</h2>
95
96<p>Some actions may have a different result depending on the current layout.
97For example, in the News Reader sample, clicking on a headline from the
98headlines list opens the article in the right hand-side pane if the UI
99is in dual pane mode, but will launch a separate activity if the UI is in
100single-pane mode:</p>
101
102<pre>
103&#64;Override
104public void onHeadlineSelected(int index) {
105    mArtIndex = index;
106    if (mIsDualPane) {
107        /* display article on the right pane */
108        mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
109    } else {
110        /* start a separate activity */
111        Intent intent = new Intent(this, ArticleActivity.class);
112        intent.putExtra("catIndex", mCatIndex);
113        intent.putExtra("artIndex", index);
114        startActivity(intent);
115    }
116}
117</pre>
118
119<p>Likewise, if the app is in dual-pane mode, it should set up the action bar
120with tabs for navigation, whereas if the app is in single-pane mode, it should set
121up navigation with a spinner widget. So your code should also check which case is
122appropriate:</p>
123
124<pre>
125final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" };
126
127public void onCreate(Bundle savedInstanceState) {
128    ....
129    if (mIsDualPane) {
130        /* use tabs for navigation */
131        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
132        int i;
133        for (i = 0; i &lt; CATEGORIES.length; i++) {
134            actionBar.addTab(actionBar.newTab().setText(
135                CATEGORIES[i]).setTabListener(handler));
136        }
137        actionBar.setSelectedNavigationItem(selTab);
138    }
139    else {
140        /* use list navigation (spinner) */
141        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
142        SpinnerAdapter adap = new ArrayAdapter<String>(this,
143                R.layout.headline_item, CATEGORIES);
144        actionBar.setListNavigationCallbacks(adap, handler);
145    }
146}
147</pre>
148
149
150<h2 id="TaskReuseFrag">Reuse Fragments in Other Activities</h2>
151
152<p>A recurring pattern in designing for multiple screens is having a portion of
153your interface that's implemented as a pane on some screen configurations and
154as a separate activity on other configurations. For example, in the News Reader
155sample, the news article text is presented in the right side pane on
156large screens, but is a separate activity on smaller screens.</p>
157
158<p>In cases like this, you can usually avoid code duplication by reusing the
159same {@link android.app.Fragment} subclass in several activities.  For example,
160<code>ArticleFragment</code>
161is used in the dual-pane layout:</p>
162
163{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all}
164
165<p>And reused (without a layout) in the activity layout for smaller screens
166(<code>ArticleActivity</code>):</p>
167
168<pre>
169ArticleFragment frag = new ArticleFragment();
170getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();
171</pre>
172
173<p>Naturally, this has the same effect as declaring the fragment in an XML
174layout, but in this case an XML layout is unnecessary work because the article fragment
175is the only component of this activity.</p>
176
177<p>One very important point to keep in mind when designing your fragments is
178to not create a strong coupling to a specific activity. You can usually do that
179by defining an interface that abstracts all the ways in which the fragment
180needs to interact with its host activity, and then the host activity
181implements that interface:</p>
182
183<p>For example, the News Reader app's <code>HeadlinesFragment</code> does precisely that:</p>
184
185<pre>
186public class HeadlinesFragment extends ListFragment {
187    ...
188    OnHeadlineSelectedListener mHeadlineSelectedListener = null;
189
190    /* Must be implemented by host activity */
191    public interface OnHeadlineSelectedListener {
192        public void onHeadlineSelected(int index);
193    }
194    ...
195
196    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
197        mHeadlineSelectedListener = listener;
198    }
199}
200</pre>
201
202<p>Then, when the user selects a headline, the fragment notifies the listener specified by the host
203activity (as opposed to notifying a specific hard-coded activity):</p>
204
205<pre>
206public class HeadlinesFragment extends ListFragment {
207    ...
208    &#64;Override
209    public void onItemClick(AdapterView&lt;?&gt; parent,
210                            View view, int position, long id) {
211        if (null != mHeadlineSelectedListener) {
212            mHeadlineSelectedListener.onHeadlineSelected(position);
213        }
214    }
215    ...
216}
217</pre>
218
219<p>This technique is discussed further in the guide to <a
220href="{@docRoot}guide/practices/tablets-and-handsets.html">Supporting Tablets and Handsets</a>.</p>
221
222
223<h2 id="TaskHandleConfigChanges">Handle Screen Configuration Changes</h2>
224
225<p>If you are using separate activities to implement separate parts of your interface,
226you have to keep in mind that it may be necessary to react to certain
227configuration changes (such as a rotation change) in order to keep your
228interface consistent.</p>
229
230<p>For example, on a typical 7" tablet running Android 3.0 or higher, the News Reader sample uses a
231separate activity to display the news article when running in portrait mode,
232but uses a two-pane layout when in landscape mode.</p>
233
234<p>This means that when the user is in portrait mode and the activity for viewing an article is
235onscreen, you need to detect that the orientation changed to landscape and
236react appropriately by ending the activity and return to the main activity so the content can
237display in the two-pane layout:</p>
238
239<pre>
240public class ArticleActivity extends FragmentActivity {
241    int mCatIndex, mArtIndex;
242
243    &#64;Override
244    protected void onCreate(Bundle savedInstanceState) {
245        super.onCreate(savedInstanceState);
246        mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
247        mArtIndex = getIntent().getExtras().getInt("artIndex", 0);
248
249        // If should be in two-pane mode, finish to return to main activity
250        if (getResources().getBoolean(R.bool.has_two_panes)) {
251            finish();
252            return;
253        }
254        ...
255}
256</pre>
257
258
259