如何在缩放时停止ScrollView(在TextView上)滚动到顶部

问题描述 投票:0回答:1

我想在滚动的TextView上实现缩放。

下面的代码在缩放时总是滚动到顶部。

我想保持其scrollY完好无损。请告诉我该怎么做。

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。

android kotlin textview scrollview scale
1个回答
0
投票

[好,这一定是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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.