RecyclerView getChildCount() and getItemCount() returns same value
Asked Answered
D

3

30

RecyclerView has an InfiniteScrollListener and inside that listener, in OnScrolled() method I calculate visible and total item count to check if new items should be loaded. When totalItemCount and visibleItemCount are the same, it causes an infinite loop of loading. The listener works flawlessly with my other RecyclerViews which does NOT have CoordinatorLayout or NestedScrollView as a parent. I want to keep this structure because the customer won't accept a change.

I've got a fragment inside an activity that has a layout like this

CoordinatorLayout{
 NestedScrollView{
    RecyclerView{
    }
 }
}

sdk versions

compileSdkVersion 24
buildToolsVersion "23.0.3"
...
minSdkVersion 21
targetSdkVersion 24

fragment_profile.xml as parent layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:layout_behavior="com.paxotic.paxira.util.behavior.AppBarFlingBehavior">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="@dimen/app_bar_height_small"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#000000"
                app:layout_collapseMode="parallax">

                <ImageView
                    android:id="@+id/img_background"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:alpha="0.5"
                    android:scaleType="centerCrop"
                    tools:src="@drawable/bg_greeting_activity" />

            </RelativeLayout>

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

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

    <include
        layout="@layout/activity_profile_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

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

fragment_profile.xml as child layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.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:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    ...

    <android.support.v7.widget.RecyclerView
        android:id="@+id/feedRecycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:paddingBottom="@dimen/spacing_normal"
        tools:listitem="@layout/list_item_activity" />

    ...

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

ProfileFragment.java: relative function

private void init() {

    feedAdapter = new FeedAdapter(host);
    feedAdapter.setHasStableIds(true);

    LinearLayoutManager layoutManager = new LinearLayoutManager(host);
    feedRecycler.setLayoutManager(layoutManager);
    feedRecycler.setNestedScrollingEnabled(false);
    feedRecycler.setHasFixedSize(true);
    feedRecycler.setAdapter(feedAdapter);

    infiniteScrollListener = new InfiniteScrollListener(host, layoutManager) {
        @Override
        public void onLoadMore(int pageIndex) {
            if (!feedAdapter.isLoading) {
                loadFeed(pageIndex);
            }
        }
    };
    feedRecycler.addOnScrollListener(infiniteScrollListener);
}

InfiniteScrollListener.java

public abstract class InfiniteScrollListener extends RecyclerView.OnScrollListener {
    private int previousTotal = 0; // The total number of items in the dataset after the last load
    private boolean loading = false; // True if we are still waiting for the last set of data to load.
    private static final int VISIBLE_THRESHOLD = 5; // The minimum amount of items to have below your current scroll position before loading more.
    int firstVisibleItem, visibleItemCount, totalItemCount;

    private int current_page = 0;
    private int loadingItemCount;

    private LinearLayoutManager mLinearLayoutManager;

    public InfiniteScrollListener(Context context, LinearLayoutManager linearLayoutManager) {
        this.mLinearLayoutManager = linearLayoutManager;
        loadingItemCount = context.getResources().getInteger(R.integer.feed_pull_item_count);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        if (dy < 0) return;        // check for scroll down

        visibleItemCount = mLinearLayoutManager.getChildCount();
        totalItemCount = mLinearLayoutManager.getItemCount();
        firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();

        synchronized (this) {

            if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + VISIBLE_THRESHOLD)) {
                // End has been reached, Do something
                current_page++;
                onLoadMore(current_page);
                loading = true;
            }
        }
    }

    public abstract void onLoadMore(int current_page);

    public void setLoading(boolean loading) {
        this.loading = loading;
    }

    @Override
    public void onScrollStateChanged(RecyclerView view, int scrollState) {
        // Don't take any action on changed
    }
}
Dogger answered 13/7, 2016 at 21:32 Comment(4)
Hey, have you found the solution yet? I have the same problem..Idiom
I found the core problem, not solution unfortunately @DarkLeonhart. The problem is the NestedScrollView's acts as it's visible on the screen so it draws all the children and RecyclerView returns all visible children which are all the items it's adapter has, thus, gives us same value with getChildCount() and getItemCount(). I've changed my UI for now. I load only 10 items with RecyclerView and have a button which leads to another activity which has infinite scrolling.Dogger
@DarkLeonhart have you found the solution?Oilskin
as @Dogger the problem is when the RecyclreView is inside NestedScrollView, so it seems that RecyclerView's scroll listener doesn't affect anything. Instead, I used NestedScrollView's scroll listener based on https://mcmap.net/q/324911/-how-to-put-recyclerview-inside-nestedscrollviewIdiom
N
1

you are getting the same values from getChildCount() getItemCount(), because the recyclerview will returns the total number of items/child.

you must override one of the method, as per your usage, to get the value you want. Do this in your custom recyclerview adapter.

To do this, add this to your recyclerview adapter:

@Overide
public int getChildCount() {
//return your desired value here after obtaining it
}
Niphablepsia answered 27/12, 2020 at 14:29 Comment(0)
H
1

You get the real count of views. If adapter has 15 items and ((LinearLayoutManager) myRecyclerView.getLayoutManager()).getChildCount() returns you 7, there is two items visible in view due to max height or static height value after you update new item its till persists same height.

The count of items in the model (adapter) and the count of views are not the same thing.

RecyclerView doesn't hold views for all 15 items in memory, it only has 7 in your case. When you scroll, it reuses those 7 views for displaying different items. The purpose of this is improving the rendering performance.

If you want to get 15, then you should use mAdapter.getItemCount() instead.

Hardboard answered 9/4, 2021 at 13:19 Comment(0)
R
0

You shouldn't use recyclerView vertical in nestedScrollview. For finding wich item to show on RecyclerView and start getting a new item you can use your adapter. In the RecyclerView.Adapter<> you can override onViewAttachedToWindow():

@Override
public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
    super.onViewAttachedToWindow(holder);
    if ((getItemCount() - 1) == holder.getAdapterPosition()){
        Log.d(TAG,"list done");
        onLoadMore(current_page);
    } else {
        Log.d(TAG,"remain item : " + (getItemCount() - holder.getAdapterPosition() - 1));
    }
}
Rectory answered 30/8, 2018 at 11:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.