Home | History | Annotate | Download | only in multiscreen
      1 page.title=Implementing Adaptative UI Flows
      2 parent.title=Designing for Multiple Screens
      3 parent.link=index.html
      4 
      5 trainingnavtop=true
      6 previous.title=Supporting Different Screen Densities
      7 previous.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
     29 Handsets</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
     45 flow may be different. For example, if your application is in the dual-pane
     46 mode, clicking on an item on the left pane will simply display the content on
     47 the right pane; if it is in single-pane mode, the content should be displayed
     48 on 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
     54 the first things you will probably have to do is determine what layout the user is currently
     55 viewing. For example, you might want to know whether the user is in "single
     56 pane" mode or "dual pane" mode. You can do that by querying if a given view
     57 exists and is visible:</p>
     58 
     59 <pre class="prettyprint">
     60 public 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,
     76 which 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
     79 components is to check whether they are available before performing an operation on
     80 them. For example, in the News Reader sample app, there is a button that opens a
     81 menu, but that button only exists when running on versions older than Android 3.0 (because it's
     82 function is taken over by the {@link android.app.ActionBar} on API level 11+). So, to add the event
     83 listener for this button, you can do:</p>
     84 
     85 <pre class="prettyprint">
     86 Button catButton = (Button) findViewById(R.id.categorybutton);
     87 OnClickListener listener = /* create your listener here */;
     88 if (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.
     97 For example, in the News Reader sample, clicking on a headline from the
     98 headlines list opens the article in the right hand-side pane if the UI
     99 is in dual pane mode, but will launch a separate activity if the UI is in
    100 single-pane mode:</p>
    101 
    102 <pre>
    103 &#64;Override
    104 public 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
    120 with tabs for navigation, whereas if the app is in single-pane mode, it should set
    121 up navigation with a spinner widget. So your code should also check which case is
    122 appropriate:</p>
    123 
    124 <pre>
    125 final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" };
    126 
    127 public 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
    153 your interface that's implemented as a pane on some screen configurations and
    154 as a separate activity on other configurations. For example, in the News Reader
    155 sample, the news article text is presented in the right side pane on
    156 large 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
    159 same {@link android.app.Fragment} subclass in several activities.  For example,
    160 <code>ArticleFragment</code>
    161 is 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>
    169 ArticleFragment frag = new ArticleFragment();
    170 getSupportFragmentManager().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
    174 layout, but in this case an XML layout is unnecessary work because the article fragment
    175 is the only component of this activity.</p>
    176 
    177 <p>One very important point to keep in mind when designing your fragments is
    178 to not create a strong coupling to a specific activity. You can usually do that
    179 by defining an interface that abstracts all the ways in which the fragment
    180 needs to interact with its host activity, and then the host activity
    181 implements that interface:</p>
    182 
    183 <p>For example, the News Reader app's <code>HeadlinesFragment</code> does precisely that:</p>
    184 
    185 <pre>
    186 public 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
    203 activity (as opposed to notifying a specific hard-coded activity):</p>
    204 
    205 <pre>
    206 public 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
    220 href="{@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,
    226 you have to keep in mind that it may be necessary to react to certain
    227 configuration changes (such as a rotation change) in order to keep your
    228 interface consistent.</p>
    229 
    230 <p>For example, on a typical 7" tablet running Android 3.0 or higher, the News Reader sample uses a
    231 separate activity to display the news article when running in portrait mode,
    232 but 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
    235 onscreen, you need to detect that the orientation changed to landscape and
    236 react appropriately by ending the activity and return to the main activity so the content can
    237 display in the two-pane layout:</p>
    238 
    239 <pre>
    240 public 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