首页 > 技术文章 > Android graphic:surfaceview 挖洞过程的透明区域

geeks 2013-08-23 17:54 原文

 

1) 透明区域收集的激发机制

 ViewRootImpl::PerformTraversal 函数作图过程中, 满足一些条件时,就会调用gathertransparentregion来挖洞。

 条件是 1) view上面有一个surfaceview  2) didlayout is true.

   didlayout 是真的来源一般是调用了requestlayout()函数。  一般当view的size或者透明性发生了修改的时候,这个函数会被调用。

 

  requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onMeasure onLayout来对重新设置自己位置。Call this when something has changed which has invalidated the layout of this view

    

2)透明区域收集的算法

摘要如下:

 

(1). 调用父类View的成员函数gatherTransparentRegion来检查当前正在处理的视图容器是否需要绘制。如果需要绘制的话,那么就会将它所占据的区域从参数region所占据的区域移除,这是因为参数region所描述的区域开始的时候是等于窗口的顶层视图的大小的,也就是等于窗口的整个大小的。

(2). 调用当前正在处理的视图容器的每一个子视图的成员函数gatherTransparentRegion来继续往下收集透明区域。
在接下来的Step 6中,我们再详细分析当前正在处理的视图容器的每一个子视图的透明区域的收集过程,现在我们主要分析View类的成员函数gatherTransparentRegion的实现,如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
......
public boolean gatherTransparentRegion(Region region) {
final AttachInfo attachInfo = mAttachInfo;
if (region != null && attachInfo != null) {
final int pflags = mPrivateFlags;
if ((pflags & SKIP_DRAW) == 0) {
// The SKIP_DRAW flag IS NOT set, so this view draws. We need to
// remove it from the transparent region.
final int[] location = attachInfo.mTransparentLocation;
getLocationInWindow(location);
region.op(location[0], location[1], location[0] + mRight - mLeft,
location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
} else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBGDrawable != null) {
// The ONLY_DRAWS_BACKGROUND flag IS set and the background drawable
// exists, so we remove the background drawable's non-transparent
// parts from this transparent region.
applyDrawableToTransparentRegion(mBGDrawable, region);
}
}
return true;
}
......
}

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

View类的成员函数gatherTransparentRegion首先是检查当前正在处理的视图的前景是否需要绘制,即检查成员变量mPrivateFlags的值的SKIP_DRAW位是否等于0。如果等于0的话,那么就说明当前正在处理的视图的前景是需要绘制的。在这种情况下,View类的成员函数gatherTransparentRegion就会将当前正在处理的视图所占据的区域从参数region所描述的区域中移除,以便当前正在处理的视图的前景可以显示出来。

另一方面,如果当前正在处理的视图的前景不需要绘制,但是该视图的背景需要绘制,并且该视图是设置有的,即成员变量mPrivateFlags的值的SKIP_DRAW位不等于0,并且成员变量mBGDrawable的值不等于null,这时候View类的成员函数gatherTransparentRegion就会调用另外一个成员函数applyDrawableToTransparentRegion来将该背景中的不透明区域从参数region所描述的区域中移除,以便当前正在处理的视图的背景可以显示出来。

回到ViewGroup类的成员函数gatherTransparentRegion中,当前正在处理的视图容器即为当前正在处理的窗口的顶层视图,前面我们已经假设它里面嵌入有一个SurfaceView子视图,因此,接下来就会收集该SurfaceView子视图所设置的透明区域,这是通过调用SurfaceView类的成员函数gatherTransparentRegion来实现的。

接下来,我们就继续分析SurfaceView类的成员函数gatherTransparentRegion的实现,以便可以继续了解SurfaceView的挖洞过程。

Step 6. SurfaceView.gatherTransparentRegion

public class SurfaceView extends View {
......
@Override
public boolean gatherTransparentRegion(Region region) {
if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
return super.gatherTransparentRegion(region);
}
boolean opaque = true;
if ((mPrivateFlags & SKIP_DRAW) == 0) {
// this view draws, remove it from the transparent region
opaque = super.gatherTransparentRegion(region);
} else if (region != null) {
int w = getWidth();
int h = getHeight();
if (w>0 && h>0) {
getLocationInWindow(mLocation);
// otherwise, punch a hole in the whole hierarchy
int l = mLocation[0];
int t = mLocation[1];
region.op(l, t, l+w, t+h, Region.Op.UNION);
}
}
if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
opaque = false;
}
return opaque;
}
......
}

这个函数定义在文件frameworks/base/core/java/android/view/SurfaceView.java中。

SurfaceVie类的成员函数gatherTransparentRegion首先是检查当前正在处理的SurfaceView是否是用作窗口面板的,即它的成员变量mWindowType的值是否等于WindowManager.LayoutParams.TYPE_APPLICATION_PANEL。如果等于的话,那么就会调用父类View的成员函数gatherTransparentRegion来检查该面板是否需要绘制。如果需要绘制,那么就会将它所占据的区域从参数region所描述的区域移除。

假设当前正在处理的SurfaceView不是用作窗口面板的,那么SurfaceVie类的成员函数gatherTransparentRegion接下来就会直接检查当前正在处理的SurfaceView是否是需要在宿主窗口的绘图表面上进行绘制,即检查成员变量mPrivateFlags的值的SKIP_DRAW位是否等于1。如果需要的话,那么也会调用父类View的成员函数gatherTransparentRegion来将它所占据的区域从参数region所描述的区域移除。

假设当前正在处理的SurfaceView不是用作窗口面板,并且也是不需要在宿主窗口的绘图表面上进行绘制的,而参数region的值又不等于null,那么SurfaceVie类的成员函数gatherTransparentRegion就会先计算好当前正在处理的SurfaceView所占据的区域,然后再将该区域添加到参数region所描述的区域中去,这样就可以得到窗口的一个新的透明区域。

最后,SurfaceVie类的成员函数gatherTransparentRegion判断当前正在处理的SurfaceView的绘图表面的像素格式是否设置有透明值。如果有的话,那么就会将变量opaque的值设置为false,否则的话,变量opaque的值就保持为true。变量opaque的值最终会返回给调用者,这样调用者就可以知道当前正在处理的SurfaceView的绘图表面是否是半透明的了。

至此,我们就分析完成SurfaceView的挖洞过程了,接下来我们继续分析SurfaceView的绘制过程。

 

3)透明区域的显现

 

 

推荐阅读