學(xué)習(xí)資料
http://www.itdecent.cn/u/51d1fd73fb72
這里是跟隨原作者的博客進(jìn)行的自定義view的學(xué)習(xí),感謝大神的無私分享
這里盜用原作者的圖,看一下效果

作為一個(gè)終極菜雞,當(dāng)面對(duì)這個(gè)自定義控件的時(shí)候,其實(shí)我的內(nèi)心是拒絕的,但是冷靜下來分析一波 :
首先看一下效果,具體分析:
1.百分比提示框
2.進(jìn)度條
具體分析下百分比提示框 :
分為 圓角矩形 、 下面的三角、提示文字 三部分
那么想一想我們需要準(zhǔn)備的東西 (我這里先忽略提示文字):
畫矩形 :
畫筆 、畫筆寬度 、 矩形顏色 、 矩形的寬和高、 矩形圓角度數(shù)
畫三角 :
畫筆 、畫筆寬度 、 三角顏色 、 三角形的高 、 畫三角使用的path
畫進(jìn)度框 :
進(jìn)度款分為兩部分 : 背景進(jìn)度 、當(dāng)前進(jìn)度
背景畫筆、背景顏色 、 當(dāng)前進(jìn)度畫筆 、當(dāng)前進(jìn)度顏色 、 、 當(dāng)前進(jìn)度值 畫筆寬度
那么接下來我們就先準(zhǔn)備這些東西吧
/*
* 矩形和三角形畫筆
* */
Paint rectPaint;
/*
* 矩形、三角畫筆寬度
* */
private int rectPaintWidth;
/**
* 進(jìn)度條、矩形、三角 顏色
*/
private int progressColor = 0xFFf66b12;
/*
* 矩形高度
* */
private int rectWidth;
private int rectHeight;
/*
* 準(zhǔn)備圓角矩形 , 圓角度數(shù) ,
* */
RectF rectF = new RectF();
private int roundRectRadius;
/*
* 畫三角形的path
* */
Path path = new Path();
/*
* 三角形高度
* */
private int triangleHeight;
/*
* 進(jìn)度條背景畫筆
* */
Paint bgProPaint;
/*
* 進(jìn)度條畫筆寬度
* */
private int progressWidth;
/**
* 進(jìn)度條背景顏色
*/
private int bgColor = 0xFFe1e5e8;
/*
* 當(dāng)前進(jìn)度畫筆
* */
Paint currentProPaint;
/**
* 當(dāng)前進(jìn)度
*/
private float currentProgress;
/*
* 三角與直線的margin距離
* */
private int marginTop;
/*
* 整個(gè)控件的高度寬度
* */
private int mViewHeight;
好了,,需要的東西差不多都準(zhǔn)備好了, 我們就先把我們的小媳婦兒小對(duì)象們new出來,然后一些該賦值的數(shù)據(jù)也賦下值吧;
那么先來給各種寬度高度賦值吧 , dp2px()這個(gè)方法不用說了吧。
private void init() {
progressWidth = dp2px(4); //進(jìn)度條畫筆寬度 ,即進(jìn)度條高度
rectPaintWidth = dp2px(1); //矩形畫筆寬度
rectWidth = dp2px(30); //矩形寬度
rectHeight = dp2px(15);//矩形高度
triangleHeight = dp2px(3); //三角高度
marginTop = dp2px(8); //三角與直線的距離
roundRectRadius = dp2px(2);//圓角度數(shù)
textPaintSize = dp2px(10); //字體大小
//控件總共的高度 : 矩形 + 矩形畫筆高 + 三角 + margin + 進(jìn)度條;
mViewHeight = rectHeight + triangleHeight + marginTop + progressWidth + rectPaintWidth;
}
然后接下來,畫筆對(duì)象 , 既然要生成這么多畫筆對(duì)象, 矩形,三角,兩個(gè)進(jìn)度條,那不如這些:
/*
* 統(tǒng)一處理畫筆
* 畫筆寬度
* 畫筆顏色
* 風(fēng)格
*
* */
private Paint initPaint(int strokeWidth, int color, Paint.Style style) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color); //顏色
paint.setStyle(style);//風(fēng)格
paint.setStrokeWidth(strokeWidth); //畫筆寬度
return paint;
}
然后在構(gòu)造方法中,該準(zhǔn)備的我們都準(zhǔn)備好了
public MyHorizontalProgressBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
rectPaint = initPaint(rectPaintWidth, progressColor, Paint.Style.FILL);
bgProPaint = initPaint(progressWidth, bgColor, Paint.Style.STROKE);
currentProPaint = initPaint(progressWidth, progressColor, Paint.Style.STROKE);
// inieTextPaint();
}
準(zhǔn)備好了。那就畫唄 onDraw()不必多說
我們就從上到下依次畫,當(dāng)然說好了,先忽略文字的
畫矩形 、畫三角 、 畫背景進(jìn)度條、畫當(dāng)前進(jìn)度
/*
* 畫矩形
* */
private void drawRect(Canvas canvas) {
rectF.set(0, 0, rectWidth, rectHeight);
canvas.drawRoundRect(rectF, roundRectRadius, roundRectRadius, rectPaint);
}
/*
* 畫三角
* */
private void drawTriangle(Canvas canvas) {
/*
*
* moveTo 移動(dòng)起點(diǎn)
* lineTo 連接直線
*
* */
path.moveTo(rectWidth / 2 - triangleHeight, rectHeight);
path.lineTo(rectWidth / 2, rectHeight + triangleHeight);
path.lineTo(rectWidth / 2 + triangleHeight, rectHeight);
canvas.drawPath(path, rectPaint); // 繪制Path
path.reset();
}
/*
* 畫進(jìn)度條背景
* float startX, float startY, float stopX, float stopY,
* @NonNull Paint paint
*
* startX getPaddingLeft()
* startY 矩形高 + 三角高 + margin
* stopX getWidth() - getPaddingRight();
* stopT 矩形高 + 三角高 + margin
* */
canvas.drawLine(getPaddingLeft(), rectHeight + triangleHeight + marginTop,
getWidth() - getPaddingRight(), rectHeight + triangleHeight + marginTop
, bgProPaint);
/*
* 畫當(dāng)前進(jìn)度條
*
* stopX currentProgress
* */
canvas.drawLine(getPaddingLeft(), rectHeight + triangleHeight + marginTop, currentProgress, rectHeight + triangleHeight + marginTop,
currentProPaint);
講波道理 ,畫到現(xiàn)在,感覺也沒什么。只要思路清晰了。還是很 easy的嘛。
先不管別的 ,該畫的都畫了,擦了。還有提示文字沒畫那 ;
那我們就畫畫文字唄,我為什么最后畫文字,因?yàn)槲屹\煩這個(gè)基線(baseLine),其他的全部記得了,只記得這個(gè)基線煩 ;
復(fù)習(xí)下 :
參考資料 :
http://blog.csdn.net/aigestudio/article/details/41447349
干嘣一個(gè)FontMertics, 字體測(cè)量 ,我擦。什么還不知道就特么開始量了。操蛋,好吧 ,那也先看看,F(xiàn)ontMertics定義了五個(gè)成員變量
top、ascent 、desent、bottom 、leading
首先我們要知道BaseLine ,賊煩,在Android中,文字繪制都是從BaseLine開始的;
Ascent(上坡度) 從BaseLine往上至字符最高處
Desent(下坡度) 從BaseLine往下至字符最低處
Leading (行間距) 從上一行字符的desent到該行的ascent的距離
Paint有一個(gè)唯一的子類 ,TextPaint是專門為文本繪制量身定做的筆;
好吧好吧 :我只記得了一個(gè)愛哥的計(jì)算baseLine的公式,然而我并沒有理解,有理解的大神給我講一下啊。
BaseLine
X : (畫布寬度 - 文字寬度)/2;
Y : 畫布寬度/2 - (desent + ascent)/2;
誰他么知道什么意思
那么那么 :
private void drawText(Canvas canvas, String text) {
int BaseX = (int) (rectWidth / 2 - textPaint.measureText(text) / 2);
int BaseY = (int) ((rectHeight / 2) - (textPaint.ascent() + textPaint.descent()) / 2);
canvas.drawText(text, BaseX, BaseY, textPaint);
}
這下算是全畫完了, 接下來,也是應(yīng)該讓我們的進(jìn)度條來點(diǎn)動(dòng)畫效果了
準(zhǔn)備個(gè) ValueAnimator 、 準(zhǔn)備個(gè)dur
準(zhǔn)備你奶奶個(gè)腿 。運(yùn)行了一下,發(fā)現(xiàn)哎呦我曹,,我的 wrap_content 怎么沒什么卵子用 。這不廢話 ,人那話怎么說的來
如果我們的view還需要使用wrap_content屬性,那么還必須重寫 onMeasure()方法;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measuredWidth(widthMeasureSpec), measuredHeight(heightMeasureSpec));
}
private int measuredWidth(int widthMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int size = MeasureSpec.getSize(widthMeasureSpec);
switch (mode) {
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
break;
case MeasureSpec.EXACTLY:
mWidth = size;
break;
}
return mWidth;
}
private int measuredHeight(int heightMeasureSpec) {
int mode = MeasureSpec.getMode(heightMeasureSpec);//模式
int size = MeasureSpec.getSize(heightMeasureSpec); //大小
switch (mode) {
case MeasureSpec.EXACTLY: //精確值模式
mHeight = size;
break;
case MeasureSpec.AT_MOST: //最大值模式
case MeasureSpec.UNSPECIFIED: // ?? 忘了
mHeight = mViewHeight;
break;
}
return mHeight;
}
哼哼 ,,測(cè)量也測(cè)量了。這回行了吧。那接下來就真的準(zhǔn)備下動(dòng)畫吧
準(zhǔn)備一個(gè)ValueAnimator 、準(zhǔn)備一個(gè)動(dòng)畫時(shí)間、來個(gè)延時(shí)時(shí)間看著舒服點(diǎn)、還帶準(zhǔn)備什么? 先看看代碼
public void startAnimator() {
valueAnimator = ValueAnimator.ofFloat(0, 100);
valueAnimator.setDuration(duration);
valueAnimator.setStartDelay(startDelay);
valueAnimator.setInterpolator(new LinearInterpolator()); //差值器
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float index = (float) animation.getAnimatedValue();
//進(jìn)度數(shù)值只顯示整數(shù),我們自己的需求,可以忽略
text = formatNum(format2Int(index));
currentProgress = index * mWidth / 100;
if(index == 100){
if(finishLinstener != null){
finishLinstener.finished();
}
}
invalidate();
}
});
valueAnimator.start();
}
ValueAnimator本身不提供任何動(dòng)畫效果,它更像一個(gè)數(shù)值發(fā)生器 , 我們?cè)贏nimatorUpdateListener中監(jiān)聽數(shù)值的變化,從而完成動(dòng)畫的變換;
插值器
通過插值器,可以定義動(dòng)畫變換速率 ,這一點(diǎn)非常類似物理中的加速度 ,主要作用是控制目標(biāo)變量的變數(shù)值進(jìn)行對(duì)應(yīng)的變化;
簡(jiǎn)單的說,就是可以控制動(dòng)畫 ,先快后慢,還是先慢后快,或者是勻速變化;
差不多了。吧。。。我曹,我忘了。圓角矩形也要跟著移動(dòng)。。。其實(shí)我特么是懶得不愿動(dòng)了,上來就是倆大嘴巴子。
頭一遭這么正八經(jīng)的寫,感覺寫寫真的有用,這么復(fù)習(xí)一遍,許多知識(shí)點(diǎn)又加深了。以后多寫寫,還是蠻好的嘛。