gpt4 book ai didi

android - 切换 fragment 时跳转滚动

转载 作者:行者123 更新时间:2023-12-03 13:27:44 26 4
gpt4 key购买 nike

在 ScrollView 内部,我在两个不同高度的 fragment 之间动态切换。
不幸的是,这会导致跳跃。可以在以下动画中看到它:

  • 我向下滚动,直到到达“显示黄色”按钮。
  • 按下“显示黄色”将一个巨大的蓝色 fragment 替换为一个微小的黄色 fragment 。发生这种情况时,两个按钮都会跳到屏幕的末尾。

  • 我希望两个按钮在切换到黄色 fragment 时保持在同一位置。怎么可能呢?

    roll

    源代码位于 https://github.com/wondering639/stack-dynamiccontent分别 https://github.com/wondering639/stack-dynamiccontent.git

    相关代码 fragment :

    activity_main.xml
    <?xml version="1.0" encoding="utf-8"?>

    <androidx.core.widget.NestedScrollView 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/myScrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
    android:id="@+id/textView"
    android:layout_width="0dp"
    android:layout_height="800dp"
    android:background="@color/colorAccent"
    android:text="@string/long_text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <Button
    android:id="@+id/button_fragment1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginLeft="16dp"
    android:text="show blue"
    app:layout_constraintEnd_toStartOf="@+id/button_fragment2"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
    android:id="@+id/button_fragment2"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="16dp"
    android:layout_marginRight="16dp"
    android:text="show yellow"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/button_fragment1"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

    <FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@+id/button_fragment2">

    </FrameLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

    MainActivity.kt
    package com.example.dynamiccontent

    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.widget.Button

    class MainActivity : AppCompatActivity() {

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

    // onClick handlers
    findViewById<Button>(R.id.button_fragment1).setOnClickListener {
    insertBlueFragment()
    }

    findViewById<Button>(R.id.button_fragment2).setOnClickListener {
    insertYellowFragment()
    }


    // by default show the blue fragment
    insertBlueFragment()
    }


    private fun insertYellowFragment() {
    val transaction = supportFragmentManager.beginTransaction()
    transaction.replace(R.id.fragment_container, YellowFragment())
    transaction.commit()
    }


    private fun insertBlueFragment() {
    val transaction = supportFragmentManager.beginTransaction()
    transaction.replace(R.id.fragment_container, BlueFragment())
    transaction.commit()
    }


    }

    fragment_blue.xml:
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:background="#0000ff"
    tools:context=".BlueFragment" />

    fragment_yellow.xml
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="20dp"
    android:background="#ffff00"
    tools:context=".YellowFragment" />

    提示

    请注意,这当然是展示我的问题的最小工作示例。在我的真实项目中,我也有 @+id/fragment_container 下面的 View .所以给 @+id/fragment_container 一个固定的大小对我来说不是一个选择 - 切换到低黄色 fragment 时会导致很大的空白区域。

    更新:建议的解决方案概述

    我出于测试目的实现了建议的解决方案,并添加了我的个人经验。

    Cheticamp 的回答,https://stackoverflow.com/a/60323255

    -> 可用 https://github.com/wondering639/stack-dynamiccontent/tree/60323255

    -> FrameLayout 包装内容,短代码

    Pavneet_Singh 的回答,https://stackoverflow.com/a/60310807

    -> 可用 https://github.com/wondering639/stack-dynamiccontent/tree/60310807

    -> FrameLayout 获取蓝色 fragment 的大小。所以没有内容包装。切换到黄色 fragment 时,它与它后面的内容(如果它后面有任何内容)之间存在间隙。虽然没有额外的渲染!
    ** 更新 ** 提供了第二个版本,展示了如何做到这一点。检查答案的评论。

    Ben P.的回答,https://stackoverflow.com/a/60251036

    -> 可用 https://github.com/wondering639/stack-dynamiccontent/tree/60251036

    -> FrameLayout 包装内容。比 Cheticamp 的解决方案更多的代码。触摸“显示黄色”按钮两次会导致“错误”(按钮跳到底部,实际上是我原来的问题)。有人可能会争论在切换到“显示黄色”按钮后只是禁用它,所以我不会认为这是一个真正的问题。

    最佳答案

    更新 : 将其他 View 保留在 framelayout 的正下方为了自动处理场景,我们需要使用 onMeasure要实现自动处理,请执行以下步骤

    • 创建自定义ConstraintLayout作为(或可以使用 MaxHeightFrameConstraintLayout lib ):

    import android.content.Context
    import android.os.Build
    import android.util.AttributeSet
    import androidx.constraintlayout.widget.ConstraintLayout
    import kotlin.math.max

    /**
    * Created by Pavneet_Singh on 2020-02-23.
    */

    class MaxHeightConstraintLayout @kotlin.jvm.JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
    ) : ConstraintLayout(context, attrs, defStyleAttr){

    private var _maxHeight: Int = 0

    // required to support the minHeight attribute
    private var _minHeight = attrs?.getAttributeValue(
    "http://schemas.android.com/apk/res/android",
    "minHeight"
    )?.substringBefore(".")?.toInt() ?: 0

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

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    _minHeight = minHeight
    }

    var maxValue = max(_maxHeight, max(height, _minHeight))

    if (maxValue != 0 && && maxValue > minHeight) {
    minHeight = maxValue
    }
    _maxHeight = maxValue
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    }

    并在您的布局中使用它来代替 ConstraintLayout
    <?xml version="1.0" encoding="utf-8"?>

    <androidx.core.widget.NestedScrollView 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/myScrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.pavneet_singh.temp.MaxHeightConstraintLayout
    android:id="@+id/constraint"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
    android:id="@+id/textView"
    android:layout_width="0dp"
    android:layout_height="800dp"
    android:background="@color/colorAccent"
    android:text="Some long text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <Button
    android:id="@+id/button_fragment1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginLeft="16dp"
    android:text="show blue"
    app:layout_constraintEnd_toStartOf="@+id/button_fragment2"
    app:layout_constraintHorizontal_bias="0.3"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
    android:id="@+id/button_fragment2"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="16dp"
    android:layout_marginRight="16dp"
    android:text="show yellow"
    app:layout_constraintHorizontal_bias="0.3"
    app:layout_constraintStart_toEndOf="@+id/button_fragment1"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
    android:id="@+id/button_fragment3"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="16dp"
    android:layout_marginRight="16dp"
    android:text="show green"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.3"
    app:layout_constraintStart_toEndOf="@+id/button_fragment2"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

    <FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/button_fragment3" />

    <TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="additional text\nMore data"
    android:textSize="24dp"
    app:layout_constraintTop_toBottomOf="@+id/fragment_container" />

    </com.example.pavneet_singh.temp.MaxHeightConstraintLayout>

    </androidx.core.widget.NestedScrollView>

    这将跟踪高度并在每个 fragment 更改期间应用它。

    输出:



    注意:如 comments 中所述之前,设置 minHeight将导致额外的渲染 channel ,并且在当前版本的 ConstraintLayout 中无法避免.

    使用自定义 FrameLayout 的旧方法

    这是一个有趣的要求,我的方法是通过创建自定义 View 来解决它。

    理念 :

    我对解决方案的想法是通过跟踪容器中最大的 child 或 child 的总高度来调整容器的高度。

    尝试 :

    我最初的几次尝试是基于修改 NestedScrollView 的现有行为。通过扩展它,但它不提供对所有必要数据或方法的访问。定制导致对所有场景和边缘情况的支持不佳。

    后来,我通过创建自定义 Framelayout 来实现解决方案用不同的方法。

    解决方案实现

    在实现高度测量阶段的自定义行为时,我深入挖掘并使用 getSuggestedMinimumHeight 操作高度同时跟踪 child 的高度以实现最优化的解决方案,因为它不会导致任何额外或显式渲染,因为它将在内部渲染周期期间管理高度,因此创建自定义 FrameLayout类来实现解决方案并覆盖 getSuggestedMinimumHeight作为:
    class MaxChildHeightFrameLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
    ) : FrameLayout(context, attrs, defStyleAttr) {

    // to keep track of max height
    private var maxHeight: Int = 0

    // required to get support the minHeight attribute
    private val minHeight = attrs?.getAttributeValue(
    "http://schemas.android.com/apk/res/android",
    "minHeight"
    )?.substringBefore(".")?.toInt() ?: 0


    override fun getSuggestedMinimumHeight(): Int {
    var maxChildHeight = 0
    for (i in 0 until childCount) {
    maxChildHeight = max(maxChildHeight, getChildAt(i).measuredHeight)
    }
    if (maxHeight != 0 && layoutParams.height < (maxHeight - maxChildHeight) && maxHeight > maxChildHeight) {
    return maxHeight
    } else if (maxHeight == 0 || maxHeight < maxChildHeight) {
    maxHeight = maxChildHeight
    }
    return if (background == null) minHeight else max(
    minHeight,
    background.minimumHeight
    )
    }

    }

    现在替换 FrameLayoutMaxChildHeightFrameLayoutactivity_main.xml作为:
    <?xml version="1.0" encoding="utf-8"?>

    <androidx.core.widget.NestedScrollView 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/myScrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
    android:id="@+id/textView"
    android:layout_width="0dp"
    android:layout_height="800dp"
    android:background="@color/colorAccent"
    android:text="Some long text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <Button
    android:id="@+id/button_fragment1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginLeft="16dp"
    android:text="show blue"
    app:layout_constraintEnd_toStartOf="@+id/button_fragment2"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
    android:id="@+id/button_fragment2"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="16dp"
    android:layout_marginRight="16dp"
    android:text="show yellow"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/button_fragment1"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

    <com.example.pavneet_singh.temp.MaxChildHeightFrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:minHeight="2dp"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@+id/button_fragment2"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.core.widget.NestedScrollView>
    getSuggestedMinimumHeight()将用于在 View 渲染生命周期中计算 View 的高度。

    输出:



    具有更多 View 、 fragment 和不同高度。 (分别为 400dp、20dp、500dp)

    关于android - 切换 fragment 时跳转滚动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60217689/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com