Scott Main | 801fda5 | 2011-12-09 17:27:21 -0800 | [diff] [blame] | 1 | page.title=Supporting Different Screen Sizes |
| 2 | parent.title=Designing for Multiple Screens |
| 3 | parent.link=index.html |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | next.title=Supporting Different Screen Densities |
| 7 | next.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 | <ol> |
| 18 | <li><a href="#TaskUseWrapMatchPar">Use "wrap_content" and "match_parent"</a></li> |
| 19 | <li><a href="#TaskUseRelativeLayout">Use RelativeLayout</a></li> |
| 20 | <li><a href="#TaskUseSizeQuali">Use Size Qualifiers</a></li> |
| 21 | <li><a href="#TaskUseSWQuali">Use the Smallest-width Qualifier</a></li> |
| 22 | <li><a href="#TaskUseAliasFilters">Use Layout Aliases</a></li> |
| 23 | <li><a href="#TaskUseOriQuali">Use Orientation Qualifiers</a></li> |
| 24 | <li><a href="#TaskUse9Patch">Use Nine-patch Bitmaps</a></li> |
| 25 | </ol> |
| 26 | |
| 27 | <h2>You should also read</h2> |
| 28 | |
| 29 | <ul> |
| 30 | <li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li> |
| 31 | </ul> |
| 32 | |
| 33 | <h2>Try it out</h2> |
| 34 | |
| 35 | <div class="download-box"> |
| 36 | <a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Download |
| 37 | the sample app</a> |
| 38 | <p class="filename">NewsReader.zip</p> |
| 39 | </div> |
| 40 | |
| 41 | </div> |
| 42 | </div> |
| 43 | |
| 44 | <p>This lesson shows you how to support different screen sizes by:</p> |
| 45 | <ul> |
| 46 | <li>Ensuring your layout can be adequately resized to fit the screen</li> |
| 47 | <li>Providing appropriate UI layout according to screen configuration</li> |
| 48 | <li>Ensuring the correct layout is applied to the correct screen</li> |
| 49 | <li>Providing bitmaps that scale correctly</li> |
| 50 | </ul> |
| 51 | |
| 52 | |
| 53 | <h2 id="TaskUseWrapMatchPar">Use "wrap_content" and "match_parent"</h2> |
| 54 | |
| 55 | <p>To ensure that your layout is flexible and adapts to different screen sizes, |
| 56 | you should use <code>"wrap_content"</code> and <code>"match_parent"</code> for the width |
| 57 | and height of some view components. If you use <code>"wrap_content"</code>, the width |
| 58 | or height of the view is set to the minimum size necessary to fit the content |
| 59 | within that view, while <code>"match_parent"</code> (also known as |
| 60 | <code>"fill_parent"</code> before API level 8) makes the component expand to match the size of its |
| 61 | parent view.</p> |
| 62 | |
| 63 | <p>By using the <code>"wrap_content"</code> and <code>"match_parent"</code> size values instead of |
| 64 | hard-coded sizes, your views either use only the space required for that |
| 65 | view or expand to fill the available space, respectively. For example:</p> |
| 66 | |
| 67 | {@sample development/samples/training/multiscreen/newsreader/res/layout/onepane_with_bar.xml all} |
| 68 | |
| 69 | <p>Notice how the sample uses <code>"wrap_content"</code> and <code>"match_parent"</code> |
| 70 | for component sizes rather than specific dimensions. This allows the layout |
| 71 | to adapt correctly to different screen sizes and orientations.</p> |
| 72 | |
| 73 | <p>For example, this is what this layout looks like in portrait and landscape |
| 74 | mode. Notice that the sizes of the components adapt automatically to the |
| 75 | width and height:</p> |
| 76 | |
| 77 | <img src="{@docRoot}images/training/layout-hvga.png" /> |
| 78 | <p class="img-caption"><strong>Figure 1.</strong> The News Reader sample app in portrait (left) |
| 79 | and landscape (right).</p> |
| 80 | |
| 81 | |
| 82 | <h2 id="TaskUseRelativeLayout">Use RelativeLayout</h2> |
| 83 | |
| 84 | <p>You can construct fairly complex layouts using nested instances of {@link |
| 85 | android.widget.LinearLayout} and |
| 86 | combinations of <code>"wrap_content"</code> and <code>"match_parent"</code> sizes. |
| 87 | However, {@link android.widget.LinearLayout} does not allow you to precisely control the |
| 88 | spacial relationships of child views; views in a {@link android.widget.LinearLayout} simply line up |
| 89 | side-by-side. If you need child views to be oriented in variations other than a straight line, a |
| 90 | better solution is often to use a {@link android.widget.RelativeLayout}, which allows |
| 91 | you to specify your layout in terms of the spacial relationships between |
| 92 | components. For instance, you can align one child view on the left side and another view on |
| 93 | the right side of the screen.</p> |
| 94 | |
| 95 | <p>For example:</p> |
| 96 | |
| 97 | <pre> |
| 98 | <?xml version="1.0" encoding="utf-8"?> |
| 99 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 100 | android:layout_width="match_parent" |
| 101 | android:layout_height="match_parent"> |
| 102 | <TextView |
| 103 | android:id="@+id/label" |
| 104 | android:layout_width="match_parent" |
| 105 | android:layout_height="wrap_content" |
| 106 | android:text="Type here:"/> |
| 107 | <EditText |
| 108 | android:id="@+id/entry" |
| 109 | android:layout_width="match_parent" |
| 110 | android:layout_height="wrap_content" |
| 111 | android:layout_below="@id/label"/> |
| 112 | <Button |
| 113 | android:id="@+id/ok" |
| 114 | android:layout_width="wrap_content" |
| 115 | android:layout_height="wrap_content" |
| 116 | android:layout_below="@id/entry" |
| 117 | android:layout_alignParentRight="true" |
| 118 | android:layout_marginLeft="10dp" |
| 119 | android:text="OK" /> |
| 120 | <Button |
| 121 | android:layout_width="wrap_content" |
| 122 | android:layout_height="wrap_content" |
| 123 | android:layout_toLeftOf="@id/ok" |
| 124 | android:layout_alignTop="@id/ok" |
| 125 | android:text="Cancel" /> |
| 126 | </RelativeLayout> |
| 127 | </pre> |
| 128 | |
| 129 | <p>Figure 2 shows how this layout appears on a QVGA screen.</p> |
| 130 | |
| 131 | <img src="{@docRoot}images/training/relativelayout1.png" /> |
| 132 | <p class="img-caption"><strong>Figure 2.</strong> Screenshot on a QVGA screen (small screen).</p> |
| 133 | |
| 134 | <p>Figure 3 shows how it appears on a larger screen.</p> |
| 135 | |
| 136 | <img src="{@docRoot}images/training/relativelayout2.png" /> |
| 137 | <p class="img-caption"><strong>Figure 3.</strong> Screenshot on a WSVGA screen (large screen).</p> |
| 138 | |
| 139 | <p>Notice that although the size of the components changed, their |
| 140 | spatial relationships are preserved as specified by the {@link |
| 141 | android.widget.RelativeLayout.LayoutParams}.</p> |
| 142 | |
| 143 | |
| 144 | <h2 id="TaskUseSizeQuali">Use Size Qualifiers</h2> |
| 145 | |
| 146 | <p>There's only so much mileage you can get from a flexible layout or relative layout |
| 147 | like the one in the previous sections. While those layouts adapt to |
| 148 | different screens by stretching the space within and around components, they |
| 149 | may not provide the best user experience for each screen size. Therefore, your |
| 150 | application should not only implement flexible layouts, but should also provide |
| 151 | several alternative layouts to target different screen configurations. You do |
| 152 | so by using <a href="http://developer.android.com/guide/practices/screens_support.html#qualifiers">configuration qualifiers</a>, which allows the runtime |
| 153 | to automatically select the appropriate resource based on the current device’s |
| 154 | configuration (such as a different layout design for different screen sizes).</p> |
| 155 | |
| 156 | <p>For example, many applications implement the "two pane" pattern for large |
| 157 | screens (the app might show a list of items on one pane and the content on |
| 158 | another pane). Tablets and TVs are large enough for both panes to fit |
| 159 | simultaneously on screen, but phone screens have to show them separately. So, |
| 160 | to implement these layouts, you could have the following files:</p> |
| 161 | |
| 162 | <ul> |
| 163 | <li><code>res/layout/main.xml</code>, single-pane (default) layout: |
| 164 | |
| 165 | {@sample development/samples/training/multiscreen/newsreader/res/layout/onepane.xml all} |
| 166 | </li> |
| 167 | <li><code>res/layout-xlarge/main.xml</code>, two-pane layout: |
| 168 | |
| 169 | {@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all} |
| 170 | </li> |
| 171 | </ul> |
| 172 | |
| 173 | <p>Notice the <code>xlarge</code> qualifier in the directory name of the second layout. This layout |
| 174 | will be selected on devices with screens classified as extra-large (for example, 10" tablets). The |
| 175 | other layout (without qualifiers) will be selected for smaller devices.</p> |
| 176 | |
| 177 | |
| 178 | <h2 id="TaskUseSWQuali">Use the Smallest-width Qualifier</h2> |
| 179 | |
| 180 | <p>One of the difficulties developers had in pre-3.2 Android devices was the |
| 181 | "large" screen size bin, which encompasses the Dell Streak, the original Galaxy |
| 182 | Tab, and 7" tablets in general. However, many applications may want to show |
| 183 | different layouts for different devices in this category (such as for 5" and 7" devices), even |
| 184 | though they are all considered to be "large" screens. That's why Android introduced the |
| 185 | "Smallest-width" qualifier (amongst others) in Android 3.2.</p> |
| 186 | |
| 187 | <p>The Smallest-width qualifier allows you to target screens that have a certain minimum |
| 188 | width given in dp. For example, the typical 7" tablet has a minimum width of |
| 189 | 600 dp, so if you want your UI to have two panes on those screens (but a single |
| 190 | list on smaller screens), you can use the same two layouts from the previous section for single |
| 191 | and two-pane layouts, but instead of the <code>xlarge</code> size qualifier, use |
| 192 | <code>sw600dp</code> to indicate the two-pane layout is for screens on which the smallest-width |
| 193 | is 600 dp:</p> |
| 194 | |
| 195 | <ul> |
| 196 | <li><code>res/layout/main.xml</code>, single-pane (default) layout: |
| 197 | |
| 198 | {@sample development/samples/training/multiscreen/newsreader/res/layout/onepane.xml all} |
| 199 | </li> |
| 200 | <li><code>res/layout-sw600dp/main.xml</code>, two-pane layout: |
| 201 | |
| 202 | {@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all} |
| 203 | </li> |
| 204 | </ul> |
| 205 | |
| 206 | <p>This means that devices whose smallest width is greater than or equal to |
| 207 | 600dp will select the <code>layout-sw600dp/main.xml</code> (two-pane) layout, |
| 208 | while smaller screens will select the <code>layout/main.xml</code> (single-pane) |
| 209 | layout.</p> |
| 210 | |
| 211 | <p>However, this won't work well on pre-3.2 devices, because they don't |
| 212 | recognize <code>sw600dp</code> as a size qualifier, so you still have to use the <code>xlarge</code> |
| 213 | qualifier as well. So, you should have a file named |
| 214 | <code>res/layout-xlarge/main.xml</code> |
| 215 | which is identical to <code>res/layout-sw600dp/main.xml</code>. In the next section |
| 216 | you'll see a technique that allows you to avoid duplicating the layout files this way.</p> |
| 217 | |
| 218 | |
| 219 | <h2 id="TaskUseAliasFilters">Use Layout Aliases</h2> |
| 220 | |
| 221 | <p>The smallest-width qualifier is available only on Android 3.2 and above. |
| 222 | Therefore, you should also still use the abstract size bins (small, normal, |
| 223 | large and xlarge) to be compatible with earlier versions. For example, if you |
| 224 | want to design your UI so that it shows a single-pane UI on phones but a |
| 225 | multi-pane UI on 7" tablets and larger devices, you'd have to supply these |
| 226 | files:</p> |
| 227 | |
| 228 | <p><ul> |
| 229 | <li><code>res/layout/main.xml:</code> single-pane layout</li> |
| 230 | <li><code>res/layout-xlarge:</code> multi-pane layout</li> |
| 231 | <li><code>res/layout-sw600dp:</code> multi-pane layout</li> |
| 232 | </ul></p> |
| 233 | |
| 234 | <p>The last two files are identical, because one of them will be matched by |
| 235 | Android 3.2 devices, and the other one is for the benefit of tablets with |
| 236 | earlier versions of Android.</p> |
| 237 | |
| 238 | <p>To avoid this duplication of the same file for tablets (and the maintenance |
| 239 | headache resulting from it), you can use alias files. For example, you can define the following |
| 240 | layouts:</p> |
| 241 | |
| 242 | <ul> |
| 243 | <li><code>res/layout/main.xml</code>, single-pane layout</li> |
| 244 | <li><code>res/layout/main_twopanes.xml</code>, two-pane layout</li> |
| 245 | </ul> |
| 246 | |
| 247 | <p>And add these two files:</p> |
| 248 | |
| 249 | <p><ul> |
| 250 | <li><code>res/values-xlarge/layout.xml</code>: |
| 251 | <pre> |
| 252 | <resources> |
| 253 | <item name="main" type="layout">@layout/main_twopanes</item> |
| 254 | </resources> |
| 255 | </pre> |
| 256 | </li> |
| 257 | |
| 258 | <li><code>res/values-sw600dp/layout.xml</code>: |
| 259 | <pre> |
| 260 | <resources> |
| 261 | <item name="main" type="layout">@layout/main_twopanes</item> |
| 262 | </resources> |
| 263 | </pre> |
| 264 | |
| 265 | </li> |
| 266 | </ul></p> |
| 267 | |
| 268 | <p>These latter two files have identical content, but they don’t actually define |
| 269 | the layout. They merely set up {@code main} to be an alias to {@code main_twopanes}. Since |
| 270 | these files have <code>xlarge</code> and <code>sw600dp</code> selectors, they are |
| 271 | applied to tablets regardless of Android version (pre-3.2 tablets match |
| 272 | {@code xlarge}, and post-3.2 will match <code>sw600dp</code>).</p> |
| 273 | |
| 274 | |
| 275 | <h2 id="TaskUseOriQuali">Use Orientation Qualifiers</h2> |
| 276 | |
| 277 | <p>Some layouts work well in both landscape and portrait orientations, but most of them can |
| 278 | benefit from adjustments. In the News Reader sample app, here is how the layout |
| 279 | behaves in each screen size and orientation:</p> |
| 280 | |
| 281 | <p><ul> |
| 282 | <li><b>small screen, portrait:</b> single pane, with logo</li> |
| 283 | <li><b>small screen, landscape:</b> single pane, with logo</li> |
| 284 | <li><b>7" tablet, portrait:</b> single pane, with action bar</li> |
| 285 | <li><b>7" tablet, landscape:</b> dual pane, wide, with action bar</li> |
| 286 | <li><b>10" tablet, portrait:</b> dual pane, narrow, with action bar</li> |
| 287 | <li><b>10" tablet, landscape:</b> dual pane, wide, with action bar</li> |
| 288 | </ul></p> |
| 289 | |
| 290 | <p>So each of these layouts is defined in an XML file in the |
| 291 | <code>res/layout/</code> directory. To then assign each layout to the various screen |
| 292 | configurations, the app uses layout aliases to match them to |
| 293 | each configuration:</p> |
| 294 | |
| 295 | <p><code>res/layout/onepane.xml:</code></p> |
| 296 | {@sample development/samples/training/multiscreen/newsreader/res/layout/onepane.xml all} |
| 297 | |
| 298 | <p><code>res/layout/onepane_with_bar.xml:</code></p> |
| 299 | {@sample development/samples/training/multiscreen/newsreader/res/layout/onepane_with_bar.xml all} |
| 300 | |
| 301 | <p><code>res/layout/twopanes.xml</code>:</p> |
| 302 | {@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all} |
| 303 | |
| 304 | <p><code>res/layout/twopanes_narrow.xml</code>:</p> |
| 305 | {@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes_narrow.xml all} |
| 306 | |
| 307 | <p>Now that all possible layouts are defined, it's just a matter of mapping the correct layout to |
| 308 | each configuration using the configuration qualifiers. You can now do it using the layout alias |
| 309 | technique:</p> |
| 310 | |
| 311 | <p><code>res/values/layouts.xml</code>:</p> |
| 312 | {@sample development/samples/training/multiscreen/newsreader/res/values/layouts.xml all} |
| 313 | |
| 314 | <p><code>res/values-sw600dp-land/layouts.xml</code>:</p> |
| 315 | {@sample development/samples/training/multiscreen/newsreader/res/values-sw600dp-land/layouts.xml |
| 316 | all} |
| 317 | |
| 318 | <p><code>res/values-sw600dp-port/layouts.xml</code>:</p> |
| 319 | {@sample development/samples/training/multiscreen/newsreader/res/values-sw600dp-port/layouts.xml |
| 320 | all} |
| 321 | |
| 322 | <p><code>res/values-xlarge-land/layouts.xml</code>:</p> |
| 323 | {@sample development/samples/training/multiscreen/newsreader/res/values-xlarge-land/layouts.xml all} |
| 324 | |
| 325 | <p><code>res/values-xlarge-port/layouts.xml</code>:</p> |
| 326 | {@sample development/samples/training/multiscreen/newsreader/res/values-xlarge-port/layouts.xml all} |
| 327 | |
| 328 | |
| 329 | |
| 330 | <h2 id="TaskUse9Patch">Use Nine-patch Bitmaps</h2> |
| 331 | |
| 332 | <p>Supporting different screen sizes usually means that your image resources |
| 333 | must also be capable of adapting to different sizes. For example, a button |
| 334 | background must fit whichever button shape it is applied to.</p> |
| 335 | |
| 336 | <p>If you use simple images on components that can change size, you will |
| 337 | quickly notice that the results are somewhat less than impressive, since the |
| 338 | runtime will stretch or shrink your images uniformly. The solution is using nine-patch bitmaps, |
| 339 | which are specially |
| 340 | formatted PNG files that indicate which areas can and cannot be stretched.</p> |
| 341 | |
| 342 | <p>Therefore, when designing bitmaps that will be used on components with |
| 343 | variable size, always use nine-patches. To convert a bitmap into a nine-patch, |
| 344 | you can start with a regular image (figure 4, shown with in 4x zoom for clarity).</p> |
| 345 | |
| 346 | <img src="{@docRoot}images/training/button.png" /> |
| 347 | <p class="img-caption"><strong>Figure 4.</strong> <code>button.png</code></p> |
| 348 | |
| 349 | <p>And then run it through the <ode |
| 350 | href="{@docRoot}guide/developing/tools/draw9patch.html"><code>draw9patch</code></a> utility of the |
| 351 | SDK (which is located in the <code>tools/</code> directory), in which you can mark the areas that |
| 352 | should be stretched by drawing pixels along the left and top borders. You can also mark the area |
| 353 | that should hold the content by drawing pixels along the right and bottom borders, resulting in |
| 354 | figure 5.</p> |
| 355 | |
| 356 | <img src="{@docRoot}images/training/button_with_marks.png" /> |
| 357 | <p class="img-caption"><strong>Figure 5.</strong> <code>button.9.png</code></p> |
| 358 | |
| 359 | <p>Notice the black pixels along the borders. The ones on the top and left |
| 360 | borders indicate the places where the image can be stretched, and the ones on |
| 361 | the right and bottom borders indicate where the content should be |
| 362 | placed.</p> |
| 363 | |
| 364 | <p>Also, notice the <code>.9.png</code> extension. You must use this |
| 365 | extension, since this is how the framework detects that this is a nine-patch |
| 366 | image, as opposed to a regular PNG image.</p> |
| 367 | |
| 368 | <p>When you apply this background to a component (by setting |
| 369 | <code>android:background="@drawable/button"</code>), the framework stretches |
| 370 | the image correctly to accommodate the size of the button, as shown in various sizes in figure |
| 371 | 6.</p> |
| 372 | |
| 373 | <img src="{@docRoot}images/training/buttons_stretched.png" /> |
| 374 | <p class="img-caption"><strong>Figure 6.</strong> A button using the <code>button.9.png</code> |
| 375 | nine-patch in various sizes.</p> |
| 376 | |