自定义view仿KeepApp Splash广告效果

device-2018-05-22-074539.gif

这是一个常见的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();
            }
        });
    }
}

源码地址

你可能感兴趣的:(自定义view仿KeepApp Splash广告效果)