Fragment中transition动画卡顿 及 硬件加速

来源:互联网 发布:linux下安装jdk rpm 编辑:程序博客网 时间:2024/05/16 15:20

Fragment中transition动画卡顿 及 硬件加速

  前几天开发遇到了一个问题,有一个页面比较复杂,Fragment中嵌套了Fragment,因此在显示transition动画时比较卡,体验很不好。于是我去网上查了一下,看到了国外一位同学提供了一个方法。

···java
// 如果不是使用了support library,就需要重写onCreateAnmator()
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
Animation animation = super.onCreateAnimation(transit, enter, nextAnim);

// HW layer support only exists on API 11+if (Build.VERSION.SDK_INT >= 11) {    if (animation == null && nextAnim != 0) {        animation = AnimationUtils.loadAnimation(getActivity(), nextAnim);    }    if (animation != null) {        getView().setLayerType(View.LAYER_TYPE_HARDWARE, null);        animation.setAnimationListener(new AnimationListener() {            public void onAnimationEnd(Animation animation) {                getView().setLayerType(View.LAYER_TYPE_NONE, null);            }            // ...other AnimationListener methods go here...        });    }}return animation;

}
“`

  可以看到他是将Fragment的onCreateAnimation重写,将getView()所返回的view使用硬件加速,在动画完成后又将view的硬件加速取消。我按照他的方法试了一下,还真不卡了~!

  既然硬件加速这么nb,当然要去看看它到底是何方神圣了。

Beginning in Android 3.0 (API level 11), the Android 2D rendering pipeline supports hardware acceleration, meaning that all drawing operations that are performed on a View’s canvas use the GPU. Because of the increased resources required to enable hardware acceleration, your app will consume more RAM.

  从Android3.0开始,Android的2D渲染就支持硬件加速了,这意味着所有在View的canvas上的操作都会使用GPU了。因为硬件加速需要更多的资源,所以你的应用汇消耗该多的内存。

Hardware acceleration is enabled by default if your Target API level is >=14, but can also be explicitly enabled. If your application uses only standard views and Drawables, turning it on globally should not cause any adverse drawing effects. However, because hardware acceleration is not supported for all of the 2D drawing operations, turning it on might affect some of your custom views or drawing calls. Problems usually manifest themselves as invisible elements, exceptions, or wrongly rendered pixels. To remedy this, Android gives you the option to enable or disable hardware acceleration at multiple levels.

  如果你的应用的Target API level >= 14,那么硬件加速就是默认开启的。当然,你也可以手动显示的开启。如果你的应用只使用了标准的Views和Drawables,将硬件加速在全局开启,不会有任何不良效果。然而,由于硬件加速不是支持所有的2D绘制操作,将其打开有可能会影响到一些自定义View或者一些绘制操作。这些不良影响经常表现为:有些元素不见了或者像素渲染得不对。为了补救这些问题,Android系统可以让你在不同级别上开启或关闭硬件加速。

Hardware layers can deliver faster and smoother animations when your application is hardware accelerated. Running an animation at 60 frames per second is not always possible when animating complex views that issue a lot of drawing operations. This can be alleviated by using hardware layers to render the view to a hardware texture. The hardware texture can then be used to animate the view, eliminating the need for the view to constantly redraw itself when it is being animated. The view is not redrawn unless you change the view’s properties, which calls invalidate(), or if you call invalidate() manually. If you are running an animation in your application and do not obtain the smooth results you want, consider enabling hardware layers on your animated views.

  当你的应用在使用硬件加速时,硬件图层的动画会更快更流畅。如果Views比较复杂,上面有很多绘制操作,那么运行动画时很难总是保持在每秒60帧。这样带来的卡顿可以由硬件图层来减轻,硬件图层会将view在hardware texture上渲染。这样,hardware texture就可以用来生成动画效果,而不会重绘view。这个view将永远不会重绘,除非你改变了view的属性,或者你手动调用invalidate()。如果你的应用的动画不够流畅,可以考虑将你需要用animate的view放在硬件图层上

  那么为什么使用硬件加速会使动画更加流畅呢?文档也给出了解释

Software-based drawing model

In the software drawing model, views are drawn with the following two steps:

1.Invalidate the hierarchy

2.Draw the hierarchy

  在软件绘制模型中,views的绘制分为以下两步:
1. invalidate层次结构
2. 绘制层次结构

Whenever an application needs to update a part of its UI, it invokes invalidate() (or one of its variants) on any view that has changed content. The invalidation messages are propagated all the way up the view hierarchy to compute the regions of the screen that need to be redrawn (the dirty region). The Android system then draws any view in the hierarchy that intersects with the dirty region. Unfortunately, there are two drawbacks to this drawing model:

  无论何时应用想要更行它的一部分UI,它都会调用改变了内容的view的invalidate()方法。这个invalidation消息将会一路向上传遍整个层次结构,然后来计算dirty region。Android系统将会重绘任何和dirty region有重合的view。不幸的是,这种绘制模型有两个缺陷。

First, this model requires execution of a lot of code on every draw pass. For example, if your application calls invalidate() on a button and that button sits on top of another view, the Android system redraws the view even though it hasn’t changed.

  首先,在整个绘制过程中需要执行大量的代码。比如,如果你的应用调用了一个button的invalidate()方法,碰巧这个button是在某个其他的view之上,那么即使这个view没有改变,Android系统仍然会重绘这个view。

The second issue is that the drawing model can hide bugs in your application. Since the Android system redraws views when they intersect the dirty region, a view whose content you changed might be redrawn even though invalidate() was not called on it. When this happens, you are relying on another view being invalidated to obtain the proper behavior. This behavior can change every time you modify your application. Because of this, you should always call invalidate() on your custom views whenever you modify data or state that affects the view’s drawing code.
Note: Android views automatically call invalidate() when their properties change, such as the background color or the text in a TextView.

  第二,这种绘制模型可能会掩盖你应用中的bug。既然Android系统会重绘和dirty region相交的view,那么有可能这个view没有调用invalidate(),但仍然被重绘了。如果这种情况发生了,你就是在指望其他的view被调用了invalidate()来保证你的view显示正常。显然,这不靠谱,情况每次都可能变。因此,如果你的自定义view的内容发生了改变,你就总是需要调用invalidate()。

  注意:Android系统在view的属性改变时会自动调用invalidate(),比如说背景色改变或者TextView的文本改变。

Hardware accelerated drawing model

The Android system still uses invalidate() and draw() to request screen updates and to render views, but handles the actual drawing differently. Instead of executing the drawing commands immediately, the Android system records them inside display lists, which contain the output of the view hierarchy’s drawing code. Another optimization is that the Android system only needs to record and update display lists for views marked dirty by an invalidate() call. Views that have not been invalidated can be redrawn simply by re-issuing the previously recorded display list. The new drawing model contains three stages:

  在硬件加速绘制模型中,Android系统仍然是使用invalidate()和draw()来让屏幕更新和渲染视图,但是绘制的实际处理过程会不同。Android系统不会直接执行绘制命令,而是将他们记录在内置的display lists中。这个display lists包含了view的层次结构的绘制代码的对外输出。其他的好处是Android系统只需要来给 被标记为dirty的view记录和更新display lists。一个没有被invalidate的view会因为被补充到display lists中而被重绘。这个绘制模型包含这样3步:

1.Invalidate the hierarchy

2.Record and update display lists

3.Draw the display lists

  1.invalidate层次结构,2.记录和更新display lists, 3.绘制display lists

With this model, you cannot rely on a view intersecting the dirty region to have its draw() method executed. To ensure that the Android system records a view’s display list, you must call invalidate(). Forgetting to do so causes a view to look the same even after it has been changed.

  在这个模型中,你不能指望一个view回应为和dirty region相交而被重绘。为了保证Android系统会记录view的display list,你需要调用invalidate()。忘记调用invalidate()会让view看起来像没有改变一样。

Using display lists also benefits animation performance because setting specific properties, such as alpha or rotation, does not require invalidating the targeted view (it is done automatically). This optimization also applies to views with display lists (any view when your application is hardware accelerated.) For example, assume there is a LinearLayout that contains a ListView above a Button. The display list for the LinearLayout looks like this:

  使用display list同样对动画的性能有好处,因为设置特定的属性,如alpha或者rotation没有必要invalidate view(它是自动invalidate的)。比如在一个Linearlayout中,有一个ListView在Button之上,那么那个LinearLayout的display list看起来就像这样:

DrawDisplayList(ListView)

DrawDisplayList(Button)

Assume now that you want to change the ListView’s opacity. After invoking setAlpha(0.5f) on the ListView, the display list now contains this:

  假设现在你想要改变ListView的透明度。在调用ListView.setAlpha(0.5f)之后,display list现在包含:

SaveLayerAlpha(0.5)

DrawDisplayList(ListView)

Restore

DrawDisplayList(Button)

The complex drawing code of ListView was not executed. Instead, the system only updated the display list of the much simpler LinearLayout. In an application without hardware acceleration enabled, the drawing code of both the list and its parent are executed again.

  ListView复杂的绘制代码就没有被执行。替代它的是操作是,系统更新一下简单得多的LinearLayout的display list。在一个没有硬件加速的应用中,ListView和它的parent的绘制代码都会被执行。

1 0
原创粉丝点击