简体   繁体   English

如何将 RecyclerView 放在 NestedScrollView 中?

[英]How to put RecyclerView inside NestedScrollView?

With creation of NestedScrollView you can put scrolling view inside another scrolling view as long as those implement NestedScrollingChild and NestedScrollingParent correctly.通过创建NestedScrollView,您可以将滚动视图放在另一个滚动视图中,只要它们正确实现了NestedScrollingChildNestedScrollingParent

(This is not bad design pattern "Ian Lake (from Google) actually recommends putting a RecyclerView inside a nestedscrollview here: plus.google.com/u/0/+AndroidDevelopers/posts/9kZ3SsXdT2T") (这是不错的设计模式“Ian Lake(来自谷歌)实际上建议将RecyclerView放在nestedscrollview 中:plus.google.com/u/0/+AndroidDevelopers/posts/9kZ3SsXdT2T”)

I want to put RecyclerView inside NestedScrollView and fortunately RecyclerView implements NestedScrollingChild so you can put it inside NestedScrollView .我想把RecyclerView放在NestedScrollView里面,幸运的是 RecyclerView 实现了NestedScrollingChild这样你就可以把它放在NestedScrollView里面。

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild

I have read these posts:我已经阅读了这些帖子:

How to use RecyclerView inside NestedScrollView? 如何在 NestedScrollView 中使用 RecyclerView?

NestedScrolling with NestedScrollView, RecyclerView (Horizontal), inside a CoordinatorLayout NestedScrolling 与 NestedScrollView、RecyclerView(水平),在 CoordinatorLayout 内

But the problem with most voted solution is, it calls all of the items of RecyclerView so for example if it is an endless RecyclerView and when the user reaches the end of the list you want to make a network request then with that solution the RecyclerView calls server repeatedly because it automatically reaches the last item of RecyclerView .但是大多数投票解决方案的问题是,它调用RecyclerView所有项目,例如,如果它是一个无尽的 RecyclerView,并且当用户到达列表的末尾时,您想要发出网络请求,然后使用该解决方案RecyclerView调用服务器重复,因为它会自动到达RecyclerView的最后一项。

Anyway, how to set parameter so I can put RecyclerView inside NestedScrollView .(actually I want to put a viewgroup like framelayout or relativelayout as a single childe of nestedscrollview and then I want to put recyclerview inside framelayout or relativelayout)无论如何,如何设置参数以便我可以将RecyclerView放在NestedScrollView 。(实际上我想将像 framelayout 或 relativelayout 这样的视图组作为nestedscrollview 的单个子对象,然后我想将 recyclerview 放在 framelayout 或 relativelayout 中)

When I put RecyclerView inside NestedScrollView there is nothing to display.当我将RecyclerView放在NestedScrollView ,没有什么可显示的。


In order to create a sample project you can use cheesesquare and change the CheeseDetailActivity to have a RecyclerView.为了创建您可以使用示例项目cheesesquare和改变CheeseDetailActivity有RecyclerView。


Although the answer of BNK is not correct but BNK has tried a lot.虽然BNK的答案不正确,但BNK已经尝试了很多。 So I award him the bounty.所以我给他赏金。 Still looking for nice solution....仍在寻找好的解决方案......

The following is my new updated answer:以下是我新更新的答案:

<android.support.v4.widget.NestedScrollView
        android:id="@+id/scrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <android.support.v7.widget.CardView
                android:id="@+id/cardview1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Info CardView1"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/cheese_ipsum" />

                </LinearLayout>

            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                android:id="@+id/cardview2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/cardview1"
                android:layout_margin="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Info CardView2"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/cheese_ipsum" />

                </LinearLayout>

            </android.support.v7.widget.CardView>

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/cardview2"
                android:clipToPadding="false"
                android:paddingTop="0dp"/>

        </RelativeLayout>

    </android.support.v4.widget.NestedScrollView>

In Activity:在活动中:

        RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(true); // true: with header
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);            
        final MyLinearLayoutManager layoutManager = new MyLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false, getScreenHeight(this));
        // final CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(recyclerViewAdapter);  
        // recyclerView.setNestedScrollingEnabled(false); // Disables scrolling for RecyclerView, however, CustomLinearLayoutManager used instead of MyLinearLayoutManager

I have also updated to My GitHub's sample project我还更新了我的 GitHub 示例项目

Screenshot:截屏:

在此处输入图片说明


Here is solution to call server only when you are realy need to load more data.这是仅在您确实需要加载更多数据时才调用服务器的解决方案。 In this way you can put your endless RecyclerView and many other views inside NestedScrollView.通过这种方式,您可以将无尽的 RecyclerView 和许多其他视图放在 NestedScrollView 中。 For me it is working well.对我来说,它运行良好。

1. Create EndlessParentScrollListener class to handle scroll events from NestedSrollView. 1.创建EndlessParentScrollListener类来处理来自 NestedSrollView 的滚动事件。

public abstract class EndlessParentScrollListener implements NestedScrollView.OnScrollChangeListener {
        // The current offset index of data you have loaded
        private int currentPage = 0;
        // The total number of items in the dataset after the last load
        private int previousTotalItemCount = 0;
        // True if we are still waiting for the last set of data to load.
        private boolean loading = true;
        // Sets the starting page index
        private int startingPageIndex = 0;
        // The minimum amount of pixels to have below your current scroll position
        // before loading more.
        private int visibleThresholdDistance = 300;

        RecyclerView.LayoutManager mLayoutManager;

        public EndlessParentScrollListener(RecyclerView.LayoutManager layoutManager) {
            this.mLayoutManager = layoutManager;
        }

        @Override
        public void onScrollChange(NestedScrollView scrollView, int x, int y, int oldx, int oldy) {
            // We take the last son in the scrollview
            View view = scrollView.getChildAt(scrollView.getChildCount() - 1);
            int distanceToEnd = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));

            int totalItemCount = mLayoutManager.getItemCount();
            // If the total item count is zero and the previous isn't, assume the
            // list is invalidated and should be reset back to initial state
            if (totalItemCount < previousTotalItemCount) {
                this.currentPage = this.startingPageIndex;
                this.previousTotalItemCount = totalItemCount;
                if (totalItemCount == 0) {
                    this.loading = true;
                }
            }

            // If it’s still loading, we check to see if the dataset count has
            // changed, if so we conclude it has finished loading and update the current page
            // number and total item count.
            if (loading && (totalItemCount > previousTotalItemCount)) {
                loading = false;
                previousTotalItemCount = totalItemCount;
            }

            // If it isn’t currently loading, we check to see if we have breached
            // the visibleThreshold and need to reload more data.
            // If we do need to reload some more data, we execute onLoadMore to fetch the data.
            // threshold should reflect how many total columns there are too
            if (!loading && distanceToEnd <= visibleThresholdDistance) {
                currentPage++;
                onLoadMore(currentPage, totalItemCount);
                loading = true;
            }
        }

        // Defines the process for actually loading more data based on page
        public abstract void onLoadMore(int page, int totalItemsCount);
    }

2. Set listener 2.设置监听器

private void initRecycler() {  
        //TODO init recycler adapter here

        recycler.setNestedScrollingEnabled(false);          
        LinearLayoutManager _layoutManager = new LinearLayoutManager(this);
        recycler.setLayoutManager(_layoutManager);
        NestedScrollView scrollView = (NestedScrollView) findViewById(R.id.scrollView);
        scrollView.setOnScrollChangeListener(new EndlessParentScrollListener(_layoutManager) {
                @Override
                public void onLoadMore(int page, int totalItemsCount) {                     
                    if (loadedItemCount < serverItemsCount)
                        customLoadMoreDataFromApi();
                }
            });
        customLoadMoreDataFromApi();
    }

Short xml if someone find it usefull:如果有人觉得它有用,请使用简短的 xml:

<android.support.design.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:background="@android:color/background_light"
        android:fitsSystemWindows="true">


        <android.support.design.widget.AppBarLayout>
            ...
        </android.support.design.widget.AppBarLayout>

        <android.support.v4.widget.NestedScrollView
            android:id="@+id/scrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            android:scrollbars="vertical"
            android:scrollbarAlwaysDrawVerticalTrack="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                android:scrollbarAlwaysDrawVerticalTrack="false"
                android:scrollbars="vertical">

                <!-- some views goes here-->

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recyclerFeed"
                    android:scrollbars="vertical"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

                <!-- and possibly here-->

            </LinearLayout>
        </android.support.v4.widget.NestedScrollView>

    </android.support.design.widget.CoordinatorLayout>

So put RecyclerView inside NestedScrollView directly will unfortunately display nothing.所以直接把 RecyclerView 放在 NestedScrollView 里面,不幸的是什么都不显示。 However, there is a way to put the recyclerview inside the NestedScrollView indirectly - just use a frameLayout as the third party to hold your recyclerview.但是,有一种方法可以将 recyclerview 间接放在 NestedScrollView 中 - 只需使用 frameLayout 作为第三方来保存您的 recyclerview。

This is the framelayout which holds the nested recyclerview in your activity class:这是在您的活动类中保存嵌套 recyclerview 的框架布局:

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context=".ExampleFragment"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            >
        </android.support.v4.widget.NestedScrollView>
   </FrameLayout>

Put your fragment into the framelayout (code sits within the activity class):将您的片段放入框架布局(代码位于活动类中):

 ExampleFragment exampleFragment = new ExampleFragment();

    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.add(R.id.container, exampleFragment);
    ft.commit();

In your exampleFragment , you can then put your recyclerview.在您的exampleFragment 中,您可以放置​​您的回收站视图。

<?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/post_container"
        android:background="#E0E0E0">

            <android.support.v7.widget.RecyclerView
                android:id="@+id/my_recycler_view"
                android:scrollbars="vertical"

                android:layout_width="match_parent"
                android:layout_height="match_parent">
            </android.support.v7.widget.RecyclerView>


    </RelativeLayout>

This is the fragment code:这是片段代码:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    super.onCreateView(inflater, container, savedInstanceState);
            llLayout = (RelativeLayout) inflater.inflate(R.layout.example_fragment_layout, container, false);
                    mRecyclerView = (RecyclerView) llLayout.findViewById(R.id.my_recycler_view);

The following is the CheeseSquare XML layout you should have:以下是您应该拥有的CheeseSquare XML 布局:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/detail_backdrop_height"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        android:fitsSystemWindows="true">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginStart="48dp"
            app:expandedTitleMarginEnd="64dp">

            <ImageView
                android:id="@+id/backdrop"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:fitsSystemWindows="true"
                app:layout_collapseMode="parallax" />

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_collapseMode="pin" />

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

   <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context=".ExampleFragment"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            >
        </android.support.v4.widget.NestedScrollView>
   </FrameLayout>

    <android.support.design.widget.FloatingActionButton
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"
        android:src="@drawable/ic_discuss"
        android:layout_margin="@dimen/fab_margin"
        android:clickable="true"/>

</android.support.design.widget.CoordinatorLayout>

I have wasted at least a week behind this and the only solution that works is to remove the nestedscrollview .我在这之后至少浪费了一周,唯一有效的解决方案是删除nestedscrollview I know that is not the answer to this particular question of adding recyclerview inside nestedscrollview , but to make the recyclerview to actually recycle the view , you HAVE TO REMOVE the nestedscrollview .我知道这不是在nestedscrollview中添加recyclerview特定问题的答案,但要使recyclerview 实际回收视图,您必须删除nestedscrollview If you happen to have multiple view inside instead of just the recyclerview , You have to add them as items of the recyclerview by setting different viewholders, and then adding如果你碰巧里面有多个view而不仅仅是recyclerview ,你必须通过设置不同的 viewholder 将它们添加为recyclerview的项目,然后添加

app:layout_behavior="@string/appbar_scrolling_view_behavior"

to the recyclerview or the parent of recyclerview (the parent cannot have any other view other than Recyclerview for this to work).到recyclerview或父recyclerview (父不能超过Recyclerview其它为此工作的任何其它视图)。

In my case, the requirement was as follows:就我而言,要求如下:

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

I needed to have two views before recyclerview and I wanted them to do nestedscroll along with recyclerview .我需要在recyclerview之前有两个视图,我希望它们与recyclerview一起做nestedscroll。 The only thing that worked was by removing these to views and adding them as the first two items of recyclerview , and then adding唯一有效的方法是将这些删除到视图并将它们添加为recyclerview的前两项,然后添加

app:layout_behavior="@string/appbar_scrolling_view_behavior"

to the swipetorefresh layoutswipetorefresh布局

Hope this helps someone.希望这可以帮助某人。 I will add more info If required.如果需要,我会添加更多信息。

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

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