| 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> |