Android 自定義View 二

在上一篇文章android 自定義view 中我們總結了自定義View的幾大步驟如下:

1、自定義View的屬性

2、在View的構造方法中獲得我們自定義的屬性

3、重寫onMesure方法

4、重寫onLayout方法

5、重寫onDraw方法

因爲上篇文章中我們完成了LabelImageView中的一個LabelView子view,所以不用重寫onLayout方法,下面接着上篇文章我們來完成LabelImageView這個ViewGroup控件。既然LabelImageView是一個ViewGroup,那麼我們就必須自己決定其子控件的佈局,我們的子控件就是一個ImgeView和許多的LabelView。而onLayout就是給子控件佈局的地方。在上代碼前,我們先看看效果:


上面的效果就是在ImageView裏面可以添加我們自己的標籤,這個在某些APP裏面可以應用到,下面是LabelImageView的onLayout方法代碼:

 @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed){
            int childCount = getChildCount();
            for(int i=0;i<childCount;i++){
                View v = getChildAt(i);
                if(v instanceof ImageView){ //佈局ImageView
                    v.layout(l+getPaddingLeft(),t+getPaddingTop(),r-getPaddingRight(),b-getPaddingBottom());
                }else if(v instanceof LabelView){//佈局LabelView
                    LabelData labelData = ((LabelView)v).getLabelData();
                    float pos[] = labelData.getPosition();
                    int cl = (int) (getWidth()*pos[0]);
                    int ct = (int) (getHeight()*pos[1]);
                    int cr = cl+ v.getMeasuredWidth();
                    int cb = ct+ v.getMeasuredHeight();
                    v.layout(cl,ct,cr,cb);
                }
            }
        }
    }

其實就是獲取子控件,然後調用子控件的layout方法進行佈局,注意這個changed屬性,google官方api裏說當view有新的size或position時changed爲true,但當我用動畫改變一個view的大小或位置時,onlayout方法是不會調用的,用setLayoutParams重新設置View的寬高,changed確實會爲true。


在android開發時,有時我們需要得到某個View的寬高,最簡單的方法就是調用View的getHeight()和getWidth()了,但是使用時很多時候都發現他們返回的是0,得不到我們想要的值。下面打印下activity的啓動流程來分析下:


只有在activity調用了onAttachToWindow後,纔會開啓view的onMeasure方法,測量view的大小,只有在調用了view的onlayout方法後,view纔有真正的寬高,view的getWidth()和getHeight()才能得到真的的值。所以我們在activity的onResume方法裏及其前面的生命週期裏都無法得到view的寬高。如果我們必須要得到view的寬高呢,有辦法麼?答案當然是有,下面介紹三種常見的方法:

方法一:

//手動調用測量方法。 制定測量規則 參數表示size + mode
    int width =View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
    int height =View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
    mLabelImageView.measure(width,height);
    //調用measure方法之後就可以獲取寬高。
    height=mLabelImageView.getMeasuredHeight();
    width=mLabelImageView.getMeasuredWidth();
    Log.e(TAG,"-----------  measure  width: "+ width + "  height: " + height);

注意,measure()方法是實際測量的方法,而在繪製佈局過程中調用的onMeasure()只是制定測量規則. 在自定義佈局中我們一般重寫onMeasure()方法,measure()方法是final的,子類無法重寫。 measure函數有2個參數,int widthMeasureSpec 和 int heightMeasureSpec表示具體的測量規則。 
這兩個數值不是普通的數值, 它表示: size + mode ,例如: 
int widthMeasureSpec= View.MeasureSpec.makeMeasureSpec(1000,View.MeasureSpec.EXACTLY); 
int heightMeasureSpec= View.MeasureSpec.makeMeasureSpec(1000,View.MeasureSpec.AT_MOST); 
模式分爲: 
View.MeasureSpec.EXACTLY:表示父視圖希望子類的大小是specSize中制定的大小. 
View.MeasureSpec.AT_MOST:父試圖希望子類的大小最高不超過specSize中制定的大小. 
View.MeasureSpec.UNSPECIFIED:父試圖不對子類實施任何限制,子試圖可以得到自己想得到的任意大小. 

值得一提的是View.MeasureSpec.UNSPECIFIED 其值爲0,所以前面可以簡寫成:view.measure(0,0),但是值得注意的是,這個只是測量值,一般情況下view的測量值寬高跟真實值寬高是一致的,但是也有不一樣的情況,比如ImageView設置不同的scaleType,測量值跟真實值有可以就不一樣。

方法二(設置View樹樁結構監聽器):

  mLabelImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
            @Override
            public void onGlobalLayout() {
                mLabelImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                int h = mLabelImageView.getHeight();
                int w = mLabelImageView.getWidth();
                Log.e(TAG,"-----------  onGlobalLayout  width: "+ w + "  height: " + h);
            }
        });

方法三(增加組件繪製之前的監聽):

   mLabelImageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mLabelImageView.getViewTreeObserver().removeOnPreDrawListener(this);
                int h = mLabelImageView.getHeight();
                int w = mLabelImageView.getWidth();
                Log.e(TAG,"-----------  onPreDraw  width: "+ w + "  height: " + h);
                return false;
            }
        });
下面看看打印信息:


   如上正確打印出了view的寬高,推薦使用後面兩種方式。

未完待續!

更多精彩Android技術可以關注我們的微信公衆號,掃一掃下方的二維碼或搜索關注公共號: Android老鳥

                                                



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