自定义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了。
            }
        }

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值