* commit '37191e1dc783a5558eb8ee4f096bd8350c34415d': AndroidU lesson on designing for multiple screens.
@@ -448,6 +448,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 \
|
||||
|
||||
BIN
docs/html/images/training/button.png
Executable file
|
After Width: | Height: | Size: 720 B |
BIN
docs/html/images/training/button_with_marks.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
docs/html/images/training/buttons_stretched.png
Executable file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
docs/html/images/training/layout-hvga.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
docs/html/images/training/relativelayout1.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/html/images/training/relativelayout2.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
@@ -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',
|
||||
|
||||
BIN
docs/html/resources/samples/images/NewsReader.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
docs/html/shareables/training/NewsReader.zip
Normal file
258
docs/html/training/multiscreen/adaptui.jd
Normal 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;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main_layout);
|
||||
|
||||
View articleView = findViewById(R.id.article);
|
||||
mIsDualPane = articleView != null &&
|
||||
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>
|
||||
@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 < 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 {
|
||||
...
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> 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;
|
||||
|
||||
@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>
|
||||
|
||||
|
||||
87
docs/html/training/multiscreen/index.jd
Normal 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, it’s important
|
||||
that you design your application to be compatible with all screen sizes so it’s 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>
|
||||
127
docs/html/training/multiscreen/screendensities.jd
Normal 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 (it’s 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>
|
||||
<Button android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/clickme"
|
||||
android:layout_marginTop="20dp" />
|
||||
</pre>
|
||||
|
||||
<p>When specifying text size, always use <code>sp</code>:</p>
|
||||
|
||||
<pre>
|
||||
<TextView android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="20sp" />
|
||||
</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>@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>
|
||||
|
||||
376
docs/html/training/multiscreen/screensizes.jd
Normal 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>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Type here:"/>
|
||||
<EditText
|
||||
android:id="@+id/entry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/label"/>
|
||||
<Button
|
||||
android:id="@+id/ok"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/entry"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:text="OK" />
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toLeftOf="@id/ok"
|
||||
android:layout_alignTop="@id/ok"
|
||||
android:text="Cancel" />
|
||||
</RelativeLayout>
|
||||
</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 device’s
|
||||
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>
|
||||
<resources>
|
||||
<item name="main" type="layout">@layout/main_twopanes</item>
|
||||
</resources>
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
<li><code>res/values-sw600dp/layout.xml</code>:
|
||||
<pre>
|
||||
<resources>
|
||||
<item name="main" type="layout">@layout/main_twopanes</item>
|
||||
</resources>
|
||||
</pre>
|
||||
|
||||
</li>
|
||||
</ul></p>
|
||||
|
||||
<p>These latter two files have identical content, but they don’t 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="@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>
|
||||
|
||||