Android 球形进度条,动态控制时间与最大进度


老规矩,先上效果图:





1.我直接写好了一个自定义View类,直接贴代码:

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

/**
 * Author: ManitZhang
 * Date: 2018/5/20
 * email: manitozhang@foxmail.com
 * 球形进度条
 */

public class CircleProgress extends View {

    //View的宽高
    private int width;
    private int height;
    //View的画笔
    private Paint wavePaint;
    private Paint textPaint;
    private Paint circlePaint;

    //波浪的路径
    private Path path;
    //sin曲线的长度:一个周期长度
    private int cycle = 100;
    //每次平移的长度,为四分之一个周期
    private int translateX = cycle / 5;
    //sin曲线振幅的高度
    private int waveHeight = 20;
    //sin曲线的起点坐标
    private Point startPoint;
    //当前波浪的进度
    private int progress = 0;
    //当前波浪的速度
    private int waveSpeech = 80;
    //是否启用了自动增长进度
    private boolean isAutoIncrease = false;
    protected OnCircleProgressListener onCircleProgressListener;

    public CircleProgress(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initPaint(context);
    }

    public CircleProgress(Context context) {
        super(context);
        initPaint(context);
    }

    private void initPaint(Context context) {
        path = new Path();

        wavePaint = new Paint();
        wavePaint.setAntiAlias(true);
        wavePaint.setStyle(Paint.Style.FILL);
        wavePaint.setColor(Color.parseColor("#FBE201"));

        circlePaint = new Paint();
        circlePaint.setStrokeWidth(5);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setAntiAlias(true);
        circlePaint.setColor(Color.parseColor("#FBE201"));

        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(50);
        textPaint.setColor(Color.WHITE);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //由于是一个圆形,所以取的值是宽高的最小值
        width = measureSize(400, widthMeasureSpec);
        height = measureSize(400, heightMeasureSpec);
        width = Math.min(width, height);
        height = Math.min(width, height);
        setMeasuredDimension(width, height);
        //初始化起点,为屏幕外的一个周期
        startPoint = new Point(-cycle * 4, 0);
    }

    /**
     * 测量宽高
     *
     * @param defaultSize
     * @param measureSpec
     * @return
     */
    private int measureSize(int defaultSize, int measureSpec) {
        int result = defaultSize;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        switch (mode) {
            case MeasureSpec.UNSPECIFIED:
                result = defaultSize;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = size;
                break;
        }
        return result;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //设置内间距
        setPadding(20, 20, 20, 20);
        //裁剪画布为圆形
        clipCicle(canvas);
        //绘制圆形边框
        drawCicleBorder(canvas);
        //绘制波浪区域
        drawWavePath(canvas);
        //绘制进度文字
        drawProcessText(canvas);

        //自动增长进度
        if (isAutoIncrease) {
            if (progress >= 100) {
                progress = 0;
            } else {
                progress++;
            }
        }
        //更新UI
        postInvalidateDelayed(waveSpeech);
    }

    /**
     * 裁剪画布为圆形
     *
     * @param canvas
     */
    private void clipCicle(Canvas canvas) {
        Path circlePath = new Path();
        circlePath.addCircle(width / 2, height / 2, width / 2, Path.Direction.CW);
        canvas.clipPath(circlePath);
    }

    /**
     * 绘制圆形边框
     *
     * @param canvas
     */
    private void drawCicleBorder(Canvas canvas) {
        canvas.drawPaint(circlePaint);
        canvas.drawCircle(width / 2, height / 2, width / 2, circlePaint);
    }

    /**
     * 绘制波浪区域
     *
     * @param canvas
     */
    private void drawWavePath(Canvas canvas) {
        //根据进度改变起点坐标的y值
        startPoint.y = (int) ((1 - (progress / 100.0)) * (height / 2 + width / 2));
        Log.e("TAG", "startPoint.y:" + startPoint.y);
        //移动区域起点
        path.moveTo(startPoint.x, startPoint.y);
        int j = 1;
        //循环绘制正弦曲线区域,循环两个周期
        for (int i = 1; i <= 8; i++) {
            if (i % 2 == 0) {
                //波峰
                path.quadTo(startPoint.x + (cycle * j), startPoint.y + waveHeight,
                        startPoint.x + (cycle * 2) * i, startPoint.y);
            } else {
                //波谷
                path.quadTo(startPoint.x + (cycle * j), startPoint.y - waveHeight,
                        startPoint.x + (cycle * 2) * i, startPoint.y);
            }
            j += 2;
        }
        //绘制封闭的区域
        path.lineTo(width, height);//右下角
        path.lineTo(startPoint.x, height);//左下角
        path.lineTo(startPoint.x, startPoint.y);//起点
        path.close();
        //绘制区域
        canvas.drawPath(path, wavePaint);
        path.reset();
        //一开始的起点是在-160,160 = 40 + 40 + 40 + 40,走完一个周期则回到原点
        if (startPoint.x + translateX >= 0) {
            startPoint.x = -cycle * 4;
        } else {
            startPoint.x += translateX;
        }
    }

    /**
     * 绘制进度文字
     *
     * @param canvas
     */
    private void drawProcessText(Canvas canvas) {
        //画布的大小
        Rect targetRect = new Rect(0, 0, width, height);
        Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
        int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
        // 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()
        textPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(progress + "%", targetRect.centerX(), baseline, textPaint);
        onCircleProgressListener.OnCircleProgress(progress);
    }

    /**
     * 定义一个接口监听进度
     */
    public interface OnCircleProgressListener{
        boolean OnCircleProgress(int progress);
    }

    public void setOnCircleProgressListener(OnCircleProgressListener onCircleProgressListener) {
        this.onCircleProgressListener = onCircleProgressListener;
    }

    /**
     * 开启自动增长
     */
    public void startIncrease() {
        isAutoIncrease = true;
        invalidate();
    }

    /**
     * 设置当前进度
     *
     * @param progress 进度
     */
    public void setProgress(int progress) {
        if (progress > 100 || progress < 0)
            throw new RuntimeException(getClass().getName() + "请设置[0,100]之间的值");

        this.progress = progress;
        invalidate();
    }

    /**
     * 通过动画设置当前进度
     *
     * @param targetProcess 进度 <=100
     * @param duration      动画时长
     */
    public void setProgress(final int targetProcess, int duration) {
        if (progress > 100 || progress < 0)
            throw new RuntimeException(getClass().getName() + "请设置[0,100]之间的值");

        ValueAnimator progressAnimator = ValueAnimator.ofInt(progress, targetProcess);
        progressAnimator.setDuration(duration);
        progressAnimator.setTarget(progress);
        progressAnimator.setInterpolator(new LinearInterpolator());
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setProgress((Integer) animation.getAnimatedValue());
            }
        });
        progressAnimator.start();
    }

    /**
     * 获取当前进度
     *
     * @return
     */
    public int getProgress() {
        return progress;
    }

}
2.我们在布局调用这个写好的自定义View:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.test.cp.MainActivity">

    <com.test.cp.CircleProgress
        android:id="@+id/cp_loading"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_centerInParent="true" />

    <Button
        android:id="@+id/btn_loading"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="开始加载" />

</RelativeLayout>

3.然后我们就可以直接在MainActivity调用这个自定义View了:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private CircleProgress mCpLoading;
    private Button mBtnLoading;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mCpLoading = findViewById(R.id.cp_loading);//自定义View
        mBtnLoading = findViewById(R.id.btn_loading);//按钮
        //按钮点击
        mBtnLoading.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCpLoading.setProgress(100,5000);
            }
        });
        //我在自定义View写了一个接口回调,来监听进度
        mCpLoading.setOnCircleProgressListener(new CircleProgress.OnCircleProgressListener() {
            @Override
            public boolean OnCircleProgress(int progress) {
                if(progress==100){
                    mCpLoading.setProgress(0);
                }
                return false;
            }
        });
    }
}

4.到这里我们就已经完成了,如果看着不合适的或者波动较小或

者较大,可以自己在自定义View里面调节值,如果有不懂的可以

问我




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值