自定义滑动刻度进度条

作者: 拓荒者C | 来源:发表于2019-08-09 11:49 被阅读3146次

    这次来弄的是自定义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);
        }
    }
    

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

    相关文章

      网友评论

        本文标题:自定义滑动刻度进度条

        本文链接:https://www.haomeiwen.com/subject/fpycjctx.html