如何使用SeekBar正确滚动ScrollView

时间:2019-06-10 15:20:15

标签: android android-seekbar

我正在尝试创建一个自定义的垂直SeekBar,它将用于滚动ScrollView中的文本。这是SeekBar

class CustomSeekBar : SeekBar {

constructor(context: Context) : super(context) {}

constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    super.onSizeChanged(h, w, oldh, oldw)
}

@Synchronized
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(heightMeasureSpec, widthMeasureSpec)
    setMeasuredDimension(measuredHeight, measuredWidth)
}

override fun onDraw(c: Canvas) {

    c.rotate(90f)
    c.translate(0f, -width.toFloat())

    super.onDraw(c)
}

override fun onTouchEvent(event: MotionEvent): Boolean {

    super.onTouchEvent(event)
    if (!isEnabled) {
        return false
    }

    when (event.action) {
        MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP -> {

            val i = (max - (max * event.y / height)).toInt()
            progress = 100 - i
            onSizeChanged(width, height, 0, 0)
        }

        MotionEvent.ACTION_CANCEL -> {
        }
    }
    return true
}
}

这是我的代码,试图将ScrollView附加到我的片段SeekBar中的onStart

       override fun onStart() {
    super.onStart()

    val helpScrollView = view!!.findViewById<ScrollView>(R.id.helpScrollView)

    val helpSeekBar = view!!.findViewById<CustomSeekBar>(R.id.helpSeekBar)

   helpScrollView.viewTreeObserver.addOnGlobalLayoutListener {

       val scroll: Int = getScrollRange(helpScrollView)

       helpSeekBar.max = scroll
   }

    val seekBarListener = object : SeekBar.OnSeekBarChangeListener {

        override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {

            val min = 0
            if(progress < min) {
                seekBar?.progress = min;}

            helpScrollView.scrollTo(0, progress)

        }
        override fun onStartTrackingTouch(seekBar: SeekBar?) {
        }
        override fun onStopTrackingTouch(seekBar: SeekBar?) {

        }
    }

    helpSeekBar.setOnSeekBarChangeListener(seekBarListener)

}

private fun getScrollRange(scrollView: ScrollView): Int {

    var scrollRange = 0
    if (scrollView.childCount > 0) {
        val child = scrollView.getChildAt(0)
        scrollRange =
            Math.max(0, child.height - (scrollView.height - scrollView.paddingBottom - scrollView.paddingTop))
    }

    return scrollRange
}

SeekBar似乎仅对下半部分的触摸有反应,并且拇指阴影仍在屏幕上水平滚动。 ScrollView稍微移动,但仅在滚动开始时沿一个方向移动。我在做什么错了?

1 个答案:

答案 0 :(得分:1)


在底部检查更新的答案以寻求解决方案


我们是谁来判断您需要做什么,客户几乎总是对的!

无论如何,如果您仍然想这样做,(可能会或可能不会),以下是我认为体系结构应尝试的外观:

  1. SeekBar是愚蠢的,不应该知道它在做什么。您给它一个最小值(0?)和一个最大值(n)。您订阅其侦听器并收到“进度”更新。 (哈,这很容易)
  2. 包含文本的ScrollView在基础上也非常不智能,可以告诉它滚动到某个位置(我猜这将涉及一些工作,但我相信这并不困难)。
  3. 计算MAX值将决定步骤2的难度。如果将滚动内容中的每个“步骤”都用作“文本行”,则可以,但是您需要考虑布局更改和可能会改变文本大小的重新测量。不应“疯狂”,但请记住这一点,否则当用户旋转手机时,您将收到第一份错误报告:)
  4. 如果文本是动态的(可以实时更改),则第3步的功能应尽可能高效,您希望这些数字“尽快”。
  5. 响应来自搜索栏的进度更改,如果无法使ScrollView表现出所需的效果,则使滚动内容的滚动变得非常容易(找到了一种非常容易的方式)或变得非常复杂。

替代方法

如果您的文本真的很长,我敢打赌,如果将文本分成几行并使用recyclerview进行显示,您将获得更好的性能,这将使滚动“稍微容易一些”,因为您可以移动自己的recyclerview到一个位置(行!)。

更新

好吧,出于好奇,我尝试了这个。我启动了AS,创建了一个带有“空活动”的新项目,并添加了一个内部带有TextView的ScrollView和一个位于底部(水平)的SeekBar。

以下是所有要点的要点:

https://gist.github.com/Gryzor/5a68e4d247f4db1e0d1d77c40576af33

该解决方案的核心功能是开箱即用

scrollView.scrollTo(0, progress),其中progress由seekBar的onProgressChanged()中的框架回调返回给您。

现在的关键是为搜索栏设置正确的“最大值”。

这是通过 dodging 在ScrollView中使用以下方法获得的:

(应归功于我的想法,我来自here

    private fun getScrollRange(scrollView: ScrollView): Int {

        var scrollRange = 0
        if (scrollView.childCount > 0) {
            val child = scrollView.getChildAt(0)
            scrollRange =
                Math.max(0, child.height - (scrollView.height - scrollView.paddingBottom - scrollView.paddingTop))
        }

        return scrollRange
    }

假设您正在等待seekBar进行测量/布局(因此为什么我必须在treeObserver中执行此操作),这可以正常工作。