android-fragments - nestedscrollview 内 RecyclerView 的 onBindViewHolder 立即为所有项目调用

标签 android-fragments android-recyclerview android-viewpager android-collapsingtoolbarlayout android-nestedscrollview

我有一个 CollapsingToolbar 和一个底部导航栏,如图所示:

<androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:fitsSystemWindows="true"
        app:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintTop_toTopOf="parent">

        <!-- Scrollable view here -->

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/height335"
            android:background="@drawable/ic_appbar_bg"
            android:fitsSystemWindows="true"
            app:elevation="0dp">

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/collapsingToolbar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                app:contentScrim="@color/colorPrimaryDark"
                app:toolbarId="@+id/toolbar"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

                <androidx.appcompat.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    android:background="@android:color/transparent"
                    android:contentInsetStart="0dp"
                    android:contentInsetLeft="0dp"
                    android:gravity="center"
                    app:contentInsetEnd="0dp"
                    app:contentInsetEndWithActions="0dp"
                    app:contentInsetLeft="0dp"
                    app:contentInsetRight="0dp"
                    app:contentInsetStart="0dp"
                    app:contentInsetStartWithNavigation="2dp"
                    app:layout_collapseMode="pin"
                    app:theme="@style/ToolbarTheme">

                    <include
                        android:id="@+id/toolbar_header_view"
                        layout="@layout/widget_header_view_top"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        bind:cardDetails="@{viewModel}" />
                </androidx.appcompat.widget.Toolbar>

                <include
                    layout="@layout/widget_header"
                    bind:cardDetails="@{viewModel}" />

            </com.google.android.material.appbar.CollapsingToolbarLayout>
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipe"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <!-- just a nestedscrollview
            override fun onTouchEvent(ev: MotionEvent): Boolean {
                return scrollable && super.onTouchEvent(ev)
            }

            override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
                return scrollable && super.onInterceptTouchEvent(ev)
            }

            fun setScrollingEnabled(enabled: Boolean) {
                scrollable = enabled
            } -->
            <com.android.utils.LockableNestedScrollView
                android:id="@+id/nestedScrollView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"
                android:clipChildren="false"
                android:clipToPadding="false"
                android:fillViewport="true">

               <LinearLayout>
                    <Some views...
                    <androidx.fragment.app.FragmentContainerView
                        android:id="@+id/nav_host_fragment"
                        android:layout_width="match_parent"
                        app:layout_behavior="@string/appbar_scrolling_view_behavior"
                        android:layout_height="match_parent" />

                </LinearLayout>

            </com.android.utils.LockableNestedScrollView>

        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="@{()->viewModel.fabClicked()}"
        android:src="@drawable/ic_plus"
        app:fabCustomSize="@dimen/fab70"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        android:elevation="@dimen/elevation3"
        android:paddingTop="@dimen/padding10"
        app:itemTextAppearanceActive="@style/BottomNavigationViewTextStyle"
        app:itemTextAppearanceInactive="@style/BottomNavigationViewTextStyle"
        app:labelVisibilityMode="labeled"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/nav_view" />

单击导航栏项目时,片段容器将替换为片段。问题是在其中一个片段中我有一个回收 View 。 recyclerview 中所有项目的 onBindView 都会同时被调用。我需要避免这种情况主要是因为我正在尝试像这样实现分页:

override fun onBindViewHolder(holder: TopUpViewHolder, position: Int) {
    val item = myDataset[position]
    holder.bind(item)
    if (position == this.itemCount - 1){
        // do your load more task here
        viewModel.fetchTopUpData()
    }
}

(我尝试附加滚动监听器并实现像 this 这样的分页。但是即使用户尚未到达末尾,canScrollVertically(1) 也会被调用,这可能是因为nestedscrollview本身。)

这是我的 recyclerview 片段:

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

<data>

    <import type="android.view.View" />

    <variable
        name="viewmodel"
        type="com.android.ui.topup.viewmodel.TopUpViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:padding="@dimen/padding15">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:orientation="vertical">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/status_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="@dimen/padding20"
            android:visibility="@{viewmodel.headerVisibility}">

            <TextView
                android:id="@+id/recentLabel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margin20"
                android:text="@string/recent_top_ups"
                android:textAllCaps="true"
                android:textColor="@color/topup_label_text_color"
                android:textSize="@dimen/text12"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/viewAll"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margin20"
                android:onClick="@{()->viewmodel.viewAllClicked()}"
                android:text="@string/view_all"
                android:textColor="@color/topup_label_color_orenge"
                android:textSize="@dimen/text14"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/topUpRCView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:nestedScrollingEnabled="false"
            android:clipChildren="false"
            android:clipToPadding="false"
            app:scrollListener="@{viewmodel.scrollListener}" />

        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="@{viewmodel.fetchingFlag?View.VISIBLE:View.GONE}" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/empty_list"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="@{viewmodel.listEmpty}"
        android:orientation="vertical">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:src="@drawable/ic_illustration_receipt"/>
        <TextView
            android:id="@+id/empty_list_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/you_have_no_topup_requests"
            android:fontFamily="@font/colfaxregular"
            android:textColor="#333547"
            android:layout_marginTop="36dp"
            android:textSize="@dimen/text16"
            android:alpha=".3"/>

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

我尝试过的事情:

  1. 在 recyclerview 中添加了 android:nestedScrollingEnabled="false"
  2. 添加了app:layout_behavior="@string/appbar_scrolling_view_behavior"
  3. 将 recyclerview 的高度更改为 match_parent wrap_content 也为 0 并将布局权重设置为 1(RCV 位于线性布局内)
  4. https://stackoverflow.com/a/44470106/6341943 => 由于某种原因添加标题 View
  5. https://stackoverflow.com/a/37558761/6341943 => 不能这样做,因为我有一个底部导航栏

现在是奇怪的部分 如果我将片段保留在 View 页面中,由于某种原因,问题不会发生。 (即使这个 hack 也不可用,因为它弄乱了我的 collpsingToolbarLayout)

最佳答案

我已经浪费了至少一周的时间,唯一有效的解决方案是删除 nestedscrollview。要使 recyclerview 实际上回收 View ,您必须删除 nestedscrollview。如果您碰巧有多个 view 而不仅仅是 recyclerview,则必须通过设置不同的 viewholder 将它们添加为 recyclerview 的项目,然后添加

app:layout_behavior="@string/appbar_scrolling_view_behavior"

到recyclerview或recyclerview的父级(父级不能有除Recyclerview之外的任何其他 View 才能正常工作)。

就我而言,要求如下:

  <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:fitsSystemWindows="true"
        app:layout_constraintTop_toTopOf="parent">

        <!-- Scrollable view here -->

        <com.google.android.material.appbar.AppBarLayout
            ....   >

            <com.google.android.material.appbar.CollapsingToolbarLayout
                ....>

                <androidx.appcompat.widget.Toolbar
                    ....>

                    <include
                        android:id="@+id/toolbar_header_view"
                        .... />
                </androidx.appcompat.widget.Toolbar>

                <include layout="@layout/widget_header" />
            </com.google.android.material.appbar.CollapsingToolbarLayout>
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            ....>
            <LinearLayout orientation="vertical" ....>
                <View .../>  <!-- I needed to have these two views along with recyclerview and i wanted them to do nestedscroll along with recyclerview -->
                <View .../>
                <androidx.recyclerview.widget.RecyclerView
                .... />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

recyclerview之前我需要有两个 View ,并且我希望它们与recyclerview一起进行nestedscroll。唯一有效的方法是将它们删除到 View 中并将它们添加为 recyclerview 的前两项,然后添加

app:layout_behavior="@string/appbar_scrolling_view_behavior"

swipetorefresh布局

希望这对某人有帮助。如果需要,我会添加更多信息。

关于android-fragments - nestedscrollview 内 RecyclerView 的 onBindViewHolder 立即为所有项目调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59169890/

相关文章:

android - 在Android上强制溢出菜单按钮

android - 如何在单个 Activity 中显示第一个 fragment 的第二个 fragment

java - 在同一 Activity 的 2 个 fragment 之间发送 ArrayList

android - FrameLayout.onMeasure() 中的 NullPointerException

java - 修改适配器后 Butterknife 未加载布局

java - 在 RecyclerView.Adapter 内部滚动到位置

android - NullPointerException - 尝试在空对象引用上调用虚拟方法 RecyclerView$ViewHolder.shouldIgnore()'

android - 根据 RecyclerView 每行中的数据类型膨胀行 View

Android:避免多次打开对话框,但允许隐藏和显示相同的对话框

android - Fragment 中的 ViewPager 在返回时丢失内容