RecyclerView Item Decoration - Why are margins being added to the bottom of my viewholders?

772 Views Asked by At

I'm trying to add Right and Left margins to my RecyclerView on the first and last item with Item Decorations.

The right and left margins are being successfully added to the first and last, however the ones in the middle have margins on the bottom and I have NO IDEA where they are coming from.

enter image description here

ItemDecoration:

public class MapItemDecoration extends RecyclerView.ItemDecoration {

    private final int spacing;

    public MapItemDecoration(int spacing) {
        this.spacing = spacing;
    }

    @Override
    public void getItemOffsets(Rect outRect, @NonNull View view,
                               @NonNull RecyclerView parent,
                               @NonNull RecyclerView.State state
    ) {
        final int position = parent.getChildLayoutPosition(view);
        final int itemCount = state.getItemCount();

        
        if (position == 0) {
            //Adds margins on first item on the right side
            outRect.set(0, 0, spacing, 0);
        } else if (itemCount > 0 && position == itemCount - 1) {
            //Adds margins to the last item on the left side
            outRect.set(spacing, 0, 0, 0);
        } else {
            //Margins between first and last item on both right and left side.
            outRect.set(spacing, 0, spacing, 0);
        }
    }
}

enter image description here

ViewHolder:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content">

    <ImageView
        android:id="@+id/image"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
adapter = new MapAdapter(listLocations, this);
int spacing = SpacingUtils.convertIntToDP(this, 32);
recyclerView.addItemDecoration(new MapItemDecoration(spacing));
recyclerView.setLayoutManager(new LinearLayoutManager(
     this, LinearLayoutManager.HORIZONTAL, false
));
recyclerView.setAdapter(adapter);

enter image description here

Does anyone know what I'm doing incorrectly? Why are margins being added to the bottom of my viewholders even though I have the value set as 0 inside my ItemDecorations?

Edit :____________________________________________________

I tried setting a specified dp width on the ImageView inside the Viewholder

New ViewHolder:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/image"
        android:layout_width="150dp"
        android:layout_height="150dp"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

enter image description here

This seems to remove the bottom margins. But I need the ImageView to be using the attributes

<ImageView
        android:id="@+id/image"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="H,1:1"

so my viewholder is scaled accordingly to the screensize. Is there another solution without setting the imageview as a specified width? Or maybe there is a way I could do it with percentage in the parent constraint layout.

1

There are 1 best solutions below

0
On

This fixed the margins from appearing on the bottom:

Removing the attributes

android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,1:1"

on my ImageView and setting it as

android:layout_width="match_parent"
android:layout_height="match_parent"

And since I still wanted a square ImageView and a viewholder that was sized based on the screensize, I programmatically sized it inside my onCreateViewHolder

Thanks to this answer by Ben P. https://stackoverflow.com/a/51202132/11110509

@NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View itemView = inflater.inflate(R.layout.viewholder_map, parent, false);

        ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
        int width = (int) (parent.getWidth() * 0.4);
        layoutParams.width = width;
        layoutParams.height = width;
        itemView.setLayoutParams(layoutParams);
        return new ViewHolder(itemView);
    }

And the ViewHolder:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <ImageView
        android:id="@+id/iv_location"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

This way I still get the square imageview, and also scaled viewholder size based on the device.