直播类app开发,实现一个进度条效果

进度条是很多软件中必须存在的效果,今天我们就看看直播类app开发实现的进度条效果。

效果&使用

图例分别为:

  • 修改读条起点为y轴正方向
  • 消失性读条
  • 正常读条

使用:

  • 1 在xml中添加控件
<com.lloydfinch.ProgressTrackBar
    android:id="@+id/progress_track_bar"
    android:layout_width="62dp"
    android:layout_height="62dp"
    app:p_second_color="#E91E63"
    app:p_width="3dp" />

<com.lloydfinch.ProgressTrackBar
    android:id="@+id/progress_track_bar2"
    android:layout_width="62dp"
    android:layout_height="62dp"
    app:p_first_color="#18B612"
    app:p_second_color="#00000000"
    app:p_width="3dp" />

<com.lloydfinch.ProgressTrackBar
    android:id="@+id/progress_track_bar3"
    android:layout_width="62dp"
    android:layout_height="62dp"
    app:p_first_color="#ffd864"
    app:p_second_color="#1C3F7C"
    app:p_width="3dp" />
  • 2 在直播类app开发的代码中启动倒计时
val trackBar = findViewById<ProgressTrackBar>(R.id.progress_track_bar)
trackBar.setStartAngle(-90F) // 从-90度开始读条
trackBar.setOnProgressListener { // 进度回调
    Log.d("ProgressTrackBar", "progress is $it")
}
trackBar.startTask(0) { // 开始计时,传入读条结束的回调
    Log.d("ProgressTrackBar", "progress run finish")
}

// 从0开始计时
findViewById<ProgressTrackBar>(R.id.progress_track_bar2).startTask(0)

// 从20开始计时
findViewById<ProgressTrackBar>(R.id.progress_track_bar3).startTask(20)

思路&编码

核心思路就一个: 画原环。我们要画两个圆环,一个下层的完整圆环作为底色,一个上层的圆弧作为进度。重点就是计算圆弧弧度的问题了。

假设当前进度是current,最大进度是max,那么当前圆弧进度就是:(current/max)*360,然后我们直接调用:

// oval: 放置圆弧的矩形
// startAngle: 开始绘制的起点角度,方向是顺时针计算的。0就x正半轴,90就是y轴负半轴
// sweepAngle: 要绘制的圆弧的弧度,就是上述: (current/max)x360
// false: 表示不连接到圆心,表示绘制一个圆弧
canvas.drawArc(oval, startAngle, sweepAngle, false, mPaint);

直播类app开发就能绘制出对应的圆弧。

所以,我们这样:

// 绘制下层: 圆形
mPaint.setColor(firstLayerColor);
canvas.drawCircle(x, y, radius, mPaint);

// 绘制上层: 圆弧
mPaint.setColor(secondLayerColor);
float sweepAngle = (currentProgress / maxProgress) * 360;
canvas.drawArc(oval, startAngle, sweepAngle, false, mPaint);

我们先用下层颜色绘制一个圆形,然后用上层颜色绘制个圆弧,然后不断触发重绘,就能得到想要的效果。

但是,如果我们想要的是: 随着进度变大,圆弧越来越短呢?比如示例图的第二个效果。说白了就是让上层随着时间流逝而变小,直到消失,怎么实现呢?

其实,说白了就是时间越长,弧度越小,直播类app开发做减法即可,我们用(max-current)来作为已读进度,这样随着时间流逝,进度就越来越小。

有人说,这样不对啊,这样(max-current)不就越读越小了吗,这样画出来的弧度就越来越短了,最后完全漏出了底层,给人的感觉是倒着读的。没错,所以,我们只绘制一层,我们用下层颜色来绘制圆弧!这样,随着时间流逝,弧度越来越小,因为圆弧是用下层颜色绘制的,所以视觉上就是: 下层越来越少。给人的感觉就是: 上层越来越大以至于盖住了下层。

逻辑如下:

// 用下层颜色 绘制 剩下的弧度
mPaint.setColor(firstLayerColor);
float leaveAngle = ((maxProgress - currentProgress) / maxProgress) * 360;
canvas.drawArc(oval, startAngle, leaveAngle, false, mPaint);

可以看到,这里直播类app开发只绘制一层,随着时间流逝,圆弧越来越短,给人的感觉就是: 圆弧消失。就达到了示例图中 第二个圆弧的效果。

整体代码如下:

public class ProgressTrackBar extends View {


    private static final int DEFAULT_FIRST_COLOR = Color.WHITE;
    private static final int DEFAULT_SECOND_COLOR = Color.parseColor("#FFA12F");

    private static final int PROGRESS_WIDTH = 6;
    private static final float MAX_PROGRESS = 360F;
    private static final int DEFAULT_SPEED = 1;

    private Paint mPaint;
    private float startAngle = 0;
    private int firstLayerColor = DEFAULT_FIRST_COLOR;
    private int secondLayerColor = DEFAULT_SECOND_COLOR;
    private final RectF oval = new RectF(); // 圆形轨迹
    private float maxProgress = MAX_PROGRESS; // 最大进度:ms
    private float currentProgress = 0F; // 当前进度:ms
    private int speed = DEFAULT_SPEED; // 速度(多长时间更新一次UI):ms
    private int progressWidth = PROGRESS_WIDTH; // 进度条宽度

    private OnProgressFinished onProgressFinished;

    private Handler taskHandler;
    private OnProgress runnable; //进度回调

    // 顶层颜色是否是透明
    private boolean isSecondColorTransparent = false;

    public ProgressTrackBar(Context context) {
        super(context);
        init();
    }

    public ProgressTrackBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressTrackBar);
        firstLayerColor = typedArray.getColor(R.styleable.ProgressTrackBar_p_first_color, DEFAULT_FIRST_COLOR);
        secondLayerColor = typedArray.getColor(R.styleable.ProgressTrackBar_p_second_color, DEFAULT_SECOND_COLOR);
        startAngle = typedArray.getFloat(R.styleable.ProgressTrackBar_p_start, 0F);
        progressWidth = typedArray.getDimensionPixelSize(R.styleable.ProgressTrackBar_p_width, PROGRESS_WIDTH);
        maxProgress = typedArray.getDimension(R.styleable.ProgressTrackBar_p_max_progress, MAX_PROGRESS);

        typedArray.recycle();

        init();
    }

    public ProgressTrackBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        refresh();
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(progressWidth);
    }

    public void setFirstLayerColor(int firstLayerColor) {
        this.firstLayerColor = firstLayerColor;
    }

    public void setSecondLayerColor(int secondLayerColor) {
        this.secondLayerColor = secondLayerColor;
        refresh();
    }

    public void setMaxProgress(float maxProgress) {
        this.maxProgress = maxProgress;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public void setStartAngle(float startAngle) {
        this.startAngle = startAngle;
    }

    public void setProgressWidth(int progressWidth) {
        this.progressWidth = progressWidth;
    }

    public void setOnProgressListener(OnProgress runnable) {
        this.runnable = runnable;
    }

    public void setOnProgressFinished(OnProgressFinished onProgressFinished) {
        this.onProgressFinished = onProgressFinished;
    }

    private void initTask() {
        taskHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                if (currentProgress < maxProgress) {
                    currentProgress += speed;
                    postInvalidate();
                    if (runnable != null) {
                        runnable.onProgress(currentProgress);
                    }
                    taskHandler.sendEmptyMessageDelayed(0, speed);
                } else {
                    stopTask();
                }
            }
        };
    }

    private void refresh() {
        isSecondColorTransparent = (secondLayerColor == Color.parseColor("#00000000"));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int x = getWidth() >> 1;
        int y = getHeight() >> 1;
        int center = Math.min(x, y);
        int radius = center - progressWidth;

        int left = x - radius;
        int top = y - radius;
        int right = x + radius;
        int bottom = y + radius;
        oval.set(left, top, right, bottom);

        // 这里需要处理一下上层是透明的情况
        if (isSecondColorTransparent) {
            // 用下层颜色 绘制 剩下的弧度
            mPaint.setColor(firstLayerColor);
            float leaveAngle = ((maxProgress - currentProgress) / maxProgress) * 360;
            canvas.drawArc(oval, startAngle, leaveAngle, false, mPaint);
        } else {
            // 绘制下层
            mPaint.setColor(firstLayerColor);
            canvas.drawCircle(x, y, radius, mPaint);

            // 绘制上层
            mPaint.setColor(secondLayerColor);
            float sweepAngle = (currentProgress / maxProgress) * 360;
            canvas.drawArc(oval, startAngle, sweepAngle, false, mPaint);
        }
    }

    public void startTask(int progress) {
        currentProgress = progress;
        initTask();
        taskHandler.sendEmptyMessage(0);
    }

    public void startTask(int progress, OnProgressFinished onProgressFinished) {
        this.onProgressFinished = onProgressFinished;
        currentProgress = progress;
        initTask();
        taskHandler.sendEmptyMessage(0);
    }

    public void stopTask() {
        if (onProgressFinished != null) {
            onProgressFinished.onFinished();
        }
        if (taskHandler != null) {
            taskHandler.removeCallbacksAndMessages(null);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopTask();
    }

    public interface OnProgressFinished {
        void onFinished();
    }

    public interface OnProgress {
        void onProgress(float progress);
    }
}

总结

核心思路就一个: 直播类app开发如果上层要用透明盖住下层,这是不可能的,所以不如用上层的相对值去绘制下层
声明:本文由云豹科技转发自奔波儿灞取经博客,如有侵权请联系作者删除

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值