Come faccio a far funzionare WRAP_CONTENT su un RecyclerView


181

Ho un DialogFragmentche contiene un RecyclerView(un elenco di carte).

All'interno di questo ci RecyclerViewsono uno o più CardViewsche possono avere qualsiasi altezza.

Voglio dare a questo DialogFragmentl'altezza corretta in base a CardViewsciò che è contenuto all'interno.

Normalmente questo sarebbe semplice, vorrei impostare wrap_contentilRecyclerView come questo.

<android.support.v7.widget.RecyclerView ...
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"   
    android:clickable="true"   
    android:scrollbars="vertical" >

</android.support.v7.widget.RecyclerView>

Perché sto usando un RecyclerView questo non funziona, vedi:

https://issuetracker.google.com/issues/37001674

e

L'altezza della vista del riciclatore nidificato non ne racchiude il contenuto

In entrambe queste pagine le persone suggeriscono di estendere LinearLayoutManager e ignorareonMeasure()

Ho usato per la prima volta il LayoutManager fornito da qualcuno nel primo link:

public static class WrappingLayoutManager extends LinearLayoutManager {

        public WrappingLayoutManager(Context context) {
            super(context);
        }

        private int[] mMeasuredDimension = new int[2];

        @Override
        public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                              int widthSpec, int heightSpec) {
            final int widthMode = View.MeasureSpec.getMode(widthSpec);
            final int heightMode = View.MeasureSpec.getMode(heightSpec);
            final int widthSize = View.MeasureSpec.getSize(widthSpec);
            final int heightSize = View.MeasureSpec.getSize(heightSpec);

            measureScrapChild(recycler, 0,
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);

            int width = mMeasuredDimension[0];
            int height = mMeasuredDimension[1];

            switch (widthMode) {
                case View.MeasureSpec.EXACTLY:
                case View.MeasureSpec.AT_MOST:
                    width = widthSize;
                    break;
                case View.MeasureSpec.UNSPECIFIED:
            }

            switch (heightMode) {
                case View.MeasureSpec.EXACTLY:
                case View.MeasureSpec.AT_MOST:
                    height = heightSize;
                    break;
                case View.MeasureSpec.UNSPECIFIED:
            }

            setMeasuredDimension(width, height);
        }

        private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                       int heightSpec, int[] measuredDimension) {
            View view = recycler.getViewForPosition(position);
            if (view != null) {
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                        getPaddingLeft() + getPaddingRight(), p.width);
                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                        getPaddingTop() + getPaddingBottom(), p.height);
                view.measure(childWidthSpec, childHeightSpec);
                measuredDimension[0] = view.getMeasuredWidth();
                measuredDimension[1] = view.getMeasuredHeight();
                recycler.recycleView(view);
            }
        }
    }

Tuttavia, questo non ha funzionato perché

heightSize = View.MeasureSpec.getSize(heightSpec);

restituisce un valore molto grande a cui sembra essere correlato match_parent .

Commentando height = heightSize;(nel secondo caso switch) sono riuscito a far funzionare l'altezza ma solo se un TextViewbambino all'interno diCardView non avvolge il proprio testo (una lunga frase).

Non appena quello TextView termina il testo, l'altezza DOVREBBE aumentare, ma non lo è. Ha calcolato l'altezza per quella frase lunga come una linea singola, non una linea avvolta (2 o più).

Qualche consiglio su come dovrei migliorare questo in LayoutManagermodo che il mio RecyclerViewlavori WRAP_CONTENT?

Modifica: questo gestore del layout potrebbe funzionare per la maggior parte delle persone, ma ha ancora problemi con lo scorrimento e il calcolo delle altezze del wrapping delle visualizzazioni di testo

public class MyLinearLayoutManager extends LinearLayoutManager {

public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout)    {
    super(context, orientation, reverseLayout);
}

private int[] mMeasuredDimension = new int[2];

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                      int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int width = 0;
    int height = 0;
    for (int i = 0; i < getItemCount(); i++) {
        measureScrapChild(recycler, i,
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);

        if (getOrientation() == HORIZONTAL) {
            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            height = height + mMeasuredDimension[1];
            if (i == 0) {
                width = mMeasuredDimension[0];
            }
        }
    }
    switch (widthMode) {
        case View.MeasureSpec.EXACTLY:
            width = widthSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    switch (heightMode) {
        case View.MeasureSpec.EXACTLY:
            height = heightSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    setMeasuredDimension(width, height);
}

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        View view = recycler.getViewForPosition(position);
        if (view != null) {
            RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
            int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                    getPaddingLeft() + getPaddingRight(), p.width);
            int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom(), p.height);
            view.measure(childWidthSpec, childHeightSpec);
            measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
            measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
            recycler.recycleView(view);
        }
    }
}

4
Sembra che Google sia finalmente riuscito a risolverlo :Jan 22, 2016: This has been merged into the internal tree, should be available with the next version of support library.
Marcin Orlowski,

Risposte:


187

Dalla libreria di supporto Android 23.2.1 aggiornamento, tutti wrap_content dovrebbe funzionare correttamente.

Aggiorna la versione di una libreria nel gradlefile OPPURE per ulteriori informazioni:

compile 'com.android.support:recyclerview-v7:23.2.1'

risolti alcuni problemi come bug corretti relativi a vari metodi di misura specifica

Controlla http://developer.android.com/tools/support-library/features.html#v7-recyclerview

puoi controllare la cronologia delle revisioni della libreria di supporto


Stavo usando un LayoutManager e immediatamente l'ho aggiornato 23.2.0inizia a bloccare la mia app. Quindi, ho seguito la tua risposta e il mio RecyclerViewnon è avvolgente. Perché?
Abubakar Oladeji,

3
Fisso. Ho scritto il mio LayoutManager personalizzato in wrap_content degli elementi nella vista Riciclatore ma dopo l'aggiornamento ha iniziato a bloccarsi con un'eccezione basata sull'indice. E quando l'ho rimosso, non si è arrestato di nuovo in modo anomalo, ma la mia vista non si è conclusa finché non ho inizializzato la vista Riciclatore con un LinearLayoutManager.
Abubakar Oladeji,

25
Non dimenticare di chiamare mRecyclerView.setNestedScrollingEnabled(false);altrimenti la vista del riciclatore gestirà comunque lo scorrimento stesso invece di passare gli eventi ai genitori.
WindRider

4
Wrap-Content per RecyclerView non è ancora del tutto supportato. Dai
Elye il

1
Questa non è la soluzione per il problema dato. Anche nella versione 27 di Support non funziona. Nella prossima risposta di orange01 viene detto alla soluzione di avvolgere RecyclerView all'interno di un RelativeLayout.
Christian,

65

AGGIORNAMENTO 02.07.2020
Questo metodo può impedire il riciclaggio e non deve essere utilizzato su set di dati di grandi dimensioni .

AGGIORNAMENTO 05.07.2019

Se stai usando RecyclerViewall'interno di a ScrollView, cambia ScrollViewin androidx.core.widget.NestedScrollView. All'interno di questa vista non è necessario impacchettare RecyclerViewall'interno di a RelativeLayout.

<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <!-- other views -->

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <!-- other views -->

    </LinearLayout>

</androidx.core.widget.NestedScrollView>

Finalmente ho trovato la soluzione per questo problema.

Tutto quello che devi fare è avvolgere RecyclerViewin a RelativeLayout. Forse ci sono altre viste che potrebbero funzionare.

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

4
non so perché la tua soluzione sia giusta. Ma funziona per me. Grazie mille <3
My Will

1
Lo stesso .. ha provato un sacco di cose ma in qualche modo ha funzionato. Non ho idea del perché o del come. Grazie mille! FrameLayout non ha funzionato per me, ma RelativeLayout ha funzionato.
sarora,

2
Questo ha funzionato anche per me quando stavo usando RecyclerView all'interno di un ConstraintLayout.
Allan Veloso,

questo non ha funzionato per me fino a quando ho impostato sia altezza che larghezza per recyclerview su "match_parent"
darkrider1287

Questo ha funzionato per me, ho anche un ricordo quando l'ho visto, l'ho usato in passato, grazie @ orange01 !!
Ivor,

53

Ecco la versione raffinata della classe che sembra funzionare e manca di problemi con altre soluzioni:

package org.solovyev.android.views.llm;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

/**
 * {@link android.support.v7.widget.LinearLayoutManager} which wraps its content. Note that this class will always
 * wrap the content regardless of {@link android.support.v7.widget.RecyclerView} layout parameters.
 *
 * Now it's impossible to run add/remove animations with child views which have arbitrary dimensions (height for
 * VERTICAL orientation and width for HORIZONTAL). However if child views have fixed dimensions
 * {@link #setChildSize(int)} method might be used to let the layout manager know how big they are going to be.
 * If animations are not used at all then a normal measuring procedure will run and child views will be measured during
 * the measure pass.
 */
public class LinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

    private static final int CHILD_WIDTH = 0;
    private static final int CHILD_HEIGHT = 1;
    private static final int DEFAULT_CHILD_SIZE = 100;

    private final int[] childDimensions = new int[2];

    private int childSize = DEFAULT_CHILD_SIZE;
    private boolean hasChildSize;

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context) {
        super(context);
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public static int makeUnspecifiedSpec() {
        return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);

        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
        final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;

        final int unspecified = makeUnspecifiedSpec();

        if (exactWidth && exactHeight) {
            // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
            super.onMeasure(recycler, state, widthSpec, heightSpec);
            return;
        }

        final boolean vertical = getOrientation() == VERTICAL;

        initChildDimensions(widthSize, heightSize, vertical);

        int width = 0;
        int height = 0;

        // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
        // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
        // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
        // called whiles scrolling)
        recycler.clear();

        final int stateItemCount = state.getItemCount();
        final int adapterItemCount = getItemCount();
        // adapter always contains actual data while state might contain old data (f.e. data before the animation is
        // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
        // state
        for (int i = 0; i < adapterItemCount; i++) {
            if (vertical) {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, widthSpec, unspecified, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                height += childDimensions[CHILD_HEIGHT];
                if (i == 0) {
                    width = childDimensions[CHILD_WIDTH];
                }
                if (height >= heightSize) {
                    break;
                }
            } else {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, unspecified, heightSpec, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                width += childDimensions[CHILD_WIDTH];
                if (i == 0) {
                    height = childDimensions[CHILD_HEIGHT];
                }
                if (width >= widthSize) {
                    break;
                }
            }
        }

        if ((vertical && height < heightSize) || (!vertical && width < widthSize)) {
            // we really should wrap the contents of the view, let's do it

            if (exactWidth) {
                width = widthSize;
            } else {
                width += getPaddingLeft() + getPaddingRight();
            }

            if (exactHeight) {
                height = heightSize;
            } else {
                height += getPaddingTop() + getPaddingBottom();
            }

            setMeasuredDimension(width, height);
        } else {
            // if calculated height/width exceeds requested height/width let's use default "onMeasure" implementation
            super.onMeasure(recycler, state, widthSpec, heightSpec);
        }
    }

    private void logMeasureWarning(int child) {
        if (BuildConfig.DEBUG) {
            Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                    "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
        }
    }

    private void initChildDimensions(int width, int height, boolean vertical) {
        if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
            // already initialized, skipping
            return;
        }
        if (vertical) {
            childDimensions[CHILD_WIDTH] = width;
            childDimensions[CHILD_HEIGHT] = childSize;
        } else {
            childDimensions[CHILD_WIDTH] = childSize;
            childDimensions[CHILD_HEIGHT] = height;
        }
    }

    @Override
    public void setOrientation(int orientation) {
        // might be called before the constructor of this class is called
        //noinspection ConstantConditions
        if (childDimensions != null) {
            if (getOrientation() != orientation) {
                childDimensions[CHILD_WIDTH] = 0;
                childDimensions[CHILD_HEIGHT] = 0;
            }
        }
        super.setOrientation(orientation);
    }

    public void clearChildSize() {
        hasChildSize = false;
        setChildSize(DEFAULT_CHILD_SIZE);
    }

    public void setChildSize(int childSize) {
        hasChildSize = true;
        if (this.childSize != childSize) {
            this.childSize = childSize;
            requestLayout();
        }
    }

    private void measureChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] dimensions) {
        final View child = recycler.getViewForPosition(position);

        final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();

        final int hPadding = getPaddingLeft() + getPaddingRight();
        final int vPadding = getPaddingTop() + getPaddingBottom();

        final int hMargin = p.leftMargin + p.rightMargin;
        final int vMargin = p.topMargin + p.bottomMargin;

        final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
        final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

        final int childWidthSpec = getChildMeasureSpec(widthSpec, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
        final int childHeightSpec = getChildMeasureSpec(heightSpec, vPadding + vMargin + vDecoration, p.height, canScrollVertically());

        child.measure(childWidthSpec, childHeightSpec);

        dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
        dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;

        recycler.recycleView(child);
    }
}

Questo è disponibile anche come libreria . Link alla classe pertinente .


Funziona perfettamente per me. Grazie.
natario,

10
Per favore, usa una versione aggiornata di github poiché è cambiata molto da quando ho pubblicato la risposta.
se.solovyev,

grazie per il tuo lavoro. Sto avendo problemi a lavorare con bambini di diversa altezza. Se ho 10 figli 150dp, funziona; se uno di questi è 300dp, l'ultimo sarà nascosto. Qualche idea?
natario,

3
Più specificamente, mi sento come se misura i bambini prima che onBindViewHolder()venga chiamato. Questo è male perché a quel punto chiamo, ad esempio, in holder.textView.setText(longText)modo che quel bambino diventi più alto, ma non si riflette sull'altezza del riciclatore. Se hai qualche idea (come modifiche rapide all'adattatore), te ne sarei grato.
natario,

Grazie. La tua risposta / biblioteca mi ha aiutato a superare il problema dell'altezza dell'orizzontale RecyclerViewall'interno verticaleRecyclerView .
Sufian,

16

AGGIORNARE

Con l'aggiornamento della libreria di supporto Android 23.2, tutti i WRAP_CONTENT dovrebbero funzionare correttamente.

Aggiorna la versione di una libreria nel file gradle.

compile 'com.android.support:recyclerview-v7:23.2.0'

Risposta originale

Come ho risposto a un'altra domanda, è necessario utilizzare il metodo onMeasure () originale quando l'altezza della vista del riciclatore è maggiore dell'altezza dello schermo. Questo gestore di layout può calcolare ItemDecoration e può scorrere con altro.

    public class MyLinearLayoutManager extends LinearLayoutManager {

public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout)    {
    super(context, orientation, reverseLayout);
}

private int[] mMeasuredDimension = new int[2];

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                      int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int width = 0;
    int height = 0;
    for (int i = 0; i < getItemCount(); i++) {
        measureScrapChild(recycler, i,
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);

        if (getOrientation() == HORIZONTAL) {
            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            height = height + mMeasuredDimension[1];
            if (i == 0) {
                width = mMeasuredDimension[0];
            }
        }
    }

    // If child view is more than screen size, there is no need to make it wrap content. We can use original onMeasure() so we can scroll view.
    if (height < heightSize && width < widthSize) {

        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        setMeasuredDimension(width, height);
    } else {
        super.onMeasure(recycler, state, widthSpec, heightSpec);
    }
}

private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                               int heightSpec, int[] measuredDimension) {

   View view = recycler.getViewForPosition(position);

   // For adding Item Decor Insets to view
   super.measureChildWithMargins(view, 0, 0);
    if (view != null) {
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                    getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view), p.width);
            int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom() + getPaddingBottom() + getDecoratedBottom(view) , p.height);
            view.measure(childWidthSpec, childHeightSpec);

            // Get decorated measurements
            measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
            measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
            recycler.recycleView(view);
        }
    }
}

risposta originale: https://stackoverflow.com/a/28510031/1577792


2
Come possiamo ottenere lo stesso gridlayoutmanagere staggeredgridlayoutmanagerconsiderare il conteggio degli span in mente
Amrut Bidri,

10

Ecco la versione c # per Android mono

/* 
* Ported by Jagadeesh Govindaraj (@jaganjan)
 *Copyright 2015 serso aka se.solovyev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Contact details
 *
 * Email: se.solovyev @gmail.com
 * Site:  http://se.solovyev.org
 */


using Android.Content;
using Android.Graphics;
using Android.Support.V4.View;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Java.Lang;
using Java.Lang.Reflect;
using System;
using Math = Java.Lang.Math;

namespace Droid.Helper
{
    public class WrapLayoutManager : LinearLayoutManager
    {
        private const int DefaultChildSize = 100;
        private static readonly Rect TmpRect = new Rect();
        private int _childSize = DefaultChildSize;
        private static bool _canMakeInsetsDirty = true;
        private static readonly int[] ChildDimensions = new int[2];
        private const int ChildHeight = 1;
        private const int ChildWidth = 0;
        private static bool _hasChildSize;
        private static  Field InsetsDirtyField = null;
        private static int _overScrollMode = ViewCompat.OverScrollAlways;
        private static RecyclerView _view;

        public WrapLayoutManager(Context context, int orientation, bool reverseLayout)
            : base(context, orientation, reverseLayout)
        {
            _view = null;
        }

        public WrapLayoutManager(Context context) : base(context)
        {
            _view = null;
        }

        public WrapLayoutManager(RecyclerView view) : base(view.Context)
        {
            _view = view;
            _overScrollMode = ViewCompat.GetOverScrollMode(view);
        }

        public WrapLayoutManager(RecyclerView view, int orientation, bool reverseLayout)
            : base(view.Context, orientation, reverseLayout)
        {
            _view = view;
            _overScrollMode = ViewCompat.GetOverScrollMode(view);
        }

        public void SetOverScrollMode(int overScrollMode)
        {
            if (overScrollMode < ViewCompat.OverScrollAlways || overScrollMode > ViewCompat.OverScrollNever)
                throw new ArgumentException("Unknown overscroll mode: " + overScrollMode);
            if (_view == null) throw new ArgumentNullException(nameof(_view));
            _overScrollMode = overScrollMode;
            ViewCompat.SetOverScrollMode(_view, overScrollMode);
        }

        public static int MakeUnspecifiedSpec()
        {
            return View.MeasureSpec.MakeMeasureSpec(0, MeasureSpecMode.Unspecified);
        }

        public override void OnMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec,
            int heightSpec)
        {
            var widthMode = View.MeasureSpec.GetMode(widthSpec);
            var heightMode = View.MeasureSpec.GetMode(heightSpec);

            var widthSize = View.MeasureSpec.GetSize(widthSpec);
            var heightSize = View.MeasureSpec.GetSize(heightSpec);

            var hasWidthSize = widthMode != MeasureSpecMode.Unspecified;
            var hasHeightSize = heightMode != MeasureSpecMode.Unspecified;

            var exactWidth = widthMode == MeasureSpecMode.Exactly;
            var exactHeight = heightMode == MeasureSpecMode.Exactly;

            var unspecified = MakeUnspecifiedSpec();

            if (exactWidth && exactHeight)
            {
                // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
                base.OnMeasure(recycler, state, widthSpec, heightSpec);
                return;
            }

            var vertical = Orientation == Vertical;

            InitChildDimensions(widthSize, heightSize, vertical);

            var width = 0;
            var height = 0;

            // it's possible to get scrap views in recycler which are bound to old (invalid) adapter
            // entities. This happens because their invalidation happens after "onMeasure" method.
            // As a workaround let's clear the recycler now (it should not cause any performance
            // issues while scrolling as "onMeasure" is never called whiles scrolling)
            recycler.Clear();

            var stateItemCount = state.ItemCount;
            var adapterItemCount = ItemCount;
            // adapter always contains actual data while state might contain old data (f.e. data
            // before the animation is done). As we want to measure the view with actual data we
            // must use data from the adapter and not from the state
            for (var i = 0; i < adapterItemCount; i++)
            {
                if (vertical)
                {
                    if (!_hasChildSize)
                    {
                        if (i < stateItemCount)
                        {
                            // we should not exceed state count, otherwise we'll get
                            // IndexOutOfBoundsException. For such items we will use previously
                            // calculated dimensions
                            MeasureChild(recycler, i, widthSize, unspecified, ChildDimensions);
                        }
                        else
                        {
                            LogMeasureWarning(i);
                        }
                    }
                    height += ChildDimensions[ChildHeight];
                    if (i == 0)
                    {
                        width = ChildDimensions[ChildWidth];
                    }
                    if (hasHeightSize && height >= heightSize)
                    {
                        break;
                    }
                }
                else
                {
                    if (!_hasChildSize)
                    {
                        if (i < stateItemCount)
                        {
                            // we should not exceed state count, otherwise we'll get
                            // IndexOutOfBoundsException. For such items we will use previously
                            // calculated dimensions
                            MeasureChild(recycler, i, unspecified, heightSize, ChildDimensions);
                        }
                        else
                        {
                            LogMeasureWarning(i);
                        }
                    }
                    width += ChildDimensions[ChildWidth];
                    if (i == 0)
                    {
                        height = ChildDimensions[ChildHeight];
                    }
                    if (hasWidthSize && width >= widthSize)
                    {
                        break;
                    }
                }
            }

            if (exactWidth)
            {
                width = widthSize;
            }
            else
            {
                width += PaddingLeft + PaddingRight;
                if (hasWidthSize)
                {
                    width = Math.Min(width, widthSize);
                }
            }

            if (exactHeight)
            {
                height = heightSize;
            }
            else
            {
                height += PaddingTop + PaddingBottom;
                if (hasHeightSize)
                {
                    height = Math.Min(height, heightSize);
                }
            }

            SetMeasuredDimension(width, height);

            if (_view == null || _overScrollMode != ViewCompat.OverScrollIfContentScrolls) return;
            var fit = (vertical && (!hasHeightSize || height < heightSize))
                      || (!vertical && (!hasWidthSize || width < widthSize));

            ViewCompat.SetOverScrollMode(_view, fit ? ViewCompat.OverScrollNever : ViewCompat.OverScrollAlways);
        }

        private void LogMeasureWarning(int child)
        {
#if DEBUG
            Log.WriteLine(LogPriority.Warn, "LinearLayoutManager",
                "Can't measure child #" + child + ", previously used dimensions will be reused." +
                "To remove this message either use #SetChildSize() method or don't run RecyclerView animations");
#endif
        }

        private void InitChildDimensions(int width, int height, bool vertical)
        {
            if (ChildDimensions[ChildWidth] != 0 || ChildDimensions[ChildHeight] != 0)
            {
                // already initialized, skipping
                return;
            }
            if (vertical)
            {
                ChildDimensions[ChildWidth] = width;
                ChildDimensions[ChildHeight] = _childSize;
            }
            else
            {
                ChildDimensions[ChildWidth] = _childSize;
                ChildDimensions[ChildHeight] = height;
            }
        }

        public void ClearChildSize()
        {
            _hasChildSize = false;
            SetChildSize(DefaultChildSize);
        }

        public void SetChildSize(int size)
        {
            _hasChildSize = true;
            if (_childSize == size) return;
            _childSize = size;
            RequestLayout();
        }

        private void MeasureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize,
            int[] dimensions)
        {
            View child = null;
            try
            {
                child = recycler.GetViewForPosition(position);
            }
            catch (IndexOutOfRangeException e)
            {
                Log.WriteLine(LogPriority.Warn, "LinearLayoutManager",
                    "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);
            }

            if (child != null)
            {
                var p = child.LayoutParameters.JavaCast<RecyclerView.LayoutParams>()

                var hPadding = PaddingLeft + PaddingRight;
                var vPadding = PaddingTop + PaddingBottom;

                var hMargin = p.LeftMargin + p.RightMargin;
                var vMargin = p.TopMargin + p.BottomMargin;

                // we must make insets dirty in order calculateItemDecorationsForChild to work
                MakeInsetsDirty(p);
                // this method should be called before any getXxxDecorationXxx() methods
                CalculateItemDecorationsForChild(child, TmpRect);

                var hDecoration = GetRightDecorationWidth(child) + GetLeftDecorationWidth(child);
                var vDecoration = GetTopDecorationHeight(child) + GetBottomDecorationHeight(child);

                var childWidthSpec = GetChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.Width,
                    CanScrollHorizontally());
                var childHeightSpec = GetChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.Height,
                    CanScrollVertically());

                child.Measure(childWidthSpec, childHeightSpec);

                dimensions[ChildWidth] = GetDecoratedMeasuredWidth(child) + p.LeftMargin + p.RightMargin;
                dimensions[ChildHeight] = GetDecoratedMeasuredHeight(child) + p.BottomMargin + p.TopMargin;

                // as view is recycled let's not keep old measured values
                MakeInsetsDirty(p);
            }
            recycler.RecycleView(child);
        }

        private static void MakeInsetsDirty(RecyclerView.LayoutParams p)
        {
            if (!_canMakeInsetsDirty)
            {
                return;
            }
            try
            {
                if (InsetsDirtyField == null)
                {
                   var klass = Java.Lang.Class.FromType (typeof (RecyclerView.LayoutParams));
                    InsetsDirtyField = klass.GetDeclaredField("mInsetsDirty");
                    InsetsDirtyField.Accessible = true;
                }
                InsetsDirtyField.Set(p, true);
            }
            catch (NoSuchFieldException e)
            {
                OnMakeInsertDirtyFailed();
            }
            catch (IllegalAccessException e)
            {
                OnMakeInsertDirtyFailed();
            }
        }

        private static void OnMakeInsertDirtyFailed()
        {
            _canMakeInsetsDirty = false;
#if DEBUG
            Log.Warn("LinearLayoutManager",
                "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
#endif
        }
    }
}

quasi ... sostituisci var p = (RecyclerView.LayoutParams) child.LayoutParametersconvar p = child.LayoutParameters.JavaCast<RecyclerView.LayoutParams>()
Roubachof il

perché dichiarate statiche le variabili membro?
Esskar,

@esskar penso che la variabile sia usata in metodi statici, guarda la versione java se hai dei dubbi
Jagadeesh Govindaraj,

6

RecyclerViewaggiunto il supporto per wrap_contentin 23.2.0cui era buggy, 23.2.1 era solo stabile, in modo da poter utilizzare:

compile 'com.android.support:recyclerview-v7:24.2.0'

Puoi vedere la cronologia delle revisioni qui:

https://developer.android.com/topic/libraries/support-library/revisions.html

Nota:

Si noti inoltre che dopo aver aggiornato la libreria di supporto, RecyclerViewsarà rispettato wrap_content, così come match_parentse si dispone di una vista elemento di un RecyclerViewset poiché match_parentla vista singola riempirà l'intero schermo


1
@Yvette ok certo
ColdFusion


4

Il problema con lo scorrimento e il wrapping del testo è che questo codice presuppone che sia la larghezza che l'altezza siano impostate su wrap_content. Tuttavia, è LayoutManagernecessario sapere che la larghezza orizzontale è limitata. Quindi, invece di creare il tuo widthSpecper ogni vista figlio, usa l'originale widthSpec:

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int width = 0;
    int height = 0;
    for (int i = 0; i < getItemCount(); i++) {
        measureScrapChild(recycler, i,
                widthSpec,
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);

        if (getOrientation() == HORIZONTAL) {
            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            height = height + mMeasuredDimension[1];
            if (i == 0) {
                width = mMeasuredDimension[0];
            }
        }
    }
    switch (widthMode) {
        case View.MeasureSpec.EXACTLY:
            width = widthSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    switch (heightMode) {
        case View.MeasureSpec.EXACTLY:
            height = heightSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    setMeasuredDimension(width, height);
}

private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,int heightSpec, int[] measuredDimension) {
    View view = recycler.getViewForPosition(position);
    if (view != null) {
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom(), p.height);
        view.measure(widthSpec, childHeightSpec);
        measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
        measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
        recycler.recycleView(view);
    }
}

3

Prova questo (è una brutta soluzione ma può funzionare): nel onCreatemetodo del tuo Activityo nel onViewCreatedmetodo del tuo frammento. Imposta un callback pronto per essere attivato al RecyclerViewprimo rendering, in questo modo:

vRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                calculeRecyclerViewFullHeight();
            }
        });

Nel calculeRecyclerViewFullHeightcalcolare l' RecyclerViewaltezza completa in base all'altezza dei suoi figli.

protected void calculateSwipeRefreshFullHeight() {
        int height = 0;
        for (int idx = 0; idx < getRecyclerView().getChildCount(); idx++ ) {
            View v = getRecyclerView().getChildAt(idx);
            height += v.getHeight();
        }
        SwipeRefreshLayout.LayoutParams params = getSwipeRefresh().getLayoutParams();
        params.height = height;
        getSwipeRefresh().setLayoutParams(params);
    }

Nel mio caso il mio RecyclerViewè contenere in un SwipeRefreshLayoutper quel motivo sto impostando l'altezza su SwipeRefreshViewe non su RecyclerViewma se non ne SwipeRefreshViewhai, puoi impostare l'altezza suRecyclerView invece.

Fammi sapere se questo ti ha aiutato o no.


Come posso ottenere il metodo getRecyclerView ()?
asubanovsky,

@asubanovsky È un metodo che restituisce solo la tua RecyclerViewistanza.
4gus71n,

2
Ricorda di rimuovere globalLayoutListener in onGlobalLayout ()
Matthew

3

Ora funziona come hanno rilasciato una versione 23.2, come indicato in questo post . Citando il blogpost ufficiale

Questa versione offre una nuova entusiasmante funzionalità all'API LayoutManager: l'auto-misurazione! Ciò consente a RecyclerView di ridimensionarsi in base alla dimensione del suo contenuto. Ciò significa che sono ora possibili scenari precedentemente non disponibili, come l'utilizzo di WRAP_CONTENT per una dimensione di RecyclerView. Ora tutti i LayoutManager integrati supportano la misurazione automatica.


3

Inserisci semplicemente RecyclerView in un NestedScrollView. Funziona perfettamente

<android.support.v4.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="25dp">
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/kliste"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
            </android.support.v4.widget.NestedScrollView>

2

Avevo usato alcune delle soluzioni di cui sopra, ma funzionava per widthma height.

  1. Se hai specificato un numero compileSdkVersionmaggiore di 23 , puoi utilizzare direttamente RecyclerView fornito nelle rispettive librerie di supporto della vista Riciclatore, come per 23 sarà 'com.android.support:recyclerview-v7:23.2.1'. Queste librerie di supporto supportano gli attributi di wrap_contentlarghezza e altezza.

Devi aggiungerlo alle tue dipendenze

compile 'com.android.support:recyclerview-v7:23.2.1'
  1. Se hai compileSdkVersionmeno di 23 anni , puoi utilizzare la soluzione di seguito indicata.

Ho trovato questo thread di Google relativo a questo problema. In questo thread, c'è un contributo che porta all'implementazione di LinearLayoutManager .

L'ho provato sia in altezza che in larghezza e ha funzionato bene in entrambi i casi.

/*
 * Copyright 2015 serso aka se.solovyev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Contact details
 *
 * Email: se.solovyev@gmail.com
 * Site:  http://se.solovyev.org
 */

package org.solovyev.android.views.llm;

import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

import java.lang.reflect.Field;

/**
 * {@link android.support.v7.widget.LinearLayoutManager} which wraps its content. Note that this class will always
 * wrap the content regardless of {@link android.support.v7.widget.RecyclerView} layout parameters.
 * <p/>
 * Now it's impossible to run add/remove animations with child views which have arbitrary dimensions (height for
 * VERTICAL orientation and width for HORIZONTAL). However if child views have fixed dimensions
 * {@link #setChildSize(int)} method might be used to let the layout manager know how big they are going to be.
 * If animations are not used at all then a normal measuring procedure will run and child views will be measured during
 * the measure pass.
 */
public class LinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

    private static boolean canMakeInsetsDirty = true;
    private static Field insetsDirtyField = null;

    private static final int CHILD_WIDTH = 0;
    private static final int CHILD_HEIGHT = 1;
    private static final int DEFAULT_CHILD_SIZE = 100;

    private final int[] childDimensions = new int[2];
    private final RecyclerView view;

    private int childSize = DEFAULT_CHILD_SIZE;
    private boolean hasChildSize;
    private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
    private final Rect tmpRect = new Rect();

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context) {
        super(context);
        this.view = null;
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        this.view = null;
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(RecyclerView view) {
        super(view.getContext());
        this.view = view;
        this.overScrollMode = ViewCompat.getOverScrollMode(view);
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
        super(view.getContext(), orientation, reverseLayout);
        this.view = view;
        this.overScrollMode = ViewCompat.getOverScrollMode(view);
    }

    public void setOverScrollMode(int overScrollMode) {
        if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
            throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
        if (this.view == null) throw new IllegalStateException("view == null");
        this.overScrollMode = overScrollMode;
        ViewCompat.setOverScrollMode(view, overScrollMode);
    }

    public static int makeUnspecifiedSpec() {
        return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);

        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
        final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;

        final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
        final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;

        final int unspecified = makeUnspecifiedSpec();

        if (exactWidth && exactHeight) {
            // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
            super.onMeasure(recycler, state, widthSpec, heightSpec);
            return;
        }

        final boolean vertical = getOrientation() == VERTICAL;

        initChildDimensions(widthSize, heightSize, vertical);

        int width = 0;
        int height = 0;

        // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
        // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
        // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
        // called whiles scrolling)
        recycler.clear();

        final int stateItemCount = state.getItemCount();
        final int adapterItemCount = getItemCount();
        // adapter always contains actual data while state might contain old data (f.e. data before the animation is
        // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
        // state
        for (int i = 0; i < adapterItemCount; i++) {
            if (vertical) {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, widthSize, unspecified, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                height += childDimensions[CHILD_HEIGHT];
                if (i == 0) {
                    width = childDimensions[CHILD_WIDTH];
                }
                if (hasHeightSize && height >= heightSize) {
                    break;
                }
            } else {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, unspecified, heightSize, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                width += childDimensions[CHILD_WIDTH];
                if (i == 0) {
                    height = childDimensions[CHILD_HEIGHT];
                }
                if (hasWidthSize && width >= widthSize) {
                    break;
                }
            }
        }

        if (exactWidth) {
            width = widthSize;
        } else {
            width += getPaddingLeft() + getPaddingRight();
            if (hasWidthSize) {
                width = Math.min(width, widthSize);
            }
        }

        if (exactHeight) {
            height = heightSize;
        } else {
            height += getPaddingTop() + getPaddingBottom();
            if (hasHeightSize) {
                height = Math.min(height, heightSize);
            }
        }

        setMeasuredDimension(width, height);

        if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
            final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
                    || (!vertical && (!hasWidthSize || width < widthSize));

            ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
        }
    }

    private void logMeasureWarning(int child) {
        if (BuildConfig.DEBUG) {
            Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                    "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
        }
    }

    private void initChildDimensions(int width, int height, boolean vertical) {
        if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
            // already initialized, skipping
            return;
        }
        if (vertical) {
            childDimensions[CHILD_WIDTH] = width;
            childDimensions[CHILD_HEIGHT] = childSize;
        } else {
            childDimensions[CHILD_WIDTH] = childSize;
            childDimensions[CHILD_HEIGHT] = height;
        }
    }

    @Override
    public void setOrientation(int orientation) {
        // might be called before the constructor of this class is called
        //noinspection ConstantConditions
        if (childDimensions != null) {
            if (getOrientation() != orientation) {
                childDimensions[CHILD_WIDTH] = 0;
                childDimensions[CHILD_HEIGHT] = 0;
            }
        }
        super.setOrientation(orientation);
    }

    public void clearChildSize() {
        hasChildSize = false;
        setChildSize(DEFAULT_CHILD_SIZE);
    }

    public void setChildSize(int childSize) {
        hasChildSize = true;
        if (this.childSize != childSize) {
            this.childSize = childSize;
            requestLayout();
        }
    }

    private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
        final View child;
        try {
            child = recycler.getViewForPosition(position);
        } catch (IndexOutOfBoundsException e) {
            if (BuildConfig.DEBUG) {
                Log.w("LinearLayoutManager", "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);
            }
            return;
        }

        final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();

        final int hPadding = getPaddingLeft() + getPaddingRight();
        final int vPadding = getPaddingTop() + getPaddingBottom();

        final int hMargin = p.leftMargin + p.rightMargin;
        final int vMargin = p.topMargin + p.bottomMargin;

        // we must make insets dirty in order calculateItemDecorationsForChild to work
        makeInsetsDirty(p);
        // this method should be called before any getXxxDecorationXxx() methods
        calculateItemDecorationsForChild(child, tmpRect);

        final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
        final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

        final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
        final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());

        child.measure(childWidthSpec, childHeightSpec);

        dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
        dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;

        // as view is recycled let's not keep old measured values
        makeInsetsDirty(p);
        recycler.recycleView(child);
    }

    private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
        if (!canMakeInsetsDirty) {
            return;
        }
        try {
            if (insetsDirtyField == null) {
                insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
                insetsDirtyField.setAccessible(true);
            }
            insetsDirtyField.set(p, true);
        } catch (NoSuchFieldException e) {
            onMakeInsertDirtyFailed();
        } catch (IllegalAccessException e) {
            onMakeInsertDirtyFailed();
        }
    }

    private static void onMakeInsertDirtyFailed() {
        canMakeInsetsDirty = false;
        if (BuildConfig.DEBUG) {
            Log.w("LinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
        }
    }
}


1

ti suggerisco di inserire il recyclerview in qualsiasi altro layout (è preferibile il layout relativo). Quindi modifica l'altezza / larghezza del recyclerview come padre principale per quel layout e imposta l'altezza / larghezza del layout padre come contenuto a capo. per me funziona


0

Sostituisci measureScrapChildper seguire il codice:

private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
        int heightSpec, int[] measuredDimension)
    {
        View view = recycler.GetViewForPosition(position);
        if (view != null)
        {
            MeasureChildWithMargins(view, widthSpec, heightSpec);
            measuredDimension[0] = view.MeasuredWidth;
            measuredDimension[1] = view.MeasuredHeight;
            recycler.RecycleView(view);
        }
    }

Uso xamarin, quindi questo è codice c #. Penso che questo possa essere facilmente "tradotto" in Java.


0

Aggiorna la tua vista con valore null invece del viewgroup principale nel metodo onCreateViewHolder del viewholder dell'adapter.

@Override
public AdapterItemSku.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = inflator.inflate(R.layout.layout_item, null, false);
    return new MyViewHolder(view);
}

0

Devi inserire un FrameLayout come Vista principale, quindi inserire un RelativeLayout con ScrollView e almeno il tuo RecyclerView, funziona per me.

Il vero trucco qui è il RelativeLayout ...

Felice di aiutare.


-1

Non ho lavorato sulla mia risposta, ma per come la conosco StaggridLayoutManager con no. della griglia 1 può risolvere il tuo problema in quanto StaggridLayout regolerà automaticamente la sua altezza e larghezza in base alle dimensioni del contenuto. se funziona, non dimenticare di controllarlo come una risposta giusta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.