RecyclerView entfernt den Teiler / Dekorator nach dem letzten Element

74

Ich habe eine recht einfache RecyclerView.
So stelle ich den Teiler ein:

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

Und das ist 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>

Das Problem ist aus irgendeinem Grund, dass der Teiler nicht nur zwischen den Elementen erstellt wird. Aber auch nach dem letzten Punkt. Und ich möchte es nur zwischen den Artikeln, nicht nach jedem Artikel.

Haben Sie eine Idee, wie Sie verhindern können, dass der Teiler nach dem letzten Element angezeigt wird?

Slobodan Antonijević
quelle
1
Verwenden Sie keinen Standardteiler. Sie fügen den Teiler in Ihr XML-Element in der Recycler-Ansicht ein und zeigen oder verbergen die Basis nach Bedarf.
James
Veröffentlichen Sie Ihren Dekorationscode für Teilerartikel.
Bhuvanesh BS

Antworten:

114

Versuchen Sie diesen Code, er zeigt keinen Teiler für das letzte Element an. Mit dieser Methode haben Sie mehr Kontrolle über den Zeichenteiler.

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

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

    @Override
    public void onDrawOver(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>

Stellen Sie Ihren Teiler so ein:

RecyclerView.ItemDecoration dividerItemDecoration = new DividerItemDecorator(ContextCompat.getDrawable(context, R.drawable.divider));
recyclerView.addItemDecoration(dividerItemDecoration);
Bhuvanesh BS
quelle
5
Upvoted, aber sollte es nicht entweder i <= childCount - 2 oder i <childCount - 1 sein ?
Slobodan Antonijević
Wenn Sie keinen Teiler für das letzte Element zeichnen möchten, müssen Sie einen davon festlegen.
Bhuvanesh BS
1
Gibt es eine Möglichkeit, mit dem Teiler zu reduzieren?
Giedrius Šlikas
2
Sicher. Durch Anpassen des Wertes von dividerLeft und dividerRight in der Ondraw-Methode
Bhuvanesh BS
2
Die Lösung funktionierte nicht, aber onDrawOverstattdessen onDrawhat den Job
Przemo
62

Wenn Sie nicht wie Teiler tun gezeichnet hinter werden, können Sie einfach kopieren oder erweitern DividerItemDecoration Klasse und ihre Zeichnungsverhalten ändern durch Änderung for (int i = 0; i < childCount; i++)zufor (int i = 0; i < childCount - 1; i++)

Fügen Sie dann Ihren Dekorateur als hinzu recyclerView.addItemDecoration(your_decorator);

Vorherige Lösung

Wie hier vorgeschlagen , können Sie DividerItemDecoration folgendermaßen erweitern:

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 == state.getItemCount() - 1) {
                outRect.setEmpty();
            } else {
                super.getItemOffsets(outRect, view, parent, state);
            }
        }
    }
);

@ Rebecca Hsieh wies darauf hin:

Dies funktioniert, wenn Ihre Elementansicht in RecyclerView keinen transparenten Hintergrund hat, z.

<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 wird von RecyclerView aufgerufen, um die untergeordnete Position zu messen. Diese Lösung setzt den letzten Teiler hinter den letzten Punkt. Daher sollte die Elementansicht in RecyclerView einen Hintergrund haben, der den letzten Teiler abdeckt, und dies lässt sie wie versteckt aussehen.

Maksim Turaev
quelle
1
Diese Lösung ist sauberer als die akzeptierte, da sie sich auf die Adapterposition und nicht auf den untergeordneten Index stützt. Um es wiederverwendbar zu machen, ersetzen Sie die anonyme innere Klasse durch eine öffentliche Unterklasse, z public class MiddleDividerItemDecoration extends DividerItemDecoration{....
Iwo Banas
1
Wenn Sie den Standardteiler festlegen möchten, ist die obige Lösung in Ordnung. Wenn Sie mehr Anpassungen wie Größe und Farbe des Teilers benötigen, müssen Sie Ihren eigenen Teiler wie die akzeptierte Antwort @lwo Banas
Bhuvanesh BS
9
Ich weiß nicht warum, aber outRect.setEmpty () funktioniert bei mir nicht.
Verlorene Übersetzung
2
@MaksimTuraev Ich verstehe deinen Code nicht. Wenn .setEmpty () das Rechteck auf 0,0,0,0 setzt, warum funktioniert es dann nur mit einem nicht transparenten Hintergrund?
Ruben2112
1
@ Ruben2112, setEmpty () für das letzte Element besagt, dass es nicht verschoben werden sollte (kein Auffüllen). Wenn sich der letzte Teiler nicht darunter bewegt, wird der letzte Teiler ausgeblendet. Es sei denn, es hat einen transparenten Hintergrund.
Glacoon
9

Die akzeptierte Antwort reserviert keinen Platz für die Dekoration, da sie nicht überschreibt getItemOffsets()

Ich habe die DividerItemDecoration aus der Support-Bibliothek optimiert, um die Dekoration vom letzten Element auszuschließen

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());
    }
}

Verwenden Sie zum Auftragen des Dekorateurs

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

Die Quelle für die Aufnahme der Orientierung finden Sie hier https://gist.github.com/abdulalin/146f8ca42aa8322692b15663b8d508ff

AbdulAli
quelle
4

Hier ist die Kotlin-Version der akzeptierten Antwort :

class DividerItemDecorator(private val divider: Drawable?) : RecyclerView.ItemDecoration() {

    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val dividerLeft = parent.paddingLeft
        val dividerRight = parent.width - parent.paddingRight
        val childCount = parent.childCount
        for (i in 0..childCount - 2) {
            val child: View = parent.getChildAt(i)
            val params =
                child.layoutParams as RecyclerView.LayoutParams
            val dividerTop: Int = child.bottom + params.bottomMargin
            val dividerBottom = dividerTop + (divider?.intrinsicHeight?:0)
            divider?.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom)
            divider?.draw(canvas)
        }
    }
}
sma6871
quelle
3

Hier ist die DividerDecoratorKlasse, die ich in meinen Apps verwende und die das Endergebnis des letzten Elements entfernt.

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);
        }
    }
}

Sie können es RecyclerViewmit dem folgenden Code auf Ihr einstellen :

mRecyclerViewEvent.addItemDecoration(new DividerDecorator(context));

Hier ist die recyclerview_divider.xml

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

<solid android:color="@color/DividerColor" />

Savepopulation
quelle
1
Ich habe Sie positiv bewertet, aber das Überschreiben von onDrawOver hat unerwünschte Ergebnisse geliefert. Der Teiler benimmt sich ständig schlecht. Die onDraw-Überschreibung erwies sich für mich als besser.
Slobodan Antonijević
Das Problem mit onDrawOver ist, dass es immer der letzte sichtbare Teiler ist, der beim Scrollen fehlt.
Morten Holmgaard
2

Erstellen Sie Ihre eigene Divider-Klasse ( Beispiel hier )

Überprüfen Sie im Code, der den Teiler zeichnet, zuerst, ob Sie den Teiler für das letzte Element in der Liste zeichnen. Wenn ja, zeichne es nicht.

Beachten Sie jedoch, dass beim Überschreiben OnDrawOverdie Oberseite Ihrer Ansicht einschließlich Bildlaufleisten usw. angezeigt wird. Halten Sie sich am besten daran OnDraw. Viele Beispiele bei Google, aber dies ist ein gutes Tutorial zum Erstellen eigener Dekorateure.

Kuffs
quelle
Ja, onDrawOver lieferte auf einigen Geräten fehlerhafte Ergebnisse.
Slobodan Antonijević
2

Erweiterungsfunktion für Kotlin:

fun RecyclerView.addItemDecorationWithoutLastDivider() {

    if (layoutManager !is LinearLayoutManager)
        return

    addItemDecoration(object :
        DividerItemDecoration(context, (layoutManager as LinearLayoutManager).orientation) {

        override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
            super.getItemOffsets(outRect, view, parent, state)

            if (parent.getChildAdapterPosition(view) == state.itemCount - 1)
                outRect.setEmpty()
            else
                super.getItemOffsets(outRect, view, parent, state)
        }
    })
}

Sie können es leicht benutzen:

recyclerView.addItemDecorationWithoutLastDivider()
seyfullah.bilgin
quelle
0

Dies ist eine angepasste Version der Android-Unterstützung, bei DividerItemDecorationder das letzte Element ignoriert wird:

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

Hauptunterschied ist:

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()
}
Mohsen Mirhoseini
quelle
Ihre Lösung (im Link) enthält, ?wo @NonNullin Java steht.
CoolMind
1
Dies sieht schrecklich aus, als würde Java automatisch in Kotlin konvertiert. Besonders die setDrawableMethode ...
Marc Plano-Lesay
-2

Wenn Sie bereits eingestellt haben mit:

    DividerItemDecoration dividerItemDecoration;
    dividerItemDecoration  = new DividerItemDecoration(context, 
    DividerItemDecoration.VERTICAL);
    mRecyclerView.addItemDecoration(dividerItemDecoration);

und wenn Sie entfernen möchten:

    mRecyclerView.removeItemDecoration(dividerItemDecoration);
Amanpreet Singh
quelle