1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
page.title=Layout Tricks: Creating Efficient Layouts
@jd:body
<p>The Android UI toolkit offers several layout managers that are
rather easy to use and, most of the time, you only need the basic
features of these layout managers to implement a user interface.</p>
<p>Sticking to the basic features is unfortunately not the most efficient
way to create user interfaces. A common example is the abuse of
{@link android.widget.LinearLayout}, which leads to a proliferation of
views in the view hierarchy. Every view — or worse, every layout
manager — that you add to your application comes at a cost:
initialization, layout and drawing become slower. The layout pass can be
especially expensive when you nest several <code>LinearLayout</code>
that use the {@link android.R.attr#layout_weight weight}
parameter, which requires the child to be measured twice.</p>
<p>Let's consider a very simple and common example of a layout: a list item
with an icon on the left, a title at the top and an optional description
underneath the title. Here is what such an item looks like:</p>
<div style="text-align: center;"><img src="images/relativelayout_1.png" alt="Simple list item"></div>
<p>To clearly understand how the views, one {@link android.widget.ImageView} and
two {@link android.widget.TextView}, are positioned with respect to each other,
here is the wireframe of the layout as captured by <a
href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">HierarchyViewer</a
>:</p>
<div style="text-align: center;"><img src="images/relativelayout_wire_1.png" alt="Wireframe of the simple list item"></div>
<p>Implementing this layout is straightforward with <code>LinearLayout</code>.
The item itself is a horizontal <code>LinearLayout</code> with an
<code>ImageView</code> and a vertical <code>LinearLayout</code>, which contains
the two <code>TextView</code>. Here's the source code of this layout:</p>
<pre class="prettyprint"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:src="@drawable/icon" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="My Application" />
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:text="Simple application that shows how to use RelativeLayout" />
</LinearLayout>
</LinearLayout></pre>
<p>This layout works but can be wasteful if you instantiate it for every list
item of a {@link android.widget.ListView}. The same layout can be rewritten
using a single {@link android.widget.RelativeLayout}, thus saving one view, and
even better one level in view hierarchy, per list item. The implementation of
the layout with a <code>RelativeLayout</code> remains simple:</p>
<pre class="prettyprint"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="6dip"
android:src="@drawable/icon" />
<TextView
android:id="@+id/secondLine"
android:layout_width="fill_parent"
android:layout_height="26dip"
android:layout_toRightOf="@id/icon"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:singleLine="true"
android:ellipsize="marquee"
android:text="Simple application that shows how to use RelativeLayout" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/icon"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_above="@id/secondLine"
android:layout_alignWithParentIfMissing="true"
android:gravity="center_vertical"
android:text="My Application" />
</RelativeLayout></pre>
<p>This new implementation behaves exactly the same way as the previous
implementation, except in one case. The list item we want to display has two
lines of text: the title and an <em>optional</em> description. When a
description is not available for a given list item, the application would simply
set the visibility of the second <code>TextView</code> to
{@link android.view.View#GONE}. This works perfectly with the <code>LinearLayout</code>
implementation but not with the <code>RelativeLayout</code> version:</p>
<div style="text-align: center;"><img src="images/relativelayout_2.png" alt="RelativeLayout and description GONE"></div>
<div style="text-align: center;"><img src="images/relativelayout_wire_2.png" alt="RelativeLayout and description GONE"></div>
<p>In a <code>RelativeLayout</code>, views are aligned with their parent, with the
<code>RelativeLayout</code> itself, or with other views. For instance, we declared that
the description is aligned with the bottom of the <code>RelativeLayout</code> and
that the title is positioned above the description and anchored to the
parent's top. With the description GONE, RelativeLayout doesn't know
where to position the title's bottom edge. To solve this problem, you
can use a very special layout parameter called
{@link android.R.attr#layout_alignWithParentIfMissing}.
</p>
<p>This boolean parameter simply tells RelativeLayout to use its own edges as
anchors when a constraint target is missing. For instance, if you position a
view to the right of a GONE view and set <code>alignWithParentIfMissing</code>
to <code>true</code>, <code>RelativeLayout</code> will instead anchor the view
to its left edge. In our case, using <code>alignWithParentIfMissing</code> will
cause <code>RelativeLayout</code> to align the title's bottom with its own
bottom. The result is the following:</p>
<div style="text-align: center;"><img src="images/relativelayout_3.png" alt="RelativeLayout, description GONE and alignWithParentIfMissing"></div>
<div style="text-align: center;"><img src="images/relativelayout_wire_3.png" alt="RelativeLayout, description GONE and alignWithParentIfMissing"></div>
<p>The
behavior of our layout is now perfect, even when the description is
GONE. Even better, the hierarchy is simpler and because we are not
using LinearLayout's weights it's also more efficient. The difference
between the two implementations becomes obvious when comparing the view
hierarchies in HierarchyViewer:</p>
<div style="text-align: center;"><img src="images/layouts_comparison_small.png" alt="LinearLayout vs RelativeLayout"></div>
<p>Again, the difference will be much more important when you use such a layout
for every item in a ListView for instance. Hopefully this simple
example showed you that getting to know your layouts is the best way to
learn how to optimize your UI.</p>
|