AndroidU lesson on designing for multiple screens.

This change adds the text for the AndroidU lesson "Designing for Multiple
Screens", which shows how to write applications that adapt properly
to screen size, density and orientation for maximum compatibility.
Update makefile and resources-data.js for rendering source in HTML
Add ZIP file for sample

Change-Id: I671bb3063d5bf02681bc547ffe5262a9df22037a
This commit is contained in:
Scott Main
2011-12-09 17:27:21 -08:00
parent 89c3bc3842
commit 801fda548c
14 changed files with 860 additions and 0 deletions

View File

@@ -432,6 +432,8 @@ web_docs_sample_code_flags := \
resources/samples/MultiResolution "Multiple Resolutions" \
-samplecode $(sample_dir)/NFCDemo \
resources/samples/NFCDemo "NFC Demo" \
-samplecode $(sample_dir)/training/multiscreen/newsreader \
resources/samples/newsreader "News Reader" \
-samplecode $(sample_dir)/NotePad \
resources/samples/NotePad "Note Pad" \
-samplecode $(sample_dir)/SpellChecker/SampleSpellCheckerService \

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -557,6 +557,16 @@ var ANDROID_RESOURCES = [
en: 'A sample application that shows how to use resource directory qualifiers to provide different resources for different screen configurations.'
}
},
{
tags: ['sample', 'new', 'bestpractices'],
path: 'samples/newsreader/index.html',
title: {
en: 'News Reader'
},
description: {
en: 'A sample app demonstrating best practices to support multiple screen sizes and densities.'
}
},
{
tags: ['sample', 'data'],
path: 'samples/NFCDemo/index.html',

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

View File

@@ -0,0 +1,258 @@
page.title=Implementing Adaptative UI Flows
parent.title=Designing for Multiple Screens
parent.link=index.html
trainingnavtop=true
previous.title=Supporting Different Screen Densities
previous.link=screendensities.html
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#TaskDetermineCurLayout">Determine the Current Layout</a></li>
<li><a href="#TaskReactToLayout">React According to Current Layout</a></li>
<li><a href="#TaskReuseFrag">Reuse Fragments in Other Activities</a></li>
<li><a href="#TaskHandleConfigChanges">Handle Screen Configuration Changes</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Supporting Tablets and
Handsets</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Download
the sample app</a>
<p class="filename">NewsReader.zip</p>
</div>
</div>
</div>
<p>Depending on the layout that your application is currently showing, the UI
flow may be different. For example, if your application is in the dual-pane
mode, clicking on an item on the left pane will simply display the content on
the right pane; if it is in single-pane mode, the content should be displayed
on its own (in a different activity).</p>
<h2 id="TaskDetermineCurLayout">Determine the Current Layout</h2>
<p>Since your implementation of each layout will be a little different, one of
the first things you will probably have to do is determine what layout the user is currently
viewing. For example, you might want to know whether the user is in "single
pane" mode or "dual pane" mode. You can do that by querying if a given view
exists and is visible:</p>
<pre class="prettyprint">
public class NewsReaderActivity extends FragmentActivity {
boolean mIsDualPane;
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
View articleView = findViewById(R.id.article);
mIsDualPane = articleView != null &amp;&amp;
articleView.getVisibility() == View.VISIBLE;
}
}
</pre>
<p>Notice that this code queries whether the "article" pane is available or not,
which is much more flexible than hard-coding a query for a specific layout.</p>
<p>Another example of how you can adapt to the existence of different
components is to check whether they are available before performing an operation on
them. For example, in the News Reader sample app, there is a button that opens a
menu, but that button only exists when running on versions older than Android 3.0 (because it's
function is taken over by the {@link android.app.ActionBar} on API level 11+). So, to add the event
listener for this button, you can do:</p>
<pre class="prettyprint">
Button catButton = (Button) findViewById(R.id.categorybutton);
OnClickListener listener = /* create your listener here */;
if (catButton != null) {
catButton.setOnClickListener(listener);
}
</pre>
<h2 id="TaskReactToLayout">React According to Current Layout</h2>
<p>Some actions may have a different result depending on the current layout.
For example, in the News Reader sample, clicking on a headline from the
headlines list opens the article in the right hand-side pane if the UI
is in dual pane mode, but will launch a separate activity if the UI is in
single-pane mode:</p>
<pre>
&#64;Override
public void onHeadlineSelected(int index) {
mArtIndex = index;
if (mIsDualPane) {
/* display article on the right pane */
mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
} else {
/* start a separate activity */
Intent intent = new Intent(this, ArticleActivity.class);
intent.putExtra("catIndex", mCatIndex);
intent.putExtra("artIndex", index);
startActivity(intent);
}
}
</pre>
<p>Likewise, if the app is in dual-pane mode, it should set up the action bar
with tabs for navigation, whereas if the app is in single-pane mode, it should set
up navigation with a spinner widget. So your code should also check which case is
appropriate:</p>
<pre>
final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" };
public void onCreate(Bundle savedInstanceState) {
....
if (mIsDualPane) {
/* use tabs for navigation */
actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
int i;
for (i = 0; i &lt; CATEGORIES.length; i++) {
actionBar.addTab(actionBar.newTab().setText(
CATEGORIES[i]).setTabListener(handler));
}
actionBar.setSelectedNavigationItem(selTab);
}
else {
/* use list navigation (spinner) */
actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
SpinnerAdapter adap = new ArrayAdapter<String>(this,
R.layout.headline_item, CATEGORIES);
actionBar.setListNavigationCallbacks(adap, handler);
}
}
</pre>
<h2 id="TaskReuseFrag">Reuse Fragments in Other Activities</h2>
<p>A recurring pattern in designing for multiple screens is having a portion of
your interface that's implemented as a pane on some screen configurations and
as a separate activity on other configurations. For example, in the News Reader
sample, the news article text is presented in the right side pane on
large screens, but is a separate activity on smaller screens.</p>
<p>In cases like this, you can usually avoid code duplication by reusing the
same {@link android.app.Fragment} subclass in several activities. For example,
<code>ArticleFragment</code>
is used in the dual-pane layout:</p>
{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all}
<p>And reused (without a layout) in the activity layout for smaller screens
(<code>ArticleActivity</code>):</p>
<pre>
ArticleFragment frag = new ArticleFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();
</pre>
<p>Naturally, this has the same effect as declaring the fragment in an XML
layout, but in this case an XML layout is unnecessary work because the article fragment
is the only component of this activity.</p>
<p>One very important point to keep in mind when designing your fragments is
to not create a strong coupling to a specific activity. You can usually do that
by defining an interface that abstracts all the ways in which the fragment
needs to interact with its host activity, and then the host activity
implements that interface:</p>
<p>For example, the News Reader app's <code>HeadlinesFragment</code> does precisely that:</p>
<pre>
public class HeadlinesFragment extends ListFragment {
...
OnHeadlineSelectedListener mHeadlineSelectedListener = null;
/* Must be implemented by host activity */
public interface OnHeadlineSelectedListener {
public void onHeadlineSelected(int index);
}
...
public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
mHeadlineSelectedListener = listener;
}
}
</pre>
<p>Then, when the user selects a headline, the fragment notifies the listener specified by the host
activity (as opposed to notifying a specific hard-coded activity):</p>
<pre>
public class HeadlinesFragment extends ListFragment {
...
&#64;Override
public void onItemClick(AdapterView&lt;?&gt; parent,
View view, int position, long id) {
if (null != mHeadlineSelectedListener) {
mHeadlineSelectedListener.onHeadlineSelected(position);
}
}
...
}
</pre>
<p>This technique is discussed further in the guide to <a
href="{@docRoot}guide/practices/tablets-and-handsets.html">Supporting Tablets and Handsets</a>.</p>
<h2 id="TaskHandleConfigChanges">Handle Screen Configuration Changes</h2>
<p>If you are using separate activities to implement separate parts of your interface,
you have to keep in mind that it may be necessary to react to certain
configuration changes (such as a rotation change) in order to keep your
interface consistent.</p>
<p>For example, on a typical 7" tablet running Android 3.0 or higher, the News Reader sample uses a
separate activity to display the news article when running in portrait mode,
but uses a two-pane layout when in landscape mode.</p>
<p>This means that when the user is in portrait mode and the activity for viewing an article is
onscreen, you need to detect that the orientation changed to landscape and
react appropriately by ending the activity and return to the main activity so the content can
display in the two-pane layout:</p>
<pre>
public class ArticleActivity extends FragmentActivity {
int mCatIndex, mArtIndex;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
mArtIndex = getIntent().getExtras().getInt("artIndex", 0);
// If should be in two-pane mode, finish to return to main activity
if (getResources().getBoolean(R.bool.has_two_panes)) {
finish();
return;
}
...
}
</pre>

View File

@@ -0,0 +1,87 @@
page.title=Designing for Multiple Screens
trainingnavtop=true
startpage=true
next.title=Supporting Different Screen Sizes
next.link=screensizes.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and prerequisites</h2>
<ul>
<li>Android 1.6 or higher (2.1+ for the sample app)</li>
<li>Basic knowledge of <a
href="http://developer.android.com/guide/topics/fundamentals/activities.html">Activities</a> and
<a href="http://developer.android.com/guide/topics/fundamentals/fragments.html">Fragments</a></li>
<li>Experience building an Android <a
href="http://developer.android.com/guide/topics/ui/index.html"> User Interface</a></li>
<li>Several features require the use of the <a
href="{@docRoot}sdk/compatibility-library.html">support library</a></li>
</ul>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Download
the sample app</a>
<p class="filename">NewsReader.zip</p>
</div>
</div>
</div>
<p>Android powers hundreds of device types with several different screen sizes,
ranging from small phones to large TV sets. Therefore, its important
that you design your application to be compatible with all screen sizes so its available to as many
users as possible.</p>
<p>But being compatible with different device types is not enough. Each screen
size offers different possibilities and challenges for user interaction, so in
order to truly satisfy and impress your users, your application must go beyond merely
<em>supporting</em> multiple screens: it must <em>optimize</em> the user
experience for each screen configuration.</p>
<p>This class shows you how to implement a user interface that's
optimized for several screen configurations.</p>
<p>The code in each lesson comes from a sample application that demonstrates best practices in
optimizing for multiple screens. You can download the sample (to the right) and use it as a source
of reusable code for your own application.</p>
<p class="note"><strong>Note:</strong> This class and the associated sample use the <a
href="{@docRoot}sdk/compatibility-library.html">support library</a> in order to use the {@link
android.app.Fragment} APIs on versions lower than Android 3.0. You must download and add the
library to your application in order to use all APIs in this class.</p>
<h2>Lessons</h2>
<dl>
<dt><b><a href="screensizes.html">Supporting Different Screen Sizes</a></b></dt>
<dd>This lesson walks you through how to design layouts that adapts
several different screen sizes (using flexible dimensions for
views, {@link android.widget.RelativeLayout}, screen size and orientation qualifiers,
alias filters, and nine-patch bitmaps).</dd>
<dt><b><a href="screendensities.html">Supporting Different Screen
Densities</a></b></dt>
<dd>This lesson shows you how to support screens that have different
pixel densities (using density-independent pixels and providing
bitmaps appropriate for each density).</dd>
<dt><b><a href="adaptui.html">Implementing Adaptative UI Flows</a></b></dt>
<dd>This lesson shows you how to implement your UI flow in a way
that adapts to several screen size/density combinations
(run-time detection of active layout, reacting according to
current layout, handling screen configuration changes).</dd>
</dl>

View File

@@ -0,0 +1,127 @@
page.title=Supporting Different Densities
parent.title=Designing for Multiple Screens
parent.link=index.html
trainingnavtop=true
previous.title=Supporting Different Screen Sizes
previous.link=screensizes.html
next.title=Implementing Adaptative UI Flows
next.link=adaptui.html
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#TaskUseDP">Use Density-independent Pixels</a></li>
<li><a href="#TaskProvideAltBmp">Provide Alternative Bitmaps</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
<li><a href="{@docRoot}guide/practices/ui_guidelines/icon_design.html">Icon Design
Guidelines</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Download
the sample app</a>
<p class="filename">NewsReader.zip</p>
</div>
</div>
</div>
<p>This lesson shows you how to support different screen densities
by providing different resources and using resolution-independent units of
measurements.</p>
<h2 id="TaskUseDP">Use Density-independent Pixels</h2>
<p>One common pitfall you must avoid when designing your layouts is using
absolute pixels to define distances or sizes. Defining layout dimensions with
pixels is a problem because different screens have different pixel densities,
so the same number of pixels may correspond to different physical sizes on
different devices. Therefore, when specifying dimensions, always use either
<code>dp</code> or <code>sp</code> units. A <code>dp</code> is a density-independent pixel
that corresponds to the physical size of a pixel at 160 dpi. An <code>sp</code> is the same
base unit, but is scaled by the user's preferred text size (its a
scale-independent pixel), so you should use this measurement unit when defining
text size (but never for layout sizes).</p>
<p>For example, when you specify spacing between two views, use <code>dp</code>
rather than <code>px</code>:</p>
<pre>
&lt;Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="&#64;string/clickme"
android:layout_marginTop="20dp" /&gt;
</pre>
<p>When specifying text size, always use <code>sp</code>:</p>
<pre>
&lt;TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" /&gt;
</pre>
<h2 id="TaskProvideAltBmp">Provide Alternative Bitmaps</h2>
<p>Since Android runs in devices with a wide variety of screen densities,
you should always provide your bitmap resources tailored to each of
the generalized density buckets: low, medium, high and extra-high density.
This will help you achieve good graphical quality and performance on all
screen densities.</p>
<p>To generate these images, you should start with your raw resource in
vector format and generate the images for each density using the following
size scale:</p>
<p><ul>
<li><code>xhdpi</code>: 2.0
<li><code>hdpi</code>: 1.5
<li><code>mdpi</code>: 1.0 (baseline)
<li><code>ldpi</code>: 0.75
</ul></p>
<p>This means that if you generate a 200x200 image for <code>xhdpi</code>
devices, you should generate the same resource in 150x150 for <code>hdpi</code>,
100x100 for <code>mdpi</code> and finally a 75x75 image for <code>ldpi</code>
devices.</p>
<p>Then, place the generated image files in the appropriate subdirectory
under <code>res/</code> and the system will pick the correct one automatically
based on the screen density of the device your application is running on:</p>
<pre class="classic no-pretty-print">
MyProject/
res/
drawable-xhdpi/
awesomeimage.png
drawable-hdpi/
awesomeimage.png
drawable-mdpi/
awesomeimage.png
drawable-ldpi/
awesomeimage.png
</pre>
<p>Then, any time you reference <code>&#64;drawable/awesomeimage</code>, the system selects the
appropriate bitmap based on the screen's dpi.</p>
<p>For more tips and guidelines for creating icon assets for your application, see the <a
href="{@docRoot}guide/practices/ui_guidelines/icon_design.html">Icon Design
Guidelines</a>.</p>

View File

@@ -0,0 +1,376 @@
page.title=Supporting Different Screen Sizes
parent.title=Designing for Multiple Screens
parent.link=index.html
trainingnavtop=true
next.title=Supporting Different Screen Densities
next.link=screendensities.html
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#TaskUseWrapMatchPar">Use "wrap_content" and "match_parent"</a></li>
<li><a href="#TaskUseRelativeLayout">Use RelativeLayout</a></li>
<li><a href="#TaskUseSizeQuali">Use Size Qualifiers</a></li>
<li><a href="#TaskUseSWQuali">Use the Smallest-width Qualifier</a></li>
<li><a href="#TaskUseAliasFilters">Use Layout Aliases</a></li>
<li><a href="#TaskUseOriQuali">Use Orientation Qualifiers</a></li>
<li><a href="#TaskUse9Patch">Use Nine-patch Bitmaps</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Download
the sample app</a>
<p class="filename">NewsReader.zip</p>
</div>
</div>
</div>
<p>This lesson shows you how to support different screen sizes by:</p>
<ul>
<li>Ensuring your layout can be adequately resized to fit the screen</li>
<li>Providing appropriate UI layout according to screen configuration</li>
<li>Ensuring the correct layout is applied to the correct screen</li>
<li>Providing bitmaps that scale correctly</li>
</ul>
<h2 id="TaskUseWrapMatchPar">Use "wrap_content" and "match_parent"</h2>
<p>To ensure that your layout is flexible and adapts to different screen sizes,
you should use <code>"wrap_content"</code> and <code>"match_parent"</code> for the width
and height of some view components. If you use <code>"wrap_content"</code>, the width
or height of the view is set to the minimum size necessary to fit the content
within that view, while <code>"match_parent"</code> (also known as
<code>"fill_parent"</code> before API level 8) makes the component expand to match the size of its
parent view.</p>
<p>By using the <code>"wrap_content"</code> and <code>"match_parent"</code> size values instead of
hard-coded sizes, your views either use only the space required for that
view or expand to fill the available space, respectively. For example:</p>
{@sample development/samples/training/multiscreen/newsreader/res/layout/onepane_with_bar.xml all}
<p>Notice how the sample uses <code>"wrap_content"</code> and <code>"match_parent"</code>
for component sizes rather than specific dimensions. This allows the layout
to adapt correctly to different screen sizes and orientations.</p>
<p>For example, this is what this layout looks like in portrait and landscape
mode. Notice that the sizes of the components adapt automatically to the
width and height:</p>
<img src="{@docRoot}images/training/layout-hvga.png" />
<p class="img-caption"><strong>Figure 1.</strong> The News Reader sample app in portrait (left)
and landscape (right).</p>
<h2 id="TaskUseRelativeLayout">Use RelativeLayout</h2>
<p>You can construct fairly complex layouts using nested instances of {@link
android.widget.LinearLayout} and
combinations of <code>"wrap_content"</code> and <code>"match_parent"</code> sizes.
However, {@link android.widget.LinearLayout} does not allow you to precisely control the
spacial relationships of child views; views in a {@link android.widget.LinearLayout} simply line up
side-by-side. If you need child views to be oriented in variations other than a straight line, a
better solution is often to use a {@link android.widget.RelativeLayout}, which allows
you to specify your layout in terms of the spacial relationships between
components. For instance, you can align one child view on the left side and another view on
the right side of the screen.</p>
<p>For example:</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"&gt;
&lt;TextView
android:id="&#64;+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Type here:"/&gt;
&lt;EditText
android:id="&#64;+id/entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="&#64;id/label"/&gt;
&lt;Button
android:id="&#64;+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="&#64;id/entry"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dp"
android:text="OK" /&gt;
&lt;Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="&#64;id/ok"
android:layout_alignTop="&#64;id/ok"
android:text="Cancel" /&gt;
&lt;/RelativeLayout&gt;
</pre>
<p>Figure 2 shows how this layout appears on a QVGA screen.</p>
<img src="{@docRoot}images/training/relativelayout1.png" />
<p class="img-caption"><strong>Figure 2.</strong> Screenshot on a QVGA screen (small screen).</p>
<p>Figure 3 shows how it appears on a larger screen.</p>
<img src="{@docRoot}images/training/relativelayout2.png" />
<p class="img-caption"><strong>Figure 3.</strong> Screenshot on a WSVGA screen (large screen).</p>
<p>Notice that although the size of the components changed, their
spatial relationships are preserved as specified by the {@link
android.widget.RelativeLayout.LayoutParams}.</p>
<h2 id="TaskUseSizeQuali">Use Size Qualifiers</h2>
<p>There's only so much mileage you can get from a flexible layout or relative layout
like the one in the previous sections. While those layouts adapt to
different screens by stretching the space within and around components, they
may not provide the best user experience for each screen size. Therefore, your
application should not only implement flexible layouts, but should also provide
several alternative layouts to target different screen configurations. You do
so by using <a href="http://developer.android.com/guide/practices/screens_support.html#qualifiers">configuration qualifiers</a>, which allows the runtime
to automatically select the appropriate resource based on the current devices
configuration (such as a different layout design for different screen sizes).</p>
<p>For example, many applications implement the "two pane" pattern for large
screens (the app might show a list of items on one pane and the content on
another pane). Tablets and TVs are large enough for both panes to fit
simultaneously on screen, but phone screens have to show them separately. So,
to implement these layouts, you could have the following files:</p>
<ul>
<li><code>res/layout/main.xml</code>, single-pane (default) layout:
{@sample development/samples/training/multiscreen/newsreader/res/layout/onepane.xml all}
</li>
<li><code>res/layout-xlarge/main.xml</code>, two-pane layout:
{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all}
</li>
</ul>
<p>Notice the <code>xlarge</code> qualifier in the directory name of the second layout. This layout
will be selected on devices with screens classified as extra-large (for example, 10" tablets). The
other layout (without qualifiers) will be selected for smaller devices.</p>
<h2 id="TaskUseSWQuali">Use the Smallest-width Qualifier</h2>
<p>One of the difficulties developers had in pre-3.2 Android devices was the
"large" screen size bin, which encompasses the Dell Streak, the original Galaxy
Tab, and 7" tablets in general. However, many applications may want to show
different layouts for different devices in this category (such as for 5" and 7" devices), even
though they are all considered to be "large" screens. That's why Android introduced the
"Smallest-width" qualifier (amongst others) in Android 3.2.</p>
<p>The Smallest-width qualifier allows you to target screens that have a certain minimum
width given in dp. For example, the typical 7" tablet has a minimum width of
600 dp, so if you want your UI to have two panes on those screens (but a single
list on smaller screens), you can use the same two layouts from the previous section for single
and two-pane layouts, but instead of the <code>xlarge</code> size qualifier, use
<code>sw600dp</code> to indicate the two-pane layout is for screens on which the smallest-width
is 600 dp:</p>
<ul>
<li><code>res/layout/main.xml</code>, single-pane (default) layout:
{@sample development/samples/training/multiscreen/newsreader/res/layout/onepane.xml all}
</li>
<li><code>res/layout-sw600dp/main.xml</code>, two-pane layout:
{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all}
</li>
</ul>
<p>This means that devices whose smallest width is greater than or equal to
600dp will select the <code>layout-sw600dp/main.xml</code> (two-pane) layout,
while smaller screens will select the <code>layout/main.xml</code> (single-pane)
layout.</p>
<p>However, this won't work well on pre-3.2 devices, because they don't
recognize <code>sw600dp</code> as a size qualifier, so you still have to use the <code>xlarge</code>
qualifier as well. So, you should have a file named
<code>res/layout-xlarge/main.xml</code>
which is identical to <code>res/layout-sw600dp/main.xml</code>. In the next section
you'll see a technique that allows you to avoid duplicating the layout files this way.</p>
<h2 id="TaskUseAliasFilters">Use Layout Aliases</h2>
<p>The smallest-width qualifier is available only on Android 3.2 and above.
Therefore, you should also still use the abstract size bins (small, normal,
large and xlarge) to be compatible with earlier versions. For example, if you
want to design your UI so that it shows a single-pane UI on phones but a
multi-pane UI on 7" tablets and larger devices, you'd have to supply these
files:</p>
<p><ul>
<li><code>res/layout/main.xml:</code> single-pane layout</li>
<li><code>res/layout-xlarge:</code> multi-pane layout</li>
<li><code>res/layout-sw600dp:</code> multi-pane layout</li>
</ul></p>
<p>The last two files are identical, because one of them will be matched by
Android 3.2 devices, and the other one is for the benefit of tablets with
earlier versions of Android.</p>
<p>To avoid this duplication of the same file for tablets (and the maintenance
headache resulting from it), you can use alias files. For example, you can define the following
layouts:</p>
<ul>
<li><code>res/layout/main.xml</code>, single-pane layout</li>
<li><code>res/layout/main_twopanes.xml</code>, two-pane layout</li>
</ul>
<p>And add these two files:</p>
<p><ul>
<li><code>res/values-xlarge/layout.xml</code>:
<pre>
&lt;resources>
&lt;item name="main" type="layout">&#64;layout/main_twopanes&lt;/item>
&lt;/resources>
</pre>
</li>
<li><code>res/values-sw600dp/layout.xml</code>:
<pre>
&lt;resources>
&lt;item name="main" type="layout">&#64;layout/main_twopanes&lt;/item>
&lt;/resources>
</pre>
</li>
</ul></p>
<p>These latter two files have identical content, but they dont actually define
the layout. They merely set up {@code main} to be an alias to {@code main_twopanes}. Since
these files have <code>xlarge</code> and <code>sw600dp</code> selectors, they are
applied to tablets regardless of Android version (pre-3.2 tablets match
{@code xlarge}, and post-3.2 will match <code>sw600dp</code>).</p>
<h2 id="TaskUseOriQuali">Use Orientation Qualifiers</h2>
<p>Some layouts work well in both landscape and portrait orientations, but most of them can
benefit from adjustments. In the News Reader sample app, here is how the layout
behaves in each screen size and orientation:</p>
<p><ul>
<li><b>small screen, portrait:</b> single pane, with logo</li>
<li><b>small screen, landscape:</b> single pane, with logo</li>
<li><b>7" tablet, portrait:</b> single pane, with action bar</li>
<li><b>7" tablet, landscape:</b> dual pane, wide, with action bar</li>
<li><b>10" tablet, portrait:</b> dual pane, narrow, with action bar</li>
<li><b>10" tablet, landscape:</b> dual pane, wide, with action bar</li>
</ul></p>
<p>So each of these layouts is defined in an XML file in the
<code>res/layout/</code> directory. To then assign each layout to the various screen
configurations, the app uses layout aliases to match them to
each configuration:</p>
<p><code>res/layout/onepane.xml:</code></p>
{@sample development/samples/training/multiscreen/newsreader/res/layout/onepane.xml all}
<p><code>res/layout/onepane_with_bar.xml:</code></p>
{@sample development/samples/training/multiscreen/newsreader/res/layout/onepane_with_bar.xml all}
<p><code>res/layout/twopanes.xml</code>:</p>
{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all}
<p><code>res/layout/twopanes_narrow.xml</code>:</p>
{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes_narrow.xml all}
<p>Now that all possible layouts are defined, it's just a matter of mapping the correct layout to
each configuration using the configuration qualifiers. You can now do it using the layout alias
technique:</p>
<p><code>res/values/layouts.xml</code>:</p>
{@sample development/samples/training/multiscreen/newsreader/res/values/layouts.xml all}
<p><code>res/values-sw600dp-land/layouts.xml</code>:</p>
{@sample development/samples/training/multiscreen/newsreader/res/values-sw600dp-land/layouts.xml
all}
<p><code>res/values-sw600dp-port/layouts.xml</code>:</p>
{@sample development/samples/training/multiscreen/newsreader/res/values-sw600dp-port/layouts.xml
all}
<p><code>res/values-xlarge-land/layouts.xml</code>:</p>
{@sample development/samples/training/multiscreen/newsreader/res/values-xlarge-land/layouts.xml all}
<p><code>res/values-xlarge-port/layouts.xml</code>:</p>
{@sample development/samples/training/multiscreen/newsreader/res/values-xlarge-port/layouts.xml all}
<h2 id="TaskUse9Patch">Use Nine-patch Bitmaps</h2>
<p>Supporting different screen sizes usually means that your image resources
must also be capable of adapting to different sizes. For example, a button
background must fit whichever button shape it is applied to.</p>
<p>If you use simple images on components that can change size, you will
quickly notice that the results are somewhat less than impressive, since the
runtime will stretch or shrink your images uniformly. The solution is using nine-patch bitmaps,
which are specially
formatted PNG files that indicate which areas can and cannot be stretched.</p>
<p>Therefore, when designing bitmaps that will be used on components with
variable size, always use nine-patches. To convert a bitmap into a nine-patch,
you can start with a regular image (figure 4, shown with in 4x zoom for clarity).</p>
<img src="{@docRoot}images/training/button.png" />
<p class="img-caption"><strong>Figure 4.</strong> <code>button.png</code></p>
<p>And then run it through the <ode
href="{@docRoot}guide/developing/tools/draw9patch.html"><code>draw9patch</code></a> utility of the
SDK (which is located in the <code>tools/</code> directory), in which you can mark the areas that
should be stretched by drawing pixels along the left and top borders. You can also mark the area
that should hold the content by drawing pixels along the right and bottom borders, resulting in
figure 5.</p>
<img src="{@docRoot}images/training/button_with_marks.png" />
<p class="img-caption"><strong>Figure 5.</strong> <code>button.9.png</code></p>
<p>Notice the black pixels along the borders. The ones on the top and left
borders indicate the places where the image can be stretched, and the ones on
the right and bottom borders indicate where the content should be
placed.</p>
<p>Also, notice the <code>.9.png</code> extension. You must use this
extension, since this is how the framework detects that this is a nine-patch
image, as opposed to a regular PNG image.</p>
<p>When you apply this background to a component (by setting
<code>android:background="&#64;drawable/button"</code>), the framework stretches
the image correctly to accommodate the size of the button, as shown in various sizes in figure
6.</p>
<img src="{@docRoot}images/training/buttons_stretched.png" />
<p class="img-caption"><strong>Figure 6.</strong> A button using the <code>button.9.png</code>
nine-patch in various sizes.</p>