简体   繁体   English

Nestedscrollview 内的 Viewpager2 包含两个 Fragment,Recyclerview 有问题

[英]Viewpager2 inside Nestedscrollview consist two Fragment with Recyclerview having problem

I made a viewpager2 which has two Fragments , inside each Fragment there is a Recyclerview .我制作了一个viewpager2 ,它有两个Fragments ,每个Fragment里面都有一个Recyclerview The viewpager itself is inside a Nestedscrollview in order to hide the toolbar when scroll up. viewpager 本身位于Nestedscrollview内,以便在向上滚动时隐藏工具栏。 Here is my code:这是我的代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>

<com.google.android.material.appbar.AppBarLayout
    android:id="@+id/appbarlayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        app:layout_scrollFlags="scroll|enterAlways">

    </androidx.appcompat.widget.Toolbar>

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAlignment="center"
        app:tabGravity="fill"
        app:tabMode="fixed"/>
</com.google.android.material.appbar.AppBarLayout>

<androidx.core.widget.NestedScrollView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        
        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"/>

    </LinearLayout>

</androidx.core.widget.NestedScrollView>

<include layout="@layout/material_design_floating_action_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

As I said the viewPager2 have two fragment each of them have a recyclerview.正如我所说,viewPager2 有两个片段,每个片段都有一个 recyclerview。 Here problem is, fragment 2 recyclerView take the same height of fragment 1 recyclerView though both recyclerView have different list items and their height should be depends on the list items.这里的问题是,片段 2 recyclerView 与片段 1 recyclerView 具有相同的高度,尽管两个 recyclerView 具有不同的列表项,并且它们的高度应该取决于列表项。 I mean, I am expecting these recyclerViews height should act separately based on the list.我的意思是,我希望这些 recyclerViews 高度应该根据列表单独执行。 How can I solve this issue?我该如何解决这个问题? Please let me know if you need fragment code.如果您需要片段代码,请告诉我。

Edit: Activity code which holds the viewPager2编辑:包含 viewPager2 的活动代码

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initToolbar();

    init();


    viewPager.setAdapter(createCardAdapter());
    new TabLayoutMediator(tabLayout, viewPager,
            new TabLayoutMediator.TabConfigurationStrategy() {
                @Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                    //tab.setText("Tab " + (position + 1));
                    if(position == 0){

                        tab.setText("Home");
                    }else if(position == 1){
                        tab.setText("Events");
                    }
                }
            }).attach();

    RunnTimePermissions.requestForAllRuntimePermissions(this);

    showNotifyDialog();
}

ViewPager Adapter Code: ViewPager 适配器代码:

public class ViewPagerAdapter extends FragmentStateAdapter {
private static final int CARD_ITEM_SIZE = 2;
public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
    super(fragmentActivity);
}
@NonNull @Override public Fragment createFragment(int position) {

    switch (position){

        case 0:
            return HomeFragment.newInstance("abc","abc");
           // break;
        case 1:
            return EventListFragment.newInstance("abc","abc");
           // break;
    }
    return HomeFragment.newInstance("abc","abc");
}
@Override public int getItemCount() {
    return CARD_ITEM_SIZE;
}
} 

Fragment 1 layout:片段 1 布局:

<?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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragment.EventListFragment">


<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_event_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:layout_marginTop="16dp"
    />

 </FrameLayout>

Fragment 2 layout片段 2 布局

<?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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragment.MedicineListFragment">

     
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_medicine_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:layout_marginTop="16dp"
    />

 </FrameLayout>

Alright buddy.好的伙计。 I did it.我做的。 I have the solution to this issue.我有这个问题的解决方案。

First off, here's the official response from Google after I opened an issue.首先,这是我打开一个问题后谷歌的官方回应。 https://issuetracker.google.com/issues/188474850?pli=1 https://issuetracker.google.com/issues/188474850?pli=1

Anyway, on to the fix.无论如何,继续修复。 Replace the NestedScrollView with this class:用这个 class 替换NestedScrollView

https://gist.github.com/AfzalivE/fdce03eeee8e16203bcc37ba26d7abf3 https://gist.github.com/AfzalivE/fdce03eeee8e16203bcc37ba26d7abf3

The idea is to basically create a very light-weight version of NestedScrollView .这个想法基本上是创建一个非常轻量级的NestedScrollView版本。 Instead of messing with child heights, we listen to the children's scroll and either let them scroll, or forward the scrolling to the BottomSheetBehavior .我们不会弄乱孩子的身高,而是听孩子的滚动,然后让他们滚动,或者将滚动转发到BottomSheetBehavior For example when the RecyclerView has been scrolled all the way to the top, then it doesn't consume any of the scrolling, so we can forward that to the bottom sheet so it can scroll.例如,当RecyclerView一直滚动到顶部时,它不会消耗任何滚动,因此我们可以将其转发到底部工作表以便它可以滚动。 And we only allow the RecyclerView to scroll when the Bottom sheet is expanded.我们只允许RecyclerView在底部工作表展开时滚动。

Also, I must add that this scenario works out of the box with Jetpack Compose + Accompanist-pager so just do that if you really need perfect functionality.另外,我必须补充一点,这个场景可以使用 Jetpack Compose + Accompanist-pager 开箱即用,所以如果你真的需要完美的功能,那就这样做吧。

class BottomSheetScrollView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs),
    NestedScrollingParent2 {

    private val TAG = "NestedScroll3"
    private val childHelper = NestedScrollingChildHelper(this).apply {
        isNestedScrollingEnabled = true
    }
    private var behavior: BottomSheetBehavior<*>? = null
    var started = false
    var canScroll = false
    var pendingCanScroll = false
    var dyPreScroll = 0

    init {
        ViewCompat.setNestedScrollingEnabled(this, true)
    }

    private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
        override fun onStateChanged(bottomSheet: View, newState: Int) {
            onNextScrollStop(newState == BottomSheetBehavior.STATE_EXPANDED)
            Log.d(
                "BottomSheet",
                "Can scroll CHANGED to: $canScroll, because bottom sheet state is ${
                    getBottomSheetStateString(newState)
                }"
            )
        }

        override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
    }


    fun onNextScrollStop(canScroll: Boolean) {
        pendingCanScroll = canScroll
    }

    override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
        // ViewPager2's RecyclerView does not participate in this nested scrolling.
        // This allows it to show it's overscroll indicator.
        if (target is RecyclerView) {
            val layoutManager = target.layoutManager as LinearLayoutManager
            if (layoutManager.orientation == LinearLayoutManager.HORIZONTAL) {
                target.isNestedScrollingEnabled = false
            }
        }

        if (!started) {
            Log.d(TAG, "started nested scroll from $target")
            childHelper.startNestedScroll(axes, type)
            started = true
        }

        return true
    }

    override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {
        Log.d(TAG, "accepted nested scroll from $target")
    }

    override fun onStopNestedScroll(target: View, type: Int) {
        if (started) {
            childHelper.stopNestedScroll(type)
            started = false
            Log.d(
                TAG,
                "stopped nested scroll from $target, changing canScroll from $canScroll to $pendingCanScroll"
            )
            canScroll = pendingCanScroll
        }
    }

    override fun onNestedScroll(
        target: View,
        dxConsumed: Int,
        dyConsumed: Int,
        dxUnconsumed: Int,
        dyUnconsumed: Int,
        type: Int
    ) {
        Log.d(
            TAG,
            "onNestedScroll: dxC: $dxConsumed, dyC: $dyConsumed, dxU: $dxUnconsumed, dyU: $dyUnconsumed"
        )
        if (dyUnconsumed == dyPreScroll && dyPreScroll < 0) {
            canScroll = false
            Log.d(TAG, "Can scroll CHANGED to: $canScroll, because scrolled to the top of the list")
        }
    }

    override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
        Log.d(
            TAG,
            "onNestedPreScroll: dx: $dx, dy: $dy, consumed: [ ${consumed.joinToString(", ")} ]"
        )
        if (!canScroll) {
            childHelper.dispatchNestedPreScroll(dx, dy, consumed, null, type)
            // Ensure all dy is consumed to prevent premature scrolling when not allowed.
            consumed[1] = dy
        } else {
            dyPreScroll = dy
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        behavior = findBottomSheetBehaviorParent(parent) as BottomSheetBehavior<*>?
        behavior?.addBottomSheetCallback(bottomSheetCallback)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        behavior?.removeBottomSheetCallback(bottomSheetCallback)
    }

    private fun findBottomSheetBehaviorParent(parent: ViewParent?): CoordinatorLayout.Behavior<*>? {
        if (parent !is View) {
            throw IllegalArgumentException(
                "None of this view's ancestors are associated with BottomSheetBehavior"
            )
        }

        val layoutParams = parent.layoutParams
        return if (layoutParams is CoordinatorLayout.LayoutParams && layoutParams.behavior != null) {
            layoutParams.behavior
        } else {
            findBottomSheetBehaviorParent((parent as View).parent)
        }
    }

    private fun getBottomSheetStateString(state: Int): String {
        return when (state) {
            1 -> "STATE_DRAGGING"
            2 -> "STATE_SETTLING"
            3 -> "STATE_EXPANDED"
            4 -> "STATE_COLLAPSED"
            5 -> "STATE_HIDDEN"
            6 -> "STATE_HALF_EXPANDED"
            else -> "0"
        }
    }
}

I do not understand why you have recyclerview inside FrameLayout.我不明白你为什么在 FrameLayout 中有 recyclerview。

Here problem is, fragment 2 recyclerView take the same height of fragment 1 recyclerView though both recyclerView have different list items and their height should be depends on the list items这里的问题是,片段 2 recyclerView 与片段 1 recyclerView 具有相同的高度,尽管两个 recyclerView 具有不同的列表项,并且它们的高度应该取决于列表项

RecyclerView generally always depends on the item_layout until we have not fixed its height n width to match_parent . RecyclerView通常总是依赖于item_layout直到我们没有将它的height n width固定为match_parent

In your case you have fixed RecyclerView android:layout_width and android:layout_width to match_parent in both the Fragment.在您的情况下,您已将 RecyclerView android:layout_widthandroid:layout_width为两个片段中的match_parent

Try this:尝试这个:

Fragment 1片段 1

<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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragment.EventListFragment">


<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_event_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    />

 </FrameLayout>

Fragment 2片段 2

<?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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragment.MedicineListFragment">

     
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_medicine_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    />

 </FrameLayout>

PS: assuming each reacyclerView's item_layout would be having height as wrap_content PS:假设每个 recyclerView 的item_layout的高度为wrap_content

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM