学习资料
//www.greatytc.com/u/51d1fd73fb72
这里是跟随原作者的博客进行的自定义view的学习,感谢大神的无私分享
这里盗用原作者的图,看一下效果
作为一个终极菜鸡,当面对这个自定义控件的时候,其实我的内心是拒绝的,但是冷静下来分析一波 :
首先看一下效果,具体分析:
1.百分比提示框
2.进度条
具体分析下百分比提示框 :
分为 圆角矩形 、 下面的三角、提示文字 三部分
那么想一想我们需要准备的东西 (我这里先忽略提示文字):
画矩形 :
画笔 、画笔宽度 、 矩形颜色 、 矩形的宽和高、 矩形圆角度数
画三角 :
画笔 、画笔宽度 、 三角颜色 、 三角形的高 、 画三角使用的path
画进度框 :
进度款分为两部分 : 背景进度 、当前进度
背景画笔、背景颜色 、 当前进度画笔 、当前进度颜色 、 、 当前进度值 画笔宽度
那么接下来我们就先准备这些东西吧
/*
* 矩形和三角形画笔
* */
Paint rectPaint;
/*
* 矩形、三角画笔宽度
* */
private int rectPaintWidth;
/**
* 进度条、矩形、三角 颜色
*/
private int progressColor = 0xFFf66b12;
/*
* 矩形高度
* */
private int rectWidth;
private int rectHeight;
/*
* 准备圆角矩形 , 圆角度数 ,
* */
RectF rectF = new RectF();
private int roundRectRadius;
/*
* 画三角形的path
* */
Path path = new Path();
/*
* 三角形高度
* */
private int triangleHeight;
/*
* 进度条背景画笔
* */
Paint bgProPaint;
/*
* 进度条画笔宽度
* */
private int progressWidth;
/**
* 进度条背景颜色
*/
private int bgColor = 0xFFe1e5e8;
/*
* 当前进度画笔
* */
Paint currentProPaint;
/**
* 当前进度
*/
private float currentProgress;
/*
* 三角与直线的margin距离
* */
private int marginTop;
/*
* 整个控件的高度宽度
* */
private int mViewHeight;
好了,,需要的东西差不多都准备好了, 我们就先把我们的小媳妇儿小对象们new出来,然后一些该赋值的数据也赋下值吧;
那么先来给各种宽度高度赋值吧 , dp2px()这个方法不用说了吧。
private void init() {
progressWidth = dp2px(4); //进度条画笔宽度 ,即进度条高度
rectPaintWidth = dp2px(1); //矩形画笔宽度
rectWidth = dp2px(30); //矩形宽度
rectHeight = dp2px(15);//矩形高度
triangleHeight = dp2px(3); //三角高度
marginTop = dp2px(8); //三角与直线的距离
roundRectRadius = dp2px(2);//圆角度数
textPaintSize = dp2px(10); //字体大小
//控件总共的高度 : 矩形 + 矩形画笔高 + 三角 + margin + 进度条;
mViewHeight = rectHeight + triangleHeight + marginTop + progressWidth + rectPaintWidth;
}
然后接下来,画笔对象 , 既然要生成这么多画笔对象, 矩形,三角,两个进度条,那不如这些:
/*
* 统一处理画笔
* 画笔宽度
* 画笔颜色
* 风格
*
* */
private Paint initPaint(int strokeWidth, int color, Paint.Style style) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color); //颜色
paint.setStyle(style);//风格
paint.setStrokeWidth(strokeWidth); //画笔宽度
return paint;
}
然后在构造方法中,该准备的我们都准备好了
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();
}
准备好了。那就画呗 onDraw()不必多说
我们就从上到下依次画,当然说好了,先忽略文字的
画矩形 、画三角 、 画背景进度条、画当前进度
/*
* 画矩形
* */
private void drawRect(Canvas canvas) {
rectF.set(0, 0, rectWidth, rectHeight);
canvas.drawRoundRect(rectF, roundRectRadius, roundRectRadius, rectPaint);
}
/*
* 画三角
* */
private void drawTriangle(Canvas canvas) {
/*
*
* moveTo 移动起点
* 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();
}
/*
* 画进度条背景
* 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);
/*
* 画当前进度条
*
* stopX currentProgress
* */
canvas.drawLine(getPaddingLeft(), rectHeight + triangleHeight + marginTop, currentProgress, rectHeight + triangleHeight + marginTop,
currentProPaint);
讲波道理 ,画到现在,感觉也没什么。只要思路清晰了。还是很 easy的嘛。
先不管别的 ,该画的都画了,擦了。还有提示文字没画那 ;
那我们就画画文字呗,我为什么最后画文字,因为我贼烦这个基线(baseLine),其他的全部记得了,只记得这个基线烦 ;
复习下 :
参考资料 :
http://blog.csdn.net/aigestudio/article/details/41447349
干嘣一个FontMertics, 字体测量 ,我擦。什么还不知道就特么开始量了。操蛋,好吧 ,那也先看看,FontMertics定义了五个成员变量
top、ascent 、desent、bottom 、leading
首先我们要知道BaseLine ,贼烦,在Android中,文字绘制都是从BaseLine开始的;
Ascent(上坡度) 从BaseLine往上至字符最高处
Desent(下坡度) 从BaseLine往下至字符最低处
Leading (行间距) 从上一行字符的desent到该行的ascent的距离
Paint有一个唯一的子类 ,TextPaint是专门为文本绘制量身定做的笔;
好吧好吧 :我只记得了一个爱哥的计算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);
}
这下算是全画完了, 接下来,也是应该让我们的进度条来点动画效果了
准备个 ValueAnimator 、 准备个dur
准备你奶奶个腿 。运行了一下,发现哎呦我曹,,我的 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;
}
哼哼 ,,测量也测量了。这回行了吧。那接下来就真的准备下动画吧
准备一个ValueAnimator 、准备一个动画时间、来个延时时间看着舒服点、还带准备什么? 先看看代码
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();
//进度数值只显示整数,我们自己的需求,可以忽略
text = formatNum(format2Int(index));
currentProgress = index * mWidth / 100;
if(index == 100){
if(finishLinstener != null){
finishLinstener.finished();
}
}
invalidate();
}
});
valueAnimator.start();
}
ValueAnimator本身不提供任何动画效果,它更像一个数值发生器 , 我们在AnimatorUpdateListener中监听数值的变化,从而完成动画的变换;
插值器
通过插值器,可以定义动画变换速率 ,这一点非常类似物理中的加速度 ,主要作用是控制目标变量的变数值进行对应的变化;
简单的说,就是可以控制动画 ,先快后慢,还是先慢后快,或者是匀速变化;
差不多了。吧。。。我曹,我忘了。圆角矩形也要跟着移动。。。其实我特么是懒得不愿动了,上来就是俩大嘴巴子。
头一遭这么正八经的写,感觉写写真的有用,这么复习一遍,许多知识点又加深了。以后多写写,还是蛮好的嘛。