当前位置: 代码网 > it编程>App开发>Android > android 自定义view实现彩虹进度条功能

android 自定义view实现彩虹进度条功能

2024年07月03日 Android 我要评论
实现一个彩虹色进度条功能,不说明具体用途大家应该能猜到。想找别人造的轮子,但是没有合适的,所以决定自己实现一个。相关知识android 自定义viewlineargradient 线性渐变实现步骤自定

实现一个彩虹色进度条功能,不说明具体用途大家应该能猜到。想找别人造的轮子,但是没有合适的,所以决定自己实现一个。

相关知识

android 自定义view

lineargradient 线性渐变

实现步骤

自定义view

自定义一个tmcview类继承view

重写两个构造方法。构造方法一共有4个,这里边重写两个

重写ongsizechanged方法,用来获取控件宽、高,来计算内部组件尺寸。

重写ondraw方法,里边要描画背景drawbackground,分段数据drawsection,和seekbar图片drawimage。

import android.content.context;
import android.graphics.canvas;
import android.util.attributeset;
import android.view.view;
import androidx.annotation.nullable;
import java.util.list;
public class tmcview extends view {
    public tmcview(context context) {
        super(context);
    }
    public tmcview(context context, @nullable attributeset attrs) {
        super(context, attrs);
    }
    @override
    protected void onsizechanged(int w, int h, int oldw, int oldh) {
        super.onsizechanged(w, h, oldw, oldh);
    }
    @override
    protected void ondraw(canvas canvas) {
        super.ondraw(canvas);
        drawbackground(canvas);
        drawsection(canvas);
        drawimage(canvas);
    }
    /**
     * 画背景
     * @param canvas 画布
     */
    private void drawbackground(canvas canvas) {
    }
    /**
     * 画分段数据
     * @param canvas 画布
     */
    private void drawsection(canvas canvas) {
    }
    /**
     * 画图片
     * @param canvas 画布
     */
    private void drawimage(canvas canvas) {
    }
    /**
     * 更新view
     * @param total 总长度
     * @param rest 剩余长度
     * @param sections 分段数据
     */
    public void updateview(int total, int rest, list<sectiondata> sections){
    }

实现几个重构方法

标注:

整体宽度为图片宽度44px

背景条宽度30px,外边距7px,圆角15px

带颜色的条宽度20xp,外边距5px,圆角15px

自定义view的坐标轴是以view的左上角位置为原点,向右为x轴正方向,向下为y轴正方向

在视图中用rectf创建背景,和颜色条,并在onsizechanged中设置尺寸

public class tmcview extends view {
    private final rectf backgroundrectf = new rectf();
    private final rectf colorrectf = new rectf();
    public tmcview(context context) {
        super(context);
    }
    public tmcview(context context, @nullable attributeset attrs) {
        super(context, attrs);
    }
    @override
    protected void onsizechanged(int w, int h, int oldw, int oldh) {
        super.onsizechanged(w, h, oldw, oldh);
        //设置矩形 left top right bottom 左上右下点的值  按照标注计算得出
        backgroundrectf.set(7, 0, 37, h);
        colorrectf.set(12, 5, 32, h-5);
    }
    ...
}

绘制背景条

实现drawbackground方法,画背景需要一根画笔paint 为了避免重复创建,声明为成员变量

public class tmcview extends view {
    /*背景画笔*/
    private final paint backpaint = new paint(paint.anti_alias_flag);
    private final rectf backgroundrectf = new rectf();
    private final rectf colorrectf = new rectf();
    public tmcview(context context) {
        super(context);
    }
    public tmcview(context context, @nullable attributeset attrs) {
        super(context, attrs);
    }
    @override
    protected void onsizechanged(int w, int h, int oldw, int oldh) {
        super.onsizechanged(w, h, oldw, oldh);
        //设置矩形 left top right bottom 左上右下点的值  按照标注计算得出
        backgroundrectf.set(7, 0, 37, h);
        colorrectf.set(12, 5, 32, h-5);
    }
    @override
    protected void ondraw(canvas canvas) {
        super.ondraw(canvas);
        drawbackground(canvas);
        drawsection(canvas);
        drawimage(canvas);
    }
    /**
     * 画背景
     * @param canvas 画布
     */
    private void drawbackground(canvas canvas) {
        backpaint.setstyle(paint.style.fill);
        backpaint.setantialias(true); //抗锯齿
        backpaint.setcolor(color.parsecolor("#ffffff"));
        //画圆角矩形,15为圆角的角度
        canvas.drawroundrect(backgroundrectf, 15, 15, backpaint);
    }
...
}

  布局中加入tmcview

<com.bigxuan.tesapp.view.tmcview
        android:id="@+id/tmc"
        android:layout_width="44px"
        android:layout_height="690px"
        android:layout_marginend="1230px"
        android:layout_marginbottom="100px"
        app:layout_constraintbottom_tobottomof="parent"
        app:layout_constraintend_toendof="parent"
        />

绘制颜色条

实现drawsection方法,这里要用到线性渐变lineargradient

lineargradient 简单说,指定每一段的颜色和位置百分比,就能实现每一段显示不同颜色。

但它默认是渐变色,要想不变就在每一段的开始和结束位置都设置相同的颜色。

再创建一个画笔 paint,画颜色条

public class tmcview extends view {
    private final paint backpaint = new paint(paint.anti_alias_flag);
    private final paint colorpaint = new paint(paint.anti_alias_flag);
    private final rectf backgroundrectf = new rectf();
    private final rectf colorrectf = new rectf();
    public tmcview(context context) {
        super(context);
    }
    public tmcview(context context, @nullable attributeset attrs) {
        super(context, attrs);
    }
    @override
    protected void onsizechanged(int w, int h, int oldw, int oldh) {
        super.onsizechanged(w, h, oldw, oldh);
        //设置矩形 left top right bottom 左上右下点的值  按照标注计算得出
        backgroundrectf.set(7, 0, 37, h);
        colorrectf.set(12, 5, 32, h-5);
    }
    @override
    protected void ondraw(canvas canvas) {
        super.ondraw(canvas);
        drawbackground(canvas);
        drawsection(canvas);
        drawimage(canvas);
    }
    /**
     * 画背景
     * @param canvas 画布
     */
    private void drawbackground(canvas canvas) {
        backpaint.setstyle(paint.style.fill);
        backpaint.setantialias(true); //抗锯齿
        backpaint.setcolor(color.parsecolor("#ffffff"));
        //画圆角矩形,15为圆角的角度
        canvas.drawroundrect(backgroundrectf, 15, 15, backpaint);
    }
    /**
     * 画分段数据
     * @param canvas 画布
     */
    private void drawsection(canvas canvas) {
        int[] colorarray = {
                color.parsecolor("#ff0000"), color.parsecolor("#ff0000"),
                color.parsecolor("#00ff00"), color.parsecolor("#00ff00"),
                color.parsecolor("#0000ff"), color.parsecolor("#0000ff")
        };
        float[] positionarray = {
                0f, 0.2f,
                0.2f, 0.6f,
                0.6f, 1f
        };
        colorpaint.setstyle(paint.style.fill);
        colorpaint.setantialias(true);
        //指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度
        colorpaint.setshader(new lineargradient(0, 0, 0, colorrectf.bottom, colorarray, positionarray, shader.tilemode.repeat));
        canvas.drawroundrect(colorrectf,15, 15, colorpaint);
    }
    ...
}

绘制进度图片

app加入图片资源,根据资源id获取bitmap对象,绘制。

需要注意的是,坐标轴的顶点在左上角,绘制图片时也是以图片左上顶点位置做定位,图片位置是view的高度减掉图片的高度,才能显示在正确位置。

public class tmcview extends view {
    private context context;
    private final paint backpaint = new paint(paint.anti_alias_flag);
    private final paint colorpaint = new paint(paint.anti_alias_flag);
    private final rectf backgroundrectf = new rectf();
    private final rectf colorrectf = new rectf();
    /*图片资源id*/
    private int imgresid;
    /*图片位置*/
    private int imgpos;
    public tmcview(context context) {
        super(context);
    }
    public tmcview(context context, @nullable attributeset attrs) {
        super(context, attrs);
        this.context = context;
        this.imgresid = r.drawable.icon_seekbar_day;
    }
    @override
    protected void onsizechanged(int w, int h, int oldw, int oldh) {
        super.onsizechanged(w, h, oldw, oldh);
        //设置矩形 left top right bottom 左上右下点的值  按照标注计算得出
        backgroundrectf.set(7, 0, 37, h);
        colorrectf.set(12, 5, 32, h-5);
        imgpos = h - 44; // 设置一个初始位置
    }
    @override
    protected void ondraw(canvas canvas) {
        super.ondraw(canvas);
        drawbackground(canvas);
        drawsection(canvas);
        drawimage(canvas);
    }
    /**
     * 画背景
     * @param canvas 画布
     */
    private void drawbackground(canvas canvas) {
        backpaint.setstyle(paint.style.fill);
        backpaint.setantialias(true); //抗锯齿
        backpaint.setcolor(color.parsecolor("#ffffff"));
        //画圆角矩形,15为圆角的角度
        canvas.drawroundrect(backgroundrectf, 15, 15, backpaint);
    }
    /**
     * 画分段数据
     * @param canvas 画布
     */
    private void drawsection(canvas canvas) {
        int[] colorarray = {
                color.parsecolor("#ff0000"), color.parsecolor("#ff0000"),
                color.parsecolor("#00ff00"), color.parsecolor("#00ff00"),
                color.parsecolor("#0000ff"), color.parsecolor("#0000ff")
        };
        float[] positionarray = {
                0f, 0.2f,
                0.2f, 0.6f,
                0.6f, 1f
        };
        colorpaint.setstyle(paint.style.fill);
        colorpaint.setantialias(true);
        //指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度
        colorpaint.setshader(new lineargradient(0, 0, 0, colorrectf.bottom, colorarray, positionarray, shader.tilemode.repeat));
        canvas.drawroundrect(colorrectf,15, 15, colorpaint);
    }
    /**
     * 画图片
     * @param canvas 画布
     */
    private void drawimage(canvas canvas) {
        bitmap bitmap = initbitmap(imgresid);
        canvas.save();
        canvas.translate(0, 0);
        canvas.drawbitmap(bitmap, 0, imgpos, null);
        canvas.restore();
    }
    /**
     * 通过资源id 创建bitmap
     * @param resid 资源id
     * @return
     */
    private bitmap initbitmap(int resid) {
        bitmap originalbmp = bitmapfactory.decoderesource(context.getresources(), resid);
        return bitmap.createscaledbitmap(originalbmp, 44, 44, true);
    }
...
}

公共方法

暴露方法用来更新进度updateview

定义一个图片有效的运动距离为view高度减掉图片高度,函数参数为总距离和剩余距离,计算百分比后乘以有效运动距离得出图片描画位置。最后调用invalidate方法刷新view

import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.lineargradient;
import android.graphics.paint;
import android.graphics.rectf;
import android.graphics.shader;
import android.util.attributeset;
import android.view.view;
import androidx.annotation.nullable;
import com.navinfo.tesapp.r;
import java.util.list;
public class tmcview extends view {
    private context context;
    private final paint backpaint = new paint(paint.anti_alias_flag);
    private final paint colorpaint = new paint(paint.anti_alias_flag);
    private final rectf backgroundrectf = new rectf();
    private final rectf colorrectf = new rectf();
    /*图片资源id*/
    private int imgresid;
    /*图片位置*/
    private int imgpos;
    /*图片有效的运动距离*/
    private int imgvalidheight;
    public tmcview(context context) {
        super(context);
    }
    public tmcview(context context, @nullable attributeset attrs) {
        super(context, attrs);
        this.context = context;
        this.imgresid = r.drawable.icon_seekbar_day;
    }
    @override
    protected void onsizechanged(int w, int h, int oldw, int oldh) {
        super.onsizechanged(w, h, oldw, oldh);
        //设置矩形 left top right bottom 左上右下点的值  按照标注计算得出
        backgroundrectf.set(7, 0, 37, h);
        colorrectf.set(12, 5, 32, h-5);
        imgvalidheight = h - 44;
        imgpos = h - 44; // 设置一个初始位置
    }
    @override
    protected void ondraw(canvas canvas) {
        super.ondraw(canvas);
        drawbackground(canvas);
        drawsection(canvas);
        drawimage(canvas);
    }
    /**
     * 画背景
     * @param canvas 画布
     */
    private void drawbackground(canvas canvas) {
        backpaint.setstyle(paint.style.fill);
        backpaint.setantialias(true); //抗锯齿
        backpaint.setcolor(color.parsecolor("#ffffff"));
        //画圆角矩形,15为圆角的角度
        canvas.drawroundrect(backgroundrectf, 15, 15, backpaint);
    }
    /**
     * 画分段数据
     * @param canvas 画布
     */
    private void drawsection(canvas canvas) {
        int[] colorarray = {
                color.parsecolor("#ff0000"), color.parsecolor("#ff0000"),
                color.parsecolor("#00ff00"), color.parsecolor("#00ff00"),
                color.parsecolor("#0000ff"), color.parsecolor("#0000ff")
        };
        float[] positionarray = {
                0f, 0.2f,
                0.2f, 0.6f,
                0.6f, 1f
        };
        colorpaint.setstyle(paint.style.fill);
        colorpaint.setantialias(true);
        //指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度
        colorpaint.setshader(new lineargradient(0, 0, 0, colorrectf.bottom, colorarray, positionarray, shader.tilemode.repeat));
        canvas.drawroundrect(colorrectf,15, 15, colorpaint);
    }
    /**
     * 画图片
     * @param canvas 画布
     */
    private void drawimage(canvas canvas) {
        bitmap bitmap = initbitmap(imgresid);
        canvas.save();
        canvas.translate(0, 0);
        canvas.drawbitmap(bitmap, 0, imgpos, null);
        canvas.restore();
    }
    /**
     *
     * @param resid
     * @return
     */
    private bitmap initbitmap(int resid) {
        bitmap originalbmp = bitmapfactory.decoderesource(context.getresources(), resid);
        return bitmap.createscaledbitmap(originalbmp, 44, 44, true);
    }
    /**
     * 更新view
     * @param total 总长度
     * @param rest 剩余长度
     * @param sections 分段数据
     */
    public void updateview(int total, int rest, list<sectiondata> sections){
        float percent = (1f * rest) / total;
        //防止溢出
        if(percent < 0){
            return;
        }
        imgpos = (int)(percent * imgvalidheight);
        invalidate();
    }
}

updateview方法还有第三个参数,是用来传出颜色条不同段的颜色和百分比数据的。根据此数据来更新颜色条。这里需要根据业务不同自己实现,我这里就不写了。

总结

        大家可能看出来了,这个视图是用来展示导航中不同路段交通情况和当前车辆进度用的。自定义view中可以在构造方法中获取一些自定义属性,像背景条和颜色条的边距、圆角这些都可以定义到xml中,因为只适配一种屏幕尺寸所以也没有做多尺寸适配。以前也没有做过,这次记录下来。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com