美文网首页
Android 带进度波浪和右上角标的圆球

Android 带进度波浪和右上角标的圆球

作者: reaiya | 来源:发表于2019-03-28 21:07 被阅读0次

    参考文章:https://blog.csdn.net/chzphoenix/article/details/77767534

    先上效果图:


    Video_20190327_024709_134.gif

    带进度波浪和右上角标的圆球WaveCircleView

    public class WaveCircleView extends View {
        private final static String TAG = "WaveCircleView";
        private Paint mCirclePaint;
        private Paint mWavePaint;
        private Paint mCornerMarkBgPaint;
        private Paint mCornerMarkPaint;
        // 下载图标bitmap
        private Bitmap mDownloadBitmap;
        // 下载图标显示矩形区域
        private RectF mDownloadBitmapRectF;
        // 角标背景颜色
        private int mCornerMarkBgColor = 0xffF0494F;
        // 角标文字颜色
        private int mCornerMarkTxtColor = 0xffffffff;
        // 角标数量
        private int mCornerMarkNum = 0;
        // 角标背景矩形
        private RectF mCornerMarkBgRectF;
        // 圆圈颜色
        private int mCircleColor = 0xff222222;
        // 圆圈宽度
        private int mCircleWidth = 4;
        // 波浪颜色(透明度太低在混合模式下显示不出来,坑死人)
        private int mWaveColor = 0x8839B939;
        // 进度(0-100)
        private int mProgress = 0;
        // 是否正在浪
        private boolean mIsFlashing;
        // 波浪1的大小
        private int mWaveHeight1;
        // 波浪2的大小
        private int mWaveHeight2;
        // 波浪1的周期
        private float mWaveCycle1;
        // 波浪2的周期
        private float mWaveCycle2;
        // 当前波浪一的偏移
        private int mOffset1 = 0;
        // 当前波浪二的偏移
        private int mOffset2 = 0;
        // 波浪一的偏移速度
        private int mSpeed1;
        // 波浪一的偏移速度
        private int mSpeed2;
        // 遮罩模式
        PorterDuffXfermode mPorterDuffXfermode;
        // 遮罩bitmap
        private Bitmap mMaskBitmap;
        // progress属性动画
        private ObjectAnimator mProgressObjectAnimator;
        // 波浪逐渐平静下来的动画
        private ObjectAnimator mWaveStopAnimator;
        // 一直浪的动画
        private ValueAnimator mFlashingAnimator;
        // 圆圈的直径
        private int mOriginHeight;
        // 角标距圆圈顶部的距离
        private int mCornerOutTopHeight;
        // 角标宽度
        private int mCornerWidth;
        // 角标高度
        private int mCornerHeight;
    
        public WaveCircleView(Context context) {
            this(context, null);
        }
    
        public WaveCircleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WaveCircleView(Context context, @Nullable AttributeSet attrs,
                              int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        /**
         * 初始化
         */
        private void init() {
            mCirclePaint = new Paint();
            mCirclePaint.setStyle(Paint.Style.STROKE);
            mCirclePaint.setColor(mCircleColor);
            mCirclePaint.setStrokeWidth(mCircleWidth);
            mCirclePaint.setAntiAlias(true);
            mWavePaint = new Paint();
            mWavePaint.setColor(mWaveColor);
            mWavePaint.setStyle(Paint.Style.FILL);
            mWavePaint.setAntiAlias(true);
            mCornerMarkBgPaint = new Paint();
            mCornerMarkBgPaint.setColor(mCornerMarkBgColor);
            mCornerMarkBgPaint.setAntiAlias(true);
            mCornerMarkPaint = new Paint();
            mCornerMarkPaint.setColor(mCornerMarkTxtColor);
            mCornerMarkPaint.setTextSize(ResourceUtil.sp2px(getContext(),10));
            mCornerMarkPaint.setTextAlign(Paint.Align.CENTER);
            mCornerMarkPaint.setAntiAlias(true);
            mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
            mDownloadBitmap =
                    ((BitmapDrawable)getResources().getDrawable(R.drawable.ic_xiazai_jiantou)).getBitmap();
            initFlashingAnimator();
            mCornerOutTopHeight = ResourceUtil.dip2px(getContext(),2);
            mCornerWidth = ResourceUtil.dip2px(getContext(),18);
            mCornerHeight = ResourceUtil.dip2px(getContext(),12);
        }
    
        private void initFlashingAnimator() {
            mFlashingAnimator = ValueAnimator.ofInt(100);
            mFlashingAnimator.setDuration(1000);
            mFlashingAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mFlashingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    if(mIsFlashing) {
                        mOffset1 += mSpeed1;
                        mOffset2 += mSpeed2;
                        invalidate();
                    }
                }
            });
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec)
                            + ResourceUtil.dip2px(getContext(),9),
                    MeasureSpec.getMode(widthMeasureSpec)),
                    MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec)
                                    + mCornerOutTopHeight,
                            MeasureSpec.getMode(heightMeasureSpec)));
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mOriginHeight = h - mCornerOutTopHeight - mCircleWidth*2;
            Log.d(TAG,
                    "onSizeChanged() called with: w = [" + w + "], h = [" + h + "], oldw = [" + oldw + "], oldh = [" + oldh + "]");
            if(mOriginHeight > 0){
                mSpeed1 = mOriginHeight / 20;
                mSpeed2 = mOriginHeight / 30;
                mWaveHeight1 = ResourceUtil.dip2px(getContext(),10);
                mWaveHeight2 = ResourceUtil.dip2px(getContext(),5);
                if(mOriginHeight/10 < mWaveHeight1){
                    mWaveHeight1 = mOriginHeight / 10;
                    mWaveHeight2 = mOriginHeight / 20;
                }
                mCornerMarkBgRectF = new RectF(w-mCornerWidth,
                        0,
                        w,
                        mCornerHeight);
                initStopAnimator(mWaveHeight1,mWaveHeight2);
                mWaveCycle1 = (float) (3 * Math.PI / mOriginHeight);
                mWaveCycle2 = (float) (4 * Math.PI / mOriginHeight);
                mMaskBitmap = Bitmap.createBitmap(mOriginHeight,mOriginHeight,
                        Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(mMaskBitmap);
                canvas.drawOval(new RectF(0,0,
                                mOriginHeight,
                                mOriginHeight),
                        mWavePaint);
                mDownloadBitmapRectF = new RectF(mOriginHeight/4f+mCircleWidth,
                        mCornerOutTopHeight+mOriginHeight/4f+mCircleWidth,
                        mOriginHeight*3/4f+mCircleWidth,
                        h-mOriginHeight/4f-mCircleWidth);
            }
        }
    
        public int getCornerMarkNum() {
            return mCornerMarkNum;
        }
    
        public void setCornerMarkNum(int mCornerMarkNum) {
            this.mCornerMarkNum = mCornerMarkNum;
            if(!mIsFlashing){
                invalidate();
            }
        }
    
        public boolean getIsFlashing() {
            return mIsFlashing;
        }
    
        // 用于databinding
        public void setIsFlashing(boolean mIsFlashing) {
            if(mIsFlashing){
                start();
            }else{
                stop();
            }
        }
    
        public int getWaveHeight1() {
            return mWaveHeight1;
        }
    
        @Keep
        public void setWaveHeight1(int mWaveHeight1) {
            this.mWaveHeight1 = mWaveHeight1;
        }
    
        public int getWaveHeight2() {
            return mWaveHeight2;
        }
    
        @Keep
        public void setWaveHeight2(int mWaveHeight2) {
            this.mWaveHeight2 = mWaveHeight2;
        }
    
        public int getWaveProgress() {
            return mProgress;
        }
    
        @Keep
        public void setWaveProgress(int mProgress) {
            // 如果进度跨度达到20,启用过渡动画
            if(Math.abs(mProgress - this.mProgress) >= 20){
                progressAnimStart(mProgress);
            }else {
                this.mProgress = mProgress;
            }
        }
    
        /**
         * 开始浪
         */
        public void start(){
            mIsFlashing = true;
            if(!mFlashingAnimator.isStarted() || !mFlashingAnimator.isRunning()){
                mFlashingAnimator.start();
            }
        }
    
        /**
         * 停止浪
         */
        public void stop(){
            mFlashingAnimator.cancel();
            mWaveStopAnimator.start();
        }
    
        /**
         * 创建波浪停止动画
         * 两条波浪振幅逐渐减小
         */
        private void initStopAnimator(final int waveHeightA, final int waveHeightB){
            PropertyValuesHolder holderA = PropertyValuesHolder.ofInt("WaveHeight1", 0);
            PropertyValuesHolder holderB = PropertyValuesHolder.ofInt("WaveHeight2", 0);
            mWaveStopAnimator = ObjectAnimator.ofPropertyValuesHolder(this, holderA, holderB);
            mWaveStopAnimator.setDuration(1000);
            mWaveStopAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                }
                @Override
                public void onAnimationEnd(Animator animation) {
                    mIsFlashing = false;
                    mWaveHeight1 = waveHeightA;
                    mWaveHeight2 = waveHeightB;
                }
                @Override
                public void onAnimationCancel(Animator animation) {
                    mWaveHeight1 = waveHeightA;
                    mWaveHeight2 = waveHeightB;
                }
                @Override
                public void onAnimationRepeat(Animator animation) {
                }
            });
            mWaveStopAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    //改变曲线的偏移,达到波浪运动的效果
                    mOffset1 += mSpeed1;
                    mOffset2 += mSpeed2;
                    invalidate();
                }
            });
        }
    
        /**
         * 缓慢涨/降进度
         * @param progress
         */
        public void progressAnimStart(int progress){
            if(mProgressObjectAnimator != null && mProgressObjectAnimator.isRunning()){
                mProgressObjectAnimator.cancel();
            }
            if(mWaveStopAnimator != null && mWaveStopAnimator.isRunning()){
                mWaveStopAnimator.cancel();
            }
            if(!mIsFlashing) {
                start();
            }
            mProgressObjectAnimator = ObjectAnimator.ofInt(this,"WaveProgress",progress);
            mProgressObjectAnimator.setDuration(1000);
            mProgressObjectAnimator.start();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            float radius = mOriginHeight/2f;
            // 画圆圈
            canvas.drawCircle(mCircleWidth+radius,
                    mCircleWidth+radius + mCornerOutTopHeight,mCircleWidth/2f+radius,
                    mCirclePaint);
            canvas.drawColor(Color.TRANSPARENT);
            // 将以下内容存储为一个层
            int sc = canvas.saveLayer(mCircleWidth,mCircleWidth+mCornerOutTopHeight,
                    mOriginHeight+mCircleWidth,
                    mCornerOutTopHeight+mOriginHeight+mCircleWidth,null,Canvas.ALL_SAVE_FLAG);
            if(mIsFlashing){
                for (int i = 0; i <= mOriginHeight; i++) {
                    canvas.drawLine(i+mCircleWidth,
                            (float) getWaveY(i,mOffset1,mWaveHeight1,mWaveCycle1) + mCornerOutTopHeight + mCircleWidth,
                            i+mCircleWidth,
                            mOriginHeight + mCircleWidth + mCornerOutTopHeight,
                            mWavePaint);
                    canvas.drawLine(i+mCircleWidth,
                            (float) getWaveY(i,mOffset2,mWaveHeight2,mWaveCycle2) + mCornerOutTopHeight + mCircleWidth,
                            i+mCircleWidth,
                            mOriginHeight + mCircleWidth + mCornerOutTopHeight,
                            mWavePaint);
                }
            }else{// 如果没有浪就风平浪静
                float height = (1 - mProgress / 100.0f) * mOriginHeight;
                canvas.drawRect(mCircleWidth, height + mCornerOutTopHeight + mCircleWidth,
                        mOriginHeight + mCircleWidth, getHeight() - mCircleWidth, mWavePaint);
                canvas.drawRect(mCircleWidth, height + mCornerOutTopHeight + mCircleWidth, mOriginHeight + mCircleWidth
                        , getHeight() - mCircleWidth, mWavePaint);
            }
            // 画遮罩
            mWavePaint.setXfermode(mPorterDuffXfermode);
            canvas.drawBitmap(mMaskBitmap,mCircleWidth,mCornerOutTopHeight+mCircleWidth,
                    mWavePaint);
            mWavePaint.setXfermode(null);
            canvas.restoreToCount(sc);
    
            // 画下载图标
            canvas.drawBitmap(mDownloadBitmap,null,mDownloadBitmapRectF
                    ,null);
            if(mCornerMarkNum != 0) {
                // 画角标背景
                canvas.drawRoundRect(mCornerMarkBgRectF, mCornerHeight/2f,
                        mCornerHeight/2f, mCornerMarkBgPaint);
                // 画角标文字
                canvas.drawText(String.valueOf(mCornerMarkNum>99 ? 99 : mCornerMarkNum),
                        mOriginHeight+2*mCircleWidth,
                        ResourceUtil.dip2px(getContext(), 9),
                        mCornerMarkPaint);
            }
        }
    
        /**
         * 波浪的函数,用于求y值
         * 函数为a*sin(b*(x + c))+d
         * @param x           x轴
         * @param offset      偏移
         * @param waveHeight  振幅
         * @param waveCycle   周期
         * @return
         */
        private double getWaveY(int x, int offset, int waveHeight, float waveCycle) {
            if(mProgress == 100){
                return 0;
            }
            return waveHeight * Math.sin(waveCycle * (x + offset)) + (1 - mProgress / 100.0) * mOriginHeight;
        }
    
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            if(mProgressObjectAnimator != null && mProgressObjectAnimator.isRunning()){
                mProgressObjectAnimator.cancel();
                mProgressObjectAnimator.removeAllListeners();
                mProgressObjectAnimator = null;
            }
            if(mFlashingAnimator != null && mFlashingAnimator.isRunning()){
                mFlashingAnimator.cancel();
                mFlashingAnimator.removeAllListeners();
                mFlashingAnimator = null;
            }
            if(mWaveStopAnimator != null && mWaveStopAnimator.isRunning()){
                mWaveStopAnimator.cancel();
                mWaveStopAnimator.removeAllListeners();
                mWaveStopAnimator = null;
            }
        }
    }
    

    原理:
    画出两条正弦曲线与x轴的交叉部分,然后用一个圆形遮罩取出想要的部分。然后通过调整振幅和偏移达到波浪效果。

    写于2019-3-27

    相关文章

      网友评论

          本文标题:Android 带进度波浪和右上角标的圆球

          本文链接:https://www.haomeiwen.com/subject/jiqhbqtx.html