前言:这个签到功能一开始想找找有没有现成的轮子,后来国内国外找了一圈也没发现类似的。只能自己动手撸一个。
注意:这个自定义view我并没有适配warp_content,也就是说,你只能给它固定的高度,比如200dp。否则它会默认填满你的整个屏幕。
另外 像dip2px这种代码,每位开发者自己应该都有,我就不补充了,像图片文件,我这边也不好补充。
这篇文章不会教你如何直接使用这个自定义view,因为每个人的需求都不一样,你肯定是要在我的基础上修修改改,所以你最好能够看懂这些代码。
对于自定义view基础比较薄弱的人,看懂其实也不难,我所认为对你最大的难点是:
Q:线条如何转折?
A: 使用Path
Q:线条如何变成矩形?
A:给画笔添加setStrokeCap和setStrokeJoin(具体去源码里搜索一下就看到了)
Q:进度条的动画效果如何实现?
A:使用属性动画。需要用到三个动画,并且监听动画进度,第一个动画播放完毕后 开始启动第二个动画。(后来我发现使用animatorSet更方便,你如果感兴趣可以试试)
我只是告诉你大致的思路,具体实现从源码中参考。
思路: 1.先画出最底层的线,再画出红色的线盖住它,我们只需要控制红色线的长度变化即可。
使用(只讲述API):
signInView.setSignInDay(int);//取值:1-10 ,分别让红线停留在对应1-10个小圆点所在的地方
signInView.getSignInDay();//返回当前传入的天数
代码:
/**
* Created by Fushize on 2019/8/19.
*/
public class SignInView extends View {
private int startWidth = 20;
private int endWidth;
private Paint paint;
private Paint pointPaint;
private Paint imgPaint;
private Paint textPaint;
private int signInDay;
private float progress = 0;
private float progress2 = 0;
private float progress3 = 0;
private int maxLine = 800; //最大的长度
private final int offSetY = 200; //偏移的Y
private int offSetX = 0; //偏移的X
private final int maxHeigh = 340; //最大高度
private final int circleRadius = 7; //小圆点的半径
private int duration = 2000; //动画时长
public String[] strMoney = {"10", "20", "30", "40", "50", "60"};
public String[] strMoney2 = {"880", "500", "300", "150"};
private String[] strDay = {"1天", "2天", "3天", "4天", "5天", "6天"};
private String[] strDay2 = {"30天", "21天", "14天", "7天"};
private ObjectAnimator animator;
private ObjectAnimator animator2;
private ObjectAnimator animator3;
private @SuppressLint("DrawAllocation")
Bitmap sign_in_150;
private @SuppressLint("DrawAllocation")
Bitmap sign_in_300;
private @SuppressLint("DrawAllocation")
Bitmap sign_in_500;
private @SuppressLint("DrawAllocation")
Bitmap sign_in_880;
public SignInView(Context context) {
super(context, null);
init();
}
// 创建 getter 方法
public float getProgress() {
return progress;
}
// 创建 setter 方法
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
public SignInView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SignInView);
signInDay = ta.getInt(R.styleable.SignInView_signInDay, 0);
ta.recycle();
init();
}
public SignInView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@SuppressLint("ObjectAnimatorBinding")
private void init() {
maxLine = getContext().getResources().getDisplayMetrics().widthPixels - CommonUtil.dip2px(74);
startWidth = CommonUtil.dip2px(20);
offSetX = startWidth;
endWidth = maxLine - startWidth;
paint = new Paint();
pointPaint = new Paint();
imgPaint = new Paint();
imgPaint.setStyle(STROKE);
imgPaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setStyle(Paint.Style.FILL);
textPaint.setAntiAlias(true);
//最底层的线
//上层的线
paint.setStrokeWidth(36);
paint.setStyle(STROKE);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
//画小圆点
pointPaint.setStrokeWidth(5);
pointPaint.setStyle(FILL);
pointPaint.setStrokeJoin(Paint.Join.ROUND);
pointPaint.setStrokeCap(Paint.Cap.ROUND);
pointPaint.setColor(getResources().getColor(R.color.white));
sign_in_150 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_50, null);
sign_in_300 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_100, null);
sign_in_500 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_200, null);
sign_in_880 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_300, null);
animator = ObjectAnimator.ofFloat(this, "progress", 0.05f, 1);
animator.setDuration(duration);
// 执行动画
animator.start();
animator2 = ObjectAnimator.ofFloat(this, "progress2", 0, 1);
animator2.setDuration(duration);
animator3 = ObjectAnimator.ofFloat(this, "progress3", 0, 1);
animator3.setDuration(duration);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation, boolean isReverse) {
}
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
if (signInDay >= 6) {
animator2.start();
}
}
});
animator2.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation, boolean isReverse) {
}
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
if (signInDay >= 6) {
animator3.start();
}
}
});
animator2.addUpdateListener(valueAnimator -> {
progress2 = (Float) animator2.getAnimatedValue();
invalidate();
});
animator3.addUpdateListener(valueAnimator -> {
progress3 = (Float) animator3.getAnimatedValue();
invalidate();
});
}
public void setSignInDay(int signInDay) {
this.signInDay = signInDay;
invalidate();
}
public int getSignInDay() {
return signInDay;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(getResources().getColor(R.color.no_signin));
Path path = new Path();
path.moveTo(offSetX, offSetY);
path.lineTo(endWidth, offSetY);
path.rLineTo(0, maxHeigh);
int line = maxLine - offSetX * 2;
path.rLineTo(-line, 0);
canvas.drawPath(path, paint);
paint.setColor(getResources().getColor(R.color.main_color));
Path path1 = new Path();
path1.moveTo(offSetX, offSetY);
int yuliuWidth = (maxLine - startWidth * 2) / 7; //预留的宽度
if (signInDay == 1) {
//第一天
path1.lineTo(yuliuWidth * progress + startWidth, offSetY);
} else if (signInDay <= 6) {
//2-6天
float x = (yuliuWidth) * (signInDay);
x = x + 20;
if (x > maxLine) {
x = maxLine;
}
path1.lineTo(x * progress + startWidth, offSetY);
} else {
//六天以后
path1.lineTo(endWidth * progress, offSetY);
path1.rLineTo(0, maxHeigh * progress2);
float v = 0;
switch (signInDay) {
case 10:
v = yuliuWidth * 7;
break;
case 9:
v = yuliuWidth * 4.5f;
break;
case 8:
v = yuliuWidth * 2.8f;
break;
case 7:
v = yuliuWidth * 1.5f;
break;
}
path1.rLineTo(-v * progress3, 0);
}
canvas.drawPath(path1, paint);
//画圆点
for (int i = 0; i < 6; i++) {
canvas.drawCircle(yuliuWidth * (i + 1) + startWidth, offSetY, circleRadius, pointPaint);
}
canvas.drawCircle(yuliuWidth + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);
canvas.drawCircle(yuliuWidth * 2.8f + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);
canvas.drawCircle(yuliuWidth * 4.5f + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);
canvas.drawCircle(yuliuWidth * 6 + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);
//画金币图片
@SuppressLint("DrawAllocation") Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_10, null);
for (int i = 0; i < strMoney.length; i++) {
canvas.drawBitmap(bitmap, (yuliuWidth * (i + 1) + startWidth) - (bitmap.getWidth() / 2), 40, imgPaint);
}
textPaint.setTextSize(CommonUtil.dip2px(12));
for (int i = 0; i < strMoney.length; i++) {
textPaint.setColor(getResources().getColor(signInDay > i ? R.color.main_color : R.color.textcolor_aaa));
//画奖励金额的文字
canvas.drawText(strMoney[i], yuliuWidth * (i + 1) + startWidth - (textPaint.measureText(strMoney[i]) / 2), bitmap.getHeight() + 90, textPaint);
//画已签到天数的文字
canvas.drawText(signInDay > i ? "已签" : strDay[i], yuliuWidth * (i + 1) + startWidth - (textPaint.measureText(strDay[i]) / 2), bitmap.getHeight() + 200, textPaint);
}
//底部四张图片与文字
textPaint.setColor(getResources().getColor(R.color.textcolor_aaa));
for (int i = 0; i < strMoney2.length; i++) {
switch (i) {
case 0:
//第30天 奖励880
if (signInDay >= 10) {
textPaint.setColor(getResources().getColor(R.color.main_color));
canvas.drawText("已签", yuliuWidth + startWidth - (textPaint.measureText(strDay[i]) / 2), sign_in_880.getHeight() + maxHeigh + 200, textPaint);
} else {
//画已签到天数的文字
canvas.drawText(strDay2[i], yuliuWidth + startWidth - (textPaint.measureText(strDay[i]) / 2), sign_in_880.getHeight() + maxHeigh + 200, textPaint);
}
//画出金币图片
canvas.drawBitmap(sign_in_880, (yuliuWidth + startWidth) - (sign_in_880.getWidth() / 2), maxHeigh + 46, imgPaint);
//画奖励金额的文字
canvas.drawText(strMoney2[i], yuliuWidth + startWidth - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);
break;
case 1:
//第21天 奖励500
if (signInDay >= 9) {
textPaint.setColor(getResources().getColor(R.color.main_color));
canvas.drawText("已签", (yuliuWidth * 2.8f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_500.getHeight() + maxHeigh + 200, textPaint);
} else {
canvas.drawText(strDay2[i], (yuliuWidth * 2.8f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_500.getHeight() + maxHeigh + 200, textPaint);
}
canvas.drawBitmap(sign_in_500, (yuliuWidth * 2.8f + startWidth) - (sign_in_500.getWidth() / 2), maxHeigh + 46, imgPaint);
canvas.drawText(strMoney2[i], (yuliuWidth * 2.8f + startWidth) - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);
break;
case 2:
//第14天 奖励300
if (signInDay >= 8) {
textPaint.setColor(getResources().getColor(R.color.main_color));
canvas.drawText("已签", (yuliuWidth * 4.5f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_300.getHeight() + maxHeigh + 200, textPaint);
} else {
canvas.drawText(strDay2[i], (yuliuWidth * 4.5f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_300.getHeight() + maxHeigh + 200, textPaint);
}
canvas.drawBitmap(sign_in_300, (yuliuWidth * 4.5f + startWidth) - (sign_in_300.getWidth() / 2), maxHeigh + 46, imgPaint);
canvas.drawText(strMoney2[i], (yuliuWidth * 4.5f + startWidth) - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);
break;
case 3:
//第7天 奖励150
if (signInDay >= 7) {
textPaint.setColor(getResources().getColor(R.color.main_color));
canvas.drawText("已签", (yuliuWidth * 6f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_150.getHeight() + maxHeigh + 200, textPaint);
} else {
canvas.drawText(strDay2[i], (yuliuWidth * 6f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_150.getHeight() + maxHeigh + 200, textPaint);
}
canvas.drawBitmap(sign_in_150, (yuliuWidth * 6 + startWidth) - (sign_in_150.getWidth() / 2), maxHeigh + 46, imgPaint);
canvas.drawText(strMoney2[i], (yuliuWidth * 6 + startWidth) - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);
break;
}
}
}
}