仅使用栏而不是子视图本身滚动
Scroll by only using the bar not the child view itself
在我的应用程序中,我有一个自定义视图,如下图所示,
这是一个音频可视化工具,在放大时会在 X 轴上增长。所以我需要它在滚动视图中以滚动浏览音频图。目前布局如下,
<HorizontalScrollView
android:id="@+id/fileWaveViewScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
style="@style/CustomScrollbar">
<com.bluehub.fastmixer.common.views.FileWaveView
android:id="@+id/fileWaveView"
android:layout_width="wrap_content"
android:layout_height="120dp"
app:audioFileUiState="@{audioFileUiState}"
app:samplesReader="@{eventListener.readSamplesCallbackWithIndex(audioFileUiState.path)}"
app:fileWaveViewStore="@{fileWaveViewStore}"
tools:layout_height="120dp"/>
</HorizontalScrollView>
自定义视图 FileWaveView
从 LinearLayout 扩展而来。
现在我想要一个滚动行为,这样当我在自定义视图(音频可视化图表)内滚动时,滚动视图不应滚动。但是当我通过触摸条本身滚动时,滚动条将滚动它的内容。
我想要这种行为,因为我希望将滚动手势用于可视化器图形内的某些其他操作(以调整段选择器宽度,我将在稍后集成)。
请建议我如何制作此滚动条滚动模式,而视图将仅在滚动条中滚动。我是否应该避免使用 HorizontalScrollBar 并在自定义视图本身内创建自己的滚动行为?
TL;DR
没有任何方法可以帮助您通过幻灯片事件禁用 HorizontalScrollView 上的滚动,并且仅在使用 ScrollBar
时执行此操作。这样做的原因是 ScrollBar 不是与 HorizontalScrollView 分开的视图,并且它本身没有实现任何侦听器。第三方库可以选择处理音频可视化或音频文件,或实施我在下面发布的解决方法。不太确定它一开始是否会起作用,因为它需要大量工作,但也许值得尝试。
我不确定 HorizontalScrollView
方法中是否有类似的东西。但是有一些选项可以帮助您做到这一点。我找不到任何东西,比如教程,可以为您实现这一目标,但我可能会通过一些变通办法来解决这个问题。首先,有这个:
You cannot disable the scrolling of a ScrollView
. You would need to extend to ScrollView and override the onTouchEvent
method to return false when some condition is matched.
因此,您需要先执行此操作以防止用户滚动视图。这将禁用触摸滚动,这就是您在这种情况下所需要的。现在,我不确定这是否会 100% 有效,因为我现在无法对其进行测试,但我正在尽力提供帮助。在您禁用 ScrollView
上的触摸事件后,现在您需要隐藏 ScrollBar
,因为您不能使用它,因为它是 HorizontalScrollView
的一部分,而且它不只有他自己的听众,所以我认为每个触摸事件都会被 HorizontalScrollView
本身拦截,并且不会在内容和滚动条上分开。要在 HorizontalScrollView
中隐藏滚动条,您可以像这样使用 XML
属性:
android:scrollbars="none"
或者从你的代码中这样:
view.setVerticalScrollBarEnabled(false);
view.setHorizontalScrollBarEnabled(false);
现在您需要创建自己的滚动条或仅创建将在视图内滚动的按钮。这些按钮将改变滚动位置 onClick
事件。但首先,让我们禁用 HorizontalScrollView
.
上的滚动
class LockableScrollView extends HorizontalScrollView {
...
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private boolean mScrollable = true;
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
public boolean isScrollable() {
return mScrollable;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Do not allow touch events.
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Do not allow touch events.
return false;
}
}
现在您可以使用这个替代经典 HorizontalScrollView
:
<com.mypackagename.LockableScrollView
android:id="@+id/QuranGalleryScrollView"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
//your view
</com.mypackagename.LockableScrollView>
这是对这个问题的回答:
现在,让我们回到那些按钮。假设您在每一侧都有两个按钮,一个用于向右滚动,一个用于向左滚动。您可以像这样处理滚动:
rightBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hsv.scrollTo((int)hsv.getScrollX() + 10, (int)hsv.getScrollY());
}
});
或者如果你想让它更流畅,你可以像这样使用 onTouchListener
:
rightBtn.setOnTouchListener(new View.OnTouchListener() {
private Handler mHandler;
private long mInitialDelay = 300;
private long mRepeatDelay = 100;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mHandler != null)
return true;
mHandler = new Handler();
mHandler.postDelayed(mAction, mInitialDelay);
break;
case MotionEvent.ACTION_UP:
if (mHandler == null)
return true;
mHandler.removeCallbacks(mAction);
mHandler = null;
break;
}
return false;
}
Runnable mAction = new Runnable() {
@Override
public void run() {
hsv.scrollTo((int) hsv.getScrollX() + 10, (int) hsv.getScrollY());
mHandler.postDelayed(mAction, mRepeatDelay);
}
};
});
其中 hsv
是您的 HorizontalScrollView
。现在您可以遵循左按钮的逻辑。要在视图位于 start/end 时处理 show/hide 按钮,您可以按照以下问题和答案进行操作:
如果这不是您想要做的事情,那么我不确定您还有其他选择,因为我的知识只能到此为止。但是,也许可以考虑在 GitHub 上或其他使用音频文件的地方查看一些第三方库。就像这个:https://github.com/ferPrieto/SoundLine
如果您 运行 在实施此代码时遇到任何问题,请在评论中告诉我。由于在作为答案发布之前我没有时间对此进行测试,但我很乐意帮助您解决您以后遇到的任何问题。
在 SlothCoding 的帮助下,我解决了这个问题。我创建了一个自定义滚动条并用它来控制 HorizontalScrollView。下面给出相关代码,
自定义滚动视图
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.HorizontalScrollView
class LockedHorizontalScrollView(
context: Context,
attrs: AttributeSet? = null) : HorizontalScrollView(context, attrs) {
init {
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
return false
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
return false
}
}
滚动条
package com.bluehub.fastmixer.common.views
import android.content.Context
import android.util.AttributeSet
import android.view.*
import android.widget.HorizontalScrollView
import androidx.constraintlayout.widget.ConstraintLayout
import com.bluehub.fastmixer.databinding.CustomScrollBarBinding
class CustomHorizontalScrollBar(context: Context, attributeSet: AttributeSet?)
: ConstraintLayout(context, attributeSet) {
private val binding: CustomScrollBarBinding
private lateinit var mHorizontalScrollView: HorizontalScrollView
private lateinit var mView: View
private val mScrollBar: CustomHorizontalScrollBar
private val mScrollTrack: View
private val mScrollThumb: View
private val gestureListener = object: GestureDetector.SimpleOnGestureListener() {
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent?,
distanceX: Float,
distanceY: Float
): Boolean {
handleScrollOfThumb(e2, distanceX)
return true
}
override fun onDown(e: MotionEvent?): Boolean {
return true
}
}
private val gestureDetector: GestureDetector
init {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
binding = CustomScrollBarBinding.inflate(inflater, this, true)
mScrollBar = this
mScrollTrack = binding.scrollTrack
mScrollThumb = binding.scrollThumb
gestureDetector = GestureDetector(context, gestureListener)
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
return gestureDetector.onTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
return gestureDetector.onTouchEvent(event)
}
fun setHorizontalScrollView(horizontalScrollView: HorizontalScrollView) {
this.mHorizontalScrollView = horizontalScrollView
}
fun setControlledView(view: View) {
this.mView = view
setViewWidthListener()
}
private fun handleScrollOfThumb(event: MotionEvent?, distanceX: Float) {
event?: return
if (event.x >= mScrollThumb.left && event.x <= mScrollThumb.left + mScrollThumb.width) {
var newLeft = mScrollThumb.left - distanceX.toInt()
var newRight = mScrollThumb.right - distanceX.toInt()
if (newRight <= mScrollBar.width && newLeft >= 0) {
repositionScrollThumb(newLeft, newRight)
}
// Thumb is not at left nor right, but distanced asked to traverse by move is more
else if (mScrollThumb.left != 0 && mScrollThumb.right < mScrollBar.width - 1) {
// Distance from right end of bar
val distanceToRight = mScrollBar.width - mScrollThumb.right - 1
// Get the closest distance to move the thumb to
val newDist = if (mScrollThumb.left < distanceToRight) {
mScrollThumb.left
} else {
-distanceToRight
}
newLeft = mScrollThumb.left - newDist
newRight = mScrollThumb.right - newDist
repositionScrollThumb(newLeft, newRight)
}
}
}
private fun repositionScrollThumb(newLeft: Int, newRight: Int) {
mScrollThumb.layout(newLeft, mScrollThumb.top, newRight, mScrollThumb.bottom)
performScrollOnScrollView()
}
private fun performScrollOnScrollView() {
val ratio = mView.width.toFloat() / mScrollBar.width.toFloat()
val posToScroll = (ratio * mScrollThumb.left).toInt()
mHorizontalScrollView.post {
mHorizontalScrollView.scrollTo(posToScroll, mHorizontalScrollView.top)
}
}
private fun setViewWidthListener() {
mView.addOnLayoutChangeListener { view: View, _, _, _, _, _, _, _, _ ->
if (mScrollTrack.width == 0) return@addOnLayoutChangeListener
val w = view.width
val ratio = w.toFloat() / mScrollTrack.width.toFloat()
if (ratio == 1.0f) {
mScrollBar.visibility = View.INVISIBLE
} else {
mScrollBar.visibility = View.VISIBLE
val layoutParams = mScrollThumb.layoutParams
val newWidth = (mScrollTrack.width.toFloat() / ratio).toInt()
if (mScrollThumb.width != newWidth) {
mScrollThumb.post {
mScrollThumb.layoutParams = layoutParams
}
}
}
}
}
}
滚动条布局,
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<View
android:id="@+id/scrollTrack"
android:layout_width="match_parent"
android:layout_height="@dimen/scrollbar_track_height"
android:background="@drawable/scrollbar_horizontal_track"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintBottom_toBottomOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/scrollThumb"
android:layout_width="18dp"
android:layout_height="@dimen/scrollbar_thumb_height"
android:background="@drawable/scrollbar_horizontal_thumb"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintBottom_toBottomOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
自定义视图在滚动条内的布局,
<com.bluehub.fastmixer.common.views.LockedHorizontalScrollView
android:id="@+id/fileWaveViewScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.bluehub.fastmixer.common.views.FileWaveView
android:id="@+id/fileWaveView"
android:layout_width="wrap_content"
android:layout_height="@dimen/audio_file_view_height"
app:audioFileUiState="@{audioFileUiState}"
app:samplesReader="@{eventListener.readSamplesCallbackWithIndex(audioFileUiState.path)}"
app:fileWaveViewStore="@{fileWaveViewStore}"
tools:layout_height="120dp">
<com.bluehub.fastmixer.common.views.AudioWidgetSlider
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.bluehub.fastmixer.common.views.FileWaveView>
</com.bluehub.fastmixer.common.views.LockableHorizontalScrollView>
<com.bluehub.fastmixer.common.views.CustomHorizontalScrollBar
android:id="@+id/fileWaveViewScrollBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fileWaveViewScroll" />
终于在父视图中,
binding.apply {
fileWaveViewScrollBar.setHorizontalScrollView(fileWaveViewScroll)
fileWaveViewScrollBar.setControlledView(fileWaveView)
}
我们有一些用于滚动轨道和滚动滑块的可绘制对象。
滚动轨道,
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#9BA3C5"
android:startColor="#8388A4" />
<corners android:radius="8dp" />
</shape>
滚动拇指,
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#005A87"
android:startColor="#007AB8" />
<corners android:radius="8dp" />
</shape>
在我的应用程序中,我有一个自定义视图,如下图所示,
这是一个音频可视化工具,在放大时会在 X 轴上增长。所以我需要它在滚动视图中以滚动浏览音频图。目前布局如下,
<HorizontalScrollView
android:id="@+id/fileWaveViewScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
style="@style/CustomScrollbar">
<com.bluehub.fastmixer.common.views.FileWaveView
android:id="@+id/fileWaveView"
android:layout_width="wrap_content"
android:layout_height="120dp"
app:audioFileUiState="@{audioFileUiState}"
app:samplesReader="@{eventListener.readSamplesCallbackWithIndex(audioFileUiState.path)}"
app:fileWaveViewStore="@{fileWaveViewStore}"
tools:layout_height="120dp"/>
</HorizontalScrollView>
自定义视图 FileWaveView
从 LinearLayout 扩展而来。
现在我想要一个滚动行为,这样当我在自定义视图(音频可视化图表)内滚动时,滚动视图不应滚动。但是当我通过触摸条本身滚动时,滚动条将滚动它的内容。
我想要这种行为,因为我希望将滚动手势用于可视化器图形内的某些其他操作(以调整段选择器宽度,我将在稍后集成)。
请建议我如何制作此滚动条滚动模式,而视图将仅在滚动条中滚动。我是否应该避免使用 HorizontalScrollBar 并在自定义视图本身内创建自己的滚动行为?
TL;DR
没有任何方法可以帮助您通过幻灯片事件禁用 HorizontalScrollView 上的滚动,并且仅在使用 ScrollBar
时执行此操作。这样做的原因是 ScrollBar 不是与 HorizontalScrollView 分开的视图,并且它本身没有实现任何侦听器。第三方库可以选择处理音频可视化或音频文件,或实施我在下面发布的解决方法。不太确定它一开始是否会起作用,因为它需要大量工作,但也许值得尝试。
我不确定 HorizontalScrollView
方法中是否有类似的东西。但是有一些选项可以帮助您做到这一点。我找不到任何东西,比如教程,可以为您实现这一目标,但我可能会通过一些变通办法来解决这个问题。首先,有这个:
You cannot disable the scrolling of a
ScrollView
. You would need to extend to ScrollView and override theonTouchEvent
method to return false when some condition is matched.
因此,您需要先执行此操作以防止用户滚动视图。这将禁用触摸滚动,这就是您在这种情况下所需要的。现在,我不确定这是否会 100% 有效,因为我现在无法对其进行测试,但我正在尽力提供帮助。在您禁用 ScrollView
上的触摸事件后,现在您需要隐藏 ScrollBar
,因为您不能使用它,因为它是 HorizontalScrollView
的一部分,而且它不只有他自己的听众,所以我认为每个触摸事件都会被 HorizontalScrollView
本身拦截,并且不会在内容和滚动条上分开。要在 HorizontalScrollView
中隐藏滚动条,您可以像这样使用 XML
属性:
android:scrollbars="none"
或者从你的代码中这样:
view.setVerticalScrollBarEnabled(false);
view.setHorizontalScrollBarEnabled(false);
现在您需要创建自己的滚动条或仅创建将在视图内滚动的按钮。这些按钮将改变滚动位置 onClick
事件。但首先,让我们禁用 HorizontalScrollView
.
class LockableScrollView extends HorizontalScrollView {
...
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private boolean mScrollable = true;
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
public boolean isScrollable() {
return mScrollable;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Do not allow touch events.
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Do not allow touch events.
return false;
}
}
现在您可以使用这个替代经典 HorizontalScrollView
:
<com.mypackagename.LockableScrollView
android:id="@+id/QuranGalleryScrollView"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
//your view
</com.mypackagename.LockableScrollView>
这是对这个问题的回答:
现在,让我们回到那些按钮。假设您在每一侧都有两个按钮,一个用于向右滚动,一个用于向左滚动。您可以像这样处理滚动:
rightBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hsv.scrollTo((int)hsv.getScrollX() + 10, (int)hsv.getScrollY());
}
});
或者如果你想让它更流畅,你可以像这样使用 onTouchListener
:
rightBtn.setOnTouchListener(new View.OnTouchListener() {
private Handler mHandler;
private long mInitialDelay = 300;
private long mRepeatDelay = 100;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mHandler != null)
return true;
mHandler = new Handler();
mHandler.postDelayed(mAction, mInitialDelay);
break;
case MotionEvent.ACTION_UP:
if (mHandler == null)
return true;
mHandler.removeCallbacks(mAction);
mHandler = null;
break;
}
return false;
}
Runnable mAction = new Runnable() {
@Override
public void run() {
hsv.scrollTo((int) hsv.getScrollX() + 10, (int) hsv.getScrollY());
mHandler.postDelayed(mAction, mRepeatDelay);
}
};
});
其中 hsv
是您的 HorizontalScrollView
。现在您可以遵循左按钮的逻辑。要在视图位于 start/end 时处理 show/hide 按钮,您可以按照以下问题和答案进行操作:
如果这不是您想要做的事情,那么我不确定您还有其他选择,因为我的知识只能到此为止。但是,也许可以考虑在 GitHub 上或其他使用音频文件的地方查看一些第三方库。就像这个:https://github.com/ferPrieto/SoundLine
如果您 运行 在实施此代码时遇到任何问题,请在评论中告诉我。由于在作为答案发布之前我没有时间对此进行测试,但我很乐意帮助您解决您以后遇到的任何问题。
在 SlothCoding 的帮助下,我解决了这个问题。我创建了一个自定义滚动条并用它来控制 HorizontalScrollView。下面给出相关代码,
自定义滚动视图
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.HorizontalScrollView
class LockedHorizontalScrollView(
context: Context,
attrs: AttributeSet? = null) : HorizontalScrollView(context, attrs) {
init {
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
return false
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
return false
}
}
滚动条
package com.bluehub.fastmixer.common.views
import android.content.Context
import android.util.AttributeSet
import android.view.*
import android.widget.HorizontalScrollView
import androidx.constraintlayout.widget.ConstraintLayout
import com.bluehub.fastmixer.databinding.CustomScrollBarBinding
class CustomHorizontalScrollBar(context: Context, attributeSet: AttributeSet?)
: ConstraintLayout(context, attributeSet) {
private val binding: CustomScrollBarBinding
private lateinit var mHorizontalScrollView: HorizontalScrollView
private lateinit var mView: View
private val mScrollBar: CustomHorizontalScrollBar
private val mScrollTrack: View
private val mScrollThumb: View
private val gestureListener = object: GestureDetector.SimpleOnGestureListener() {
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent?,
distanceX: Float,
distanceY: Float
): Boolean {
handleScrollOfThumb(e2, distanceX)
return true
}
override fun onDown(e: MotionEvent?): Boolean {
return true
}
}
private val gestureDetector: GestureDetector
init {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
binding = CustomScrollBarBinding.inflate(inflater, this, true)
mScrollBar = this
mScrollTrack = binding.scrollTrack
mScrollThumb = binding.scrollThumb
gestureDetector = GestureDetector(context, gestureListener)
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
return gestureDetector.onTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
return gestureDetector.onTouchEvent(event)
}
fun setHorizontalScrollView(horizontalScrollView: HorizontalScrollView) {
this.mHorizontalScrollView = horizontalScrollView
}
fun setControlledView(view: View) {
this.mView = view
setViewWidthListener()
}
private fun handleScrollOfThumb(event: MotionEvent?, distanceX: Float) {
event?: return
if (event.x >= mScrollThumb.left && event.x <= mScrollThumb.left + mScrollThumb.width) {
var newLeft = mScrollThumb.left - distanceX.toInt()
var newRight = mScrollThumb.right - distanceX.toInt()
if (newRight <= mScrollBar.width && newLeft >= 0) {
repositionScrollThumb(newLeft, newRight)
}
// Thumb is not at left nor right, but distanced asked to traverse by move is more
else if (mScrollThumb.left != 0 && mScrollThumb.right < mScrollBar.width - 1) {
// Distance from right end of bar
val distanceToRight = mScrollBar.width - mScrollThumb.right - 1
// Get the closest distance to move the thumb to
val newDist = if (mScrollThumb.left < distanceToRight) {
mScrollThumb.left
} else {
-distanceToRight
}
newLeft = mScrollThumb.left - newDist
newRight = mScrollThumb.right - newDist
repositionScrollThumb(newLeft, newRight)
}
}
}
private fun repositionScrollThumb(newLeft: Int, newRight: Int) {
mScrollThumb.layout(newLeft, mScrollThumb.top, newRight, mScrollThumb.bottom)
performScrollOnScrollView()
}
private fun performScrollOnScrollView() {
val ratio = mView.width.toFloat() / mScrollBar.width.toFloat()
val posToScroll = (ratio * mScrollThumb.left).toInt()
mHorizontalScrollView.post {
mHorizontalScrollView.scrollTo(posToScroll, mHorizontalScrollView.top)
}
}
private fun setViewWidthListener() {
mView.addOnLayoutChangeListener { view: View, _, _, _, _, _, _, _, _ ->
if (mScrollTrack.width == 0) return@addOnLayoutChangeListener
val w = view.width
val ratio = w.toFloat() / mScrollTrack.width.toFloat()
if (ratio == 1.0f) {
mScrollBar.visibility = View.INVISIBLE
} else {
mScrollBar.visibility = View.VISIBLE
val layoutParams = mScrollThumb.layoutParams
val newWidth = (mScrollTrack.width.toFloat() / ratio).toInt()
if (mScrollThumb.width != newWidth) {
mScrollThumb.post {
mScrollThumb.layoutParams = layoutParams
}
}
}
}
}
}
滚动条布局,
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<View
android:id="@+id/scrollTrack"
android:layout_width="match_parent"
android:layout_height="@dimen/scrollbar_track_height"
android:background="@drawable/scrollbar_horizontal_track"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintBottom_toBottomOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/scrollThumb"
android:layout_width="18dp"
android:layout_height="@dimen/scrollbar_thumb_height"
android:background="@drawable/scrollbar_horizontal_thumb"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintBottom_toBottomOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
自定义视图在滚动条内的布局,
<com.bluehub.fastmixer.common.views.LockedHorizontalScrollView
android:id="@+id/fileWaveViewScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.bluehub.fastmixer.common.views.FileWaveView
android:id="@+id/fileWaveView"
android:layout_width="wrap_content"
android:layout_height="@dimen/audio_file_view_height"
app:audioFileUiState="@{audioFileUiState}"
app:samplesReader="@{eventListener.readSamplesCallbackWithIndex(audioFileUiState.path)}"
app:fileWaveViewStore="@{fileWaveViewStore}"
tools:layout_height="120dp">
<com.bluehub.fastmixer.common.views.AudioWidgetSlider
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.bluehub.fastmixer.common.views.FileWaveView>
</com.bluehub.fastmixer.common.views.LockableHorizontalScrollView>
<com.bluehub.fastmixer.common.views.CustomHorizontalScrollBar
android:id="@+id/fileWaveViewScrollBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fileWaveViewScroll" />
终于在父视图中,
binding.apply {
fileWaveViewScrollBar.setHorizontalScrollView(fileWaveViewScroll)
fileWaveViewScrollBar.setControlledView(fileWaveView)
}
我们有一些用于滚动轨道和滚动滑块的可绘制对象。
滚动轨道,
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#9BA3C5"
android:startColor="#8388A4" />
<corners android:radius="8dp" />
</shape>
滚动拇指,
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#005A87"
android:startColor="#007AB8" />
<corners android:radius="8dp" />
</shape>