美文网首页
SeekBar竖向 从上到下 从下到上

SeekBar竖向 从上到下 从下到上

作者: GDHuo | 来源:发表于2018-10-29 19:15 被阅读0次

    网上看的很多竖向的seekbar,但是进度条都是从下到上滑动,设计要求是从上到下的,就自己改了改,代码如下

    /**
     * Created by gdhuo on 2018/10/29.
     */
    public class VerticalSeekBar extends SeekBar {
        private static final String TAG = VerticalSeekBar.class.getSimpleName();
    
        public static final int ROTATION_ANGLE_CW_90 = 90;
        public static final int ROTATION_ANGLE_CW_270 = 270;
    
        private int mRotationAngle = ROTATION_ANGLE_CW_90;
    
        public VerticalSeekBar(Context context) {
            super(context);//注意是super 而不是调用其他构造函数
            initialize(context, null, 0, 0);
        }
    
        public VerticalSeekBar(Context context, AttributeSet attrs) {
            super(context, attrs);
            initialize(context, attrs, 0, 0);
        }
    
        public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            initialize(context, attrs, defStyle, 0);
        }
    
        private void initialize(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    
            if (attrs != null) {
                TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalSeekBar, defStyleAttr, defStyleRes);
                final int rotationAngle = a.getInteger(R.styleable.VerticalSeekBar_seekBarRotation, 0);
                if (isValidRotationAngle(rotationAngle)) {
                    mRotationAngle = rotationAngle;
                }
                a.recycle();
            }
        }
    
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(h, w, oldh, oldw);
        }
    
        @Override
        protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(heightMeasureSpec, widthMeasureSpec);
            setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
        }
    
        protected void onDraw(Canvas c) {
            if(mRotationAngle == ROTATION_ANGLE_CW_270) {
                //从下到上
                c.rotate(270);
                c.translate(-getHeight(), 0);//注意旋转后需要移动才可显示出来
            } else if(mRotationAngle == ROTATION_ANGLE_CW_90) {
                //从上到下
                c.rotate(90);
                c.translate(0, -getWidth());
            }
    
            super.onDraw(c);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!isEnabled()) {
                return false;
            }
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_UP:
                    if(mRotationAngle == ROTATION_ANGLE_CW_270) {
                        //从下到上
                        int progress =  getMax() - (int)(event.getY()*(getMax()*2+1)/(getHeight()*2)+0.5);
                        setProgress(progress>0?progress:0);
                    } else if(mRotationAngle == ROTATION_ANGLE_CW_90) {
                        //从上到下
                        setProgress((int) (getMax() * event.getY() / getHeight()));
                    }
                    onSizeChanged(getWidth(), getHeight(), 0, 0);
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                   break;
           }
           return true;
        }
    
        private static boolean isValidRotationAngle(int angle) {
            return (angle == ROTATION_ANGLE_CW_90 || angle == ROTATION_ANGLE_CW_270);
        }
    

    样式如下

    <declare-styleable name="VerticalSeekBar">
            <attr name="seekBarRotation">
                <enum name="CW90" value="90" />
                <enum name="CW270" value="270" />
            </attr>
        </declare-styleable>
    

    遇到的坑
    1.直接调用seekbar.setProgress()发现thumb图片位置一直不对,解决方法如下

    @Override
        public void setProgress(int progress) {//重写setProgress
            super.setProgress(progress);
            onSizeChanged(getWidth(), getHeight(), 0, 0);
        }
    

    2.按下后始终有白点,并且白点位置一直在未翻转之前的位置上移动,可以通过设置android:background="@null"去掉
    3.按下后thumb位置总是不准确,需要在滑动或放手时候重新计算progress。方法如下

    //从下到上
    int progress =  getMax() - (int)(event.getY()*(getMax()*2+1)/(getHeight()*2)+0.5);
    

    4.按下后发现样式配置里设置的selector的pressed对应的图片显示不出来

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:drawable="@drawable/seekbar_pressed" android:state_pressed="true"/>
    
        <item android:drawable="@drawable/seekbar_unselect"/>
    </selector>
    

    解决方法如下

    @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!isEnabled()) {
                return false;
            }
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    break;
                case MotionEvent.ACTION_MOVE:
                    setPressed(true);//移动的时候设置按下效果,然后重新绘制thumb
                    if (getThumb() != null) {
                        // This may be within the padding region.
                        invalidate(getThumb().getBounds());
                    }
                    calProgress(event);
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    setPressed(false);//放手的时候取消按下效果,重新绘制thumb
                    if (getThumb() != null) {
                        // This may be within the padding region.
                        invalidate(getThumb().getBounds());
                    }
                    calProgress(event);
                   break;
           }
           return true;
        }
    private void calProgress(MotionEvent event) {
            if(mRotationAngle == ROTATION_ANGLE_CW_270) {
                //从下到上
                int progress =  getMax() - (int)(event.getY()*(getMax()*2+1)/(getHeight()*2)+0.5);
                progress = progress>0?progress:0;
                setProgress(progress);
            } else if(mRotationAngle == ROTATION_ANGLE_CW_90) {
                //从上到下
                setProgress((int) (getMax() * event.getY() / getHeight()));
            }
            onSizeChanged(getWidth(), getHeight(), 0, 0);
        }
    

    5.用户调节seekbar,但是在onProgressChanged回调的参数fromUser一直为false。查看源码后需要反射setProgressInternal,解决方法

    private void reflectSetProgress(int progress) {
            //反射方法 boolean setProgressInternal(int progress, boolean fromUser, boolean animate)
            try {
                Class clazz = ProgressBar.class;
                Method method = clazz.getDeclaredMethod("setProgressInternal",int.class,boolean.class,boolean.class);
                method.setAccessible(true);
                method.invoke(this,progress,true,false);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    
        private void calProgress(MotionEvent event) {
            if(mRotationAngle == ROTATION_ANGLE_CW_270) {
                //从下到上
                int progress =  getMax() - (int)(event.getY()*(getMax()*2+1)/(getHeight()*2)+0.5);
                LogUtil.d(TAG,"set progress "+progress + " max "+getMax() + " getY "+event.getY() +" getHeight "+getHeight());
                progress = progress>0?progress:0;
                reflectSetProgress(progress);
            } else if(mRotationAngle == ROTATION_ANGLE_CW_90) {
                //从上到下
                reflectSetProgress((int) (getMax() * event.getY() / getHeight()));
            }
            onSizeChanged(getWidth(), getHeight(), 0, 0);
        }
    

    记录一下,以便日后查阅,遇到相同问题的人也可以参考参考

    相关文章

      网友评论

          本文标题:SeekBar竖向 从上到下 从下到上

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