Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> View和ViewGroup的基本繪制流程

View和ViewGroup的基本繪制流程

編輯:關於Android編程

需要了解的

先來張圖說明一下它們的關系
view和viewgroup
你還要知道ViewGroup之間是可以嵌套的.

View的繪制流程

不知道大家有沒有這種疑惑, 為什麼我們在寫布局文件的時候, 一定要寫layout_width和layout_height呢, 難道就沒有默認值嗎? 顏色, 背景, 等等其他的都有默認值, 為什麼寬高就一定要我們手動寫呢? 接下來就讓我們一起來解答這個疑惑吧. 繪制流程的源碼就不貼出來了, 有興趣的可以打開View的源碼對照著來看, 印象會更深刻, 當然, 不看源碼, 理解以下的實例代碼, 也不會影響你對整個流程的理解. 首先看一下View的繪制流程示例:
public class MyView extends View {

    private static final String TAG = "MyView";


    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * measure - > onMeasure ,view的源碼中,measure會調用onMeasure
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  //測量, 表示這個view的大小, 在View的源碼中, Measure是final修飾的, 我們只能重寫onMeasure方法
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "measure");
    }

    /**
     * layout - > setFrame 和 onLayout
     */
    @Override
    public void layout(int l, int t, int r, int b) {  //布局,決定了擺放在父容器中的哪個位置
        // TODO Auto-generated method stub
        super.layout(l, t, r, b);
        Log.d(TAG, "layout");
    }

    /**
     * draw - > onDraw 
     */
    @Override
    public void draw(Canvas canvas) {  //繪制
        // TODO Auto-generated method stub
        super.draw(canvas);
        Log.d(TAG, "draw");
    }
}
以上就是View繪制顯示在屏幕上必定會調用的三個方法, 可能你還不太理解, 不過沒關系, 先混個臉熟, 有個印象先, 下面我們一個個分析.

View的onMeasure

來看一段Demo
public class MyView extends View {

    private static final String TAG = "MyView";

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * measure -> onMeasure
     * 
     * 1.父容器拿到孩子的申請的寬高layout_width, layout_height封裝成寬高的期望widthMeasureSpec和heightMeasureSpec
     * 父容器Relativelayout(或者其他Linearlyout)
     * 調用MyView的 measure(int widthMeasureSpec, int heightMeasureSpec)傳入對孩子寬高的期望
     * measure -> onMeasure(widthMeasureSpec, heightMeasureSpec)
     * 
     * @param widthMeasureSpec 父容器(RelativeLaoyut)對孩子MyView的寬度期望, 跟layout_width相關
     * @param heightMeasureSpec 父容器(RelativeLaoyut)對孩子MyView的高度期望, 跟layout_height相關
     * 這是我們為什麼一定要指定layout_width和layout_height的原因.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
        * int widthMeasureSpec
        * 32位二進制
        * 前兩位 是測量模式 mode
        * public static final int UNSPECIFIED = 0 << MODE_SHIFT; 父容器對孩子沒有任何的限制,孩子想多大多大
        * public static final int EXACTLY     = 1 << MODE_SHIFT; 父容器對孩子有確切的大小要求,大小就會後30位
        * public static final int AT_MOST     = 2 << MODE_SHIFT; 父容器對孩子的最大值有要求,大小就會後30位
        * 後30位表示大小
        */
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        Log.d(TAG, "onMeasure mode " + (mode>>30) + " " + size);

        //super方法默認使用父容器對我的期望的寬高
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //我們也可以不調用super直接調用setMeasuredDimension(50, 50)來指定寬高
    }

}
注釋裡的setMeasuredDimension(int measuredWidth, int measuredHeight)是一個重要的方法, 它是整個測量結束的標志, 只有這個方法調用了, 我們才能調用getMeasuredWidth()或者getMeasuredHeight()方法獲得測量寬高(注意, 不是實際寬高, 實際寬高要在布局完成之後)。 自定義View如果要使用wrap_content屬性的話,則需重寫onMeasure方法。

View的layout

layout方法裡面調用setFrame(),給View的上下左右四個位置mLeft, mTop,mRight, mBottom賦值,完成布局工作. onLayout()是一個空的方法,說明具體的布局不應該由view來決定 我們的view並不需要關心layout方法, 布局的事應該交由父容器去處理, 讓它決定它的孩子應該擺放在哪個地方. 在布局完成後, 我們才能調用getWidth()和getHeight獲得實際的寬高. getWidth()和getMeasuredWidth()的區別: getMeasureWidth()方法在measure()過程結束後就可以獲取到了,而getWidth()方法要在layout()過程結束後才能獲取到。另外,getMeasureWidth()方法中的值是通過setMeasuredDimension()方法來進行設置的,而getWidth()方法中的值則是通過視圖右邊的坐標減去左邊的坐標計算出來的。

View的draw

view的繪制分為6步:

對視圖的背景進行繪制 If necessary, save the canvas’ layers to prepare for fading (暫時忽略它) 對視圖的內容進行繪制, 在onDraw(canvas)方法中完成 對當前視圖的所有子視圖進行繪制 ,調用dispatchDraw。 If necessary, draw the fading edges and restore layers (暫時忽略它) 繪制裝飾品(如滾動條)任何一個視圖都是有滾動條的,只是一般情況下我們都沒有讓它顯示出來而已.

即我們關心四個步驟:

繪制背景 繪制內容 繪制孩子 繪制裝飾

繪制需要兩個類, 畫布(Canvas)和畫筆(Paint), 通過以下Demo通過onDraw方法利用畫筆在畫布上繪制我們的圖案吧.

public class MyView extends View {

    private Paint mPaint;
    private Bitmap mBitmap;
    private Path mPath;
    private RectF mOval;


    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        //設置去鋸齒
        mPaint.setAntiAlias(true);
        //配置畫筆,畫空心圓
        mPaint.setStyle(Style.STROKE);
        //設置畫筆寬度
        mPaint.setStrokeWidth(3);

        //設置畫筆顏色
        mPaint.setColor(Color.BLUE);

        //畫圖片時需要設置圖片
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.haha);

        //設置扇形的大小
        mOval = new RectF(5, 5, 195, 195);

        initPath();
    }

    private void initPath() {
        mPath = new Path();
        //確定三個點
        int x1 = 100, y1 = 5;
        int x2 = 195, y2 = 195;
        int x3 = 5, y3 = 195;
        //移動到第一個點
        mPath.moveTo(x1, y1);
        //鏈接第一個點和第二個點
        mPath.lineTo(x2, y2);
        //鏈接第二個點和第三個點
        mPath.lineTo(x3, y3);
        mPath.lineTo(x1, y1);

    }

    /**
     * 不要在onDraw方法裡面創建新的對象,因為onDraw方法可能會頻繁調用
     */
    @Override
    protected void onDraw(Canvas canvas) {
//      6. 裁剪
//      canvas.clipPath(mPath);


//      1. 畫直線
//      int startX =5, startY = 100;
//      int stopX = 195, stopY = 100;
//      canvas.drawLine(startX, startY, stopX, stopY, mPaint);
//      2. 畫圓
//      int cx = 100,  cy = 100;
//      int radius = 80;
//      canvas.drawCircle(cx, cy, radius, mPaint);
//      3. 畫空心圓

//      4. 畫圖片
//      canvas.drawBitmap(mBitmap, 0, 0, mPaint);
//      5. 畫三角形
//      canvas.drawPath(mPath, mPaint);

        //7.畫扇形
        int startAngle = -90; //開始的角度
        int sweepAngle = 45;  //掃過的角度
        boolean useCenter = false;//是否畫出扇形的兩邊
        canvas.drawArc(mOval, startAngle, sweepAngle, useCenter, mPaint);
    }
}

View的重新繪制

invalidate(); //觸發View的重新繪制 onDraw postInvalidate(); //請求在主線程重新繪制控件 onDraw

ViewGroup的繪制流程

ViewGroup繼承View,繪制流程跟View是一致

ViewGroup的測量

相同點:measure -> onMeasure 不同點:ViewGroup需要在onMeasure去測量孩子 自定義ViewGroup一定要重寫onMeasure方法,如果不重寫則子View獲取不到寬和高。重寫是在onMeasure方法中調用measureChildern()方法,遍歷出所有子View並對其進行測量。

ViewGroup的布局

相同點:layout (父容器調用) -》 onLayout 不同點:ViewGroup需要實現onLayout方法去布局孩子,調用孩子的layout方法,指定孩子上下左右的位置 requestLayout();//請求重新布局 onLayout

ViewGroup的繪制

相同點:draw -> onDraw 不同點:ViewGroup一般不繪制自己,ViewGroup默認實現dispatchDraw去繪制孩子
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved