首页 > 解决方案 > 在视图上设置 y 值时,ScrollView 不滚动

问题描述

我对android开发相当陌生,而且我已经在这个问题上撞墙了两个多星期了。我通过学习 Kotlin 开始了我的 android 开发。

我有一个 ScrollView 作为应用程序布局的一部分,在 ScrollView 内部有一个布局。我以编程方式定位在 XML 中布局的基于 TextSwitcher 的按钮,主要是为了调整按钮的大小并将它们移出屏幕。这些按钮旨在通过 Spring 动画在屏幕上滑动。问题是当我设置按钮的 y 位置时,ScrollView 不会滚动。

我在 ScrollView 中使用了各种布局(LinearLayout、RelativeLayout、FrameLayout),它们的行为方式都相同。如果我不使用 y 值以编程方式定位按钮,这意味着让按钮纯粹通过 XML 定位(对于我当时在 ScrollView 内使用的任何布局),以便它们在屏幕上启动,ScrollView滚动很好。即使我以这种方式使用 Spring 动画,它仍然可以正常滚动,所以我认为使用动画不是问题。只有当我通过代码设置每个按钮视图的初始 y 值时,它才不会滚动。

我有一个精简的测试应用程序,没有应用程序的所有其他花里胡哨,它显示了问题。我让 ScrollView 滚动的方法是在 ScrollView 内添加布局可能需要的任何属性,并在 setButton 方法中注释掉“button.y = -y”行。例如,如果我只是将 ScrollView 中的 FrameLayout 更改为 LinearLayout,并在代码中注释掉设置 y 值,则按钮将在屏幕上布局,一个在另一个之上,并且滚动正常。单击“开始”按钮会使它们使用 Spring 动画制作动画,并且它仍然会在动画之后滚动。只需添加该 set y 行,就会导致 ScrollView 不滚动。

这是 XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusableInTouchMode="true"
    android:orientation="horizontal"
    tools:context="com.example.scrolltest.MainActivity">

    <View
        android:id="@+id/centerShim"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:visibility="invisible"
        android:layout_centerHorizontal="true"/>

    <ScrollView
        android:id="@+id/buttonScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:elevation="2dp"
        android:fillViewport="true"
        android:orientation="vertical"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_toLeftOf="@id/centerShim"
        android:scrollbars="none">

        <FrameLayout
            android:id="@+id/buttonLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <com.example.scrolltest.MyButton
                android:id="@+id/button1View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>

            <com.example.scrolltest.MyButton
                android:id="@+id/button2View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>

            <com.example.scrolltest.MyButton
                android:id="@+id/button3View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>

            <com.example.scrolltest.MyButton
                android:id="@+id/button4View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>

            <com.example.scrolltest.MyButton
                android:id="@+id/button5View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>

            <com.example.scrolltest.MyButton
                android:id="@+id/button6View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>

            <com.example.scrolltest.MyButton
                android:id="@+id/button7View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>

            <com.example.scrolltest.MyButton
                android:id="@+id/button8View"
                android:background="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:textColor="@color/white"
                    android:textSize="30sp"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentStart="true"/>
            </com.example.scrolltest.MyButton>
        </FrameLayout>
    </ScrollView>

    <TextView
        android:id="@+id/getStartedView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentBottom="true"
        android:layout_marginEnd="5dp"
        android:layout_marginBottom="5dp"
        android:background="@color/black"
        android:gravity="center"
        android:text="Get Started"
        android:textColor="@color/white"
        android:textSize="30sp"/>
</RelativeLayout>

这是主要的活动代码:

package com.example.scrolltest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.util.TypedValue
import android.view.animation.AnimationUtils
import android.widget.*
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce

class MainActivity : AppCompatActivity() {
    private var buttonAnimationOffset: Float = 0f
    // STIFFNESS_MEDIUM = 1500, higher is stiffer.
    private val stiffnessValue = 300f
    // DAMPING_RATIO_MEDIUM_BOUNCY = 0.5, higher dampens quicker.
    private val dampeningValue = 0.7f
    private val initialVelocity = 0.5f
    private val fadeDuration = 500L
    private var lastButtonAnim: SpringAnimation? = null
    private var buttonWidth = 0f
    private var buttonHeight = 0f

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val screenWidth = resources.displayMetrics.widthPixels
        val screenHeight = resources.displayMetrics.heightPixels

        var h = 0
        buttonWidth = screenWidth * 0.33f
        buttonHeight = screenHeight * 0.16f

        buttonAnimationOffset = -buttonHeight

        setButton(R.id.button1View, buttonHeight * 8, "Button 1")
        setButton(R.id.button2View, buttonHeight * 7, "Button 2")
        setButton(R.id.button3View, buttonHeight * 6, "Button 3")
        setButton(R.id.button4View, buttonHeight * 5, "Button 4")
        setButton(R.id.button5View, buttonHeight * 4, "Button 5")
        setButton(R.id.button6View, buttonHeight * 3, "Button 6")
        setButton(R.id.button7View, buttonHeight * 2, "Button 7")
        setButton(R.id.button8View, buttonHeight, "Button 8")

        val startButton = findViewById<TextView>(R.id.getStartedView)

        startButton.setOnClickListener {
            lastButtonAnim?.start()
        }

        /*
        val buttonScrollView = findViewById<ScrollView>(R.id.buttonScrollView)

        buttonScrollView.getViewTreeObserver()
            .addOnScrollChangedListener(object : ViewTreeObserver.OnScrollChangedListener {
                var lastScroll = 0
                override fun onScrollChanged() {
                    Log.i("ScrollView", "[" + buttonScrollView.scrollX.toString() + "] [" + buttonScrollView.scrollX.toString() + "]")
                    val scrollY: Int =
                        buttonScrollView.getScrollY() // For ScrollView herzontial use getScrollX()
                    if (scrollY > lastScroll) {
                        Log.e("scroll", "down scroll$scrollY")
                    } else if (scrollY < lastScroll) {
                        Log.e("scroll", "up scroll$scrollY")
                    }
                    lastScroll = scrollY
                }
            })
        */

        setButtonAnimationsAndListeners(buttonHeight)
    }

    private fun setButton( id: Int, y: Float, text: String ) {
        val button = findViewById<MyButton>(id)
        val mainLayout = findViewById<RelativeLayout>(R.id.mainLayout)

        if ( button.layoutParams == null ) {
            val buttonLayout = findViewById<FrameLayout>(R.id.buttonLayout)
            button.layoutParams = FrameLayout.LayoutParams(buttonLayout.layoutParams)
        }
        button.layoutParams.height = buttonHeight.toInt()
        button.layoutParams.width = buttonWidth.toInt()
        button.y = -y

        button.setOnClickListener {
            val tv = button.currentView as TextView

            if (tv.text.contains(" clicked"))
            {
                button.setText(tv.text.dropLast(8))
            }
            else
            {
                button.setText(tv.text.toString() + " clicked")
            }
        }

        button.setCurrentText(text)
    }

    private fun setButtonAnimationsAndListeners(height: Float) {
        val anim1 = setButtonAnimationAndListener(
            R.id.button1View,
            null,
            0f
        )

        val anim2 = setButtonAnimationAndListener(
            R.id.button2View,
            anim1,
            0f
        )

        val anim3 = setButtonAnimationAndListener(
            R.id.button3View,
            anim2,
            0f
        )

        val anim4 = setButtonAnimationAndListener(
            R.id.button4View,
            anim3,
            0f
        )

        val anim5 = setButtonAnimationAndListener(
            R.id.button5View,
            anim4,
            0f
        )

        val anim6 = setButtonAnimationAndListener(
            R.id.button6View,
            anim5,
            0f
        )

        val anim7 = setButtonAnimationAndListener(
            R.id.button7View,
            anim6,
            0f
        )

        // Last button
        lastButtonAnim = setButtonAnimationAndListener(
            R.id.button8View,
            anim7,
           height
        )
    }

    private fun setButtonAnimationAndListener(
        buttonId: Int,
        prevAnim: SpringAnimation?,
        height: Float
    ): SpringAnimation {

        val view = findViewById<MyButton>(buttonId)
        val buttonLayout = findViewById<FrameLayout>(R.id.buttonLayout)

        lateinit var anim: SpringAnimation

        if (height > 0) {
            Log.i("setAnim", "finalPosition = " + (height * ( buttonLayout.childCount - 1 )) )
            anim = SpringAnimation(
                view,
                DynamicAnimation.TRANSLATION_Y
            ).apply {
                spring = SpringForce().apply {
                    stiffness = stiffnessValue
                    dampingRatio = dampeningValue
                    finalPosition = height * ( buttonLayout.childCount - 1 )
                }
            }
        } else {
            anim = SpringAnimation(
                view,
                DynamicAnimation.TRANSLATION_Y
            ).apply {
                spring = SpringForce().apply {
                    stiffness = stiffnessValue
                    dampingRatio = dampeningValue
                }
            }
        }

        anim.setStartVelocity(
            TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                initialVelocity,
                resources.displayMetrics
            )
        )

        anim.addUpdateListener { _, value, _ ->
            Log.i("anim", "[" + getResources().getResourceEntryName(view.id) + "] value = " + value.toString() + " buttonAnimationOffset = " + buttonAnimationOffset.toString() + ", value + buttonAnimationOffset = " + (value + buttonAnimationOffset).toString())
            prevAnim?.animateToFinalPosition(value + buttonAnimationOffset)
        }

        // Set fade in and out animations for changing the text.
        val textAnimationIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in)
        textAnimationIn.duration = fadeDuration

        val textAnimationOut = AnimationUtils.loadAnimation(this, android.R.anim.fade_out)
        textAnimationOut.duration = fadeDuration

        view.setInAnimation(textAnimationIn)
        view.setOutAnimation(textAnimationOut)

        return anim
    }
}

这是按钮类,主要是为了让我可以在 onMeasure 和 onLayout 方法中添加一些跟踪。否则,它只是一个 TextSwitcher:

package com.example.scrolltest

import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.Log
import android.widget.ScrollView
import android.widget.TextSwitcher

class MyButton: TextSwitcher {
    constructor( context: Context) : super( context ) {
    }
    constructor(context: Context, attrs: AttributeSet? ): super( context, attrs ) {
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        val x = this.x
        val y = this.y
        val h = this.height
        val w = this.width
        val t = this.top
        val ty = this.translationY
        Log.i("onMeasure", "[" + getResources().getResourceEntryName(id) + "] y = " + y.toString() + ", top = " + t.toString() + ", translateY = " + ty.toString())
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)

        val x = this.x
        val y = this.y
        val h = this.height
        val w = this.width
        val t = this.top
        val ty = this.translationY
        Log.i("onLayout", "[" + getResources().getResourceEntryName(id) + "] y = " + y.toString() + ", top = " + t.toString() + ", translateY = " + ty.toString())
    }
}

我不知道为什么在每个按钮的视图上调用 set y 会导致滚动问题。任何帮助表示赞赏。谢谢!

标签: androidkotlin

解决方案


推荐阅读