【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>
  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用SeekBar控制Android手机音量的一般步骤: 1. 在布局文件中添加SeekBar 在布局文件中添加SeekBar组件,例如: ``` <SeekBar android:id="@+id/seekBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:progress="50" /> ``` 其中,max属性表示SeekBar的最大值,progress属性表示SeekBar的初始值。 2. 在Activity中获取SeekBar对象 在Activity中获取SeekBar对象,例如: ``` SeekBar seekBar = findViewById(R.id.seekBar); ``` 3. 获取AudioManager对象 获取AudioManager对象,例如: ``` AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); ``` 4. 监听SeekBar变化 使用seekBar.setOnSeekBarChangeListener()方法监听SeekBar变化,并在onProgressChanged()方法中设置音量,例如: ``` seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); ``` 其中,setStreamVolume()方法的第一个参数表示要设置的音频流的类型,第二个参数表示要设置的音量大小,第三个参数表示是否显示系统音量控制UI。 希望这些信息可以帮助您实现使用SeekBar控制Android手机音量的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值