Basit
Basit

Reputation: 1840

RecyclerView decorator adding extra padding on refresh

So, I'm facing a weird problem while implementing RecyclerView in my project. I have a custom decorator to implement a consistent top and bottom padding and a rather different value of padding in between elements. Whenever I tap on refresh, i.e. fetch data from back-end and populate RecyclerView again, the padding increases and it keeps on increasing on each refresh.

This is the correct way

When I hit refresh (causing AsyncTask to execute again), the space between items increase. And it keeps on increase with each refresh.

Padding between items increasing

I have a typical RecyclerView like this

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="false"
    android:focusableInTouchMode="false">
</android.support.v7.widget.RecyclerView>

Populating content inside RecyclerView is CardView with the following layout:

<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/cv"
android:layout_marginLeft="9dp"
android:layout_marginRight="9dp"
>

    <RelativeLayout
        android:id="@+id/itemLinearTop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingTop="17dp"
        android:paddingRight="10dp"
        android:paddingLeft="10dp">

        <TextView
            android:id="@+id/tv1"
            android:textColor="@color/logo_black"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium" />


        <TextView
            android:id="@+id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBaseline="@+id/tv1"
            android:textColor="@color/logo_black"
            android:layout_alignParentRight="true"
            android:textAppearance="?android:attr/textAppearanceMedium" />

        <!-- More elements -->
    </RelativeLayout>
</android.support.v7.widget.CardView>

I am initializing and populating RecyclerView like this:

// In PostExecute of AsyncTask
RecyclerView rv = (RecyclerView) rootView.findViewById(R.id.recyclerview);
RecycleViewAdapter adapter = new FuelPricesRecycleViewAdapter(data);
rv.setAdapter(adapter);
rv.addItemDecoration(new RecyclerViewItemDecoration(30, item_count - 1));

And this is what my RecyclerViewItemDecoration class looks like:

public class RecyclerViewItemDecoration extends RecyclerView.ItemDecoration {
    private int space = 0;
    private int item_count = 0;
    private int ADDITIONAL_PADDING = 20;

    public RecyclerViewItemDecoration(int space, int item_count) {
        this.space = space;
        this.item_count = item_count;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if(parent.getChildPosition(view) != item_count)
             outRect.bottom = space;

         // Add top margin only for the first item to avoid double space between items
         if(parent.getChildPosition(view) == 0)
             outRect.top = space + ADDITIONAL_PADDING;
         if(parent.getChildPosition(view) == item_count)
             outRect.bottom = space + ADDITIONAL_PADDING;
     }
 }

Thank you for your help!

Upvotes: 28

Views: 18379

Answers (8)

Eleftherios Myteletsis
Eleftherios Myteletsis

Reputation: 101

I had the same issue and I fixed that with the itemDecorationCount

if (recyclerView.itemDecorationCount == 0) {
            val itemDecorator = DividerItemDecoration(activity, DividerItemDecoration.VERTICAL)
            itemDecorator.setDrawable(ContextCompat.getDrawable(activity, R.drawable.recycler_divider))
            recyclerView.addItemDecoration(itemDecorator)
        }

Upvotes: 10

Pankaj kumar
Pankaj kumar

Reputation: 1377

This is happening because we are trying to add itemDecorator to the recycler view which already have itemDecorator added to it, check first if its already there then don't add again

if (0 == recyclerview.getItemDecorationCount()) {
recyclerview.addItemDecoration(new DividerItemDecoration(ContextCompat.getDrawable(getActivity(), R.drawable.divider)));
}

This 0 will be the number of itemDecorator we have in recycler view, most of the time it'll be 0.

Upvotes: 24

N Kaushik
N Kaushik

Reputation: 2208

You are adding Decoration each time recyclerview is loaded.

Use one flag instead :

private boolean flagDecoration = false;

if(!flagDecoration)
{
    rv.addItemDecoration(new RecyclerViewItemDecoration(30, item_count - 1));
    flagDecoration = true;    
}

Upvotes: 15

Jean Douglas
Jean Douglas

Reputation: 71

Apparently you are adding various decorations. I was having the same problem. What I did here and that worked out was:

1º - Declare the decoration before the onCreate () method:

public StickyHeaderDecoration mDecor;
public MyAdapterPinned MyAdapterPinned;

2º - Then I created a method that creates and populates my recycleview. In this method I check if I have already called mDecor. If so, I remove it. Otherwise, I create it. The code looks like this:

public void createRecyclerView(){
    //hide layout com progress
    hideLayoutProgressFull();

    LinearLayoutManager mLayoutManager = new 
    LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
    recyclerView.setLayoutManager(mLayoutManager);

    recyclerView.setItemAnimator(new DefaultItemAnimator());

    myAdapterPinned = new MyAdapterPinned(getContext(), mList);

    recyclerView.setAdapter(myAdapterPinned);

    if (mDecor != null)
        recyclerView.removeItemDecoration(mDecor);

    mDecor = new StickyHeaderDecoration(myAdapterPinned);
    recyclerView.addItemDecoration(mDecor);

    //hide progressDialog
    hideProgress();
}    

NOTE: I am using an adapter that sets the title to each change but you can not interfere with the result. You can use your ordinary adapter. It worked for me. I hope it works for you too;

Upvotes: 0

aotian16
aotian16

Reputation: 819

I face the same issue. Fix it with if check

    if (recyclerView.getItemDecorationAt(0) == null) { // check decoration here
        FlexboxItemDecoration agentDividerItemDecoration = new FlexboxItemDecoration(rv.getContext());
        agentDividerItemDecoration.setDrawable(recyclerView.getContext().getResources().getDrawable(R.drawable.shape_divider_flex_normal));
        recyclerView.addItemDecoration(agentDividerItemDecoration);
    } else {
        Log.i(TAG, "initTagsView: ItemDecoration not null");
    }

Upvotes: 1

0x52616A657368
0x52616A657368

Reputation: 378

Faced the same issue and resolved it by placing

mLayoutManager = new LinearLayoutManager(current_activity);
recyclerView.setLayoutManager(mLayoutManager);

in onPreExecute() method.

Just give it a try if it works

Upvotes: 1

user3682279
user3682279

Reputation: 11

I was facing the same issue, the best solution is to make your item decoration while your are initializing your layout manager in the view holder , this resolves the problem you are facing example

public class GridViewHolder extends ViewHolder {

    RecyclerView grid_list;

    public GridViewHolder(View v) {
        super(v);
        this.grid_list = (RecyclerView) v.findViewById(R.id.home_screen_item_grid_recycler_view);
        if (grid_list.getLayoutManager() == null) {
            RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(context, 4);
            grid_list.setLayoutManager(mLayoutManager);
            grid_list.addItemDecoration(new GridSpacingItemDecoration(4, 3, false));
        }
    }
}

Upvotes: 1

yugidroid
yugidroid

Reputation: 6690

You have to remove the item decoration before reseting new data.

rv.removeItemDecoration(yourDecorator)

Let me know if it works!

Upvotes: 17

Related Questions