Как правильно прокрутить ScrollView с помощью SeekBar - PullRequest
0 голосов
/ 10 июня 2019

Я пытаюсь создать пользовательский вертикальный 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 Ответ

1 голос
/ 10 июня 2019

Проверьте обновленный ответ внизу для решения


Кто мы, чтобы судить, что вам нужно делать, клиент почти всегда прав!

В любом случае, если вы все еще хотели это сделать (это может или не может работать), вот как я думаю, что архитектура должна выглядеть следующим образом:

  1. SeekBar глуп, не должен знать НИЧЕГО о том, что он делает. Вы даете ему минус (0?) И МАКС (n). Вы подписываетесь на его слушателя и получаете «прогресс» обновления. (ха, это самая легкая часть)
  2. ScrollView, содержащий текст, также очень неразумный, помимо его основ, ему можно сказать, чтобы прокрутить до позиции (это, вероятно, потребует некоторой работы, но я уверен, что это не сложно).
  3. Расчет значения MAX будет определять, насколько сложным будет шаг 2. Если вы используете каждый «шаг» в прокручиваемом контенте как «строку текста», то это нормально, но вам необходимо учитывать изменения макета и повторные измерения, которые могут изменить размер текста. Не должно быть "сумасшедшим", но имейте это в виду, иначе вы получите первый отчет об ошибке, как только пользователь повернет телефон:)
  4. Если текст динамический (может изменяться в реальном времени), ваша функция для шага 3 должна быть максимально эффективной, вам нужно, чтобы эти цифры были как можно скорее.
  5. Реагировать на изменения прогресса с панели поиска и иметь прокручиваемую прокрутку контента будет либо очень просто (вы нашли способ сделать это очень легко), либо сложно, если вы не можете заставить ScrollView вести себя так, как вам хочется.

Альтернативный подход

Если ваш текст действительно длинный, держу пари, у вас будет лучшая производительность, если вы разделите текст на строки и будете использовать его для просмотра, что сделает прокрутку «немного легче», как вы могли бы переместить свое представление переработчика в позицию (линия!).

UPDATE

Ладно, из любопытства я попробовал это. Я запустил AS, создал новый проект с «Пустым действием» и добавил ScrollView с TextView внутри и SeekBar внизу (горизонтальный).

Вот суть со всеми соответствующими битами:

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

В своей основе решение работает из коробки :

scrollView.scrollTo(0, progress), где progress возвращается вам обратным вызовом фреймворка в seekBar's onProgressChanged().

Теперь ключ должен установить правильный «Макс» для панели поиска.

Это получается путем уклонения частного метода в ScrollView с этим:

(кредит, где нужно, я получил эту идею от здесь )

    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).

...