• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Optimizing the View
2parent.title=Creating Custom Views
3parent.link=index.html
4
5trainingnavtop=true
6previous.title=Making the View Interactive
7previous.link=making-interactive.html
8
9@jd:body
10
11<div id="tb-wrapper">
12    <div id="tb">
13
14        <h2>This lesson teaches you to</h2>
15        <ol>
16            <li><a href="#less">Do Less, Less Frequently</a></li>
17            <li><a href="#accelerate">Use Hardware Acceleration</a></li>
18        </ol>
19
20        <h2>You should also read</h2>
21        <ul>
22            <li><a href="{@docRoot}guide/topics/graphics/hardware-accel.html">
23                Hardware Acceleration
24            </a>
25        </li>
26    </ul>
27<h2>Try it out</h2>
28<div class="download-box">
29<a href="{@docRoot}shareables/training/CustomView.zip"
30class="button">Download the sample</a>
31<p class="filename">CustomView.zip</p>
32</div>
33</div>
34        </div>
35
36
37<p>Now that you have a well-designed view that responds to gestures and transitions between states,
38you need to ensure
39that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, you must
40ensure that your
41animations consistently run at 60 frames per second.</p>
42
43<h2 id="less">Do Less, Less Frequently</h2>
44
45<p>To speed up your view, eliminate unnecessary code from routines that are called frequently. Start
46by working on
47{@link android.view.View#onDraw onDraw()}, which will give you the biggest payback. In particular
48you should eliminate
49allocations in {@link android.view.View#onDraw onDraw()}, because allocations may lead to a garbage
50collection that
51would cause a stutter. Allocate objects during initialization, or between animations. Never make an
52allocation while an
53animation is running.</p>
54
55<p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, you should also make sure
56it's called as
57infrequently as possible. Most calls to {@link android.view.View#onDraw onDraw()} are the result of
58a call to {@link
59android.view.View#invalidate() invalidate()}, so eliminate unnecessary calls to {@link
60android.view.View#invalidate()
61invalidate()}. When possible, call the four-parameter variant of {@link
62android.view.View#invalidate() invalidate()}
63rather than the version that takes no parameters. The no-parameter variant invalidates the entire
64view, while the
65four-parameter variant invalidates only a specified portion of the view. This approach allows draw calls to
66be more efficient and
67can eliminate unnecessary invalidation of views that fall outside the invalid rectangle.</p>
68
69<p>Another very expensive operation is traversing layouts. Any time a view calls {@link
70android.view.View#requestLayout()
71requestLayout()}, the Android UI system needs to traverse the entire view hierarchy to find out how
72big each view needs
73to be. If it finds conflicting measurements, it may need to traverse the hierarchy multiple times.
74UI designers
75sometimes create deep hierarchies of nested {@link android.view.ViewGroup ViewGroup} objects in
76order to get the UI to
77behave properly. These deep view hierarchies cause performance problems. Make your view hierarchies
78as shallow as
79possible.</p>
80
81<p>If you have a complex UI, you should consider writing a custom {@link android.view.ViewGroup
82ViewGroup} to perform
83its layout. Unlike the built-in views, your custom view can make application-specific assumptions
84about the size and
85shape of its children, and thus avoid traversing its children to calculate measurements. The
86PieChart example shows how
87to extend {@link android.view.ViewGroup ViewGroup} as part of a custom view. PieChart has child
88views, but it never
89measures them. Instead, it sets their sizes directly according to its own custom layout
90algorithm.</p>
91
92<h2 id="accelerate">Use Hardware Acceleration</h2>
93
94<p>As of Android 3.0, the Android 2D graphics system can be accelerated by the GPU (Graphics
95Processing Unit) hardware
96found in most newer Android devices. GPU hardware acceleration can result in a tremendous
97performance increase for many
98applications, but it isn't the right choice for every application. The Android framework
99gives you the ability to finely control which parts of your application are or are not
100hardware accelerated.</p>
101
102<p>See <a href="{@docRoot}guide/topics/graphics/hardware-accel.html">Hardware Acceleration</a>
103        in the Android Developers Guide for directions on how to enable acceleration at the
104        application, activity, or window level. Notice  that in addition to the directions in
105        the developer guide, you must also set your application's target API to 11 or higher by
106        specifying {@code &lt;uses-sdk
107        android:targetSdkVersion="11"/&gt;} in your {@code AndroidManifest.xml} file.</p>
108
109<p>Once you've enabled hardware acceleration, you may or may not see a performance increase.
110Mobile GPUs are very good at certain tasks, such as scaling, rotating, and translating
111bitmapped images. They are not particularly good at other tasks, such as drawing lines or curves. To
112get the most out of GPU acceleration, you should maximize the number of operations that the GPU is
113good at, and minimize the number of operations that the GPU isn't good at.</p>
114
115<p>In the PieChart example, for instance, drawing the pie is relatively expensive. Redrawing the pie
116each time it's
117rotated causes the UI to feel sluggish. The solution is to place the pie chart into a child
118{@link android.view.View} and set that
119{@link android.view.View}'s
120<a href="{@docRoot}reference/android/view/View.html#setLayerType(int, android.graphics.Paint)">
121    layer type</a> to {@link android.view.View#LAYER_TYPE_HARDWARE}, so that the GPU can cache it as
122a static
123image. The sample
124defines the child view as an inner class of {@code PieChart}, which minimizes the amount of code
125changes that are needed
126to implement this solution.</p>
127
128<pre>
129   private class PieView extends View {
130
131       public PieView(Context context) {
132           super(context);
133           if (!isInEditMode()) {
134               setLayerType(View.LAYER_TYPE_HARDWARE, null);
135           }
136       }
137
138       &#64;Override
139       protected void onDraw(Canvas canvas) {
140           super.onDraw(canvas);
141
142           for (Item it : mData) {
143               mPiePaint.setShader(it.mShader);
144               canvas.drawArc(mBounds,
145                       360 - it.mEndAngle,
146                       it.mEndAngle - it.mStartAngle,
147                       true, mPiePaint);
148           }
149       }
150
151       &#64;Override
152       protected void onSizeChanged(int w, int h, int oldw, int oldh) {
153           mBounds = new RectF(0, 0, w, h);
154       }
155
156       RectF mBounds;
157   }
158</pre>
159
160<p>After this code change, {@code PieChart.PieView.onDraw()} is called only when the view is first
161shown. During the rest
162of the application's lifetime, the pie chart is cached as an image, and redrawn at different
163rotation angles by the GPU.
164GPU hardware is particularly good at this sort of thing, and the performance difference is
165immediately noticeable.</p>
166
167<p>There is a tradeoff, though. Caching images as hardware layers consumes video memory, which is a
168limited resource.
169For this reason, the final version of {@code PieChart.PieView} only sets its layer type to
170{@link android.view.View#LAYER_TYPE_HARDWARE}
171while the user is actively scrolling. At all other times, it sets its layer type to
172{@link android.view.View#LAYER_TYPE_NONE}, which
173allows the GPU to stop caching the image.</p>
174
175<p>Finally, don't forget to profile your code. Techniques that improve performance on one view
176might negatively affect performance on another.</p>
177