安卓自定义view练习之风扇进度条

效果图如下:
在这里插入图片描述
加载中。。。
在这里插入图片描述
FansLoadingView主体代码:

/**
 * zlww 2020-08-23
 */
public class FansLoadingView extends View {

    private Paint progressPaint;
    private Paint progressRectPaint;
    private Paint progressStrokePaint;
    private Paint progressShaderPaint;
    private RectF progressBackgroundRect;

    private final FansView fansView = new FansView();
    private float progressdegree;// 旋转的角度
    private float progress;
    private Bitmap bitmap;// 记录当前 bitmap 资源的
    private int leavesCount;// 记录需要生成的树叶数目的
    private LinearGradient gradient;
    private Path clipPath = new Path();//裁剪的路径
    private String TAG = "debuging";

    private static int COLOR_PROGRESS_BACKGROUND = Color.parseColor("#97FFFF");//背景的
    private static int COLOR_PROGRESS = Color.parseColor("#EEEE00");//进度条
    private static int COLOR_PROGRESS_STROKE = Color.argb(255, 60, 179, 113);
    private int[] colors = {0x00000000, 0xf0ffffff, 0x00000000, 0xf0ffffff, 0x00000000};
    private float[] pos = {0f, 0.50f, 0.70f, 0.76f, 1.0f};
    private int defaultwidth = 20;
    private int defaultheight = 20;
    private float strokeWidth;

    public FansLoadingView(Context context) {
        super(context);
        Log.d(TAG, "FansLoadingView: ssssssss1111111111");
        init(context, null);
    }

    public FansLoadingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);//warp content 走这里
        Log.d(TAG, "FansLoadingView: ssssssss22222222222222");
        init(context, attrs);
    }

    public FansLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.d(TAG, "FansLoadingView: ssssssss3333333333333");
        init(context, attrs);
    }

    private void init(Context ctx, AttributeSet attrs) {
        // 初始化属性↓
        TypedArray array = ctx.obtainStyledAttributes(attrs, R.styleable.FansLoadingView);
        COLOR_PROGRESS_BACKGROUND = array.getColor(R.styleable.FansLoadingView_progress_background_color, COLOR_PROGRESS_BACKGROUND);
        COLOR_PROGRESS_STROKE = array.getColor(R.styleable.FansLoadingView_progress_stroke_color, COLOR_PROGRESS_STROKE);
        leavesCount = array.getInteger(R.styleable.FansLoadingView_leaves_count, 15);
        int bitmapRes = array.getResourceId(R.styleable.FansLoadingView_petal_res, R.drawable.leaf);
        bitmap = scaleBitmap(BitmapFactory.decodeResource(getResources(), bitmapRes), dp2px(16), dp2px(16));//将dp尺寸转为px,我们默认的花瓣大小为15
        strokeWidth = array.getDimension(R.styleable.FansLoadingView_strokewidth,8);
        array.recycle();

        initPaint();
        // 下面是初始化矩形
        progressBackgroundRect = new RectF();// 具体还要在 onmesure中具体设置
    }

    private void initPaint() {
        progressPaint = new Paint(); //进度的
        progressPaint.setStyle(Paint.Style.FILL);
        progressPaint.setColor(COLOR_PROGRESS_BACKGROUND);
        progressPaint.setAntiAlias(true);

        progressRectPaint = new Paint();
        progressRectPaint.setStyle(Paint.Style.FILL);
        progressRectPaint.setColor(COLOR_PROGRESS);
        progressRectPaint.setAntiAlias(true);

        progressShaderPaint = new Paint();
        progressShaderPaint.setStyle(Paint.Style.FILL);

        progressStrokePaint = new Paint(); //边框的
        progressStrokePaint.setStyle(Paint.Style.STROKE);
        progressStrokePaint.setColor(COLOR_PROGRESS_STROKE);
        progressStrokePaint.setAntiAlias(true);
        progressStrokePaint.setStrokeWidth(strokeWidth);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
                || MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            Log.d(TAG, "onMeasure: witdh is "+width+"  height is "+height);
            setMeasuredDimension(width, height);
//            autoresetSize(width, height);
        } else {//使用默认大小
            this.getLayoutParams().width = dp2px(300);
            this.getLayoutParams().height = dp2px(60);
            setMeasuredDimension(this.getLayoutParams().width,this.getLayoutParams().height);
//            autoresetSize(this.getLayoutParams().width , this.getLayoutParams().height);
        }
    }

    // 自动根据当前的视图大小修改宽高
    private void autoresetSize(int width, int height) {
        Log.d(TAG, "autoresetSize: width"+width+"  height  "+height);
        progressBackgroundRect.set(defaultwidth, defaultheight, width - defaultwidth, height - defaultheight);
        fansView.setCurPostion(height);
        clipPath.reset();
        clipPath.addRoundRect(progressBackgroundRect,90,90, Path.Direction.CCW);//重新设置clippath
        if (gradient == null)
            gradient = new LinearGradient((float) (width * 0.60), defaultheight, (float) (width * 0.75), defaultheight * 4, colors, pos, Shader.TileMode.CLAMP);//具体也需要在onmesure中改
        progressShaderPaint.setShader(gradient);//透明遮罩的
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //背景部分
        canvas.drawRoundRect(progressBackgroundRect, 90, 90, progressPaint);
        canvas.clipPath(clipPath);
        canvas.drawRoundRect(defaultwidth,defaultheight,defaultwidth+progress,progressBackgroundRect.height() + defaultheight,90,90,progressRectPaint);
        canvas.drawRoundRect(progressBackgroundRect, 90, 90, progressShaderPaint);//阴影在上
        canvas.drawRoundRect(progressBackgroundRect, 90, 90, progressStrokePaint);//外边框

        //画花瓣的,先画花瓣
        drawPetal(canvas);

        //画风扇的
        drawFansAndCycle(canvas);
    }

    /*画风扇的,一些动画操作也主要针对这里
     *每一次 draw 都是画布的新建,要有这个意识,这里要旋转
     */
    private void drawFansAndCycle(Canvas canvas) {
        canvas.save();
        canvas.rotate(progressdegree, defaultwidth + fansView.getRadius(), defaultheight + fansView.getRadius());
        canvas.drawCircle(defaultwidth + fansView.getRadius(), defaultheight + fansView.getRadius(), fansView.getRadius(), fansView.getFansCyclePaint());
        canvas.drawPath(fansView.getPath(), fansView.getFansPaint());//画路径
        canvas.restore();//恢复旋转前的样式
    }

    public void setProgressdegree(float progressdegree) {
        this.progressdegree = progressdegree;
        invalidate();
    }

    public float getProgress() {
        return progress;
    }

    public void setProgress(float progress) {
        this.progress = progress;
    }

    //关于小风扇的 start
    private static class FansView {
        private Paint fansPaint;
        private Paint fansCyclePaint;
        private static final int COLOR_FANS = COLOR_PROGRESS_STROKE;
        private Path path;
        private int radius;

        public FansView() {
            setupPaint();//这里只需要初始化画笔即可
        }

        private void setupPaint() {
            fansPaint = new Paint();//扇叶的
            fansPaint.setStyle(Paint.Style.FILL);
            fansPaint.setColor(COLOR_FANS);
            fansPaint.setAntiAlias(true);

            fansCyclePaint = new Paint();//外面的边框
            fansCyclePaint.setStyle(Paint.Style.STROKE);
            fansCyclePaint.setColor(COLOR_FANS);
            fansCyclePaint.setAntiAlias(true);
            fansCyclePaint.setStrokeWidth(8);
        }

        private void initPath(int curheight) {
            path = new Path();
            // 风扇多高由高决定,所有很多地方都是用的 curheight
            int offsetheight = 20;
            radius = (curheight - 2 * offsetheight) / 2;//风扇圆的半径
            int offsetwidth = 20;
            RectF rectF1 = new RectF(offsetwidth + (radius >> 1), offsetheight, radius + offsetwidth + (radius >> 1), offsetheight + radius);// 上
            RectF rectF2 = new RectF(offsetwidth + (radius >> 1), offsetheight + radius, radius + offsetwidth + (radius >> 1), curheight - offsetheight);// 下
            RectF rectF3 = new RectF(offsetwidth, offsetheight + (radius >> 1), offsetwidth + radius, offsetheight + (radius >> 1) + radius);// 左
            RectF rectF4 = new RectF(offsetwidth + radius, offsetheight + (radius >> 1), curheight - offsetwidth, offsetheight + (radius >> 1) + radius);// 右
            path.addArc(rectF1, -90, 180);
            path.addArc(rectF2, 90, 180);
            path.addArc(rectF3, 180, 180);
            path.addArc(rectF4, 0, 180);
        }

        public Paint getFansPaint() {
            return fansPaint;
        }

        public Paint getFansCyclePaint() {
            return fansCyclePaint;
        }

        public Path getPath() {
            return path;
        }

        //重置位置的
        public void setCurPostion(int length) {
            initPath(length);//在此才重新设置高度等
        }

        public int getRadius() {
            return radius;
        }
    }

    private int dp2px(int dpValue) {
        int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
        return px;
    }

    private Bitmap scaleBitmap(@NonNull Bitmap bitmap, float newwidth, float newheight) {
        Bitmap newBitmap = null;
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scalewidth = newwidth / width;
        float scaleheight = newheight / height;
        Matrix matrix = new Matrix();
        matrix.postScale(scalewidth, scaleheight);
        newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
        return newBitmap;
    }

    //关于吹出的叶子的 start
    private List<LeaveView> leaveViews;

    private static class LeaveView {
        //初始属性
        private int rotateDirection;//顺时针还是逆时针转动的
        private int phase;//有些运动的快,有些动的慢取值
        private float defaultDelay;
        private float waveHigh;
        private int rotateAngle;

        public int getRotateDirection() {
            return rotateDirection;
        }

        public void setRotateDirection(int rotateDirection) {
            this.rotateDirection = rotateDirection;
        }

        public int getPhase() {
            return phase;
        }

        public void setPhase(int phase) {
            this.phase = phase;
        }

        public float getDefaultDelay() {
            return defaultDelay;
        }

        public void setDefaultDelay(float defaultDelay) {
            this.defaultDelay = defaultDelay;
        }

        public float getWaveHigh() {
            return waveHigh;
        }

        public void setWaveHigh(float waveHigh) {
            this.waveHigh = waveHigh;
        }

        public int getRotateAngle() {
            return rotateAngle;
        }

        public void setRotateAngle(int rotateAngle) {
            this.rotateAngle = rotateAngle;
        }
    }

    /**
     * 画花瓣自我旋转和运动
     */
    private void drawPetal(Canvas canvas) {
        canvas.save();
        canvas.translate(fansView.getRadius() + defaultwidth, fansView.getRadius());//叶子从扇叶中间开始出现
        for (int i = 0; i < leaveViews.size(); i++) {
            LeaveView leaveView = leaveViews.get(i);
            Matrix matrixPetal = new Matrix();

            double w = 2 * Math.PI / leaveView.getPhase();
            float x = leaveView.getDefaultDelay() * progressBackgroundRect.width();
            float xTranslate = x + distance;
            float xNew = xTranslate % progressBackgroundRect.width();

            double y = (leaveView.getWaveHigh() * Math.sin(w * xNew));// k 默认为 0 , y 要取反负数先
            float angle = 4 * distance / progressBackgroundRect.width() * leaveView.getRotateDirection() * leaveView.getRotateAngle();//看转动的角度

            matrixPetal.postRotate(angle, bitmap.getWidth() >> 1, bitmap.getHeight() >> 1);
            matrixPetal.postTranslate(xNew, (float) y);//主要是 xnew在变化

            if (xTranslate > progressBackgroundRect.width()){
                //大于progressBackgroundRect.width()后才显示
                if (xNew < progressBackgroundRect.width() - 2 * dp2px(defaultwidth)){
                    canvas.drawBitmap(bitmap, matrixPetal, null);
                }
            }

        }
        canvas.restore();
    }

    private float distance;//花瓣移动距离的
    private Handler handler = new Handler();
    private final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            distance++;
            handler.postDelayed(this, 8);//每 8 毫秒执行一次
        }
    };

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        autoresetSize(getWidth(), getHeight());
        initSetting();//初始化动画
    }

    /**
     * 初始化叶子的数量
     */
    private boolean initData = false;
    private void initLeaveData() {
        leaveViews = new ArrayList<>();
        for (int i = 0; i < leavesCount; i++) {
            LeaveView leaf = new LeaveView();

            int randomDirection = (int) (Math.random() * 2 + 1);//生成结果是 [1,2)区间内的
            int rotateDirection = randomDirection % 2 == 0 ? 1 : -1;// 1 为顺时针 -1 为逆时针
            leaf.setRotateDirection(rotateDirection); //叶子旋转方向

            double periodmax = progressBackgroundRect.width() / 3;
            double periodmin = progressBackgroundRect.width() / 4;
            int period = (int) (Math.random() * (periodmax - periodmin) + periodmin);
            leaf.setPhase(period);//周期设置好了

            double waveMax = fansView.getRadius() >> 1;
            double waveMin = fansView.getRadius() >> 2;
            float randomWave = (float) (Math.random() * (waveMax - waveMin) + waveMin);
            leaf.setWaveHigh(randomWave);

            double delay = Math.random();//[0,1)
            leaf.setDefaultDelay((float) delay);//默认延迟系数

            int randomAngle = (int) (Math.random() * 360 + 1);//[1,361)
            leaf.setRotateAngle(randomAngle);
            leaveViews.add(leaf);
        }
    }

    private void initSetting() {
        if (initData)
            return;//已经初始化过了
        initData = true;
        Log.d(TAG, "initSetting: 初始化一系列的东西....");
        initLeaveData();//初始化树叶数量,在这里才初始化,只进行一次

        //风扇旋转的动画
        ObjectAnimator rotate = ObjectAnimator.ofFloat(FansLoadingView.this, "progressdegree", 0, 360);
        rotate.setDuration(2000);
        rotate.setRepeatCount(ValueAnimator.INFINITE);//无限循环播放
        rotate.setInterpolator(new LinearInterpolator());
        rotate.setRepeatMode(ValueAnimator.RESTART);
        rotate.start();

        //加载进度
        ObjectAnimator translate = ObjectAnimator.ofFloat(FansLoadingView.this, "progress", 0, progressBackgroundRect.width());
        translate.setDuration(8 * 1000);
        translate.setInterpolator(new LinearInterpolator());
        translate.setStartDelay(2000);//延迟 2 秒开始
        translate.start();

        handler.post(runnable);//开始扇叶
    }

}

布局文件:

  <com.zhlw.animationexample.ui.home.FansLoadingView
            android:id="@+id/fansloadingview"
            android:layout_width="300dp"
            android:layout_height="60dp"
            app:petal_res="@drawable/leaf"
            app:strokewidth="8dp"
            app:leaves_count="15"/>

相关attr:

<declare-styleable name="FansLoadingView">
    <attr name="progress_background_color" format="color"/>
    <attr name="progress_stroke_color" format="color"/>
    <attr name="color_fans" format="color"/>
    <attr name="leaves_count" format="integer"/>
    <attr name="petal_res" format="reference"/>
    <attr name="strokewidth" format="dimension"/>
</declare-styleable>

END

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值