f284d49293
Change-Id: I0523fb8eb8608b07cf7ca866d0c2ccfcbfc2e946
177 lines
7.4 KiB
Plaintext
177 lines
7.4 KiB
Plaintext
page.title=Optimizing the View
|
|
parent.title=Creating Custom Views
|
|
parent.link=index.html
|
|
|
|
trainingnavtop=true
|
|
previous.title=Making the View Interactive
|
|
previous.link=making-interactive.html
|
|
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#less">Do Less, Less Frequently</a></li>
|
|
<li><a href="#accelerate">Use Hardware Acceleration</a></li>
|
|
</ol>
|
|
|
|
<h2>You should also read</h2>
|
|
<ul>
|
|
<li><a href="{@docRoot}guide/topics/graphics/hardware-accel.html">
|
|
Hardware Acceleration
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
<h2>Try it out</h2>
|
|
<div class="download-box">
|
|
<a href="{@docRoot}shareables/training/CustomView.zip"
|
|
class="button">Download the sample</a>
|
|
<p class="filename">CustomView.zip</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<p>Now that you have a well-designed view that responds to gestures and transitions between states,
|
|
you need to ensure
|
|
that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, you must
|
|
ensure that your
|
|
animations consistently run at 60 frames per second.</p>
|
|
|
|
<h2 id="less">Do Less, Less Frequently</h2>
|
|
|
|
<p>To speed up your view, eliminate unnecessary code from routines that are called frequently. Start
|
|
by working on
|
|
{@link android.view.View#onDraw onDraw()}, which will give you the biggest payback. In particular
|
|
you should eliminate
|
|
allocations in {@link android.view.View#onDraw onDraw()}, because allocations may lead to a garbage
|
|
collection that
|
|
would cause a stutter. Allocate objects during initialization, or between animations. Never make an
|
|
allocation while an
|
|
animation is running.</p>
|
|
|
|
<p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, you should also make sure
|
|
it's called as
|
|
infrequently as possible. Most calls to {@link android.view.View#onDraw onDraw()} are the result of
|
|
a call to {@link
|
|
android.view.View#invalidate() invalidate()}, so eliminate unnecessary calls to {@link
|
|
android.view.View#invalidate()
|
|
invalidate()}. When possible, call the four-parameter variant of {@link
|
|
android.view.View#invalidate() invalidate()}
|
|
rather than the version that takes no parameters. The no-parameter variant invalidates the entire
|
|
view, while the
|
|
four-parameter variant invalidates only a specified portion of the view. This approach allows draw calls to
|
|
be more efficient and
|
|
can eliminate unnecessary invalidation of views that fall outside the invalid rectangle.</p>
|
|
|
|
<p>Another very expensive operation is traversing layouts. Any time a view calls {@link
|
|
android.view.View#requestLayout()
|
|
requestLayout()}, the Android UI system needs to traverse the entire view hierarchy to find out how
|
|
big each view needs
|
|
to be. If it finds conflicting measurements, it may need to traverse the hierarchy multiple times.
|
|
UI designers
|
|
sometimes create deep hierarchies of nested {@link android.view.ViewGroup ViewGroup} objects in
|
|
order to get the UI to
|
|
behave properly. These deep view hierarchies cause performance problems. Make your view hierarchies
|
|
as shallow as
|
|
possible.</p>
|
|
|
|
<p>If you have a complex UI, you should consider writing a custom {@link android.view.ViewGroup
|
|
ViewGroup} to perform
|
|
its layout. Unlike the built-in views, your custom view can make application-specific assumptions
|
|
about the size and
|
|
shape of its children, and thus avoid traversing its children to calculate measurements. The
|
|
PieChart example shows how
|
|
to extend {@link android.view.ViewGroup ViewGroup} as part of a custom view. PieChart has child
|
|
views, but it never
|
|
measures them. Instead, it sets their sizes directly according to its own custom layout
|
|
algorithm.</p>
|
|
|
|
<h2 id="accelerate">Use Hardware Acceleration</h2>
|
|
|
|
<p>As of Android 3.0, the Android 2D graphics system can be accelerated by the GPU (Graphics
|
|
Processing Unit) hardware
|
|
found in most newer Android devices. GPU hardware acceleration can result in a tremendous
|
|
performance increase for many
|
|
applications, but it isn't the right choice for every application. The Android framework
|
|
gives you the ability to finely control which parts of your application are or are not
|
|
hardware accelerated.</p>
|
|
|
|
<p>See <a href="{@docRoot}guide/topics/graphics/hardware-accel.html">Hardware Acceleration</a>
|
|
in the Android Developers Guide for directions on how to enable acceleration at the
|
|
application, activity, or window level. Notice that in addition to the directions in
|
|
the developer guide, you must also set your application's target API to 11 or higher by
|
|
specifying {@code <uses-sdk
|
|
android:targetSdkVersion="11"/>} in your {@code AndroidManifest.xml} file.</p>
|
|
|
|
<p>Once you've enabled hardware acceleration, you may or may not see a performance increase.
|
|
Mobile GPUs are very good at certain tasks, such as scaling, rotating, and translating
|
|
bitmapped images. They are not particularly good at other tasks, such as drawing lines or curves. To
|
|
get the most out of GPU acceleration, you should maximize the number of operations that the GPU is
|
|
good at, and minimize the number of operations that the GPU isn't good at.</p>
|
|
|
|
<p>In the PieChart example, for instance, drawing the pie is relatively expensive. Redrawing the pie
|
|
each time it's
|
|
rotated causes the UI to feel sluggish. The solution is to place the pie chart into a child
|
|
{@link android.view.View} and set that
|
|
{@link android.view.View}'s
|
|
<a href="{@docRoot}reference/android/view/View.html#setLayerType(int, android.graphics.Paint)">
|
|
layer type</a> to {@link android.view.View#LAYER_TYPE_HARDWARE}, so that the GPU can cache it as
|
|
a static
|
|
image. The sample
|
|
defines the child view as an inner class of {@code PieChart}, which minimizes the amount of code
|
|
changes that are needed
|
|
to implement this solution.</p>
|
|
|
|
<pre>
|
|
private class PieView extends View {
|
|
|
|
public PieView(Context context) {
|
|
super(context);
|
|
if (!isInEditMode()) {
|
|
setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
super.onDraw(canvas);
|
|
|
|
for (Item it : mData) {
|
|
mPiePaint.setShader(it.mShader);
|
|
canvas.drawArc(mBounds,
|
|
360 - it.mEndAngle,
|
|
it.mEndAngle - it.mStartAngle,
|
|
true, mPiePaint);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
mBounds = new RectF(0, 0, w, h);
|
|
}
|
|
|
|
RectF mBounds;
|
|
}
|
|
</pre>
|
|
|
|
<p>After this code change, {@code PieChart.PieView.onDraw()} is called only when the view is first
|
|
shown. During the rest
|
|
of the application's lifetime, the pie chart is cached as an image, and redrawn at different
|
|
rotation angles by the GPU.
|
|
GPU hardware is particularly good at this sort of thing, and the performance difference is
|
|
immediately noticeable.</p>
|
|
|
|
<p>There is a tradeoff, though. Caching images as hardware layers consumes video memory, which is a
|
|
limited resource.
|
|
For this reason, the final version of {@code PieChart.PieView} only sets its layer type to
|
|
{@link android.view.View#LAYER_TYPE_HARDWARE}
|
|
while the user is actively scrolling. At all other times, it sets its layer type to
|
|
{@link android.view.View#LAYER_TYPE_NONE}, which
|
|
allows the GPU to stop caching the image.</p>
|
|
|
|
<p>Finally, don't forget to profile your code. Techniques that improve performance on one view
|
|
might negatively affect performance on another.</p>
|