以下代码皆为伪代码,并不能实际显示View,忽略了很多细节,目的是为了方便日后回顾结构。
public class ViewRootImpl {
boolean mFullRedrawNeeded;
public final Surface mSurface = new Surface();
int mWidth;
int mHeight;
Rect mDirty;
private View mView;
public ViewRootImpl(){
mWidth = -1;
mHeight = -1;
mDirty = new Rect();
}
public void setView(View view) {
if (mView == null) {
mView = view;
}
}
void doTraversal() {
mWidth = 100;
mHeight = 100;
mDirty.set(0, 0, mWidth, mHeight);
performTraversals();
}
private void performTraversals() {
mDirty.set(0, 0, mWidth, mHeight);
performDraw();
}
//为了减少代码量所以一律改成void
private void performDraw() {
final boolean fullRedrawNeeded = mFullRedrawNeeded;
draw(fullRedrawNeeded);
}
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
final Rect dirty = mDirty;
drawSoftware(surface,dirty);
}
/**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface,Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
canvas = mSurface.lockCanvas(dirty);
mView.draw(canvas);
return true;
}
}
public class View {
public int mPrivateFlags;
static final int PFLAG_SKIP_DRAW = 0x00000080;
static final int PFLAG_DIRTY_MASK = 0x00200000;
public View(Context context) {
}
public void draw(Canvas canvas) {
// Step 3, draw the content
onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
}
protected void onDraw(Canvas canvas) {
}
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
//PFLAG_SKIP_DRAW = 1000 0000
//如果mPrivateFlags = 1000 0000
// 也就是说,如果此时如果mPrivateFlags为1000 0000,也就是调用draw(Canvas canvas, ViewGroup parent, long drawingTime)的View不需要绘制
// 那么就调用dispatchDraw,绘制调用draw(Canvas canvas, ViewGroup parent, long drawingTime)的View的children。
// 这里Flags 暂不深入,什么时候mPrivateFlags = 1000 0000,当前调用draw的View为ViewGroup的时候吗?需在framework中加tag去测试。
//取反0111 1111
//1000 0000
//0111 1111
//mPrivateFlags = 0000 0000,执行dispatchDraw。
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
//反之则调用draw(canvas),绘制自身。
draw(canvas);
}
return false;
}
protected void dispatchDraw(Canvas canvas) {
}
}
public abstract class ViewGroup extends View{
// Child views of this ViewGroup
private View[] mChildren = new View[1];
public ViewGroup(Context context) {
super(context);
}
public void addView(View child) {
mChildren[0] = child;
}
@Override
protected void dispatchDraw(Canvas canvas) {
int childrenCount = 1;
final long drawingTime = 0;
for (int i = 0; i < childrenCount; i++) {
View child = mChildren[0];
drawChild(canvas, child, drawingTime);
}
}
protected boolean drawChild(Canvas canvas,View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
}
public class LinearLayout extends ViewGroup {
private int mOrientation;
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
private Drawable mDivider;
public LinearLayout(Context context) {
super(context);
mOrientation = 1;
mDivider = context.getDrawable(R.drawable.divider_vertical_bright);
}
@Override
protected void onDraw(Canvas canvas) {
if (mOrientation == VERTICAL) {
drawDividersVertical(canvas);
} else {
drawDividersHorizontal(canvas);
}
}
void drawDividersVertical(Canvas canvas) {
drawHorizontalDivider(canvas);
}
void drawDividersHorizontal(Canvas canvas) {
}
void drawHorizontalDivider(Canvas canvas) {
//Bounds的数值是随便定义的
mDivider.setBounds(100, 100, 500, 500);
mDivider.draw(canvas);
}
}
divider_vertical_bright实际是放在framework中。但是文件可通过快速按2次shift键搜索文件得出,然后copy至工程目录下。
image.png
public class ImageView extends View{
private Drawable mDrawable = null;
public ImageView(Context context) {
super(context);
mDrawable = context.getDrawable(R.drawable.ic_launcher_background);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
}
}
ic_launcher_background是新建工程自带文件。
public class MainActivity extends AppCompatActivity {
private ViewRootImpl mViewRoot;
private LinearLayout mLinearLayout;
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewRoot = new ViewRootImpl();
mLinearLayout = new LinearLayout(this);
mImageView = new ImageView(this);
mLinearLayout.addView(mImageView);
mViewRoot.setView(mLinearLayout);
mViewRoot.doTraversal();
}
}
当调用mViewRoot.doTraversal()后,会调用ViewRootImpl中的drawSoftware(Surface surface,Rect dirty)。然后走到mView.draw(canvas) ,也就是mLinearLayout.draw(canvas),因为之前调用了setView(mLinearLayout)。而draw(canvas)是定义在View中供所有继承View的类使用,那么mLinearLayout.draw(canvas)会调用mLinearLayout.onDraw(canvas)和mLinearLayout.dispatchDraw(canvas)。
dispatchDraw(canvas)作用是draw the children,在ViewGroup中重写。
onDraw(canvas)作用是draw the content,ViewGroup和View中均可重写(ViewGroup中可重写,可不重写,例如LinearLayout中定义了,而FrameLayout中为重写,View中一般重写)。按实际需求来,需要改变的话,就重写。
LinearLayout调用了onDraw(canvas)后最终调用mDivider.draw(canvas);,也就是实现了mLinearLayout自身的绘制,给mLinearLayout自己绘制了个图片。
然后调用 mLinearLayout.dispatchDraw(canvas);
代码走到mImageView.draw(canvas, this, drawingTime),这里我对mPrivateFlags的赋值具体情况不太理解,但是如果不是ViewGroup的话应该是走到mImageView.draw(canvas),然后会再次调用onDraw(canvas)和dispatchDraw(canvas); ,也就是mImageView.onDraw(canvas)和mImageView.dispatchDraw(canvas); ,mImageView.dispatchDraw未重写且无具体内容,只需要看重写的mImageView.onDraw(canvas)。
//ImageView
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
}
ImageView的onDraw定义绘制了一张图。
以上流程大致可以概括如下:
1.ViewRoot
mViewRoot.doTraversal()-->mViewRoot.performTraversals() --> mViewRoot.performDraw()--> mViewRoot.draw(fullRedrawNeeded)-->mViewRoot.drawSoftware(surface,dirty) -->
2.LinearLayout
mLinearLayout.draw(canvas)
-->mLinearLayout.onDraw(canvas)-->mLinearLayout.drawHorizontalDivider(canvas)-->mDivider.draw(canvas)
-->mLinearLayout.dispatchDraw(canvas) -->mLinearLayout.drawChild(canvas, child, drawingTime)-->
3.ImageView
mImageView.draw(canvas, this, drawingTime)-->mImageView.draw(canvas)-->mImageView.onDraw(canvas)-->mDrawable.draw(canvas)
无标题.png
参考链接:
深入理解 Android 之 View 的绘制流程(四)_Draw
Android 系统架构 —— View 的硬件渲染
Android 源码分析三 View 绘制
图解View绘制原理(软件渲染篇)
手把手教你读懂源码,View的绘制流程详细剖析
Android 源码分析 - View的measure、layout、draw三大流程
Android自定义View(三)-Draw原理篇
Android View绘制(四)onDraw过程与Canvas Bitmap
源码分析篇 - Android绘制流程(二)measure、layout、draw流程
Android中View自定义XML属性详解以及R.attr与R.styleable的区别
带你一步步了解 onDraw() 和 dispatchDraw() 的区别
View绘制体系(三)——AttributeSet与TypedArray详解
android 自定义 styleable 属性
declare-styleable的使用












网友评论