Как остановить ScrollView (на TextView) от прокрутки к вершине при масштабировании - PullRequest
0 голосов
/ 09 октября 2019

Я хочу реализовать масштабирование при помощи прокрутки в моем TextView с прокруткой.

Приведенный ниже код всегда прокручивается вверх, а при масштабировании.

Я хочу сохранить его прокрутку без изменений. Пожалуйста, скажите мне, как это сделать.

import android.app.Activity
import android.os.Bundle
import android.view.ScaleGestureDetector
import kotlinx.android.synthetic.main.scroll_layout.*

class ScrollTextActivity : Activity() {
    private var scaleFactor = 1.0f
    private val defaultTextSize = 30.0f
    private val scaleListener = ScaleListener()
    private val scaleGestureDetector: ScaleGestureDetector by lazy {
        ScaleGestureDetector(this, scaleListener)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.scroll_layout)
        text_view.textSize = defaultTextSize * scaleFactor
        scroll_view.setOnTouchListener { _, e ->
            if (e.pointerCount > 1) {
                scaleGestureDetector.onTouchEvent(e)
                true
            } else
                super.onTouchEvent(e)
        }
    }

    private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        var oldFocusY = 0f
        var oldScrollY = 0
        var oldSize = defaultTextSize

        override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
            oldFocusY = detector.focusY
            oldScrollY = scroll_view.scrollY
            oldSize = text_view.textSize
            return true
        }

        override fun onScale(detector: ScaleGestureDetector): Boolean {
            scaleFactor = (scaleFactor * detector.scaleFactor)
                    .coerceAtMost(10.0f)
                    .coerceAtLeast(0.1f)
            text_view.textSize = defaultTextSize * scaleFactor
            scroll_view.scrollTo(0, (
                    (oldScrollY + oldFocusY) * text_view.textSize / oldSize
                            - oldFocusY).toInt()
            )
            return true
        }

        override fun onScaleEnd(detector: ScaleGestureDetector) {
        }
    }
}

и scroll_text.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <HorizontalScrollView
            android:id="@+id/horizontal_scroll_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/text_view"
                style="@style/TextAppearance.MaterialComponents.Body1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="start|top"
                android:scrollHorizontally="true"
                android:textIsSelectable="true"
                android:text=" Long \n long \n long \n long \n long \n long \n long \n text..."/>
        </HorizontalScrollView>
    </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

Более того, иногда он прокручивается обратно в предполагаемую позицию, когда вы

  1. Положите палец на экран и переместите его. Текст прокручивается. Держите его.
  2. Положите большой палец на экран и переместите его. Текст увеличивается. (И верх текста виден.)
  3. Поднимите только большой палец.

Но это не всегда происходит. Что еще более странно, иногда он начинает работать как положено.

Целевыми API являются minSdkVersion 19, targetSdkVersion 29.

1 Ответ

0 голосов
/ 10 октября 2019

Хорошо, это должно быть ошибка в TextView или что-то на некоторых устройствах, например, Amazon Fire HD 10 (API 22).

Другие мои устройства выглядят работающими.

Я удалил ScrollViewsи установите детектор / слушатель непосредственно в TextView, и у него все еще была та же проблема.

Я заменил ScaleGestureDetector / Listener на некоторый код ручной работы (см. ниже), и у него все еще была та же проблема.

Я сдамся и буду игнорировать такие глючные устройства.

ссылка:

//var scaleFactor = 1f
//scroll_view.setOnTouchListener(ZoomTouchListener())

inner class ZoomTouchListener : View.OnTouchListener {
    private val firstFocalPoint = PointF()
    private var firstSpan = 0f
    private var firstScaleFactor = 1f
    private var firstScrollY = 0
    private var firstScrollX = 0
    private var isInProgress = false

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouch(v: View, e: MotionEvent): Boolean {
        if (e.pointerCount > 1) {
            when (e.actionMasked) {
                MotionEvent.ACTION_POINTER_DOWN,
                MotionEvent.ACTION_DOWN -> {
                    startZooming(v, e)
                }
                MotionEvent.ACTION_POINTER_UP,
                MotionEvent.ACTION_UP,
                MotionEvent.ACTION_CANCEL -> {
                    isInProgress = false
                }
                else -> {
                    if (!isInProgress) {
                        startZooming(v, e)
                    } else {
                        val focalPoint = getFocalPoint(e)
                        scaleFactor = (firstScaleFactor * getSpan(e, focalPoint) / firstSpan)
                                .coerceAtMost(10.0f)
                                .coerceAtLeast(0.1f)
                        text_view.textSize = defaultTextSize * scaleFactor
                        v.scrollY =
                                ((firstScrollY + firstFocalPoint.y) * scaleFactor / firstScaleFactor
                                        - focalPoint.y).coerceAtLeast(0f).toInt()
                        horizontal_scroll_view.scrollX =
                                ((firstScrollX + firstFocalPoint.x) * scaleFactor / firstScaleFactor
                                        - focalPoint.x).coerceAtLeast(0f).toInt()

                    }
                }
            }
            return true
        } else
            return false
    }

    private fun startZooming(v: View, e: MotionEvent) {
        firstFocalPoint.set(getFocalPoint(e))
        firstSpan = getSpan(e, firstFocalPoint)
        firstScaleFactor = scaleFactor
        firstScrollX = v.scrollX
        firstScrollY = v.scrollY
        isInProgress = true
    }

    private fun getFocalPoint(e: MotionEvent): PointF {
        var sumX = 0f
        var sumY = 0f
        for (i in 0 until e.pointerCount) {
            sumX += e.getX(i)
            sumY += e.getY(i)
        }
        val focusX = sumX / e.pointerCount
        val focusY = sumY / e.pointerCount
        return PointF(focusX, focusY)
    }

    private fun getSpan(e: MotionEvent, focalPoint: PointF): Float {
        var devSumX = 0f
        var devSumY = 0f
        for (i in 0 until e.pointerCount) {
            devSumX += abs(e.getX(i) - focalPoint.x)
            devSumY += abs(e.getY(i) - focalPoint.y)
        }
        val devX = devSumX / e.pointerCount
        val devY = devSumY / e.pointerCount
        val spanX = devX * 2
        val spanY = devY * 2
        return hypot(spanX, spanY)
    }
}
...