项目需求
有一个文本数据比较长,需要在文本右侧加一个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>