web-dev-qa-db-fra.com

Centrer une CardView dans une RecyclerView avec un seul élément

J'utilise un RecyclerView qui contient CardViews avec un TextView et un ImageView (chaque carte représente une ville). J'ai également un onClickListener sur chaque carte qui m'amène à une liste de musées de la ville. (Le RecyclerView est rempli par une ArrayList). La liste est un RecyclerView composé du même Cardview qui défile verticalement.

Lorsqu'une ville n'a qu'un seul musée, comment puis-je afficher l'unique CardView au centre de l'écran?

Voici l'activité xml:

    <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:paddingBottom="@dimen/activity_vertical_margin"
    Android:paddingLeft="@dimen/activity_horizontal_margin"
    Android:paddingRight="@dimen/activity_horizontal_margin"
    Android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.bebbo203.mymuseum.MuseumActivity">

    <Android.support.v7.widget.RecyclerView
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:id="@+id/recyclerViewMuseum"
        Android:scrollbars="vertical"
        Android:scrollIndicators="none"
        Android:gravity="center_horizontal"
        />
</RelativeLayout>

Et voici le xml RecyclerView:

 <RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
              Android:layout_width="match_parent"
              Android:layout_height="match_parent"
              Android:baselineAligned="false"
              xmlns:card_view="http://schemas.Android.com/apk/res-auto">

    <Android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.Android.com/apk/res-auto"
        Android:layout_width="match_parent"
        Android:layout_height="150dp"
        Android:id="@+id/cardView"
        card_view:cardCornerRadius="2dp"
        card_view:cardUseCompatPadding="true"
        Android:gravity="center_horizontal"
        Android:animateLayoutChanges="true"
        >
        <FrameLayout
            Android:layout_width="match_parent"
            Android:layout_height="match_parent">

            <ImageView
                Android:layout_width="match_parent"
                Android:layout_height="150dp"
                Android:id="@+id/imageViewList"
                Android:layout_gravity="center_horizontal|top"
                Android:adjustViewBounds="true"
                Android:scaleType="centerCrop"/>

            <TextView
                Android:id="@+id/textViewList"
                Android:layout_width="match_parent"
                Android:layout_height="150dp"
                Android:textSize="40sp"
                Android:textIsSelectable="false"
                Android:textAlignment="center"
                Android:gravity="fill"
                Android:textStyle="bold"
                Android:layout_weight="1"
                Android:layout_gravity="center_horizontal|top"/>

        </FrameLayout>

    </Android.support.v7.widget.CardView>


</RelativeLayout>`

Merci pour ton aide.

Voici donc la MainActivity. Une liste de villes. Tout va bien ici. enter image description here

Lorsque je clique sur Parigi qui n'a qu'un seul musée, je voulais montrer la vue unique de la carte au centre de l'écran

enter image description here

(Et si c'est possible, je voudrais faire la vue de la carte à partir du centre de l'écran, pas du haut. Comme si la vue centrale de la carte est toujours au centre lorsque j'ouvre l'activité. Par exemple, traduire NationalGallery au centre en gardant le ordre de l'autre)

enter image description here

35
Roberto Aureli

J'ai implémenté une application HelloWorld simple, qui affiche la liste des villes et en fonction du nombre de musées qu'elle possède - montre la carte de la ville en taille réelle ou sa version centrée et enveloppée.

enter image description here

(Oui, je ne suis pas vraiment bon en arts :-))

Voici comment je l'ai fait.

TL; DR:

La partie cruciale est ItemDecoration: définissez les éléments corrects et vous obtiendrez ce dont vous avez besoin; Voici comment je l'ai fait:

    RecyclerView recyclerViewMuseum = (RecyclerView)findViewById(R.id.recyclerViewMuseum);
    recyclerViewMuseum.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
    recyclerViewMuseum.setAdapter(adapter);
    recyclerViewMuseum.addItemDecoration(new RecyclerView.ItemDecoration() {

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            if (view instanceof CityWithOneMuseumCardView) {
                int totalWidth = parent.getWidth();
                int cardWidth = getResources().getDimensionPixelOffset(R.dimen.small_card_width);
                int sidePadding = (totalWidth - cardWidth) / 2;
                sidePadding = Math.max(0, sidePadding);
                outRect.set(sidePadding, 0, sidePadding, 0);
            }
        }
    });

Voici mon modèle - City et Museum classes:

public class Museum {
    public String title;
    public Museum(String title) {
        this.title = title;
    }
}

public class City {
    public String title;
    public int imageRes;
    public List<Museum> museums = new ArrayList<>();

    public City(String title, int imageRes) {
        this.title = title;
        this.imageRes = imageRes;
    }
}

Puis Vues: CityWithManyMuseumsCardView et CityWithOneMuseumCardView. Les deux utilisent l'interface d'assistance IItemDisplayer.

public class CityWithOneMuseumCardView extends CardView implements IItemDisplayer<City> {

    public CityWithOneMuseumCardView(Context context) {
        super(context);
        LayoutInflater.from(context).inflate(R.layout.one_museum_layout, this);
    }

    @Override
    public void displayItem(City city) {
        TextView cityTitleTextView = (TextView)findViewById(R.id.cityTitleTextView);
        cityTitleTextView.setText(city.title);
    }
}

public class CityWithManyMuseumsCardView extends CardView implements IItemDisplayer<City> {

    public CityWithManyMuseumsCardView(Context context) {
        super(context);
        LayoutInflater.from(context).inflate(R.layout.many_museums_layout, this);
    }

    @Override
    public void displayItem(City city) {
        ImageView cityBackgroundImageView = (ImageView)findViewById(R.id.cityBackgroundImageView);
        cityBackgroundImageView.setImageResource(city.imageRes);
        TextView cityTitleTextView = (TextView)findViewById(R.id.cityTitleTextView);
        cityTitleTextView.setText(city.title);
        TextView cityNumberOrMuseumsTextView = (TextView)findViewById(R.id.cityNumberOrMuseumsTextView);
        cityNumberOrMuseumsTextView.setText(String.valueOf(city.museums.size()));
    }
}

public interface IItemDisplayer<TItem> {
    public void displayItem(TItem item);
}

Et leurs dispositions:

<!-- One Museum card -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:background="#BB2050AB"
    Android:layout_width="@dimen/small_card_width"
    Android:layout_height="200dp">

    <TextView
        Android:background="#AA000000"
        Android:textColor="#FFFFFF"
        Android:text="Only one museum available"
        Android:textSize="16sp"
        Android:padding="4dp"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content" />

    <TextView
        Android:id="@+id/cityTitleTextView"
        Android:layout_gravity="bottom"
        Android:background="#AAFFFFFF"
        Android:textColor="#000000"
        Android:textSize="24sp"
        Android:paddingStart="16dp"
        Android:paddingEnd="16dp"
        Android:gravity="center"
        Android:layout_width="match_parent"
        Android:layout_height="48dp" />
</FrameLayout>

<!-- Many museums card -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="240dp">
    <ImageView
        Android:id="@+id/cityBackgroundImageView"
        Android:scaleType="fitXY"
        Android:layout_width="500dp"
        Android:layout_height="match_parent" />

    <TextView
        Android:id="@+id/cityNumberOrMuseumsTextView"
        Android:layout_gravity="top|end"
        Android:background="#AA000000"
        Android:textColor="#FFFFFF"
        Android:textSize="16sp"
        Android:padding="4dp"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content" />

    <TextView
        Android:id="@+id/cityTitleTextView"
        Android:layout_gravity="bottom"
        Android:background="#AA000000"
        Android:textColor="#FFFFFF"
        Android:textSize="24sp"
        Android:paddingStart="16dp"
        Android:paddingEnd="16dp"
        Android:gravity="center_vertical"
        Android:layout_width="match_parent"
        Android:layout_height="48dp" />
</FrameLayout>

Ensuite, nous devons créer un adaptateur pour notre RecyclerViewCityAdapter.Java

public class CityAdapter  extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    final static int ITEM_TYPE_MANY_MUSEUMS = 0;
    final static int ITEM_TYPE_ONE_MUSEUM = 1;

    private List<City> items;

    public CityAdapter(List<City> items) {
        this.items = items;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        switch (viewType) {
            case ITEM_TYPE_MANY_MUSEUMS:
                return new ViewHolder(new CityWithManyMuseumsCardView(viewGroup.getContext()));
            case ITEM_TYPE_ONE_MUSEUM:
                return new ViewHolder(new CityWithOneMuseumCardView(viewGroup.getContext()));
            default:
                throw new IllegalArgumentException(String.format("Unexpected viewType: %d", viewType));
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (items == null || items.size() < position) {
            throw new IllegalArgumentException("Wrong position!");
        }

        if (items.get(position).museums.size() > 1) {
            return ITEM_TYPE_MANY_MUSEUMS;
        } else if (items.get(position).museums.size() == 1){
            return ITEM_TYPE_ONE_MUSEUM;
        }

        throw new IllegalArgumentException("Wrong number of museums!");
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((IItemDisplayer<City>) holder.itemView).displayItem(items.get(position));
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public ViewHolder(View itemView) {
            super(itemView);
        }
    }
}

J'ai téléchargé ce projet dans ma boîte de dépôt - n'hésitez pas à vérifiez-le ! J'espère que cela aide.

18
Konstantin Loginov

Pouvez-vous essayer ceci:

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 WrapContentLinearLayoutManager 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 WrapContentLinearLayoutManager(Context context) {
        super(context);
        this.view = null;
    }

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

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

    @SuppressWarnings("UnusedDeclaration")
    public WrapContentLinearLayoutManager(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");
        }
    }
}

Mettez ce WrapContentLinearLayoutManager dans votre vue de recyclage

mRecyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity));

Et cela dans le xml (avec le parent dans le parent)

<Android.support.v7.widget.RecyclerView
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_centerInParent="true"
            Android:id="@+id/recyclerViewMuseum"
            Android:scrollbars="vertical"
            Android:scrollIndicators="none"/>

Cela devrait fonctionner :)

2
koni

Essayez de définir la hauteur de RecyclerView lorsque vous n'avez qu'un ou plusieurs éléments. Reportez-vous à ce sujet pour le faire. Lorsque vous n'avez qu'un seul élément, définissez RecyclerView sur WRAP_CONTENT. Et lorsque vous avez plusieurs éléments, définissez RecyclerView sur MATCH_CONTENT. Bonne chance!

1
Phan Sinh

1.Essayez de définir LayoutParams CENTER_IN_PARENT lorsque recyclerview a 1 enfant
2. Ou utilisez un FrameLayout comme parent de recyclerView et définissez layout_gravity 3.Ou calculez simplement l'espace au-dessus de cardview et définissez marginTop sur recyclerView ou setTranslationY sur recyclerView 4.Ou ajoutez un itemDecaration avec space'height à Recylerview.

RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
    if(data != null || data.size()>1){
        rLp.removeRule(RelativeLayout.CENTER_IN_PARENT);
    }else{
        rLp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
    }
    recyclerView.setLayoutParams(rLp);

Lorsque recyclerView n'a qu'un seul enfant, utilisez ce LayoutManager:

import Android.content.Context;
import Android.support.v7.widget.LinearLayoutManager;
import Android.support.v7.widget.RecyclerView;
import Android.util.Log;
import Android.view.View;
import Android.view.ViewGroup;

public class FullyLinearLayoutManager extends LinearLayoutManager {

    private static final String TAG = FullyLinearLayoutManager.class.getSimpleName();
    private MeasureEndListener mMeasureEndListener;

    public FullyLinearLayoutManager(Context context) {
        super(context);
    }
    private float divHeight =0;
    public FullyLinearLayoutManager(Context context,float height) {
        super(context);
        divHeight = height;
    }

    public FullyLinearLayoutManager(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);

        Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode
                + " \nheightMode " + heightSpec
                + " \nwidthSize " + widthSize
                + " \nheightSize " + heightSize
                + " \ngetItemCount() " + getItemCount());

        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 {
//                LogUtils.e(mMeasuredDimension[1]);
                height = height + mMeasuredDimension[1];
                if(i!= getItemCount()-1){
                    height += divHeight;
//                    LogUtils.e(divHeight + "xxx add"+DensityUtils.dp2px(divHeight));
                }else{
                    height += 2*divHeight;
//                    LogUtils.e(divHeight+ "xxx no add"+ DensityUtils.dp2px(divHeight));
                }
                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:
        }
        if(mMeasureEndListener!=null){
            mMeasureEndListener.onMeasureEnd(width,height);
//            new Thread().interrupt();
        }
        setMeasuredDimension(width, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        try {
            View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException

            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);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }

    public interface MeasureEndListener{
        void onMeasureEnd(int width,int height);
    }

    public void setMeasureEndListener(MeasureEndListener mMeasureEndListener){
        this.mMeasureEndListener = mMeasureEndListener;
    }
}
1
tiny sunlight
        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/recyclerview"
            Android:layout_width="wrap_content"
            Android:layout_height="match_parent"
            Android:layout_marginTop="@dimen/margin_twenty"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"

            Android:layout_marginBottom="@dimen/margin_twenty">
        </Android.support.v7.widget.RecyclerView>
0
nithin joseph