web-dev-qa-db-fra.com

RecyclerView supprimer le séparateur / décorateur après le dernier élément

J'ai un assez simple RecyclerView. Voici comment je règle le diviseur

DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
itemDecorator.setDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.news_divider));
recyclerView.addItemDecoration(itemDecorator);

Et ceci est drawable/news_divider.xml

<shape xmlns:Android="http://schemas.Android.com/apk/res/Android" Android:shape="rectangle">
    <solid Android:color="@color/white_two"/>
    <size Android:height="1dp"/>
</shape>

Le problème est que, pour une raison stupide, le séparateur n’est pas simplement créé entre les éléments. Mais aussi après le dernier article. Et je le veux seulement entre les articles pas après chaque article.

Avez-vous une idée de comment empêcher le séparateur de s'afficher après le dernier élément?

A bientôt et merci

39

Essayez ce code, il ne montrera pas de diviseur pour le dernier article.

public class DividerItemDecorator extends RecyclerView.ItemDecoration {
    private Drawable mDivider;

    public DividerItemDecorator(Drawable divider) {
        mDivider = divider;
    }

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        int dividerLeft = parent.getPaddingLeft();
        int dividerRight = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i <= childCount - 2; i++) {
            View child = parent.getChildAt(i);

            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            int dividerTop = child.getBottom() + params.bottomMargin;
            int dividerBottom = dividerTop + mDivider.getIntrinsicHeight();

            mDivider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
            mDivider.draw(canvas);
        }
    }
}

divider.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:shape="rectangle">
    <size
        Android:width="1dp"
        Android:height="1dp" />
    <solid Android:color="@color/grey_300" />
</shape>

Définissez votre Divider comme ceci:

RecyclerView.ItemDecoration dividerItemDecoration = new DividerItemDecorator(ContextCompat.getDrawable(context, R.drawable.divider));
recyclerView.addItemDecoration(dividerItemDecoration);
64
Bhuvanesh BS

Comme proposé ici vous pouvez étendre DividerItemDecoration comme ceci:

recyclerView.addItemDecoration(
    new DividerItemDecoration(context, linearLayoutManager.getOrientation()) {
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            int position = parent.getChildAdapterPosition(view);
            // hide the divider for the last child
            if (position == parent.getAdapter().getItemCount() - 1) {
                outRect.setEmpty();
            } else {
                super.getItemOffsets(outRect, view, parent, state);
            }
        }
    }
);

[UPDATE]
@ Rebecca Hsieh a souligné:

Ceci fonctionne lorsque votre vue d'élément dans RecyclerView n'a pas d'arrière-plan transparent, par exemple,

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:orientation="horizontal"
    Android:background="#ffffff">
    ... 
</LinearLayout>

DividerItemDecoration.getItemOffsets est appelé par RecyclerView pour mesurer la position de l'enfant. Cette solution mettra le dernier séparateur derrière le dernier élément. Par conséquent, la vue de l'élément dans RecyclerView doit avoir un arrière-plan pour couvrir le dernier séparateur, ce qui donne l'impression qu'il est masqué.

Autre solution

Si vous n'aimez pas que le séparateur soit dessiné derrière, vous pouvez simplement copier ou étendre la classe DividerItemDecoration et changer son comportement d'affichage en modifiant for (int i = 0; i < childCount; i++) en for (int i = 0; i < childCount - 1; i++)

37
Maksim Turaev

La réponse acceptée n'alloue pas d'espace pour la décoration car elle ne remplace pas getItemOffsets()

J'ai modifié le DividerItemDecoration à partir de la bibliothèque de support pour exclure la décoration du dernier élément

public class DividerItemDecorator extends RecyclerView.ItemDecoration {

    private Drawable mDivider;
    private final Rect mBounds = new Rect();

    public DividerItemDecorator(Drawable divider) {
        mDivider = divider;
    }

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        canvas.save();
        final int left;
        final int right;
        if (parent.getClipToPadding()) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            canvas.clipRect(left, parent.getPaddingTop(), right,
                    parent.getHeight() - parent.getPaddingBottom());
        } else {
            left = 0;
            right = parent.getWidth();
        }

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            final View child = parent.getChildAt(i);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

        if (parent.getChildAdapterPosition(view) == state.getItemCount() - 1) {
            outRect.setEmpty();
        } else
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
    }
}

Pour appliquer le décorateur, utilisez

RecyclerView.ItemDecoration dividerItemDecoration = new DividerItemDecorator(dividerDrawable);
recyclerView.addItemDecoration(dividerItemDecoration);

La source d'inclusion d'orientation peut être trouvée ici https://Gist.github.com/abdulalin/146f8ca42aa8322692b15663b8d508ff

5
AbdulAli

Créez votre propre classe de diviseur ( Exemple ici )

Dans le code qui dessine le séparateur, vérifiez d’abord si vous dessinez le séparateur du dernier élément de la liste. Si c'est le cas, ne le dessinez pas.

Sachez simplement que si vous remplacez OnDrawOver, il affichera en haut de votre vue les barres de défilement, etc. Il vaut mieux s'en tenir à OnDraw. Beaucoup d'exemples sur Google mais this est un bon tutoriel sur la création de vos propres décorateurs.

2
Kuffs

Voici la classe DividerDecorator que j'utilise dans mes applications et qui supprime la dernière ligne du dernier élément.

public class DividerDecorator extends RecyclerView.ItemDecoration {
    private Drawable mDivider;

    public DividerDecorator(Context context) {
        mDivider = context.getResources().getDrawable(R.drawable.recyclerview_divider);
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}

Vous pouvez le définir sur votre RecyclerView avec le code suivant:

mRecyclerViewEvent.addItemDecoration(new DividerDecorator(context));

Voici le recyclerview_divider.xml

<size
    Android:width="1dp"
    Android:height="1dp" />

<solid Android:color="@color/DividerColor" />
2
savepopulation

Ceci est une version personnalisée de Android support DividerItemDecoration qui ignore le dernier élément:

https://Gist.github.com/mohsenoid/8ffdfa53f0465533833b0b44257aa641

la principale différence est:

private fun drawVertical(canvas: Canvas, parent: RecyclerView) {
    canvas.save()
    val left: Int
    val right: Int

    if (parent.clipToPadding) {
        left = parent.paddingLeft
        right = parent.width - parent.paddingRight
        canvas.clipRect(left, parent.paddingTop, right,
                parent.height - parent.paddingBottom)
    } else {
        left = 0
        right = parent.width
    }

    val childCount = parent.childCount
    for (i in 0 until childCount - 1) {
        val child = parent.getChildAt(i)
        parent.getDecoratedBoundsWithMargins(child, mBounds)
        val bottom = mBounds.bottom + Math.round(child.translationY)
        val top = bottom - mDivider!!.intrinsicHeight
        mDivider!!.setBounds(left, top, right, bottom)
        mDivider!!.draw(canvas)
    }
    canvas.restore()
}

private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
    canvas.save()
    val top: Int
    val bottom: Int

    if (parent.clipToPadding) {
        top = parent.paddingTop
        bottom = parent.height - parent.paddingBottom
        canvas.clipRect(parent.paddingLeft, top,
                parent.width - parent.paddingRight, bottom)
    } else {
        top = 0
        bottom = parent.height
    }

    val childCount = parent.childCount
    for (i in 0 until childCount - 1) {
        val child = parent.getChildAt(i)
        parent.layoutManager.getDecoratedBoundsWithMargins(child, mBounds)
        val right = mBounds.right + Math.round(child.translationX)
        val left = right - mDivider!!.intrinsicWidth
        mDivider!!.setBounds(left, top, right, bottom)
        mDivider!!.draw(canvas)
    }
    canvas.restore()
}
1
Mohsen Mirhoseini