自定義ViewGroup時需要注意的細節點

一:構造函數的修改,代碼如下:

       1 public TimerTextView(Context context) {
            // super(context);
             this(context,null);
          }

        public TimerTextView(Context context, AttributeSet attrs) {
//           super(context, attrs);
             this(context, attrs, 0);
          }

       public TimerTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            if (getChildCount() > 0) {
              throw new RuntimeException("IncreaseReduceTextView不允許有子元素.");
           }
        this.mContext = context;
        // 讀取自定義屬性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimerTextView);
        mBackground = ta.getResourceId(R.styleable.TimerTextView_textBackground, /*R.drawable.ic_launcher*/0);
        mTextSize = ta.getDimensionPixelSize(R.styleable.TimerTextView_textSize,
                DensityUtil.dip2px(context, mTextSize));
        mVerticalPadding = ta.getDimensionPixelSize(R.styleable
                .TimerTextView_verticalPadding, DensityUtil.dip2px(context,
                mVerticalPadding));
        mHorizontalPadding = ta.getDimensionPixelSize(R.styleable
                .TimerTextView_horizontalPadding, DensityUtil.dip2px(context,
                mHorizontalPadding));
        mViewSpace = ta.getDimensionPixelSize(R.styleable.TimerTextView_viewSpace,
                mViewSpace);
        ta.recycle();//記得回收

      //該畫筆的設置,只是爲了測量在mTextSize前提下,0000的寬度,並將獲取到的寬度給設置到onMeasure中去。
        Paint paint = new Paint();
        paint.setTextSize(mTextSize);
        mTargetWidth = (int) paint.measureText("00");//獲取到一個寬度,且是在字體大小爲mTextSize的前提下的寬度,
                                                    //記得是在MeasureSpeace的模式爲EXACTLY時,纔會有效果變化,否則是沒有的。
        initializeView();
    }

   綜上代碼,難點在於第三個構造方法上。前面兩個構造方法,你可以照抄過來使用就是了。所以主要解釋下第三個構造方法。

    1)首先構造方法中,並沒有修改爲this。

    2)自定義屬性

       自定義屬性其實很簡單,只需要在vlues目錄下創建一個attrs.xml,並寫上需要暴露出去,在xml佈局文件中配置的屬性即可。本例的attrs文件代碼如下:

        <declare-styleable name="TimerTextView">
       <attr name="textBackground" format="reference"/>
       <attr name="textSize" format="dimension"/>
       <attr name="verticalPadding" format="dimension"/>
       <attr name="horizontalPadding" format="dimension"/>
       <attr name="viewSpace" format="dimension"/>

         至於format後面代表的意思,我就不介紹了。

       而attrs配置的自定義屬性,我們需要在自定義viewGroup中去引用,代碼就是上面標紅的部分。

   3)畫筆Paint的使用

         其實這一步是可用可不用。因爲他主要是用來在measure的時候,爲TextView指定適當的寬度。獲取到在字體大小爲mTextSize的前提下的寬度

       注意:measure的時候,MeasureSpace的模式一定要爲EXACTLY纔能有效果。否則無論你怎麼設置,都是不起效果的。

   4)initializeView()

        這一步就是通過addView依次的添加想要顯示的控件了。包括設置控件的一些屬性,如textView的設置字體大小,背景顏色等等。下面舉個代碼例子:

    // 初始化視圖
    private void initializeView() {

         LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT);
        System.out.println("--showTime:002");
        lefTextView = new TextView(mContext);
        lefTextView.setLayoutParams(params);
        lefTextView.setBackgroundResource(mBackground);
        lefTextView.setBackgroundColor(Color.parseColor("#ff0000"));
//        lefTextView.setTextColor(Color.parseColor("#ffffff"));
//        lefTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
        lefTextView.setPadding(mHorizontalPadding, mVerticalPadding, mHorizontalPadding,mVerticalPadding);
        lefTextView.setGravity(Gravity.CENTER);
        addView(lefTextView);
        
        leftMaoTV = new TextView(mContext);
        leftMaoTV.setLayoutParams(params);
        leftMaoTV.setBackgroundResource(mBackground);
//        leftMaoTV.setBackgroundColor(Color.parseColor("#00ff00"));
        leftMaoTV.setTextColor(Color.parseColor("#00ff00"));
//        leftMaoTV.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
        leftMaoTV.setPadding(mHorizontalPadding, mVerticalPadding, mHorizontalPadding,mVerticalPadding);
        leftMaoTV.setGravity(Gravity.CENTER);
        addView(leftMaoTV);

   }


二:measure測量

    1)measureChildren(widthMeasureSpec, heightMeasureSpec);

        只需要將onMeasure的參數對應的傳遞過去即可

     2)定義一個局部int型變量,作爲記錄整個viewGroup的寬度

          int widthSum ;   // 總寬度, 最終結果爲自定義組件的寬度

    3)獲取到所有的子佈局個數,依次for循環遍歷所有的子佈局,測繪其寬高信息,並記錄寬高信息到第二步定義的總寬高變量widthSum,代碼如下:

     int childCount = getChildCount();

     for(int x=0;x<childCount;x++){
            View secondView = getChildAt(x);
            int secondWidth = secondView.getMeasuredWidth();
            int secondHeight = secondView.getMeasuredHeight();
            secondView.measure(MeasureSpec.makeMeasureSpec(secondWidth + mTargetWidth, MeasureSpec.EXACTLY),        MeasureSpec.makeMeasureSpec(secondHeight, MeasureSpec.EXACTLY));
            widthSum += (secondWidth +mTargetWidth);//這裏要記錄的寬度,必須和上面的measure的寬度一致
        }

   4)寬高信息都測量好了,那麼我們來最後一步的設置總寬高

    setMeasuredDimension(widthSum + mViewSpace * 4, childHeight);//具體的寬高,這一步才起到了作用,前面的都是測量準備

三:onLayout

    1)獲取到所有的子控件佈局,for循環遍歷測量每一個子佈局此時的寬高信息,然後調用layout佈局。一般此時的左邊是不斷變化的,需要定義一個局部的變量,道理類似於measure時,創建的全局變量總寬度widthSum。具體代碼如下:

   int childCount = getChildCount();
        int left = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            //第一步
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();
            //第二步
            childView.layout(left, 0, left + childWidth, childHeight);
            //第三步
            left += childWidth;
            if (i != childCount - 1) {
                left += mViewSpace;//如果是最後一個,就不用添加space了。
            }
        }

  

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