一:構造函數的修改,代碼如下:
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了。
}
}