当前位置: 移动技术网 > IT编程>移动开发>Android > Android 自定义View—清爽小巧灵活的多节点进度条

Android 自定义View—清爽小巧灵活的多节点进度条

2020年05月06日  | 移动技术网IT编程  | 我要评论

文松小品搞笑大全,绿箭侠第二季第八集,好斗魁麟五重唱

前言

最近项目有一个节点进度条的小需求,完成后,想分享出来希望可以帮到有需要的同学。

真机效果图

自定义view完整代码

开箱即用~,注释已经炒鸡详细了

/**
 * @description: 节点进度条
 * @author: dmingo
 * @date: 2020/4/15
 */
public class pointprocessbar extends view {

    /**
     * 未选中时的连线画笔
     */
    private paint mlinepaint;
    /**
     * 选中时的连线画笔
     */
    private paint mlineselectedpaint;
    /**
     * 未选中时的文字画笔
     */
    private paint mtextpaint;
    /**
     * 选中时的文字画笔
     */
    private paint mtextselpaint;

    /**
     * 未选中时的实心圆画笔
     */
    private paint mcirclepaint;
    /**
     * 选中时的内部实心圆画笔
     */
    private paint mcircleselpaint;
    /**
     * 选中时的边框圆画笔
     */
    private paint mcirclestrokeselpaint;

    /**
     * 未选中时的线,节点圆的颜色
     */
    private int mcolorunselected  = color.parsecolor("#1ca8b0d9");
    /**
     * 选中时的颜色
     */
    private int mcolorselected = color.parsecolor("#61a4e4");
    /**
     * 未选中的文字颜色
     */
    private int mcolortextunselected  = color.parsecolor("#5c030f09");

    /**
     * 绘制的节点个数,由底部节点标题数量控制
     */
    int circlecount ;

    /**
     * 连线的高度
     */
    float mlineheight = 7f;

    //圆的直径
    float mcircleheight = 50f;
    float mcircleselstroke = 8f;
    float mcirclefillradius = 15f;

    //文字大小
    float mtextsize  = 35f;

    //文字离顶部的距离
    float mmargintop = 40f;
    /**
     * 首个圆向中心偏移的距离
     */
    float marginleft = 30f;

    /**
     * 最后一个圆向中心偏移的距离
     */
    float marginright = marginleft;

    /**
     * 每个节点相隔的距离
     */
    float dividewidth;

    int defaultheight;

    /**
     * 节点底部的文字列表
     */
    list<string> textlist = new arraylist<>();

    /**
     * 文字同宽高的矩形,用来测量文字
     */
    list<rect> mbounds;
    /**
     * 存储每个圆心在同一直线上的节点圆的 x 坐标值
     */
    list<float> circlelinejunctions = new arraylist<>();
    /**
     * 选中项集合
     */
    set<integer> selectedindexset = new hashset<>();

    public pointprocessbar(context context) {
        super(context);
    }

    public pointprocessbar(context context, @nullable attributeset attrs) {
        super(context, attrs);
        initpaint();
    }

    public pointprocessbar(context context, @nullable attributeset attrs, int defstyleattr) {
        super(context, attrs, defstyleattr);
    }

    public pointprocessbar(context context, @nullable attributeset attrs, int defstyleattr, int defstyleres) {
        super(context, attrs, defstyleattr, defstyleres);
    }

    /**
     * 初始化画笔属性
     */
    private void initpaint(){

        mlinepaint = new paint();
        mlineselectedpaint = new paint();
        mcirclepaint = new paint();
        mtextpaint = new paint();
        mcirclestrokeselpaint = new paint();
        mtextselpaint=new paint();
        mcircleselpaint = new paint();

        mlinepaint.setcolor(mcolordef);
        //设置填充
        mlinepaint.setstyle(paint.style.fill);
        //笔宽像素
        mlinepaint.setstrokewidth(mlineheight);
        //锯齿不显示
        mlinepaint.setantialias(true);

        mlineselectedpaint.setcolor(mcolorselected);
        mlineselectedpaint.setstyle(paint.style.fill);
        mlineselectedpaint.setstrokewidth(mlineheight);
        mlineselectedpaint.setantialias(true);

        mcirclepaint.setcolor(mcolordef);
        //设置填充
        mcirclepaint.setstyle(paint.style.fill);
        //笔宽像素
        mcirclepaint.setstrokewidth(1);
        //锯齿不显示
        mcirclepaint.setantialias(true);

        //选中时外框空心圆圈画笔
        mcirclestrokeselpaint.setcolor(mcolorselected);
        mcirclestrokeselpaint.setstyle(paint.style.stroke);
        mcirclestrokeselpaint.setstrokewidth(mcircleselstroke);
        mcirclestrokeselpaint.setantialias(true);
        //选中时的内部填充圆画笔
        mcircleselpaint.setstyle(paint.style.fill);
        mcircleselpaint.setstrokewidth(1);
        mcircleselpaint.setantialias(true);
        mcircleselpaint.setcolor(mcolorselected);

        //普通状态的文本 画笔
        mtextpaint.settextsize(mtextsize);
        mtextpaint.setcolor(mcolortextdef);
        mtextpaint.setantialias(true);
        mtextpaint.settextalign(paint.align.center);
        //选中后的文本画笔
        mtextselpaint.settextsize(mtextsize);
        mtextselpaint.setcolor(mcolorselected);
        mtextselpaint.setantialias(true);
        mtextselpaint.settextalign(paint.align.center);
    }

    /**
     * 测量文字的长宽,将文字视为rect矩形
     */
    private void measuretext(){
        mbounds = new arraylist<>();
        for(string name : textlist){
            rect mbound = new rect();
            mtextpaint.gettextbounds(name, 0, name.length(), mbound);
            mbounds.add(mbound);
        }
    }




    /**
     * 测量view的高度
     */
    private void measureheight(){
        if (mbounds!=null && mbounds.size()!=0) {
            defaultheight = (int) (mcircleheight + mmargintop + mcircleselstroke + mbounds.get(0).height()/2);
        } else {
            defaultheight  = (int) (mcircleheight + mmargintop+mcircleselstroke);
        }
    }


    @override
    protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
        int widthspecmode = measurespec.getmode(widthmeasurespec);
        int widthspecsize = measurespec.getsize(widthmeasurespec);
        int heightspecmode = measurespec.getmode(heightmeasurespec);
        int heightspecsize = measurespec.getsize(heightmeasurespec);
        //宽高都设置为wrap_content
       if(widthspecmode == measurespec.at_most && heightspecmode == measurespec.at_most){
            //宽设置为wrap_content
            setmeasureddimension(widthspecsize,defaultheight);
        }else if(widthspecmode == measurespec.at_most){
            setmeasureddimension(widthspecsize,heightspecsize);
        }else if(heightspecmode == measurespec.at_most){
            //高设置为wrap_content
            setmeasureddimension(widthspecsize, defaultheight);
        }else{
            //宽高都设置为match_parent或具体的dp值
            setmeasureddimension(widthspecsize, heightspecsize);
        }
    }

    @override
    protected void ondraw(canvas canvas) {
        //若未设置节点标题或者选中项的列表,则取消绘制
        if (textlist == null || textlist.isempty() ||
                selectedindexset == null || selectedindexset.isempty() ||
                mbounds == null || mbounds.isempty()) {
            return;
        }
        //画灰色圆圈的个数
        circlecount=textlist.size();
        //每个圆相隔的距离(重要),可以通过这个调节节点间距
        dividewidth = (getwidth() - mcircleheight ) / (circlecount - 1);
        //绘制文字和圆形
        for (int i=0; i < circlecount ;i++){
            float cx;
            float cy;
            float textx;
            if (i==0){
                //第一个节点,圆心需要向右偏移
                cx = mcircleheight / 2 + i * dividewidth + marginleft;
                cy = mcircleheight / 2 + mcircleselstroke;
                textx = cx;
                circlelinejunctions.add(cx + mcircleheight / 2);
            }else if (i==textlist.size()-1){
                //最后一个节点,圆心需要向左偏移
                cx = mcircleheight / 2 + i * dividewidth - marginright;
                cy = mcircleheight / 2 + mcircleselstroke;
                textx = cx;
                circlelinejunctions.add(cx - mcircleheight / 2);
            }else {
                //中间部分的节点
                cx = mcircleheight / 2 + i * dividewidth;
                cy = mcircleheight / 2+mcircleselstroke;
                textx = cx;
                circlelinejunctions.add(cx - mcircleheight / 2);
                circlelinejunctions.add(cx + mcircleheight / 2);
            }
            if (getselectedindexset().contains(i)){
                //若当前位置节点被包含在选中项set中,判定此节点被选中
                canvas.drawcircle(cx , cy, mcircleheight / 2, mcirclestrokeselpaint);
                canvas.drawcircle(cx, cy, mcirclefillradius, mcircleselpaint);
                canvas.drawtext(textlist.get(i), textx, (float) (mcircleheight + mmargintop +mcircleselstroke+mbounds.get(i).height()/2.0), mtextselpaint);
            }else {
                //若当前位置节点没有被包含在选中项set中,判定此节点没有被选中
                canvas.drawcircle(cx , cy, mcircleheight / 2, mcirclepaint);
                canvas.drawtext(textlist.get(i), textx, (float) (mcircleheight + mmargintop +mcircleselstroke+mbounds.get(i).height()/2.0), mtextpaint);
            }
        }
        for(int i = 1 , j = 1 ; j <= circlelinejunctions.size() && ! circlelinejunctions.isempty()  ; ++i , j=j+2){
            if(getselectedindexset().contains(i)){
                canvas.drawline(circlelinejunctions.get(j-1),mcircleheight/2+mcircleselstroke,
                        circlelinejunctions.get(j) ,mcircleheight/2+mcircleselstroke,mlineselectedpaint);
            }else {
                canvas.drawline(circlelinejunctions.get(j-1),mcircleheight/2+mcircleselstroke,
                        circlelinejunctions.get(j) ,mcircleheight/2+mcircleselstroke,mlinepaint);
            }
        }
    }

    /**
     * 供外部调用,显示控件
     * @param titles 底部标题内容列表
     * @param indexset 选中项set
     */
    public void show(list<string> titles , set<integer> indexset){
        if(titles != null && ! titles.isempty()){
            this.textlist = titles;
        }
        if(indexset != null  && ! indexset.isempty()){
            this.selectedindexset = indexset;
        }
        measuretext();
        measureheight();
        //绘制
        invalidate();
    }

    /**
     * 更新底部节点标题内容
     * @param textlist 节点标题内容列表
     */
    public void refreshtextlist(list<string> textlist) {
        this.textlist = textlist;
        measuretext();
        measureheight();
        invalidate();
    }

    /**
     * 获取节点选中状态
     * @return 节点选中状态列表
     */
    public set<integer> getselectedindexset() {
        return selectedindexset;
    }

    /**
     * 更新选中项
     * @param set 选中项set
     */
    public void refreshselectedindexset(set<integer> set) {
        this.selectedindexset = set;
        invalidate();
    }


}

注意点

  1. 控件的节点总个数是与传入的节点底部标题列表中元素个数控制(相同)的,简而言之就是传入的标题列表中有多少个标题,节点就会绘制多少个
  2. 控件通过show方法进行view的初始化和显示内容,传入节点标题列表和节点选中项集合,控制view的选中状态和显示的内容
  3. 控件初始化显示后,可以通过refreshtextlist(),refreshselectedindexset() 更新标题和选中项
  4. 具体不同的颜色,大小可以具体在view中调整

总结

可以看到效果不复杂,因此自定义view的代码行数不多,也很容易看懂,直接拿走代码即可在项目中食用啦。

由于不同项目设计稿会有不同,这里也仅仅给有需要的同学一个思路,可以改造具体实现代码~

谢谢阅读到这里的同学~欢迎与我讨论和交流

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网