自定義View(三)的常用方法(測量、繪製、位置)

       更多內容可參考
       (一)自定義View的分類點擊打開鏈接
       (二)自定義View的構造方法及自定義屬性點擊打開鏈接點擊打開鏈接
       (三)自定義View的常用方法(測量、繪製、位置)參見本文
       (四)自定義View的具體實現

       (五)事件分發機制


         自定義view中可以重載的方法很多,最常用的有如下幾個:
         方法名                               作用         
         onDraw                              繪製View
         onMeasure                        測量View(子View)大小
         onSizeChanged                 確定View大小
         onLayout                           確定子View佈局 

             

          一、測量View大小—— onMeasure     

        自定義View時,我們使用onMeasure方法來測量View本身或子View的大小。我們通常會在xml文件中指定View的長和寬,或者在代碼中動態的設置View的長和寬,onMeasure測量的就是這些值。

        既然View的長和寬已經在用戶使用時通過xml或代碼指定了,那麼onMeasure還有什麼意義呢?     

        在onMeasure方法中,我們獲取到控件長和寬的測量模式和測量值,並根據具體情況對測量到的長寬值進行調整,最後使用 setMeasuredDimension(widthsize,heightsize)方法設置調整過的長和寬。    

         ①測量View大小    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);// 獲取佈局文件中子控件的測量值
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);// 獲取佈局文件中子控件的測量模式

        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        int width = modeWidth == MeasureSpec.EXACTLY ? sizeWidth : 10;
        int height = modeHeight == MeasureSpec.EXACTLY ? sizeHeight : 10;
        setMeasuredDimension(width, height);
    }

          ②測量子View大小——繼承自ViewGroup的控件      

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);// 獲取每一個子view
            measureChild(child, widthMeasureSpec, heightMeasureSpec);// 測量子view的寬高
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

        onMeasure方法傳入兩個int型的參數widthMeasureSpec和heightMeasureSpec,他們並不是View的長和寬,但通過他們可以獲取長寬值和長寬的測量模式。

         什麼是測量模式?

         我們來看看源碼是怎麼講的(\frameworks\base\core\java\android\view$ MeasureSpec.class)      

         /**
         * Measure specification mode: The parent has not imposed any constraint
         * on the child. It can be whatever size it wants.
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
         * Measure specification mode: The parent has determined an exact size
         * for the child. The child is going to be given those bounds regardless
         * of how big it wants to be.
         */
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        /**
         * Measure specification mode: The child can be as large as it wants up
         * to the specified size.
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;

       從源碼裏可以看到測量模式一共有三種:
       UNSPECIFIED(未指定測量模式):
       The parent has not imposed any constraint on the child. It can be whatever size it wants.
       不受父控件約束,可以是任意大小。
       實際運用中比較少用
       EXACTLY(確定測量模式): 
       The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
       父控件確切的指定了子控件的大小。
       當View的長度和寬度是確定值或者MATCH_PARENT時就是這個模式
        AT_MOST(最大值測量模式):
       The child can be as large as it wants up to the specified size.
       在限制範圍內(一般指父控件大小),可以是任意大小。

       當View的長度和寬度是WRAP_CONTENT時就是這個模式


       注意一點:onMeasure方法中不是一定要調用 super.onMeasure(widthMeasureSpec,heightMeasureSpec)這個方法的,如果對View的寬高進行修改了,要調用setMeasuredDimension(widthsize,heightsize)。


        二、繪製View—— onDraw  
   @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
    }

         在onDraw方法中,我們完成自定義View的繪製,主要使用到Canvas類和Paint類

       這裏只給出簡單的例子,以後將出專題講解自定義View的繪製

       View 更新
       在onDraw方法中我們完成View的繪製,有的時候爲了更新View比如添加動畫效果,我們可以重繪View。

       重繪View自身有兩種方法。
       invalidate() :用來簡單重繪View。例如更新其文本,色彩或觸摸交互性。View將只調用onDraw()方法再次更新其狀態。
       requestLayout():將會從onMeasure()開始更新View。這意味着你的View更新後,它改變它的大小,你需要再次測量它,並依賴於新的大小來重新繪製。


        三、確定View大小—— onSizeChanged          

 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        super.onSizeChanged(w, h, oldw, oldh);
    }

         我們先看看源碼裏怎麼描述這個方法:

/**
     * This is called during layout when the size of this view has changed. If
     * you were just added to the view hierarchy, you're called with the old
     * values of 0.
     *
     * @param w Current width of this view.
     * @param h Current height of this view.
     * @param oldw Old width of this view.
     * @param oldh Old height of this view.
     */
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    }
        This is called during layout when the size of this view has changed. If you were just added to the view hierarchy, you're called with the old values of 0.

        在佈局過程中,如果View的大小改變了這個方法就會被調用。如果這個View剛剛被添加到視圖層,那麼oldw和oldh這兩個值爲零。

       其中 w、h表示控件當前長和寬,oldw和oldh表示原來的長和寬



        四、確定子View佈局—— onLayout        

        onLayout方法是繼承ViewGroup必須實現的方法,在這個方法中,我們依次取出每一個子控件,然後調用layout方法將它放置到我們所期待的位置

     protected void onLayout(boolean changed, int l, int t, int r, int b) {
             int childCount = getChildCount();
             View child = null;
             for (int i = 0; i < childCount; i++) {
                    ……
                 child = getChildAt(i);
                 child.layout(child_l,child_t,child_r,child_b);
             }
     }

         我們重點來看child.layout(child_l,child_t,child_r,child_b);


        child_l:View左側到父View左側的長度

        child_t:View上側到父View上側的長度

        child_r:View右側到父View右側的長度

        child_b:View下側到父View下側的長度 


        下面補充關於安卓座標系的相關知識

        安卓系統的座標系有別於數學上的座標系

        下圖中藍色區域爲手機屏幕

         

          通常我們想要獲取一個view的位置信息時會調用如下方法

         getLeft():View左側到父View左側的距離

         getTop():View上側到父View上側的距離

         getRight():View右側到父View右側的距離

         getBottom():View下側到父View下側的距離 

         一定要注意,這四個方法獲取到的位置信息是相對於當前View的父View而言的,而不是相對於屏幕而言的


     另外,還有兩個方法我們也經常用到

     getX( ):View相對於父View的橫座標

     getY( ):View相對於子View的縱座標



     

                Button button = (Button)findViewById(R.id.button);
		button.getTop();
		button.getLeft();
		button.getRight();
		button.getBottom();
		button.getX();
		button.getY();

      除了View類提供的上述這些方法,MotionEvent類同樣提供了獲取位置信息的方法

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                float x = event.getX();
                float y = event.getY();
                float rawx = event.getRawX();
                float rawy = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                break;

        }
        return false;
    }

         getX();觸碰點相對於組件的x座標
         getY();觸碰點相對於組件的y座標
         getRawX();觸碰點相對於屏幕的x座標
         getRawY();觸碰點相對於屏幕的y座標

  

            到這裏,自定義View常用的幾個方法已經總結完了,最後給出一個生命週期圖,對於瞭解這幾個方法有很大的幫助







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章