自定义滑动刻度进度条

这次来弄的是自定义view的滑动进度条,继承于AppCompatSeekBar的基础上进行二次开发。先看效果图


test.gif

做到以上的效果图,就可以知道,两个模式,1:就是常用的seekbar模式,然后只不过安装自己的要求改了下滑动块而已,不多说,2:就是带有刻度点的seekbar模式,并且带有粘性数值处理,实现滑到刻度周边滑块自动吸引过去的效果。
重点说下第二种模式的,拆分下工作点:因为继承的是SeekBar,所以线和滑块我们就不用再处理了,只需要通过代码块去控制线的颜色和滑动块的背景就OK,还有就是根据数值在线上绘制相应的点。说白了就是重要点。
首先,定义相应的自定义属性,额,没办法,上头要求,所以规范点
新建一个bar_attrs.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<resources>
  <declare-styleable name="SegmentBar">
    <attr name="max_length" format="integer"/>
    <!--刻度数量-->
    <attr name="node" format="integer"/>
    <!--默认选中的刻度 -->
    <attr name="check_item" format="integer"/>
    <!--滑动块的样式-->
    <attr name="block_bg" format="reference"/>
    <!--线的颜色-->
    <attr name="line_color" format="reference"/>
    <!--刻度点的颜色-->
    <attr name="spot_color" format="reference"/>
    <!--刻度点的大小-->
    <attr name="spot_size" format="integer"/>
    <!--滑线的背景,没有滑到的线的颜色-->
    <attr name="line_bg" format="reference"/>

    <!--def模式是普通的滑动模式,sca是刻度模式,设置这个模式需要同时设置node参数-->
    <attr name="bar_type" format="enum">
      <enum name="default_type" value="0"/>
      <enum name="scale_type" value="1"/>
    </attr>
  </declare-styleable>
</resources>

然后就是代码中取得相应的属性值和设置默认值

TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SegmentBar);
        maxLength = typedArray.getInteger(R.styleable.SegmentBar_max_length,100);
        node = typedArray.getInteger(R.styleable.SegmentBar_node,0);
        blockBg = typedArray.getResourceId(R.styleable.SegmentBar_block_bg,R.drawable.bar_back);
        spotColor = typedArray.getResourceId(R.styleable.SegmentBar_spot_color,R.color.bar_point);
        type = typedArray.getInteger(R.styleable.SegmentBar_bar_type,0);
        lineColor = typedArray.getResourceId(R.styleable.SegmentBar_line_color,R.color.bar_line);
        lineBg = typedArray.getResourceId(R.styleable.SegmentBar_line_bg,R.color.bar_line_bg);
        checkItem = typedArray.getInteger(R.styleable.SegmentBar_check_item,1);
        spotSize = typedArray.getInteger(R.styleable.SegmentBar_spot_size,15);
        lineColor = ContextCompat.getColor(getContext(),lineColor);
        lineBg = ContextCompat.getColor(getContext(),lineBg);
        spotColor = ContextCompat.getColor(getContext(),spotColor);
        typedArray.recycle();
        initColor();

然后修改seekbar的线的颜色和滑动块的属性,通过代码定义一个Drawable属性来进行处理

public void initColor(){
        this.setMax(maxLength);
        this.setProgressDrawable(ContextCompat.getDrawable(getContext(),
                R.drawable.bar_background));
        /*Drawable thumbDraw = zoomDrawable(),2,2);*/
        this.setThumb(ContextCompat.getDrawable(getContext(),
                blockBg));
        Rect bounds = this.getProgressDrawable().getBounds();
        Drawable[] drawables = new Drawable[3];
        drawables[0] = new PaintDrawable(lineBg);
        drawables[2] = new ClipDrawable(new PaintDrawable(lineColor),Gravity.LEFT,ClipDrawable.HORIZONTAL);
        drawables[1] = new ClipDrawable(new PaintDrawable(lineBg),Gravity.LEFT,ClipDrawable.HORIZONTAL);
        LayerDrawable layerDrawable1 = new LayerDrawable(drawables);
        this.setProgressDrawable(layerDrawable1);
        this.getProgressDrawable().setBounds(bounds);
        if (type == DEFAULT){
            this.setProgress(getProgress()-1);
            this.setProgress(getProgress());
        }else {
            int tmp = maxLength / (node+1);
            int pro = checkItem * tmp;
            if (checkItem == 0){
                this.setProgress(1);
                this.setProgress(0);
            }else if (checkItem == node){
                this.setProgress(maxLength-1);
                this.setProgress(maxLength);
            }else {
                this.setProgress(pro-1);
                this.setProgress(pro);
            }
        }
        invalidate();
    }

然后就是在onDraw里面绘制点
绘制点之前,需要获取几个数据点,1、seekbar的线的x,y轴,和点的xy轴。
线xy轴可以就是view的高度的对半点就是y轴的点,x轴的起始点一般就是0和view的宽度
至于点的话,可以通过线的长度/点的数量(得到平均刻度值)然后乘以相应的点的位置数量值就是得到了各个点的相应的x轴坐标,拿到了相应的xy轴,就可以绘制相应的点了

/**
     * 在刻度模式下,进行绘制刻度点
     * @param canvas
     */
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        float startX = 0 ;
        float startY ;
        float stopX ;
        float stopY ;
        if (type == SCALE){
            //间隔宽度
            int nodeWidth = 0;
            if (node >= 1){
                nodeWidth = (viewWidth-mPaddingLeft-mPaddingRight)/(node+1);
            }
            if (!isActtonUp){
                checkItem = this.getProgress()/(maxLength / (node+1));
            }
            for (int i = 0;i <= (node+1);i++){
                if (i == checkItem){
                    //正在处于的刻度点
                    if (isActtonUp){
                        linePaint.setColor(tran);
                    }else {
                        linePaint.setColor(lineColor);
                    }
                }else if (i < checkItem){
                    //已经划过的刻度点
                    linePaint.setColor(lineColor);
                }else {
                    //未划过的刻度点
                    linePaint.setColor(spotColor);
                }
                stopX = mPaddingLeft+(nodeWidth*i);
                startX = stopX;
                canvas.drawCircle(startX,getMeasuredHeight()/2,spotSize,linePaint);
            }
        }
        super.onDraw(canvas);
    }

做好以上的处理,基本的view的操作就OK了,但是呢,体验感不是很ok,就是没有滑动块的粘性处理。这可不行,所以弄下粘性处理吧,让滑动块滑到点的附近,根据距离点的值来进行吸引滑动块,这个可以在 onTouchEvent里面进行事件监听和拦截,当用户的手指离开屏幕时,获取当前滑块的位置,对比左右点的相差距离,决定滑动时向左点自动滑动还是向右点自动滑动

@Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP) {
            isActtonUp = true;
            if (type == SCALE){
                //粘性处理,通过滑动的数值计算出最近的点
                int tmp = maxLength / (node+1);
                int checkPro = getProgress();
                int tmpLength = checkItem*tmp-checkPro;
                if (tmpLength < 0){
                    if (Math.abs(tmpLength) < ( tmp /2)){
                        setProgress(checkItem*tmp);
                    }else {
                        int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
                        if (t > 1){
                            checkItem += t;
                        }else {
                            checkItem += 1;
                        }
                        setProgress(checkItem*tmp);
                    }
                }else {
                    if (tmpLength < ( tmp /2)){
                        setProgress(checkItem*tmp);
                    }else {
                        int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
                        if (t > 1){
                            checkItem -= t;
                        }else {
                            checkItem -= 1;
                        }
                        setProgress(checkItem*tmp);
                    }
                }
                invalidate();
                if (onItemCheckBarListener != null){
                    onItemCheckBarListener.itemCheckBarListener(this);
                }
                return true;
            }
        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
            isActtonUp = false;
        }
        return super.onTouchEvent(event);

    }

注意的一点是,在监听到用户手势离开屏幕的事件后,得消耗此事件,别让此事件分发继续下去,不然,seekbar则会重新绘制,覆盖以上的效果,在消耗的此事件之后,seekbar的滑动停止监听事件也就无效了,到时候我们就自己最佳一个接口,然使用者可以随时拿到停止滑动的线的数值

public interface OnItemCheckBarListener {
        void itemCheckBarListener(SegmentBar segmentBar);
    }

    private OnItemCheckBarListener onItemCheckBarListener;

    public void setOnItemCheckBarListener(OnItemCheckBarListener onItemCheckBarListener){
        this.onItemCheckBarListener = onItemCheckBarListener;
    }

嗯,以上就是达到效果图的效果了
完整代码贴出:

/*
 *
 * 自定义滑动进度条
 * Created by caisongliang on 2019/7/24 14:29
 */
public class SegmentBar extends AppCompatSeekBar {


    private static final int DEFAULT = 0;//默认模式
    private static final int SCALE = 1;//有刻度模式
    private int maxLength = 100;
    private int minLength = 0;
    //滑块背景
    private int blockBg = 0;
    //刻度
    private int node = 4;
    private int type = DEFAULT;
    private int checkItem = 0;
    //刻度的点的颜色
    private int spotColor = 0 ;
    //线的颜色
    private int lineColor = 0;
    //刻度点的大小
    private int spotSize = 15;
    private int lineBg = 0;
    private int maxHeight = 0;



    public SegmentBar(Context context) {
        super(context);
        initView(null);
    }

    public SegmentBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(attrs);
    }


    public int getMaxLength() {
        return maxLength;
    }

    public void setMaxLength(int maxLength) {
        this.maxLength = maxLength;
        this.setProgress(maxLength);
    }

    public int getBlockBg() {
        return blockBg;
    }

    public void setBlockBg(int blockBg) {
        this.blockBg = blockBg;
    }

    public int getNode() {
        return node;
    }

    public void setNode(int node) {
        this.node = node;
        initColor();
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public int getSpotColor() {
        return spotColor;
    }

    public void setSpotColor(int spotColor) {
        this.spotColor = ContextCompat.getColor(getContext(),spotColor);
        invalidate();
    }

    public int getLineColor() {
        return lineColor;
    }

    public void setLineColor(int lineColor) {
        this.lineColor = ContextCompat.getColor(getContext(),lineColor);
        initColor();
    }

    public int getSpotSize() {
        return spotSize;
    }

    public void setSpotSize(int spotSize) {
        this.spotSize = spotSize;
        invalidate();
    }

    public int getLineBg() {
        return lineBg;
    }

    public void setLineBg(int lineBg) {
        this.lineBg = ContextCompat.getColor(getContext(),lineBg);
        initColor();
    }

    private boolean isActtonUp  = false;


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP) {
            isActtonUp = true;
            if (type == SCALE){
                //粘性处理,通过滑动的数值计算出最近的点
                int tmp = maxLength / (node+1);
                int checkPro = getProgress();
                int tmpLength = checkItem*tmp-checkPro;
                if (tmpLength < 0){
                    if (Math.abs(tmpLength) < ( tmp /2)){
                        setProgress(checkItem*tmp);
                    }else {
                        int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
                        if (t > 1){
                            checkItem += t;
                        }else {
                            checkItem += 1;
                        }
                        setProgress(checkItem*tmp);
                    }
                }else {
                    if (tmpLength < ( tmp /2)){
                        setProgress(checkItem*tmp);
                    }else {
                        int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
                        if (t > 1){
                            checkItem -= t;
                        }else {
                            checkItem -= 1;
                        }
                        setProgress(checkItem*tmp);
                    }
                }
                invalidate();
                if (onItemCheckBarListener != null){
                    onItemCheckBarListener.itemCheckBarListener(this);
                }
                return true;
            }
        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
            isActtonUp = false;
        }
        return super.onTouchEvent(event);

    }

    public interface OnItemCheckBarListener {
        void itemCheckBarListener(SegmentBar segmentBar);
    }

    private OnItemCheckBarListener onItemCheckBarListener;

    public void setOnItemCheckBarListener(OnItemCheckBarListener onItemCheckBarListener){
        this.onItemCheckBarListener = onItemCheckBarListener;
    }

    public SegmentBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(attrs);
    }

    public int getCheckItem() {
        return checkItem;
    }

    public void setCheckItem(int checkItem) {
        this.checkItem = checkItem;
    }

    private void initView(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SegmentBar);
        maxLength = typedArray.getInteger(R.styleable.SegmentBar_max_length,100);
        node = typedArray.getInteger(R.styleable.SegmentBar_node,0);
        blockBg = typedArray.getResourceId(R.styleable.SegmentBar_block_bg,R.drawable.bar_back);
        spotColor = typedArray.getResourceId(R.styleable.SegmentBar_spot_color,R.color.bar_point);
        type = typedArray.getInteger(R.styleable.SegmentBar_bar_type,0);
        lineColor = typedArray.getResourceId(R.styleable.SegmentBar_line_color,R.color.bar_line);
        lineBg = typedArray.getResourceId(R.styleable.SegmentBar_line_bg,R.color.bar_line_bg);
        checkItem = typedArray.getInteger(R.styleable.SegmentBar_check_item,1);
        spotSize = typedArray.getInteger(R.styleable.SegmentBar_spot_size,15);
        lineColor = ContextCompat.getColor(getContext(),lineColor);
        lineBg = ContextCompat.getColor(getContext(),lineBg);
        spotColor = ContextCompat.getColor(getContext(),spotColor);
        typedArray.recycle();
        initColor();
    }

    public void initColor(){
        this.setMax(maxLength);
        this.setProgressDrawable(ContextCompat.getDrawable(getContext(),
                R.drawable.bar_background));
        /*Drawable thumbDraw = zoomDrawable(),2,2);*/
        this.setThumb(ContextCompat.getDrawable(getContext(),
                blockBg));
        Rect bounds = this.getProgressDrawable().getBounds();
        Drawable[] drawables = new Drawable[3];
        drawables[0] = new PaintDrawable(lineBg);
        drawables[2] = new ClipDrawable(new PaintDrawable(lineColor),Gravity.LEFT,ClipDrawable.HORIZONTAL);
        drawables[1] = new ClipDrawable(new PaintDrawable(lineBg),Gravity.LEFT,ClipDrawable.HORIZONTAL);
        LayerDrawable layerDrawable1 = new LayerDrawable(drawables);
        this.setProgressDrawable(layerDrawable1);
        this.getProgressDrawable().setBounds(bounds);
        if (type == DEFAULT){
            this.setProgress(getProgress()-1);
            this.setProgress(getProgress());
        }else {
            int tmp = maxLength / (node+1);
            int pro = checkItem * tmp;
            if (checkItem == 0){
                this.setProgress(1);
                this.setProgress(0);
            }else if (checkItem == node){
                this.setProgress(maxLength-1);
                this.setProgress(maxLength);
            }else {
                this.setProgress(pro-1);
                this.setProgress(pro);
            }
        }
        invalidate();
    }

    private Drawable zoomDrawable(Drawable drawable, int w, int h) {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        Bitmap oldbmp = drawableToBitmap(drawable);
        Matrix matrix = new Matrix();
        float scaleWidth = ((float) w / width);
        float scaleHeight = ((float) h / height);
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap newbmp = BitmapUtil.scaleImage(oldbmp,w,h);
        return new BitmapDrawable(null, newbmp);
    }
    private Bitmap drawableToBitmap(Drawable drawable) {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                : Bitmap.Config.RGB_565;
        Bitmap bitmap = Bitmap.createBitmap(width, height, config);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, width, height);
        drawable.draw(canvas);
        return bitmap;
    }

    private int viewWidth = 0;
    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = getMeasuredWidth();
        initSegmentBar();
    }

    private int mPaddingLeft;
    private int mPaddingRight;
    private Context mContext;


    public void initSegmentBar(){
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            mPaddingLeft = getPaddingLeft();
            mPaddingRight = getPaddingRight();
        } else {
            mPaddingLeft = getPaddingStart();
            mPaddingRight = getPaddingEnd();
        }
    }


    @Override
    public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
        super.setOnSeekBarChangeListener(l);
    }


    Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    int tran = getContext().getResources().getColor(R.color.bar_tran);

    /**
     * 在刻度模式下,进行绘制刻度点
     * @param canvas
     */
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        float startX = 0 ;
        float startY ;
        float stopX ;
        float stopY ;
        if (type == SCALE){
            //间隔宽度
            int nodeWidth = 0;
            if (node >= 1){
                nodeWidth = (viewWidth-mPaddingLeft-mPaddingRight)/(node+1);
            }
            if (!isActtonUp){
                checkItem = this.getProgress()/(maxLength / (node+1));
            }
            for (int i = 0;i <= (node+1);i++){
                if (i == checkItem){
                    //正在处于的刻度点
                    if (isActtonUp){
                        linePaint.setColor(tran);
                    }else {
                        linePaint.setColor(lineColor);
                    }
                }else if (i < checkItem){
                    //已经划过的刻度点
                    linePaint.setColor(lineColor);
                }else {
                    //未划过的刻度点
                    linePaint.setColor(spotColor);
                }
                stopX = mPaddingLeft+(nodeWidth*i);
                startX = stopX;
                canvas.drawCircle(startX,getMeasuredHeight()/2,spotSize,linePaint);
            }
        }
        super.onDraw(canvas);
    }
}

------------------------------------转发请声明出处-----------------------------------------------

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260