原文链接 l
ViewPager and Hardware acceleration
Romain Guy在上周上传了一篇关于硬件加速的博文 Optimizing hardware layers 他解释了硬件加速在一些方面是怎样提升动画的性能,但是在另外一些方面使用硬件加速可能会适得其反。
如果一个view被硬件层所支持,那么硬件层会随着view的更新而更新,之后硬件层上的内容会被绘制到屏幕上。有意思的是,如果你在你的动画过程中改变了view,对于动画的每一帧来说将会做更多的工作,进而会出现掉帧的情况。所以如果你在调用了硬件层之后又改变了view,这是非常不明智的。
在他的文章中他提出了一个简单的方案来检查这个问题,打开手机开发者选项中的“显示硬件层更新”。vIew会在硬件层更新的时候变成绿色。
所以我尝试了一下。
我测试了一个我正在编写的APP,我发现当我滑动ViewPager的时候会变成绿色。但是我并没有设置硬件层更新啊!所以我决定寻找出“真凶”。为了找到“真凶”,我使用了一个小技巧,在滑动ViewPager的时候,我在TraceView中对方法按照方法名进行了排序,并且搜索了“android/view/View.setLayerType"方法并且一直跟踪直到我找到了真正的原因,ViewPager#enableLayers();
private void enableLayers(boolean enable) {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final int layerType = enable ?
ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;
ViewCompat.setLayerType(getChildAt(i), layerType, null);
}
}
这是一个管理是否开启ViewPager子View的硬件层的方法。在ViewPager#setScrollState()方法中被调用。
private void setScrollState(int newState) {
if (mScrollState == newState) {
return;
}
mScrollState = newState;
if (mPageTransformer != null) {
// PageTransformers can do complex things that benefit from hardware layers.
enableLayers(newState != SCROLL_STATE_IDLE);
}
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrollStateChanged(newState);
}
}
我们可以看到,当滚动状态是SCROLL_STATE_IDLE的时候,硬件层被关闭,否则将开启硬件层。这是一个非常好的优化,特别是当ViewPager使用PagerTransformer的时候。使用 PagerTransformer意味着给ViewPager的pages提供了一种自定义出入动画的能力(源码),正因如此,使用硬件层是一个明智的选择。
但是,如果PagerTransformer在ViewPager滑动的时候改变了page的view呢?
自定义的PagerTransformer可能会在view滚动的时候加深view的背景颜色,也可能会改变view内部的组件。在这种情况下,硬件层会在每一帧下都进行更新。这就是为什么我的APP会在滚动的时候变成绿色——我的PagerTransformer改变了滚动的view。
我们现在找到了问题所在,但是要怎么解决呢?首先我的想法是重写我在文章上面所提到关于ViewPager的那几个方法,但是很可惜不可以,因为它们都是private的。不过我可以换一种解决思路:注意观察,在setScrollState()方法中,调用enableLayers()方法之后,还接着调用了mOnPagerChangeListener.onPageScrollStateChanged()方法,所以我所做的就是当滚动状态不是SCROLL_STATE_IDLE的时候,在listener中重置所有ViewPager的子View的LayerType为null。
@Override
public void onPageScrollStateChanged(int scrollState) {
// A small hack to remove the HW layer that the viewpager add to each page when scrolling.
if (scrollState != ViewPager.SCROLL_STATE_IDLE) {
final int childCount = <your_viewpager>.getChildCount();
for (int i = 0; i < childCount; i++)
<your_viewpager>.getChildAt(i).setLayerType(View.LAYER_TYPE_NONE, null);
}
}
这种情况下,在ViewPager#setScrollState()方法设置了ViewPager的子View的LayerType之后,我将它们重置为了null来禁用硬件层。
问题解决了!当我滑动ViewPager的时候,再也不会出现绿色了。
解决这个问题之后,我决定把它上报给谷歌,因为我觉得肯定会有更好的方法,比如通过某种setter在ViewPager滚动的时候来禁用硬件层。