Dynamic Endless RecyclerView scrolling issues
Asked Answered
C

2

11

I have to create following layout enter image description here

So far, I have successfully created the layout and populated all views. However, I am facing problem in the making ReyclerView Endless on first fragment.

Consider the RecyclerView has 10 items on first load, now on scroll I am adding another 10 items and so on. However, the RecyclerView isn't displaying those items, it's height gets fixed at the end of 10th element. I know that the elements are loaded correctly in RecyclerView and if I try to scroll with two fingers on emulator (GenyMotion), the RecyclerView scrolls just fine.

Update :-

Code for RecyclerView's Fragment -

public class CheckInFragmentRecyclerAdapter extends RecyclerView.Adapter<CheckInFragmentRecyclerAdapter.ViewHolder> {
    final List<StoreNew> stores;

    public CheckInFragmentRecyclerAdapter(final List<StoreNew> stores) {
        this.stores = stores;
    }

    @Override
    public CheckInFragmentRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.child_check_in_fragment, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(CheckInFragmentRecyclerAdapter.ViewHolder holder, int position) {
        // Setting data
    }


    @Override
    public int getItemCount() {
        return stores.size();
    }

    /**
     * Function to clear existing data from list
     * @param stores StoreNew instance containing store information
     */
    public void update(final List<StoreNew> stores) {
        this.stores.clear();
        this.stores.addAll(stores);
        notifyDataSetChanged();
    }

    /**
     * Function to add more data to list
     * @param stores StoreNew instance containing store information
     */
    public void addNewList(final List<StoreNew> stores) {
        this.stores.addAll(stores);
        notifyDataSetChanged();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public ViewHolder(View itemView) {
            super(itemView);

            // Initializing component
        }
    }
}

Update :-

Adding layouts for used screens.

main_screen.xml - This is the home screen

<android.support.v4.widget.NestedScrollView
        android:id="@+id/scrollView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_above="@+id/home_footer"
        android:layout_below="@+id/toolbar"
        android:fillViewport="true">

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <!-- Sliding Tab for showing images -->
            <com.example.slidingtab.SlidingTabLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/white" />
            <!-- ViewPager for Images -->
            <android.support.v4.view.ViewPager
                android:id="@+id/vpOffers"
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:layout_marginTop="8dp" />
            <!-- Segmented Control for fragments -->
            <info.hoang8f.android.segmented.SegmentedGroup xmlns:segmentedgroup="http://schemas.android.com/apk/res-auto"
                android:id="@+id/segmented2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="15dp"
                android:layout_marginEnd="10dp"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginStart="10dp"
                android:layout_marginTop="10dp"
                android:orientation="horizontal"
                segmentedgroup:sc_border_width="1dp"
                segmentedgroup:sc_corner_radius="4dp"
                segmentedgroup:sc_tint_color="@color/black">

                <RadioButton
                    android:id="@+id/rbTab1"
                    style="@style/segmented_radio_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:checked="true"
                    android:textSize="@dimen/normal_text"
                    android:text="@string/check_in" />

                <RadioButton
                    android:id="@+id/rbTab2"
                    style="@style/segmented_radio_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="@dimen/normal_text"
                    android:text="@string/upload_bill" />

                <RadioButton
                    android:id="@+id/rbTab3"
                    style="@style/segmented_radio_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="@dimen/normal_text"
                    android:text="@string/redeem" />

            </info.hoang8f.android.segmented.SegmentedGroup>

            <!-- Custom wrap content ViewPager containing fragments -->
            <!-- This will make sure that the height of ViewPager is equal to height of Fragment -->
            <com.example.ui.custom.WrapContentHeightViewPager
                android:id="@+id/vpFragments"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="-7dp" />
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

WrapContentHeightViewPager.java

public class WrapContentHeightViewPager extends ViewPager {

    private static final String TAG = WrapContentHeightViewPager.class.getSimpleName();
    private int height = 0;
    private int decorHeight = 0;
    private int widthMeasuredSpec;

    private boolean animateHeight;
    private int rightHeight;
    private int leftHeight;
    private int scrollingPosition = -1;
    private boolean enabled;

    public WrapContentHeightViewPager(Context context) {
        super(context);
        init();
    }

    public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.enabled = true;
        init();
    }

    private void init() {
        addOnPageChangeListener(new OnPageChangeListener() {


            public int state;

            @Override
            public void onPageScrolled(int position, float offset, int positionOffsetPixels) {}

            @Override
            public void onPageSelected(int position) {
                if (state == SCROLL_STATE_IDLE) {
                    height = 0; // measure the selected page in-case it's a change without scrolling
                    Log.d(TAG, "onPageSelected:" + position);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                this.state = state;
            }
        });
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return this.enabled && super.onTouchEvent(event);

    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return this.enabled && super.onInterceptTouchEvent(event);

    }

    public void setPagingEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public void setAdapter(PagerAdapter adapter) {
        height = 0; // so we measure the new content in onMeasure
        super.setAdapter(new PagerAdapterWrapper(adapter));
    }

    /**
     * Allows to redraw the view size to wrap the content of the bigger child.
     *
     * @param widthMeasureSpec  with measured
     * @param heightMeasureSpec height measured
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        widthMeasuredSpec = widthMeasureSpec;
        int mode = MeasureSpec.getMode(heightMeasureSpec);

        if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
            if(height == 0) {
                // measure vertical decor (i.e. PagerTitleStrip) based on ViewPager implementation
                decorHeight = 0;
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    if(lp != null && lp.isDecor) {
                        int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
                        boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
                        if(consumeVertical) {
                            decorHeight += child.getMeasuredHeight() ;
                        }
                    }
                }

                // make sure that we have an height (not sure if this is necessary because it seems that onPageScrolled is called right after
                int position = getCurrentItem();
                View child = getViewAtPosition(position);
                if (child != null) {
                    height = measureViewHeight(child);
                }
                //Log.d(TAG, "onMeasure height:" + height + " decor:" + decorHeight);

            }
            int totalHeight = height + decorHeight + getPaddingBottom() + getPaddingTop();
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY);
            //Log.d(TAG, "onMeasure total height:" + totalHeight);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public void onPageScrolled(int position, float offset, int positionOffsetPixels) {
        super.onPageScrolled(position, offset, positionOffsetPixels);
        // cache scrolled view heights
        if (scrollingPosition != position) {
            scrollingPosition = position;
            // scrolled position is always the left scrolled page
            View leftView = getViewAtPosition(position);
            View rightView = getViewAtPosition(position + 1);
            if (leftView != null && rightView != null) {
                leftHeight = measureViewHeight(leftView);
                rightHeight = measureViewHeight(rightView);
                animateHeight = true;
                //Log.d(TAG, "onPageScrolled heights left:" + leftHeight + " right:" + rightHeight);
            } else {
                animateHeight = false;
            }
        }
        if (animateHeight) {
            int newHeight = (int) (leftHeight * (1 - offset) + rightHeight * (offset));
            if (height != newHeight) {
                //Log.d(TAG, "onPageScrolled height change:" + newHeight);
                height = newHeight;
                requestLayout();
                invalidate();
            }
        }
    }

    private int measureViewHeight(View view) {
        view.measure(getChildMeasureSpec(widthMeasuredSpec, getPaddingLeft() + getPaddingRight(), view.getLayoutParams().width), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        return view.getMeasuredHeight();
    }

    protected View getViewAtPosition(int position) {
        if(getAdapter() != null) {
            Object objectAtPosition = ((PagerAdapterWrapper) getAdapter()).getObjectAtPosition(position);
            if (objectAtPosition != null) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    if (child != null && getAdapter().isViewFromObject(child, objectAtPosition)) {
                        return child;
                    }
                }
            }
        }
        return null;
    }


    /**
     * Wrapper for PagerAdapter so we can ask for Object at index
     */
    private class PagerAdapterWrapper extends PagerAdapter {
        private final PagerAdapter innerAdapter;
        private SparseArray<Object> objects;

        public PagerAdapterWrapper(PagerAdapter adapter) {
            this.innerAdapter = adapter;
            this.objects = new SparseArray<>(adapter.getCount());
        }


        @Override
        public void startUpdate(ViewGroup container) {
            innerAdapter.startUpdate(container);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Object object = innerAdapter.instantiateItem(container, position);
            objects.put(position, object);
            return object;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            innerAdapter.destroyItem(container, position, object);
            objects.remove(position);
        }

        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            innerAdapter.setPrimaryItem(container, position, object);
        }

        @Override
        public void finishUpdate(ViewGroup container) {
            innerAdapter.finishUpdate(container);
        }

        @Override
        public Parcelable saveState() {
            return innerAdapter.saveState();
        }

        @Override
        public void restoreState(Parcelable state, ClassLoader loader) {
            innerAdapter.restoreState(state, loader);
        }

        @Override
        public int getItemPosition(Object object) {
            return innerAdapter.getItemPosition(object);
        }

        @Override
        public void notifyDataSetChanged() {
            innerAdapter.notifyDataSetChanged();
        }

        @Override
        public void registerDataSetObserver(DataSetObserver observer) {
            innerAdapter.registerDataSetObserver(observer);
        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {
            innerAdapter.unregisterDataSetObserver(observer);
        }

        @Override
        public float getPageWidth(int position) {
            return innerAdapter.getPageWidth(position);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return innerAdapter.getPageTitle(position);
        }

        @Override
        public int getCount() {
            return innerAdapter.getCount();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return innerAdapter.isViewFromObject(view, object);
        }

        public Object getObjectAtPosition(int position) {
            return objects.get(position);
        }
    }
}

first_fragment.xml

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lvCheckIn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:requiresFadingEdge="none"
    android:fadingEdgeLength="0dp"
    android:orientation="vertical" />

Adding more data to the RecyclerView on scrolling -

private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            visibleItemCount = recyclerView.getChildCount();
            totalItemCount = adapter.getItemCount();
            firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
            if (loading) {
                if (totalItemCount > previousTotal) {
                    loading = false;
                    previousTotal = totalItemCount;
                }
            }
            if (!loading && (totalItemCount - visibleItemCount)
                    <= (firstVisibleItem + visibleThreshold) && current_page < totalPages) {
                // End has been reached

                // Do something
                current_page++;
                // Sending request to server

                loading = true;
            }
        }
    };

When data is received via API (adapter already added above)-

adapter.addNewList(homePageNew.checkin_stores.stores);
Copperas answered 13/6, 2016 at 6:47 Comment(34)
your adapter is broken then...Forelli
@Forelli What is the solution then? Do you require some code?Copperas
the solution is to fix your adapter's code...Forelli
@Forelli What I have to fix ? The ReyclerView's adapter is a simple adapter which sets the values to UI components, can you describe what needs to be fixed?Copperas
how can i describe what needs to be fixed in your not posted code?Forelli
@Forelli That's what I asked in my previous comment, which code you require, since I can't post everything. Anyhow, posting code in 2 min.Copperas
@Forelli updated codeCopperas
add some Log.d inside getItemCount to see the items count used by your adapterForelli
@Forelli Initially, the log displays 8, then on using two-finger scroll, it displays 16, then 24 and so onCopperas
so you can see those 8 then 16 then 24 items?Forelli
@Forelli If I use two finger scroll on emulator (Genymotion), the items appear properly with endless list working properly. However, single finger scroll limits the RecyclerView to the end of 8th element, to continue, I have to use two finger scroll.Copperas
ok i took your code and it just works fine, with "normal" one finger scrollForelli
@Forelli I know the code will work in normal circumstances. In my opinion, the nested scrolls in my view hierarchy is causing the issue. RecyclerView isn't scaling up to full height for some reasons.Copperas
so do not place "scrollable" elements inside another "scrollable" parentForelli
@Forelli Then how do you suggest I create the layout mentioned in image above? I am using NestedScrollView, please check my attached image.Copperas
@RohanKandwal can you tell me, what you really want. Do you want to loop the available data, or you want to fetch another set when user will reach to the last position of the list ?Jenevajeni
@Jenevajeni I want to fetch another set of data, which I am fetching but I am facing issues written in question description/Copperas
Can you share the xml for your layout? For completeness, maybe also share the code for how the recyclerView is initialized and set up, and how the CheckInFragmentRecyclerAdapter is added?Rossman
@RohanKandwal You should post the code for the fetching/updating the objects from adapter.Rosalie
I have read somewhere that using notifyDataSetChanged on RecyclerViews is the worst type and the last resort for updating data in a recyclerview ... So my advice is using the method addView and other derivatives ... make a controller over your Recycler instead of an adapterNakasuji
@Rossman Code updated, please check.Copperas
@Rosalie please check.Copperas
What version of the support library are you using? I've had issues with RecyclerView heights. Reverting back to an older version fixed it for me.Stanstance
Any particular reason to put things in a ScrollView ? Where would you like to have bottom edge of RecyclerView aligned to ?Chowder
@JeffEngebretsen I am using RecyclerView v 23.4.0Copperas
@S.D. If I don't put it into scrollview then top half of the screen wouldn't scroll. Bottom edge of the recyclerview should be bottom of the screen.Copperas
@RohanKandwal A screen should scroll vertically if the bottom edge of its overall content is beyond the bottom screen edge. I see a logical flaw in UI requirements: top half of the screen wouldn't scroll.... and Bottom edge of the recyclerview should be bottom of the screen.....Chowder
@S.D. What I meant to say that the whole screen should scroll.Copperas
It seems to me, that you don't need NestedScrollView at allAcerate
@Acerate Then how to scroll full page?Copperas
@RohanKandwal You don't. Tell me the situation, when you will need it?Acerate
@Acerate As you can see in the image in my question, there are two sliding tabs in my design. If I don't add nested scrollview, I can only scroll inside the second tab. The desired behavior however should be that when user scrolls, entire page moves up.Copperas
Downgrade to 23.2.1 and see if you're still having the problem. They fixed stuff in RecyclerView in 23.3.0 which broke our usage with nested scroll containers.Stanstance
@JeffEngebretsen Unfortunately, it isn't working.Copperas
O
1

There seems to be a similar discussion here: ViewPager in a NestedScrollView Maybe the sample of the Naruto guy (https://github.com/TheLittleNaruto/SupportDesignExample/) could solve your situation.

Edit

I'm not sure if I'm getting the point of your question. Anyway here you can a possible solution to your situation.

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

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/app_bar_layout"
>
<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"
>

---- include here everything before the pager ----
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
---- this is your pager
<include layout="@layout/fragment_pager"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:layout_below="@+id/app_bar_layout"

/>

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

Then you can just listen to the RecycleView in the pager to add items as the RecycleView scrolls to bottom.

I hope it helped

Offen answered 30/6, 2016 at 14:57 Comment(1)
Finally, something worked. Will post complete code later so that it helps others tomorrow.Copperas
D
0

You can see my implementation of the endless RecyclerView scroll:

GalleryActivity.java

public class GalleryActivity extends AppCompatActivity {

private StaggeredGridLayoutManager layoutManager;
private RecyclerView recyclerView;
private ImageRecyclerViewAdapter imageAdapter;

private List<ImageItem> images;
private ImageSearchClient imageSearchClient;

private String query;
private int currentStartPosition = 1;
private boolean loading = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_gallery);
    getSupportActionBar().setHomeButtonEnabled(true);

    query = getIntent().getStringExtra(Utils.QUERY_TAG);
    if (query == null)
        return;
    imageSearchClient = new ImageSearchClient(query);

    recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    recyclerView.setHasFixedSize(true);

    layoutManager = new StaggeredGridLayoutManager(2, 1);
    recyclerView.setLayoutManager(layoutManager);

    images = new ArrayList<>();

    imageAdapter = new ImageRecyclerViewAdapter(this, images);
    recyclerView.setAdapter(imageAdapter);
    recyclerView.addOnScrollListener(new EndlessRecyclerScrollListener());

    loadMoreData();
}

private void loadMoreData() {
    loading = true;
    imageSearchClient.getService().customSearch(Utils.API_KEY, Utils.CX_KEY, query,
            Utils.IMAGE_SEARCH_TYPE,
            currentStartPosition,
            Utils.ITEMS_COUNT,
            new Callback<ImageResponse>() {
                @Override
                public void success(ImageResponse imageResponse, Response response) {
                    List<ImageItem> items = imageResponse.getItems();
                    for (ImageItem item : items) {
                        images.add(item);
                    }
                    imageAdapter.notifyDataSetChanged();
                    currentStartPosition += items.size();
                    loading = false;
                }

                @Override
                public void failure(RetrofitError error) {
                    Log.e(GalleryActivity.class.getSimpleName(),
                            error.getResponse().getReason());
                }
            });
}

private class EndlessRecyclerScrollListener extends RecyclerView.OnScrollListener {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        int[] visibleItems = layoutManager.findLastVisibleItemPositions(null);
        int lastItem = 0;
        for (int i : visibleItems) {
            lastItem = Math.max(lastItem, i);
        }
        if (lastItem > 0 && lastItem > images.size() - Utils.ITEMS_COUNT && !loading) {
            if (NetworkUtils.hasConnection(GalleryActivity.this)) {
                loadMoreData();
            } else {
                Toast.makeText(GalleryActivity.this, R.string.network_error,
                        Toast.LENGTH_SHORT).show();
            }
        }
    }
}

}

ImageRecyclerViewAdapter.java (nothing is extraordinary here):

public class ImageRecyclerViewAdapter extends RecyclerView.Adapter<ImageViewHolder> {

private List<ImageItem> itemList;
private Context context;
private int parentWidth = 0;

public ImageRecyclerViewAdapter(Context context, List<ImageItem> itemList) {
    this.context = context;
    this.itemList = itemList;
}

@Override
public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View layoutView =
            LayoutInflater.from(parent.getContext()).inflate(R.layout.gallery_card_item, null);
    ImageViewHolder imageViewHolder = new ImageViewHolder(layoutView);
    parentWidth = parent.getWidth();
    return imageViewHolder;
}

@Override
public void onBindViewHolder(final ImageViewHolder holder, int position) {
    Picasso.with(context)
            .load(itemList.get(position).getLink())
            .error(R.drawable.error_image)
            .placeholder(R.drawable.progress_animation)
            .into(new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    int targetWidth = parentWidth / 2;
                    float ratio = (float) bitmap.getHeight() / (float) bitmap.getWidth();
                    float heightFloat = ((float) targetWidth) * ratio;

                    final android.view.ViewGroup.MarginLayoutParams layoutParams
                            = (ViewGroup.MarginLayoutParams) holder.image.getLayoutParams();

                    layoutParams.height = (int) heightFloat;
                    layoutParams.width = (int) targetWidth;
                    holder.image.setLayoutParams(layoutParams);
                    holder.image.setImageBitmap(bitmap);
                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            });
}

@Override
public int getItemCount() {
    return this.itemList.size();
}

}

Doubletongued answered 21/6, 2016 at 9:34 Comment(4)
I am able to add more data to recyclerview, just that I am not able to increase the height of recyclerview.Copperas
When you say "I am able to add more data to recyclerview, just that I am not able to increase the height of recyclerview" does that mean the your pagination is just able to fetch data [images.add(item);] when you scroll beyond last item in recyclerview but your recyclerview doesn't reflects it ?Bloomsbury
Also,is it possible for you to post app image showing how your recyclerview looks?Bloomsbury
@Droidwala I can't post the image since app is a commercial one but I can explain, my pagination is able to pick additional data, add it into recyclerview's adapter successfully but since the height of RecyclerView isn't increasing, I am not able to scroll to them. However, if on emulator like genmotion, if I try to scroll using two fingers then I can scroll the recyclerview to reach next set of icons. Does this make sense?Copperas

© 2022 - 2024 — McMap. All rights reserved.