Nhảy cuộn khi chuyển đoạn


8

Bên trong một ScrollView tôi đang chuyển đổi linh hoạt giữa hai mảnh với độ cao khác nhau. Thật không may dẫn đến nhảy. Người ta có thể thấy nó trong hình ảnh động sau:

  1. Tôi đang cuộn xuống cho đến khi chạm vào nút "hiển thị màu vàng".
  2. Nhấn "hiển thị màu vàng" thay thế một mảnh màu xanh khổng lồ bằng một mảnh màu vàng nhỏ. Khi điều này xảy ra, cả hai nút nhảy xuống cuối màn hình.

Tôi muốn cả hai nút ở cùng một vị trí khi chuyển sang đoạn màu vàng. Làm thế nào có thể được thực hiện?

cuộn

Mã nguồn có sẵn tại https://github.com/wondering639/stack-dynamiccontent tương ứng https://github.com/wondering639/stack-dynamiccontent.git

Đoạn mã có liên quan:

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>

ChínhActivity.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" />

DẤU

Xin lưu ý rằng đây tất nhiên là một ví dụ hoạt động tối thiểu để thể hiện vấn đề của tôi. Trong dự án thực sự của tôi, tôi cũng có quan điểm bên dưới @+id/fragment_container. Vì vậy, đưa ra một kích thước cố định @+id/fragment_containerkhông phải là một lựa chọn cho tôi - nó sẽ gây ra một vùng trống lớn khi chuyển sang đoạn thấp, màu vàng.

CẬP NHẬT: Tổng quan về các giải pháp được đề xuất

Tôi đã thực hiện các giải pháp đề xuất cho mục đích thử nghiệm và thêm kinh nghiệm cá nhân của tôi với chúng.

câu trả lời của Cheticamp, https://stackoverflow.com/a/60323255

-> có sẵn trong https://github.com/wondering639/stack-dynamiccontent/tree/60323255

-> FrameLayout kết thúc nội dung, mã ngắn

trả lời bởi Pavneet_Singh, https://stackoverflow.com/a/60 310807

-> có sẵn trong https://github.com/wondering639/stack-dynamiccontent/tree/60310807

-> FrameLayout có kích thước của mảnh màu xanh. Vì vậy, không có nội dung gói. Khi chuyển sang đoạn màu vàng, có một khoảng cách giữa nó và nội dung theo sau nó (nếu có bất kỳ nội dung nào theo sau nó). Không có kết xuất bổ sung mặc dù! ** cập nhật ** một phiên bản thứ hai đã được cung cấp cho thấy cách thực hiện mà không có khoảng trống. Kiểm tra các ý kiến ​​để trả lời.

câu trả lời của Ben P., https://stackoverflow.com/a/60251036

-> có sẵn trong https://github.com/wondering639/stack-dynamiccontent/tree/60251036

-> FrameLayout kết thúc tốt đẹp nội dung. Mã nhiều hơn giải pháp của Cheticamp. Chạm vào nút "hiển thị màu vàng" hai lần dẫn đến "lỗi" (các nút nhảy xuống phía dưới, thực sự là vấn đề ban đầu của tôi). Người ta có thể tranh luận về việc chỉ vô hiệu hóa nút "hiển thị màu vàng" sau khi chuyển sang nút này, vì vậy tôi sẽ không coi đây là vấn đề thực sự.


Khi tôi cố gắng tái tạo điều này, "nhảy" chỉ xảy ra nếu chế độ xem được cuộn lên đủ để khoảng trống bên dưới các nút lớn hơn tổng kích thước của mảnh màu vàng. Không có gì bên dưới điều đó, vì vậy không có cách nào để giữ các nút ở vị trí. Bạn có chắc chắn rằng đây thực sự là một lỗi?
Ben P.

Sau khi suy nghĩ thêm về nhận xét của bạn, tôi nghĩ rằng bạn đúng rằng hành vi hiện tại là đúng về mặt kỹ thuật. Scrollview cố gắng lấp đầy màn hình hoàn chỉnh và sau khi chuyển sang đoạn màu vàng, không có đủ nội dung bên dưới nó, vì vậy nó cuộn để có thêm một số nội dung từ phía trên. Vì vậy, tôi có thể tưởng tượng hai giải pháp: 1) nói với scrollview không buộc phải lấp đầy màn hình 2) khi chuyển sang đoạn màu vàng, thêm chênh lệch chiều cao giữa đoạn màu xanh và màu vàng làm lớp đệm dưới cùng cho lớp ngoài cùng hoặc vào cuộn xem, nếu nó hỗ trợ trực tiếp
stefan.at.wpf

@BenP. Bạn có biết làm thế nào để làm điều này tốt nhất? ví dụ cho 1) nếu có thể hoàn toàn và cho 2) làm thế nào để làm điều đó theo cách tránh kết xuất không cần thiết càng nhiều càng tốt.
stefan.at.wpf

Câu trả lời:


1

Cập nhật : Để giữ các chế độ xem khác ngay bên dưới framelayoutvà để tự động xử lý tình huống, chúng tôi cần sử dụng onMeasuređể thực hiện xử lý tự động, hãy thực hiện các bước sau

• Tạo một tùy chỉnh ConstraintLayoutdưới dạng (hoặc có thể sử dụng 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)
    }

}

và sử dụng nó trong bố trí của bạn thay cho 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>

Điều này sẽ theo dõi chiều cao và áp dụng nó trong mỗi thay đổi mảnh.

Đầu ra:

Lưu ý: Như đã đề cập trong các nhận xét trước đây, việc đặt minHeight sẽ dẫn đến việc vượt qua kết xuất bổ sung và không thể tránh được trong phiên bản hiện tại của ConstraintLayout.


Cách tiếp cận cũ với FrameLayout tùy chỉnh

Đây là một yêu cầu thú vị và cách tiếp cận của tôi là giải quyết nó bằng cách tạo chế độ xem tùy chỉnh.

Ý tưởng :

Ý tưởng của tôi cho giải pháp là điều chỉnh chiều cao của container bằng cách theo dõi con lớn nhất hoặc tổng chiều cao của trẻ trong container.

Nỗ lực :

Một vài nỗ lực đầu tiên của tôi dựa trên việc sửa đổi hành vi hiện có NestedScrollViewbằng cách mở rộng nó nhưng nó không cung cấp quyền truy cập vào tất cả các dữ liệu hoặc phương pháp cần thiết. Tùy chỉnh dẫn đến hỗ trợ kém cho tất cả các kịch bản và trường hợp cạnh.

Sau đó, tôi đã đạt được giải pháp bằng cách tạo một tùy chỉnh Framelayoutvới cách tiếp cận khác nhau.

Giải pháp thực hiện

Trong khi thực hiện hành vi tùy chỉnh của các giai đoạn đo chiều cao, tôi đã đào sâu hơn và điều khiển chiều cao getSuggestedMinimumHeighttrong khi theo dõi chiều cao của trẻ em để thực hiện giải pháp tối ưu nhất vì nó sẽ không gây ra bất kỳ kết xuất bổ sung hoặc rõ ràng nào vì nó sẽ quản lý chiều cao trong quá trình kết xuất bên trong vì vậy hãy tạo một FrameLayoutlớp tùy chỉnh để thực hiện giải pháp và ghi đè lên getSuggestedMinimumHeightnhư:

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

}

Bây giờ thay thế FrameLayoutbằng MaxChildHeightFrameLayouttrong activity_main.xmlnhư:

<?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() sẽ được sử dụng để tính chiều cao của chế độ xem trong vòng đời hiển thị chế độ xem.

Đầu ra:

Với nhiều góc nhìn, mảnh và chiều cao khác nhau. (Lần lượt 400dp, 20dp, 500dp)


trước hết, cảm ơn bạn rất nhiều! Do sự phức tạp của chủ đề này, tôi sẽ kiểm tra câu trả lời của bạn và những câu hỏi khác vào thứ bảy. Bạn có một liên kết hoặc bạn có thể thêm thông tin về vòng đời kết xuất? Tôi chưa biết nhiều về tất cả các phương thức như onMeasure, v.v. và làm thế nào getSuggestedMinimumHeight()có liên quan đến chúng hoặc theo thứ tự chúng được thực hiện.
stefan.at.wpf

@ stefan.at.wpf onMeasurelà bước đầu tiên để đo lường quá trình kết xuất và getSuggestedMinimumHeight(và chỉ là một getter vì vậy sẽ không gây ra kết xuất bổ sung) đang được sử dụng bên trong onMeasuređể xác định chiều cao. setMinHeightsẽ gọi requestLayout () trong khi sẽ dẫn đến việc đo lường, đặt ra và vẽ cây xem bố cục
Pavneet_Singh

@ stefan.at.wpf đã thêm thông tin và trường hợp sử dụng tẻ nhạt hoặc không thể xử lý thủ công, đặc biệt khi số lượng phân đoạn, lượt xem không xác định hoặc tăng trưởng động. giải pháp này là một giải pháp động cho tất cả các kịch bản và không yêu cầu xử lý thủ công.
Pavneet_Singh

chỉ cần kiểm tra và kiểm tra câu trả lời của bạn. Thật ra tôi muốn FrameLayout bao bọc nội dung. Mặt khác, có một khoảng cách khi nội dung theo sau FrameLayout. Bạn có thêm một biến thể thứ hai cho đầy đủ không, trong đó FrameLayout kết thúc nội dung? Giống như mọi nỗ lực, vì tôi đã tìm hiểu về chu trình kết xuất :-) Kiểm tra cập nhật cho bài viết gốc của tôi, tôi đã triển khai phiên bản của bạn bao gồm nội dung theo FrameLayout. Có lẽ có thể được sử dụng làm cơ sở cho việc đóng gói FrameLayout :-)
stefan.at.wpf

@ stefan.at.wpf yeah, sau đó tôi có thể tạo bố cục ràng buộc tùy chỉnh (hoặc bất kỳ bố cục vùng chứa nào) để tránh cài đặt thủ công với chiều cao tối thiểu hoặc cách tiếp cận khác là thêm chế độ xem bổ sung (như bạn đã nói, Có thể được sử dụng làm cơ sở cho gói FrameLayout) vì vậy đây là bản demo với đầu ra mặc dù có thể thử thêm đoạn lớn thứ 3, sẽ hoạt động với phương pháp này nhưng không phải với thiết lập chiều cao thủ công.
Pavneet_Singh

1

Một giải pháp đơn giản là điều chỉnh chiều cao tối thiểu của ConstraintLayout trong NestedScrollView trước khi chuyển các đoạn. Để tránh nhảy, chiều cao của ConstraintLayout phải lớn hơn hoặc bằng

  1. số lượng mà NestedScrollView đã cuộn theo hướng "y"

thêm

  1. chiều cao của NestedScrollView .

Đoạn mã sau đây gói gọn khái niệm này:

private fun adjustMinHeight(nsv: NestedScrollView, layout: ConstraintLayout) {
    layout.minHeight = nsv.scrollY + nsv.height
}

Xin lưu ý rằng layout.minimumHeightsẽ không hoạt động cho ConstraintLayout . Bạn phải sử dụng layout.minHeight.

Để gọi hàm này, hãy làm như sau:

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

    val nsv = findViewById<NestedScrollView>(R.id.myScrollView)
    val layout = findViewById<ConstraintLayout>(R.id.constraintLayout)
    adjustMinHeight(nsv, layout)
}

Nó tương tự với insertBlueFragment () . Tất nhiên, bạn có thể đơn giản hóa điều này bằng cách thực hiện findViewById () một lần.

Đây là một video nhanh chóng về kết quả.

nhập mô tả hình ảnh ở đây

Trong video, tôi đã thêm chế độ xem văn bản ở phía dưới để thể hiện các mục bổ sung có thể tồn tại trong bố cục của bạn bên dưới đoạn. Nếu bạn xóa chế độ xem văn bản đó, mã sẽ vẫn hoạt động, nhưng bạn sẽ thấy khoảng trống ở phía dưới. Đây là những gì trông giống như:

nhập mô tả hình ảnh ở đây

Và nếu các chế độ xem bên dưới đoạn không lấp đầy chế độ xem cuộn, bạn sẽ thấy các chế độ xem bổ sung cộng với khoảng trắng ở phía dưới.

nhập mô tả hình ảnh ở đây


tốt, giải pháp đơn giản này là triển khai thủ công (tẻ nhạt) giải pháp của tôi để điều chỉnh minHeight, áp dụng trên các container khác nhau :). Dù sao, tôi cũng đã nghĩ đến cách tiếp cận này nhưng nó sẽ không hoạt động trong tất cả các kịch bản, giống như nếu đoạn đầu tiên nhỏ hơn đoạn thứ hai thì logic này sẽ thất bại vì bạn sẽ không có chiều cao mong muốn. ví dụ khác, nếu có nhiều hơn hai mảnh (khá phổ biến) và mảnh thứ ba lớn hơn mảnh trước.
Pavneet_Singh

@Pavneet_Singh Tôi chưa kiểm tra mã của bạn, vì vậy tôi không thể nhận xét về sự tương đồng. Mặc dù, từ nhận xét của bạn, tôi phỏng đoán rằng hai người không giống nhau. Tôi tin rằng những gì tôi trình bày là một cái chung. Các nút nhảy vì con của NestedScrollView (ConstraintLayout) không đủ chiều cao để che phần màn hình cuộn được cuộn ngoài màn hình cộng với những gì trên màn hình. Miễn là yêu cầu về chiều cao đó được đáp ứng (thông qua tính toán chiều cao tối thiểu), thực sự không có vấn đề gì khác xảy ra bên dưới các nút. Bạn có thể đang xem bài viết đầu tiên của tôi trước khi chỉnh sửa.
Cheticamp

Đó không phải là mã, chỉ là ý tưởng cốt lõi cho giải pháp mặc dù không có vấn đề gì. Tôi hiểu khái niệm này khá rõ và như tôi đã nói trước đây và bây giờ, yêu cầu về chiều cao của bạn sẽ không phù hợp nếu mảnh đầu tiên nhỏ nên sẽ không hoạt động. Thêm vào đó, việc thiết lập và duy trì chiều cao rõ ràng là tẻ nhạt và cũng sẽ gây ra kết xuất bổ sung.
Pavneet_Singh

@Pavneet_Singh Bây giờ tôi đang tự hỏi nếu bạn đang bình luận về câu trả lời mà bạn nghĩ là bạn. Tôi không đặt bất kỳ chiều cao nào rõ ràng và đoạn đầu tiên nhỏ không phải là vấn đề AFAIK.
Cheticamp

Cheticamp, chỉ muốn nói cảm ơn rất nhiều và cho bạn biết tôi sẽ xem xét kỹ hơn về điều này và các câu trả lời khác vào thứ bảy. Bây giờ tôi không tham gia nhiều vào vòng đời kết xuất, nhưng vì FragmentManager commitđược gọi trước đó adjustMinHeight, tôi cho rằng có một chu kỳ kết xuất bổ sung nào liên quan?
stefan.at.wpf

0

FrameLayoutBên trong của bạn activity_main.xmlcó một thuộc tính chiều cao của wrap_content.

Bố trí mảnh vỡ của con bạn đang xác định sự khác biệt chiều cao bạn nhìn thấy.

Nên đăng xml của bạn cho các mảnh con

Hãy thử đặt một chiều cao cụ thể đối với FrameLayouttrong của bạnactivity_main.xml


Cảm ơn bạn đã trả lời, giải thích của bạn có ý nghĩa. Tôi đã cập nhật bài viết của mình và thêm XML cho cả hai đoạn. Trong ví dụ này, cả hai đều có chiều cao cố định (nhưng tất nhiên là khác nhau). Trong ứng dụng "thực" của tôi, chiều cao của họ dĩ nhiên là quấn_content. Tôi không thể đặt chiều cao của FrameLayout thành một chiều cao cố định, vì trong ứng dụng thực sự của tôi, tôi có một số nội dung sau FrameLayout. Tôi đã không đưa nó vào ví dụ, xin lỗi \: Có ai biết làm thế nào tôi có thể xử lý trường hợp của mình không?
stefan.at.wpf

0

Tôi đã giải quyết điều này bằng cách tạo một trình lắng nghe bố cục theo dõi chiều cao "trước đó" và thêm phần đệm vào ScrollView nếu chiều cao mới nhỏ hơn trước.

  • Chiều caoLayoutListener.kt
class HeightLayoutListener(
        private val activity: MainActivity,
        private val root: View,
        private val previousHeight: Int,
        private val targetScroll: Int
) : ViewTreeObserver.OnGlobalLayoutListener {

    override fun onGlobalLayout() {
        root.viewTreeObserver.removeOnGlobalLayoutListener(this)

        val padding = max((previousHeight - root.height), 0)
        activity.setPaddingBottom(padding)
        activity.setScrollPosition(targetScroll)
    }

    companion object {

        fun create(fragment: Fragment): HeightLayoutListener {
            val activity = fragment.activity as MainActivity
            val root = fragment.view!!
            val previousHeight = fragment.requireArguments().getInt("height")
            val targetScroll = fragment.requireArguments().getInt("scroll")

            return HeightLayoutListener(activity, root, previousHeight, targetScroll)
        }
    }
}

Để kích hoạt trình nghe này, hãy thêm phương thức này vào cả hai đoạn của bạn:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val listener = HeightLayoutListener.create(this)
    view.viewTreeObserver.addOnGlobalLayoutListener(listener)
}

Đây là những phương thức mà người nghe gọi để thực sự cập nhật ScrollView. Thêm chúng vào hoạt động của bạn:

fun setPaddingBottom(padding: Int) {
    val wrapper = findViewById<View>(R.id.wrapper) // add this ID to your ConstraintLayout
    wrapper.setPadding(0, 0, 0, padding)

    val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(wrapper.width, View.MeasureSpec.EXACTLY)
    val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    wrapper.measure(widthMeasureSpec, heightMeasureSpec)
    wrapper.layout(0, 0, wrapper.measuredWidth, wrapper.measuredHeight)
}

fun setScrollPosition(scrollY: Int) {
    val scroll = findViewById<NestedScrollView>(R.id.myScrollView)
    scroll.scrollY = scrollY
}

Và bạn cần đặt đối số cho các đoạn của mình để người nghe biết chiều cao trước đó và vị trí cuộn trước đó là gì. Vì vậy, hãy đảm bảo thêm chúng vào các giao dịch phân đoạn của bạn:

private fun insertYellowFragment() {
    val fragment = YellowFragment().apply {
        this.arguments = createArgs()
    }

    val transaction = supportFragmentManager.beginTransaction()
    transaction.replace(R.id.fragment_container, fragment)
    transaction.commit()
}


private fun insertBlueFragment() {
    val fragment = BlueFragment().apply {
        this.arguments = createArgs()
    }

    val transaction = supportFragmentManager.beginTransaction()
    transaction.replace(R.id.fragment_container, fragment)
    transaction.commit()
}

private fun createArgs(): Bundle {
    val scroll = findViewById<NestedScrollView>(R.id.myScrollView)
    val container = findViewById<View>(R.id.fragment_container)

    return Bundle().apply {
        putInt("scroll", scroll.scrollY)
        putInt("height", container.height)
    }
}

Và rằng nên làm điều đó!


cảm ơn bạn đã trả lời rộng rãi! cho tôi một chút thời gian để thử nghiệm nó và chờ đợi những câu trả lời khác mà không nhấp nháy. Nếu không, tất nhiên tôi sẽ chấp nhận nó như một cơ sở khởi đầu tốt và thưởng tiền thưởng :-)
stefan.at.wpf

@ stefan.at.wpf Tôi đã cập nhật câu trả lời của mình bằng cách giải quyết vấn đề nhấp nháy
Ben P.

Một lần nữa, cảm ơn bạn rất nhiều! Tôi sẽ có một cái nhìn cận cảnh hơn vào thứ bảy :-)
stefan.at.wpf

Tôi đã kiểm tra phiên bản cập nhật của bạn và nó hoạt động tốt. Khi một lần nữa chạm vào màu vàng, thì nó sẽ nhảy như trong phần mô tả về vấn đề ban đầu của tôi, nhưng tôi sẽ không coi đây là một vấn đề thực tế. Phiên bản của bạn có gây ra chu kỳ kết xuất bổ sung không? Tôi có một thời gian khó khăn để chọn MỘT câu trả lời từ tất cả các câu trả lời tuyệt vời ở đây \:
stefan.at.wpf

À, vâng, phần đệm chỉ cập nhật ít nhất bằng phần trước, vì vậy nếu đoạn "trước" có màu vàng và bạn thêm màu vàng trở lại, phần đệm sẽ trở thành 0. Bạn có thể giải quyết điều đó bằng cách giữ tối đa "duy nhất" "Giá trị thay vì chỉ giá trị trước đó. Và này, hãy nâng cấp bất cứ điều gì bạn thấy hữu ích và trao giải cho bất cứ ai bạn muốn!
Ben P.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.