[英]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,您可以将滚动视图放在另一个滚动视图中,只要它们正确实现了NestedScrollingChild和NestedScrollingParent 。
(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
layout到swipetorefresh
布局
Hope this helps someone.希望这可以帮助某人。 I will add more info If required.如果需要,我会添加更多信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.