实现思路:
1.该控件由圆环和文字组成,所有我们可以继承Textview去实现;
2.考虑到设置固定宽高时,文字范围容易超出圆环,所有建议布局时用wrap_content,我们根据文字大小+文字与圆环的padding来确定控件最终的大小
效果图
上代码之前,我们来回顾下自定义的一些知识点
自定义控件我们可以分成以下几类
1. 直接继承View
需要重写onMeasure和onDraw方法,计算控件的实际宽高并绘制,注意把padding和wrap_content因素考虑进去
2. 直接继承ViewGroup,实现特殊的布局功能(例如搜索历史的流布局等)
需要重写onMeasure和onLayout方法,计算控件的实际宽高和childview的布局位置,注意把padding和wrap_content以及childview的margin因素考虑进去
3. 继承特殊View,例如本篇文章介绍的案例就是继承TextView
重写onMeasure和onDraw方法,注意点padding
4. 继承特殊ViewGroup,例如继LinearLayout,实现多个控件组合效果
重写onMeasure和onLayout方法,注意点padding等因素
自定义的属性配置attrs.xml
<declare-styleable name="SplashTimer">
<attr name="timer_duration" format="integer"/>
<attr name="android:text"/>
<attr name="android:textColor"/>
<attr name="android:textSize"/>
<attr name="timer_padding" format="dimension"/>
<attr name="timer_arcColor" format="color"/>
<attr name="timer_arcStrokeWidth" format="dimension"/>
</declare-styleable>
该控件在xml布局中设置
1.倒计时时长
2.中间文字(默认“跳转”)
3.中间文字颜色(默认白色)
4.中间文字与圆环的padding
5.圆环的颜色
6.圆环的环宽
例如:
app:timer_padding="5dp"
android:text="跳过"
android:textColor="@color/white"
app:timer_duration="2000"
app:timer_arcColor="@color/white"
app:timer_arcStrokeWidth="2dp"
完整代码
package com.jsq.crm.mvp.view.wegit.otherview;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.TextView;
import com.jsq.crm.R;
import com.jsq.crm.utils.CommonUtils;
import androidx.annotation.Nullable;
/**
* splash跳转倒计时控件
* 建议xml布局中使用wrap_content设置长宽
*/
@SuppressLint("AppCompatCustomView")
public class SplashTimerTextView extends TextView {
// 倒计时动画时间
private int duration = 0;
//倒计时文字
private String textStr;
//文字与圆环的padding
private int textPadding;
// 动画扫过的角度
private int mSweepAngle = 360;
// 属性动画
private ValueAnimator animator;
// 矩形用来保存位置大小信息
private final RectF mRect = new RectF();
// 圆弧的画笔
private Paint mArcPaint;
//圆环的宽
private int mArcStrokeWidth;
//圆环默认的颜色
private int mArcColor = 0xFFFFFF;
//圆环文字的颜色
private int mTextColor = 0xFFFFFF;
//文字的大小
private int mTextSize;
// 文字画笔
private Paint mTextPaint;
//控件的最终宽
private int mWidth;
//控件的最终高
private int mHeight;
public SplashTimerTextView(Context context) {
super(context);
init();
}
public SplashTimerTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public SplashTimerTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SplashTimer);
duration = typedArray.getInteger(R.styleable.SplashTimer_timer_duration,0);
textPadding = (int) typedArray.getDimension(R.styleable.SplashTimer_timer_padding,CommonUtils.dp2px(context,8f));
mArcStrokeWidth = (int) typedArray.getDimension(R.styleable.SplashTimer_timer_arcStrokeWidth,CommonUtils.dp2px(context,3f));
mArcColor = typedArray.getColor(R.styleable.SplashTimer_timer_arcColor, mArcColor);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.SplashTimer_android_textSize,CommonUtils.sp2px(15));
mTextColor = typedArray.getColor(R.styleable.SplashTimer_android_textColor, mTextColor);
textStr = typedArray.getString(R.styleable.SplashTimer_android_text);
if (CommonUtils.isEmpty(textStr)){
textStr = "跳过";
}
typedArray.recycle();
init();
}
/**
* 初始化画笔
*/
private void init() {
//设置画笔平滑
mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//设置画笔颜色
mArcPaint.setColor(mArcColor);
//设置画笔样式:边框类型
mArcPaint.setStyle(Paint.Style.STROKE);
//设置画笔的宽
mArcPaint.setStrokeWidth(mArcStrokeWidth);
//设置文字画笔
mTextPaint = new Paint();
//去锯齿
mTextPaint.setAntiAlias(true);
//设置字体大小
mTextPaint.setTextSize(mTextSize);
//设置画笔颜色
mTextPaint.setColor(mTextColor);
//设置画笔样式:填充类型
mTextPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//计算控件的实际宽
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST){
int desire = (int) (mTextPaint.measureText(textStr) + textPadding + mArcPaint.getStrokeWidth() + getPaddingLeft() + getPaddingRight());
mWidth = Math.min(desire,widthSize);
}else {
mWidth = widthSize;
}
//计算控件的实际高
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.AT_MOST){
int desire = (int) (mTextPaint.measureText(textStr) + textPadding + mArcPaint.getStrokeWidth() + getPaddingTop() + getPaddingBottom());
mHeight = Math.min(desire,heightSize);
}else {
mHeight = heightSize;
}
//最终决定当前控件的宽高
setMeasuredDimension(mWidth,mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
//绘制圆环
mRect.top = getPaddingTop();
mRect.left = getPaddingLeft();
mRect.bottom = getHeight() - getPaddingBottom();
mRect.right = getWidth() - getPaddingRight();
canvas.drawArc(mRect, -90, mSweepAngle, false, mArcPaint);
//绘制文字,计算文字居中绘制的位置
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float x = (mWidth - mTextPaint.measureText(textStr)) / 2;
float y = mHeight / 2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent) / 2;
canvas.drawText(textStr, x, y, mTextPaint);
startAnim();
}
/**
* 开始属性动画,通过属性动画,去更新圆环的绘制
*/
private void startAnim() {
if (mSweepAngle != 360 || duration <= 0) return;
animator = ValueAnimator.ofInt(360);
animator.setDuration(duration);
animator.addUpdateListener(animation -> {
mSweepAngle = (int) animation.getAnimatedValue();
invalidate();
});
animator.start();
}
/**
* 获取倒计时的时长,进行界面更新或逻辑
* @return
*/
public int getDuration() {
return duration;
}
/**
* 设置圆环中间显示的文字
* @param textStr
* @return
*/
public String setText(String textStr) {
return this.textStr = textStr;
}
}