• 【Android】使用SeekBar控制数据的滚动


    项目需求

    有一个文本数据比较长,需要在文本右侧加一个SeekBar,然后根据SeekBar的上下滚动来控制文本的滚动。

    项目实现

    我们使用TextView来显示文本,但是文本比较长的话,需要在TextView外面套一个ScrollView,但是我们现在这个文本是上下滚动的,很巧不巧的是我们的文本在一个上下滚动的Recyclerview的item里面,这下就很搞了,因为两个都是上下滚动的,我们需要做一个处理。

    首先,自定义一个ScrollView

    public class NonScrollableScrollView extends ScrollView {
    
        public NonScrollableScrollView(Context context) {
            super(context);
        }
    
        public NonScrollableScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public NonScrollableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            return false;
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }
    }
    
    

    重写其触摸事件方法使其不响应触摸事件。所有这样的话我们的TextView就不能通过自己滚动了,然后我们通过这个SeekBar来控制TextView的滚动

    接下来是对SeekBar的修改,以下部分内容来自知乎
    https://zhuanlan.zhihu.com/p/622534050

    @SuppressLint("AppCompatCustomView")
    public class VerticalSeekBar extends SeekBar {
        private boolean isTopToBottom = false;//显示进度方向是否从上到下,默认false(从下到上)
    
        public VerticalSeekBar(Context context) {
            super(context);
        }
    
        public VerticalSeekBar(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.VerticalSeekBar, 0, 0);
            try {
                isTopToBottom = ta.getBoolean(R.styleable.VerticalSeekBar_isTopToBottom, false);
            } finally {
                ta.recycle();
            }
        }
    
        public VerticalSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
            mOnSeekBarChangeListener = l;
    
        }
    
        void onStartTrackingTouch() {
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStartTrackingTouch(this);
            }
        }
    
        void onStopTrackingTouch() {
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStopTrackingTouch(this);
            }
        }
    
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(h, w, oldh, oldw);
        }
    
        @Override
        public synchronized void setProgress(int progress) {
            super.setProgress(progress);
            onSizeChanged(getWidth(), getHeight(), 0, 0);
        }
    
        @Override
        protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(heightMeasureSpec, widthMeasureSpec);
            setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
        }
    
        protected void onDraw(Canvas c) {
            if (isTopToBottom) {
                //显示进度方向 从上到下
                c.rotate(90);
                c.translate(0, -getWidth());
            } else {
                //显示进度方向 从下到上
                c.rotate(-90);
                c.translate(-getHeight(), 0);
            }
            super.onDraw(c);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!isEnabled()) {
                return false;
            }
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    onStartTrackingTouch();
                    trackTouchEvent(event);
                    break;
    
                case MotionEvent.ACTION_MOVE:
                    trackTouchEvent(event);
                    attemptClaimDrag();
                    break;
    
                case MotionEvent.ACTION_UP:
                    trackTouchEvent(event);
                    onStopTrackingTouch();
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                    onStopTrackingTouch();
                    break;
            }
            return true;
    //        getParent().requestDisallowInterceptTouchEvent(true);
    //        return super.onTouchEvent(event);
        }
    
        private void trackTouchEvent(MotionEvent event) {
            //关键更改2
            int progress = getMax() - (int) (getMax() * event.getY() / getHeight());//触摸进度方向 从下到上
            if (isTopToBottom) {
                progress = (int) (getMax() * event.getY() / getHeight());//触摸进度方向 从上到下
            }
            setProgress(progress);
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onProgressChanged(this, progress);
            }
        }
    
        private void attemptClaimDrag() {
            if (getParent() != null) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }
        }
    
        public boolean dispatchKeyEvent(KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                KeyEvent newEvent = null;
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_DPAD_UP:
                        newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,
                                KeyEvent.KEYCODE_DPAD_RIGHT);
                        break;
                    case KeyEvent.KEYCODE_DPAD_DOWN:
                        newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,
                                KeyEvent.KEYCODE_DPAD_LEFT);
                        break;
                    case KeyEvent.KEYCODE_DPAD_LEFT:
                        newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,
                                KeyEvent.KEYCODE_DPAD_DOWN);
                        break;
                    case KeyEvent.KEYCODE_DPAD_RIGHT:
                        newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,
                                KeyEvent.KEYCODE_DPAD_UP);
                        break;
                    default:
                        newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,
                                event.getKeyCode());
                        break;
                }
                KeyEvent.DispatcherState dispatcherState = new KeyEvent.DispatcherState();
                dispatcherState.isTracking(event);
                return newEvent.dispatch(this, dispatcherState, event);
            }
            return false;
        }
    
        /**
         * 设置显示进度方向
         *
         * @param isTopToBottom true 方向从上到下
         */
        public void setTopToBottom(boolean isTopToBottom) {
            this.isTopToBottom = isTopToBottom;
        }
    
        private OnSeekBarChangeListener mOnSeekBarChangeListener;
    
        public interface OnSeekBarChangeListener {
            void onProgressChanged(VerticalSeekBar VerticalSeekBar, int progress);
    
            void onStartTrackingTouch(VerticalSeekBar VerticalSeekBar);
    
            void onStopTrackingTouch(VerticalSeekBar VerticalSeekBar);
        }
    }
    
    

    需要在attrs文件里面添加 (需要注意,因为在知乎上面没有这个东西)

        <declare-styleable name="VerticalSeekBar">
            <attr name="isTopToBottom" format="boolean" />
        </declare-styleable>
    

    设置 progress_vertical_drawable2

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@android:id/background">
            <shape>
                <corners android:radius="5dp" />
                <solid android:color="#D9EFFF" />
            </shape>
        </item>
    
        <item android:id="@android:id/progress">
            <scale android:scaleWidth="100%">
                <shape>
                    <corners android:radius="5dp" />
                    <solid android:color="#5EB2FF" />
                </shape>
            </scale>
        </item>
        <item android:id="@android:id/secondaryProgress">
            <scale android:scaleWidth="100%">
                <shape>
                    <corners android:radius="5dp" />
                    <solid android:color="#5EB2FF" />
                </shape>
            </scale>
        </item>
    </layer-list>
    

    设置ic_thumb

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:width="@dimen/dp_20"
            android:height="@dimen/dp_20">
            <shape android:shape="oval">
                <gradient
                    android:angle="180"
                    android:endColor="#1C000000"
                    android:startColor="#1Cffffff" />
            </shape>
        </item>
        <item
            android:bottom="1dp"
            android:left="1dp"
            android:right="1dp"
            android:top="1dp">
            <shape android:shape="oval">
                <solid android:color="#5EB2FF" />
                <stroke
                    android:width="6dp"
                    android:color="#FFFFFF" />
            </shape>
        </item>
    </layer-list>
    

    设置Style

        <!--自定义SeekBar样式-->
        <style name="SeekbarStyle">
            <item name="android:indeterminateDrawable"><!--未知资源时显示-->
                @android:drawable/progress_indeterminate_horizontal
            </item>
            <item name="android:progressDrawable">@drawable/progress_vertical_drawable2</item>
            <item name="android:max">100</item>
            <item name="android:progress">0</item>
            <item name="android:maxHeight">@dimen/dp_60</item>
    <!--        <item name="android:minHeight">@dimen/dp_10</item>-->
            <item name="android:thumb">@drawable/ic_thumb</item>
    <!--        <item name="android:thumbOffset">@dimen/dp_20</item>-->
        </style>
    

    然后就可以在xml布局中使用了

      <com.complex.app.view.VerticalSeekBar
                            android:id="@+id/seekBar_Text"
                            style="@style/SeekbarStyle"
                            android:layout_width="wrap_content"
                            android:layout_height="@dimen/dp_60"
                            android:background="@android:color/transparent"
                            android:splitTrack="false"
                            app:isTopToBottom="true" />
    

    android:splitTrack="false"是为了让这个滑块周围的背景变的透明,不然就只能是一个正方形的滑块图案了

    然后我们在RecyclerView的Adapter里面进行设置

    protected void convert(BaseViewHolder helper, ElectronicFencePoint item) {
                    NonScrollableScrollView scrollView = helper.getView(R.id.ns_scroll);
                    VerticalSeekBar seekBar = helper.getView(R.id.seekBar_Text);
                    scrollView.post(() -> {
                        int maxScroll = scrollView.getChildAt(0).getHeight() - scrollView.getHeight();
                        if (maxScroll <= 0) {
                        //这里我的逻辑是当TextView的文本显示内容不多不需要滑动的时候,设置滑块滑动到底部显示
                            seekBar.setProgress(seekBar.getMax());
                        } else {
                        //当TextView的文本很多,需要滑动的时候,将滑块放在顶部
                            seekBar.setProgress(0);
                            seekBar.setMax(maxScroll);
                        }
                    });
                    seekBar.setOnSeekBarChangeListener(new VerticalSeekBar.OnSeekBarChangeListener() {
                        @Override
                        public void onProgressChanged(VerticalSeekBar VerticalSeekBar, int progress) {
                            scrollView.scrollTo(0, progress);
                        }
    
                        @Override
                        public void onStartTrackingTouch(VerticalSeekBar VerticalSeekBar) {
    
                        }
    
                        @Override
                        public void onStopTrackingTouch(VerticalSeekBar VerticalSeekBar) {
    
                        }
                    });
    
                    scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
                        @Override
                        public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                            seekBar.setProgress(scrollY);
                        }
                    });
    }
    

    补充:
    开始滑动前:
    在这里插入图片描述
    开始滑动后
    在这里插入图片描述
    这个只需要修改一下样式就好了

    ic_thumb

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:width="@dimen/dp_15"
            android:height="@dimen/dp_15">
            <shape android:shape="oval">
                <gradient
                    android:endColor="#1C000000"
                    android:startColor="#1Cffffff" />
            </shape>
        </item>
        <item
            android:bottom="1dp"
            android:left="1dp"
            android:right="1dp"
            android:top="1dp">
            <shape android:shape="oval">
                <solid android:color="#5EB2FF" />
                <stroke
                    android:width="2dp"
                    android:color="#FFFFFF" />
            </shape>
        </item>
    </layer-list>
    

    progress_vertical_drawable2

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
            <!--设置滑轨颜色:滑过部分和未滑过部分-->
            <!--未滑过部分滑轨颜色-->
            <item
                android:id="@android:id/background"
                android:height="4dp"
                android:gravity="center">
                <shape>
                    <corners android:radius="67dp"/>
                    <solid android:color="#4d000000"/>
                </shape>
            </item>
            <!--滑过部分滑轨颜色-->
            <item
                android:id="@android:id/progress"
                android:height="6dp"
                android:gravity="center">
                <clip>
                    <shape>
                        <corners android:radius="67dp"/>
                        <solid android:color="#2196F3"/>
                    </shape>
                </clip>
            </item>
    </layer-list>
    

    SeekbarStyle

        <!--自定义SeekBar样式-->
        <style name="SeekbarStyle" parent="Widget.AppCompat.SeekBar">
            <item name="android:progressDrawable">@drawable/progress_vertical_drawable2</item>
            <item name="android:thumb">@drawable/ic_thumb</item>
        </style>
    

    xml应用

                        <com.southgnss.digitalconstruction.view.VerticalSeekBar
                            android:id="@+id/seekBar_Text"
                            style="@style/SeekbarStyle"
                            android:layout_width="@dimen/dp_20"
                            android:layout_height="@dimen/dp_60"
                            android:background="@null"
                            android:splitTrack="false"
                            app:isTopToBottom="true" />
                    </LinearLayout>
    
  • 相关阅读:
    NetCore VUE 前后端分离获取IP
    JDK 动态代理原理
    物理学家用AI改写教科书!质子中发现新的夸克,可能性高达99.7%
    C++类和对象的声明、解析、使用(构造、析构函数、友元、命名空间)
    Set和Map的用法
    基于Redission实现分布式锁
    IronBarcode for .NET 2022.11.10702 Crack
    详解ConCurrentHashMap源码(jdk1.8)
    想要彻底搞的性能优化,得先从底层逻辑开始了解~
    一文看懂MySQL的行锁
  • 原文地址:https://blog.csdn.net/qq_43358469/article/details/139687786