Hoe laat ik WRAP_CONTENT werken op een RecyclerView

Ik heb een DialogFragmentmet een RecyclerView(een lijst met kaarten).

Binnen deze RecyclerViewbevinden zich een of meer CardViewsdie elke hoogte kunnen hebben.

Ik wil dit DialogFragmentde juiste hoogte geven op basis van de CardViewsdie erin zitten.

Normaal gesproken zou dit eenvoudig zijn, ik zou wrap_contentop de RecyclerViewzo instellen.

<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>

Omdat ik een RecyclerViewgebruik, werkt dit niet:

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

en

Hoogte van geneste Recycler-weergave omvat de inhoud niet

Op beide pagina’s stellen mensen voor om LinearLayoutManageruit te breiden en onMeasure()

te negeren

Ik heb eerst de LayoutManagergebruikt die iemand in de eerste link heeft gegeven:

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

Echter dit werkte nietomdat

heightSize = View.MeasureSpec.getSize(heightSpec);

retourneert een zeer grote waarde die gerelateerd lijkt te zijn aan match_parent.

Door height = heightSize;te becommentariëren (in de tweede schakelkast) slaagde ik erin om de hoogte te laten werken, maar alleen als een TextView-kind in de CardViewlaat zijn eigen tekst niet teruglopen (een lange zin).

Zodra die TextViewzijn eigen tekst omhult, MOET de hoogte toenemen, maar dat gebeurt niet. Het berekende de hoogte voor die lange zin als een enkele regel, niet als een omwikkelde regel (2 of meer).

Enig advies over hoe ik deze LayoutManagermoet verbeteren zodat mijn RecyclerViewwerkt met wrap_content?

Bewerken: deze lay-outmanager werkt misschien voor de meeste mensen, maar hij heeft nog steeds problemen met scrollen en het berekenen van de hoogte van teruglopende tekstweergaven

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

Antwoord 1, autoriteit 100%

Vanaf Android Support Library 23.2.1update, alle WRAP_CONTENTzou correct moeten werken.

Werk de versie van een bibliotheek bij in het gradle-bestand OFnaar meer:

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

een probleem opgelost, zoals Opgeloste bugs gerelateerd aan verschillende meet-specificatie-methoden

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

u kunt Ondersteuningsgeschiedenis van bibliotheekrevisies


Antwoord 2, autoriteit 49%

UPDATE 02.07.2020
Deze methode kan recycling voorkomen en mag niet worden gebruikt op grote datasets.

UPDATE 05.07.2019

Als je RecyclerViewgebruikt in een ScrollView, verander dan gewoon ScrollViewin androidx.core.widget.NestedScrollView. Binnen deze weergave is het niet nodig om RecyclerViewin een RelativeLayoutte stoppen.

<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>

Eindelijk de oplossing voor dit probleem gevonden.

Het enige wat je hoeft te doen is de RecyclerViewin een RelativeLayoutte stoppen. Misschien zijn er andere weergaven die ook kunnen werken.

<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>

Antwoord 3, autoriteit 28%

Hier is de verfijnde versie van de klasse die lijkt te werken en geen problemen heeft die andere oplossingen hebben:

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

Dit is ook beschikbaar als een bibliotheek. Link naar relevante les.


Antwoord 4, autoriteit 8%

UPDATE

Door de update van Android Support Library 23.2 zou alle WRAP_CONTENT correct moeten werken.

Update de versie van een bibliotheek in gradle-bestand.

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

Oorspronkelijk antwoord

Zoals beantwoord op een andere vraag, moet u de originele onMeasure()-methode gebruiken wanneer de weergavehoogte van uw recycler groter is dan de schermhoogte. Deze lay-outbeheerder kan ItemDecoration berekenen en met meer scrollen.

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

oorspronkelijk antwoord: https://stackoverflow.com/a/28510031/1577792


Antwoord 5, autoriteit 5%

Hier is de c#-versie voor mono-android

/* 
* 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
        }
    }
}

Antwoord 6, autoriteit 4%

Zet de recyclerview in een andere lay-out (relatieve lay-out is
wenselijk). Verander vervolgens de hoogte/breedte van recyclerview als match-ouder
naar die lay-out en stel de hoogte/breedte van de bovenliggende lay-out in als omloop
inhoud.

Bron: Deze opmerking.


Antwoord 7, autoriteit 3%

RecyclerViewheeft ondersteuning toegevoegd voor wrap_contentin 23.2.0die fouten bevatte, 23.2.1 was gewoon stabiel, dus je kunt gebruiken:

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

Je kunt de revisiegeschiedenis hier bekijken:

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

Opmerking:

Houd er rekening mee dat na het updaten van de ondersteuningsbibliotheek de RecyclerViewzowel wrap_contentals match_parentrespecteert, dus als je een itemweergave hebt van een RecyclerViewingesteld als match_parentde enkele weergave zal het hele scherm vullen


Antwoord 8, autoriteit 3%

Plaats uw RecyclerView gewoon in een NestedScrollView. Werkt perfect

<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>

Antwoord 9, autoriteit 2%

Het probleem met scrollen en tekstterugloop is dat deze code ervan uitgaat dat zowel de breedte als de hoogte zijn ingesteld op wrap_content. De LayoutManagermoet echter weten dat de horizontale breedte beperkt is. Dus in plaats van uw eigen widthSpecte maken voor elke onderliggende weergave, gebruikt u gewoon de originele 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);
    }
}

Antwoord 10, autoriteit 2%

Probeer dit (het is een vervelende oplossing, maar het kan werken):
In de onCreatemethode van je Activityof in de onViewCreatedmethode van je fragment. Stel een callback in die gereed is om te worden geactiveerd wanneer de RecyclerViewvoor het eerst wordt weergegeven, zoals dit:

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

Bereken in de calculeRecyclerViewFullHeightde volledige lengte van RecyclerViewop basis van de lengte van zijn kinderen.

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

In mijn geval is mijn RecyclerViewopgenomen in een SwipeRefreshLayoutom die reden stel ik de hoogte in op de SwipeRefreshViewen niet op de RecyclerViewmaar als je geen SwipeRefreshViewhebt, kun je in plaats daarvan de hoogte instellen op de RecyclerView.

Laat me weten of dit je heeft geholpen of niet.


Antwoord 11, autoriteit 2%

Dit werkt nu omdat ze een release hebben gemaakt in versie 23.2, zoals vermeld in deze post.
De officiële blogpost

citeren

Deze release voegt een opwindende nieuwe functie toe aan de LayoutManager API: automatisch meten! Hierdoor kan een RecyclerView zichzelf aanpassen aan de grootte van de inhoud. Dit betekent dat scenario’s die voorheen niet beschikbaar waren, zoals het gebruik van WRAP_CONTENT voor een dimensie van de RecyclerView, nu mogelijk zijn. U zult zien dat alle ingebouwde LayoutManagers nu automatische meting ondersteunen.


12

In plaats van gebruik te maken van een bibliotheek, gemakkelijkste oplossing tot de nieuwe versie uitkomt, is gewoon openen b.Android. com / 74772 . Je zult gemakkelijk de beste oplossing vinden die dat is bekend.

PS: b.Android.com/74772#C50 werkte voor mij


13

U moet een framelayout plaatsen als hoofdweergave en vervolgens in een relativelayout plaatsen met scrollview en ten minste uw recycerview, het werkt voor mij.

De echte truc hier is de relativelayout …

Blij om te helpen.


14

Ik heb niet aan mijn antwoord gewerkt, maar de manier waarop ik het weet, staggridlayoutmanager met nee. van Grid 1 kan uw probleem oplossen terwijl StaggridLayout automatisch de hoogte en breedte op de hoogte is van de inhoud. Als het werkt, vergeet dan niet om het te controleren als een rechter antwoord. Cheers ..

Other episodes