这是一个常见的app启动splash广告效果,在之前也用系统提供的CountDownTimer类写过类似的效果(CountDownTimer轻松搞定apk启动广告和获取验证码效果),不过这个效果用CountDownTimer并不是那么好实现,这里采用自定义view的方式实现的;
一说的自定view,肯定就会涉及到的onMeasure和onDraw;首先看onMeasure测量;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取宽高模式和大小
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST) {
widthSize = dipTopx(60);
}
if (heightMode == MeasureSpec.AT_MOST) {
heightSize = dipTopx(60);
}
//控件大小和中间文字大小进行对比
int textWidth = rect.width() + paddingLeft + paddingRight;
int textHeight = rect.height() + paddingTop + paddingBottom;
widthSize = Math.max(widthSize, textWidth);
heightSize = Math.max(heightSize, textHeight);
//控件宽高进行对比,确保是圆形
widthSize = Math.min(widthSize, heightSize);
heightSize = Math.min(widthSize, heightSize);
setMeasuredDimension(widthSize, heightSize);
}
一般在使用的时候,都会指定固定大小,所以onMeasure里面比较简单,测量完成了,就可以进行绘制了,绘制的话,有三个东西要进行绘制:背景圆、最外边的圆弧、中间的文字;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rectf = new RectF(circleWidth / 2, circleWidth / 2, getWidth() - circleWidth / 2, getHeight() - circleWidth / 2);
//绘制圆
canvas.drawOval(rectf, bgPaint);
//绘制圆弧
canvas.drawArc(rectf, 0, endAngle, false, mPaint);
//重新测量文字大小
mTextPaint.getTextBounds(middleTex, 0, middleTex.length(), rect);
//绘制文字
int dx = getWidth() / 2 - rect.width() / 2;
//文字基线
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(middleTex, dx, baseLine, mTextPaint);
}
先绘制最简单的圆,接着是圆弧,圆弧需要注意结束的弧度endAngle,endAngle是根据时间来变动的,在ValueAnimator的onAnimationUpdate回调中会进行计算获取,剩下就是绘制文字,在绘制文字的时候要先测量文字的大小,获取文字的宽度,还有一个比较重要的就是文字基线的计算,Paint画笔的基本使用及自定义进度条这里大致介绍了基线及如何计算;
public void start(final float max) {
if (max <= 0) {
return;
}
middleTex = (int) max + "s";
//测量文字大小
mTextPaint.getTextBounds(middleTex, 0, middleTex.length(), rect);
//动画
valueAnimator = ValueAnimator.ofFloat(0, max * 1000);
valueAnimator.setInterpolator(new LinearInterpolator());
long duration = (long) (max * 1000);
valueAnimator.setDuration(duration);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
//当前值除以最大值,再乘以360
endAngle = animatedValue / (max * 1000) * 360;
Log.e("endAngle---", "" + endAngle + "animatedValue-->" + animatedValue);
int time = (int) ((max + 1) - animatedValue / 1000);
if (endAngle >= 360) {
time = 0;
}
middleTex = time + "s";
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//监听动画完成
if (linenter != null) {
linenter.onEnd();
}
}
});
valueAnimator.start();
}
这是提供调用的开始动画的方法,动画执行的时间为max1000,也就是广告显示的时间1000毫秒,然后给动画设置addUpdateListener和addListener监听,在onAnimationUpdate回调中去计算endAngle值,
endAngle=当前值(animatedValue)/最大值(max * 1000)*360;
在onAnimationEnd进行执行完毕的回调,这样其实差不多ok了;
public class SplashIDView extends View {
//背景颜色
private int splashColor = Color.parseColor("#5c5658");
//圆弧颜色
private int circleColor = Color.RED;
//中间字体颜色
private int textColor = Color.BLACK;
//圆弧宽度
private int circleWidth;
//字体大小
private int textSize;
private Paint mPaint;
private Paint mTextPaint;
private Paint bgPaint;
//结束圆弧弧度
private float endAngle;
//中间字符串
private String middleTex;
private Rect rect = new Rect();
//字符串边距
private int paddingLeft;
private int paddingRight;
private int paddingTop;
private int paddingBottom;
private ValueAnimator valueAnimator;
public SplashIDView(Context context) {
this(context, null);
}
public SplashIDView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SplashIDView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttri(context, attrs);
}
/**
* 初始化自定义属性和画笔
*
* @param context
* @param attrs
*/
private void initAttri(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SplashIDView);
circleColor = array.getColor(R.styleable.SplashIDView_circle_color, circleColor);
textColor = array.getColor(R.styleable.SplashIDView_splash_txt_color, textColor);
circleWidth = array.getDimensionPixelSize(R.styleable.SplashIDView_circle_width, dipTopx(5));
textSize = array.getDimensionPixelSize(R.styleable.SplashIDView_splash_txt_size, dipTopx(12));
splashColor = array.getColor(R.styleable.SplashIDView_splash_bg, splashColor);
paddingLeft = array.getDimensionPixelSize(R.styleable.SplashIDView_txtpadding_left, paddingLeft);
paddingRight = array.getDimensionPixelSize(R.styleable.SplashIDView_txtpadding_right, paddingRight);
paddingTop = array.getDimensionPixelSize(R.styleable.SplashIDView_txtpadding_top, paddingTop);
paddingBottom = array.getDimensionPixelSize(R.styleable.SplashIDView_txtpadding_bottom, paddingBottom);
array.recycle();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(circleColor);
mPaint.setStrokeWidth(circleWidth);
mPaint.setStyle(Paint.Style.STROKE);
mTextPaint = new Paint();
mTextPaint.setColor(textColor);
mTextPaint.setTextSize(textSize);
bgPaint = new Paint();
bgPaint.setColor(splashColor);
bgPaint.setAntiAlias(true);
bgPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取宽高模式和大小
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST) {
widthSize = dipTopx(60);
}
if (heightMode == MeasureSpec.AT_MOST) {
heightSize = dipTopx(60);
}
//控件大小和中间文字大小进行对比
int textWidth = rect.width() + paddingLeft + paddingRight;
int textHeight = rect.height() + paddingTop + paddingBottom;
widthSize = Math.max(widthSize, textWidth);
heightSize = Math.max(heightSize, textHeight);
//控件宽高进行对比,确保是圆形
widthSize = Math.min(widthSize, heightSize);
heightSize = Math.min(widthSize, heightSize);
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rectf = new RectF(circleWidth / 2, circleWidth / 2, getWidth() - circleWidth / 2, getHeight() - circleWidth / 2);
//绘制圆
canvas.drawOval(rectf, bgPaint);
//绘制圆弧
canvas.drawArc(rectf, 0, endAngle, false, mPaint);
//重新测量文字大小
mTextPaint.getTextBounds(middleTex, 0, middleTex.length(), rect);
//绘制文字
int dx = getWidth() / 2 - rect.width() / 2;
//文字基线
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(middleTex, dx, baseLine, mTextPaint);
}
public void start(final float max) {
if (max <= 0) {
return;
}
middleTex = (int) max + "s";
//测量文字大小
mTextPaint.getTextBounds(middleTex, 0, middleTex.length(), rect);
//动画
valueAnimator = ValueAnimator.ofFloat(0, max * 1000);
valueAnimator.setInterpolator(new LinearInterpolator());
long duration = (long) (max * 1000);
valueAnimator.setDuration(duration);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
//当前值除以最大值,再乘以360
endAngle = animatedValue / (max * 1000) * 360;
Log.e("endAngle---", "" + endAngle + "animatedValue-->" + animatedValue);
int time = (int) ((max + 1) - animatedValue / 1000);
if (endAngle >= 360) {
time = 0;
}
middleTex = time + "s";
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//监听动画完成
if (linenter != null) {
linenter.onEnd();
}
}
});
valueAnimator.start();
}
/**
* 取消动画执行
*/
public void cancle() {
if (valueAnimator != null && valueAnimator.isRunning()) {
valueAnimator.cancel();
}
}
private SplashLintener linenter;
public void setOnFinishLinenter(SplashLintener linenter) {
this.linenter = linenter;
}
private int dipTopx(int dip) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
public interface SplashLintener {
/**
* 倒计时结束
*/
void onEnd();
}
}
上面这个就是所有实现代码,就是多了些自定义属性和画笔的初始化动作;
使用很简单,在xml布局中引入,然后获取控件,调用start方法,设置监听回调;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SplashIDView splashView = (SplashIDView) findViewById(R.id.splash_view);
splashView.start(6);
splashView.setOnFinishLinenter(new SplashIDView.SplashLintener() {
@Override
public void onEnd() {
//加载完成后将广告页面隐藏掉就可以了
Toast.makeText(MainActivity.this,"完成了",Toast.LENGTH_LONG).show();
}
});
}
}
源码地址