您的位置:首页 > 其它

View 的绘制流程

2016-08-20 23:23 369 查看
对于View绘制这一块,其实是老生常谈了,网上的的讲解也比较多,自己看了一些总结一下

1.View的绘制

对于View的绘制,最主要需要了解的方法就是onDraw(Canvas c)方法, 系统通知View进行绘制的时候,是调用View的Draw方法:

public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;  // Step 1, draw the background, if needed
int saveCount;

if (!dirtyOpaque) {
drawBackground(canvas);     //绘制背景
}

// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);    <strong> //调用onDraw(Canvas c)方法</strong>

// Step 4, draw the children
dispatchDraw(canvas);

// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}

// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);

// we're done...
return;
}


/**
* Implement this to do your drawing.
*
* @param canvas the canvas on which the background will be drawn
*/
protected void onDraw(Canvas canvas) {
}


这里需要注意的是onDraw方法,方法的文档写的也很清楚了, 我们要写自定义View的时候需要继承View并且Override这个方法, 

参数Canvas 被绘制的内容就是这个控件显示的内容 。接下来, 需要知道的就是如何绘制我们自己想要的东西呢? 我们需要了解两个类:

1) Canvas (画布)
2) Paint  (画笔)


一个是画笔, 一个是画布, 这样就很直观了, 就是通过画笔在画布上画你想要的东西, 做了一个小的Demo

代码:

public class SelfView extends View{
public SelfView(Context context) {
super(context);
}
public SelfView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelfView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SelfView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.e("Seeyou",  "View onMeasure");
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.e("Seeyou",  "View onLayout");
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.e("Seeyou",  "View onDraw");
canvas.drawCircle(getMeasuredWidth() / 2 ,getMeasuredWidth() / 2, getMeasuredWidth() / 2, new Paint());   // 在画布上画圆
Paint textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(200.0f);
canvas.drawText("View", getMeasuredWidth() / 2 / 2, getMeasuredWidth() / 2, textPaint);   // 在画布上写字
}
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.jj.www.viewteachdemo.MainActivity">

<cn.demo.view.SelfView

android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:background="#223344" />

</RelativeLayout>

效果:



链接: Canvas
绘制讲解

2.View的重绘

ok到这里, 基本的绘制就算掌握了, 还有一个方法需要了解, 就是View的invalidate()方法. 通常也叫作view的重绘, 顾名思义就是把画板擦干净重新画
public void invalidate ()
Added in API level 1
Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future.
This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().


说明文档告诉我们, 调用这个方法的时候, 如果这个view是visible的状态,那么就会调用它的onDraw方法

Demo:
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(center_x, center_y, 150, paint);
super.onDraw(canvas);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
center_x = event.getX();
center_y = event.getY();    //记录触摸事件的x y坐标;
invalidate();   //每次产生触摸事件的时候都会让我们的控件进行重绘
return true;
}


这样一个触摸时就会产生一个跟随触摸点移动的圆形的控件就完成了, 挺简单的吧, 动手尝试一下吧.

以上就是view的绘制和重绘, 那么接下来了解一下ViewGroup的绘制流程是什么样的呢?

3.ViewGroup绘制流程
view树结构:



view树绘制流程

Measure(测量) -> Layout(布局) -> Draw(绘制)


——|onMeasure(测量)

1.递归执行字View的onMeasure进行测量

2.执行方法setMeasuredDimension(width, heigh); 告诉父View自己需要的宽高

——|——|MeasureSpec

MeasureSpec.getSize(measureSpec);

获取父类空间传过来的可用大小

MeasureSpec.getMode(measureSpec);

获取父类空间传过来的mode

1:UNSPECIFIED(未指定),父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小

2:EXACTLY(完全),父元素决定子元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小

3:AT_MOST(至多),子元素至多达到指定大小的值

参考blog: 自定义View之onMeasure()

——|onLayout(布局)

1.通过childView.layout(l, t, r, b); 将子view进行布局

——|onDraw(绘制)

1.进行View的绘制工作

Tips:

ViewGroup 主要操作的两个方法是onMeasure(测量) onLayout(布局)

View 主要操作的两个方法是onMeasure(测量) onDraw(绘制)

View 控件不被ViewGroup包裹的话 它是没有大小的

常用方法:

1.dispatchDraw() 会通知每一个子view 进行绘制 调用draw 方法

2.requestLayout(); 会进行重新的布局
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息