一:构造函数的修改,代码如下:
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了。
}
}