중첩 된 재활용 자보기 높이가 내용을 감싸지 않습니다.


176

책 모음 (예 : 재생 목록)을 관리하는 응용 프로그램이 있습니다.

세로 RecyclerView가있는 컬렉션 목록과 각 행 내부에 가로 RecyclerView의 책 목록을 표시하고 싶습니다.

내부 가로 RecyclerView의 layout_height를 300dp로 설정하면 올바르게 표시되지만 wrap_content로 설정하면 아무것도 표시되지 않습니다. 세로 및 가로 표시 간을 전환하기 위해 프로그래밍 방식으로 레이아웃 관리자를 변경하려면 wrap_content를 사용해야합니다.

여기에 이미지 설명을 입력하십시오

내가 뭘 잘못하고 있는지 알아?

내 조각 레이아웃 :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/white">

    <com.twibit.ui.view.CustomSwipeToRefreshLayout
        android:id="@+id/swipe_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

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

            <android.support.v7.widget.RecyclerView
                android:id="@+id/shelf_collection_listview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingTop="10dp"/>

        </LinearLayout>

    </com.twibit.ui.view.CustomSwipeToRefreshLayout>
</LinearLayout>

컬렉션 요소 레이아웃 :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#FFF">

        <!-- Simple Header -->

    </RelativeLayout>

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

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="@string/empty_collection"
            android:id="@+id/empty_collection_tv"
            android:visibility="gone"
            android:gravity="center"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/collection_book_listview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/> <!-- android:layout_height="300dp" -->

    </FrameLayout>

</LinearLayout>

도서 목록 항목 :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="180dp"
              android:layout_height="220dp"
              android:layout_gravity="center">

        <ImageView
            android:id="@+id/shelf_item_cover"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:maxWidth="150dp"
            android:maxHeight="200dp"
            android:src="@drawable/placeholder"
            android:contentDescription="@string/cover"
            android:adjustViewBounds="true"
            android:background="@android:drawable/dialog_holo_light_frame"/>

</FrameLayout>

내 컬렉션 어댑터는 다음과 같습니다.

private class CollectionsListAdapter extends RecyclerView.Adapter<CollectionsListAdapter.ViewHolder> {
    private final String TAG = CollectionsListAdapter.class.getSimpleName();
    private Context mContext;

    // Create the ViewHolder class to keep references to your views
    class ViewHolder extends RecyclerView.ViewHolder {

        private final TextView mHeaderTitleTextView;
        private final TextView mHeaderCountTextView;

        private final RecyclerView mHorizontalListView;
        private final TextView mEmptyTextView;

        public ViewHolder(View view) {
            super(view);

            mHeaderTitleTextView = (TextView) view.findViewById(R.id.collection_header_tv);
            mHeaderCountTextView = (TextView) view.findViewById(R.id.collection_header_count_tv);

            mHorizontalListView = (RecyclerView) view.findViewById(R.id.collection_book_listview);
            mEmptyTextView = (TextView) view.findViewById(R.id.empty_collection_tv);
        }
    }


    public CollectionsListAdapter(Context context) {
        mContext = context;
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int i) {
        Log.d(TAG, "CollectionsListAdapter.onCreateViewHolder(" + parent.getId() + ", " + i + ")");
        // Create a new view by inflating the row item xml.
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.shelf_collection, parent, false);

        // Set the view to the ViewHolder
        ViewHolder holder = new ViewHolder(v);

        holder.mHorizontalListView.setHasFixedSize(false);
        holder.mHorizontalListView.setHorizontalScrollBarEnabled(true);

        // use a linear layout manager
        LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext);
        mLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        holder.mHorizontalListView.setLayoutManager(mLayoutManager);

        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int i) {
        Log.d(TAG, "CollectionsListAdapter.onBindViewHolder(" + holder.getPosition() + ", " + i + ")");

        Collection collection = mCollectionList.get(i);
        Log.d(TAG, "Collection : " + collection.getLabel());

        holder.mHeaderTitleTextView.setText(collection.getLabel());
        holder.mHeaderCountTextView.setText("" + collection.getBooks().size());

        // Create an adapter if none exists
        if (!mBookListAdapterMap.containsKey(collection.getCollectionId())) {
            mBookListAdapterMap.put(collection.getCollectionId(), new BookListAdapter(getActivity(), collection));
        }

        holder.mHorizontalListView.setAdapter(mBookListAdapterMap.get(collection.getCollectionId()));

    }

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

그리고 마지막으로, 책 어댑터 :

private class BookListAdapter extends RecyclerView.Adapter<BookListAdapter.ViewHolder> implements View.OnClickListener {
    private final String TAG = BookListAdapter.class.getSimpleName();

    // Create the ViewHolder class to keep references to your views
    class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView mCoverImageView;

        public ViewHolder(View view) {
            super(view);
            mCoverImageView = (ImageView) view.findViewById(R.id.shelf_item_cover);
        }
    }

    @Override
    public void onClick(View v) {
        BookListAdapter.ViewHolder holder = (BookListAdapter.ViewHolder) v.getTag();
        int position = holder.getPosition();
        final Book book = mCollection.getBooks().get(position);

        // Click on cover image
        if (v.getId() == holder.mCoverImageView.getId()) {
            downloadOrOpenBook(book);
            return;
        }
    }

    private void downloadOrOpenBook(final Book book) {
        // do stuff
    }

    private Context mContext;
    private Collection mCollection;

    public BookListAdapter(Context context, Collection collection) {
        Log.d(TAG, "BookListAdapter(" + context + ", " + collection + ")");
        mCollection = collection;
        mContext = context;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int i) {
        Log.d(TAG, "onCreateViewHolder(" + parent.getId() + ", " + i + ")");
        // Create a new view by inflating the row item xml.
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.shelf_grid_item, parent, false);

        // Set the view to the ViewHolder
        ViewHolder holder = new ViewHolder(v);
        holder.mCoverImageView.setOnClickListener(BookListAdapter.this); // Download or Open

        holder.mCoverImageView.setTag(holder);

        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int i) {
        Log.d(TAG, "onBindViewHolder(" + holder.getPosition() + ", " + i + ")");

        Book book = mCollection.getBooks().get(i);

        ImageView imageView = holder.mCoverImageView;
        ImageLoader.getInstance().displayImage(book.getCoverUrl(), imageView);
    }

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

왜 레이아웃 관리자를 세로와 가로로 바꾸려면 wrap_content를 사용해야합니까?
AmaJayJB

1
layout_height를 특정 값으로 설정하면 (수평 레이아웃 관리자의 요소 높이라고 말하면) 수직 레이아웃 관리자와 함께 목록의 첫 번째 줄만 표시합니다.
Twibit

그러나 recyclerView에는 scrollView가 내장되어 있으므로 항목을 스크롤 할 수 있습니다. 또는 여기서 요점이 누락 되었습니까? 죄송합니다 이해
AmaJayJB

내부 도서 목록이 아닌 컬렉션 항목을 스크롤하고 싶습니다 (가로 제외). ExpandableListView와 같은 것.
Twibit

2
나는 당신과 같은 질문에 직면, 당신은 지금 해결 했습니까?
VinceStyling의

답변:


46

최신 정보

버전 23.2.0에서이 기능과 관련된 많은 문제가 23.2.1에서 수정되어 대신 업데이트되었습니다.

Support Library 버전 23.2가 출시되면서 RecyclerView이제이를 지원합니다!

업데이트 build.gradle:

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

또는 그 이상의 버전.

이 릴리스에서는 LayoutManager API에 자동 측정! 이렇게하면 RecyclerView가 내용의 크기를 기준으로 자체 크기를 조정할 수 있습니다. 이는 RecyclerView의 차원에 WRAP_CONTENT를 사용하는 등 이전에는 사용할 수 없었던 시나리오가 가능하다는 것을 의미합니다. 내장 된 모든 LayoutManager가 이제 자동 측정을 지원합니다.

setAutoMeasurementEnabled()필요한 경우이를 통해 비활성화 할 수 있습니다. 여기 에서 자세히 확인 하십시오 .


4
조심해! 여기에 새로운 버그는 다음과 같습니다 stackoverflow.com/questions/35619022/...
데니스 NEK

위의 버그를 해결합니까?
razzledazzle

1
항상 제대로 작동하지는 않습니다. 각 항목 사이에 빈 공간이 있습니다.
RAHULRSANNIDHI

2
하위 리사이클 러보기를 포함하는 상위 레이아웃의 높이는 랩 컨텐츠 여야합니다.
RAHULRSANNIDHI

2
23.2.1RecyclerView 버전 은 몇 가지 버그를 수정 한 것으로 보입니다. 우리에게 아주 잘 작동합니다. 그렇지 않다면 시도해 볼 수도 있습니다24.0.0-alpha1
Joaquin Iurchuk

124

@ user2302510 솔루션이 예상보다 좋지 않습니다. 오리엔테이션 및 동적 데이터 변경에 대한 전체 해결 방법은 다음과 같습니다.

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

3
솔루션은 상당히 잘 작동하지만 구분 기호와 같은 ItemDecoration을 고려하지 않는 것 같습니다.
Twibit

11
왜 아직 확실하지 않지만 스크롤이 작동하지 않는 것 같습니다. 세로 스크롤 레이아웃 으로이 코드를 사용해 보았습니다 ... 항목이 화면 높이보다 작 으면 모든 것이 잘 작동합니다. 그래도 화면에 표시 할 수있는 것보다 많은 항목이 있으면 스크롤이 작동하지 않습니다.
Justin

1
콘텐츠 변경 애니메이션에서는 작동하지 않습니다. 리사이클 러 뷰 스냅의 크기는 부드럽게 증가 / 축소하기보다는 스냅합니다.
zyamys 2012

36
다음은 작동하는 것처럼 보이며 다른 솔루션의 문제점이없는 정제 된 버전입니다. github.com/serso/android-linear-layout-manager/blob/master/lib/…
se.solovyev

2
scrollview 내부 에서이 recyclerview를 사용하고 있으며이 코드를 사용하여 recylerview 랩 내용을 만듭니다. 그러나 스크롤 스크롤보기는 부드럽 지 않습니다. 해결책은 stackoverflow.com/a/32283439/3736955
Jemshit Iskenderov

39

위의 코드는 MeasureSpec.UNSPECIFIED를 사용하여 항목 높이와 너비를 모두 측정하므로 항목을 "wrap_content"로 만들어야하는 경우 제대로 작동하지 않습니다. 몇 가지 문제가 발생하면 해당 솔루션을 수정하여 항목을 확장 할 수 있습니다. 유일한 차이점은 부모가 높이 또는 너비를 제공한다는 것입니다. MeasureSpec은 레이아웃 방향에 따라 다릅니다.

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++) {


        if (getOrientation() == HORIZONTAL) {

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

            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            measureScrapChild(recycler, i,
                    widthSpec,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);
            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);
    recycler.bindViewToPosition(view, 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);
    }
}
}

감사합니다! 이 솔루션은 저에게 가장 효과적이었습니다. Denis '는 충분히 측정하지 않았고보기는 여전히 스크롤 가능했습니다. Sinan 's는 한 명의 자녀가있을 때만 올바르게 행동하는 것처럼 보였습니다. 참고 : 내 자녀 조회수wrap_content
kassim

그것이 부모의 경계에 switch (heightMode) { case View.MeasureSpec.AT_MOST: if (height < heightSize) break; case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.UNSPECIFIED: }
부딪쳤을

탁월한 솔루션. 이것은 완벽하게 작동했습니다. 참고로, 생성자를 사용하는 대신 Constant final int ORIENTATION (개인이 다른 문제가있는 경우)을 사용하여 개인 인스턴스 변수와 setter / getter 메소드를 만든 다음 전달 된 방향을 확인하는 데 사용했습니다. .
PGMacDesign

1
Lollipop 이상에서 문제가 발생했습니다. RecyclerView를 스크롤 할 때 RecyclerView를 래핑하는 ScrollView가 제대로 작동하지 않습니다 (플링 없음). 이 문제를 해결하려면 NotScrollableRecyclerView (사용자 정의)를 작성하고 수퍼 구현을 호출하지 않는 onTouchEvent 메소드 인 onInterceptTouchEvent를 대체해야하며 두 메소드 모두에서 false를 리턴해야합니다.
ultraon

이것은 작동하지만 아이들의 키가 계산 된 후 높이를 측정하는 것이 불필요하게 복잡한 이유를 이해하지 못합니다 ...
Joe Maher

22

기존 레이아웃 관리자는 아직 랩 내용을 지원하지 않습니다.

랩 컨텐츠를 측정하기 위해 기존의 것을 확장하고 onMeasure 메소드를 대체하는 새 LayoutManager를 작성할 수 있습니다.


1
그것은 내가 기대 한 것이 아니지만 사실 인 것 같습니다. 참조 : RecyclerView.LayoutManager & LinearLayoutManager
Twibit

@ yiğit, recyclerView는 기본적으로 컨텐츠 랩핑을 지원합니까?
Sinan Kozak

3
RecyclerView가 아닌 ​​LayoutManager에 의존합니다. 로드맵에 있습니다.
yigit

@yigit setHasFixedSize(boolean)기본 레이아웃 관리자 에서는 이것이 쓸모 없습니까? 어쨌든, 로드맵에 있다는 것을 알게되어 기쁩니다. 그것이 곧 나오기를 바랍니다.
goncalossilva

LM의 크기가 어댑터 내용에 따라 다르면 LM의 크기를 고정 할 수 없기 때문입니다. 주요 차이점은 RV가 onMeasure 이전 또는 onLayout 이전의 업데이트를 처리하는지 여부입니다. 또한 크기 조정을 사용하면 LM이 자체적으로 올바르게 측정하기 위해 최종 크기를 알아야하는 반면, 사전 레이아웃은 측정 후에 발생합니다 (롤백과 같습니다).
yigit

22

@ yiğit이 언급했듯이 onMeasure ()를 재정의해야합니다. @ user2302510과 @DenisNek 모두 좋은 답변을 제공하지만 ItemDecoration을 지원하려면이 사용자 정의 레이아웃 관리자를 사용할 수 있습니다.

화면에 표시 할 수있는 것보다 많은 항목이 있으면 다른 답변을 스크롤 할 수 없습니다. 화면 크기보다 많은 항목이있을 때 onMeasure ()의 기본 구현을 사용합니다.

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

GridLayoutManager와 함께 사용하려면 GridLayoutManager에서 확장하고 변경하십시오.

for (int i = 0; i < getItemCount(); i++)

for (int i = 0; i < getItemCount(); i = i + getSpanCount())

4
이것이 올바르게 작동하는 솔루션입니다.이 스레드에서 다른 모든 것을 테스트했으며 모두 문제가있었습니다
wasyl

이 솔루션은 저에게도 매우 효과적이었습니다. 유일한 문제는 자식의 줄 바꿈 텍스트를 올바르게 설명하지 않는다는 것입니다. 해당 문제에 대한 해결책은 위의 답변을 참조하십시오.
Bishbulb

gridlayout에는 작동하지 않습니다 (예, 변경 사항을 적용했습니다). 먼저 작동하는 것처럼 보이고 방향을 앞뒤로 변경하고 높이는 원래 높이와 비교하여 변경됩니다. 또한보기는 예상대로 작동하지 않습니다 (예 : onclicks는 간단한 gridlayoutmanager를 사용하는 동안 대부분의 경우 작동하지 않습니다).
Csabi

우리가 어떻게에 대한이 같은 얻을 수 있습니다 gridlayoutmanagerstaggeredgridlayoutmanager염두에 스팬 수를 고려?
Amrut Bidri

이 MyLinearLayoutManager는 자식 리사이클 러보기 또는 부모에 적용되어야합니까? 아니면 둘다?
CommonSenseCode

21

2016 년 3 월 업데이트

으로 안드로이드 지원 라이브러리 23.2.1 지원 라이브러리 버전의. 따라서 모든 WRAP_CONTENT 가 올바르게 작동해야합니다.

gradle 파일에서 라이브러리 버전을 업데이트하십시오.

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

이렇게하면 RecyclerView가 내용의 크기에 따라 자체 크기를 조정할 수 있습니다. 이는 RecyclerView의 차원에 WRAP_CONTENT를 사용하는 등 이전에는 사용할 수 없었던 시나리오가 가능하다는 것을 의미 합니다 .

setAutoMeasureEnabled (true) 를 호출해야합니다.

업데이트시 다양한 측정 사양 방법과 관련된 버그 수정

https://developer.android.com/topic/libraries/support-library/features.html을 확인 하십시오.


14

이 답변은 Denis Nek가 제공 한 솔루션을 기반으로합니다. 분배기와 같은 장식을 고려하지 않는 문제를 해결합니다.

public class WrappingRecyclerViewLayoutManager extends LinearLayoutManager {

public WrappingRecyclerViewLayoutManager(Context context)    {
    super(context, VERTICAL, false);
}

public WrappingRecyclerViewLayoutManager(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);
        Rect outRect = new Rect();
        calculateItemDecorationsForChild(view, outRect);
        measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
        measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin + outRect.bottom + outRect.top;
        recycler.recycleView(view);
    }
}

}


1
마지막으로 올바르게 작동하는 유일한 것입니다. 훌륭한 일. 감사!
falc0nit3

3
java.lang.IndexOutOfBoundsException : 잘못된 항목 위치 0 (0)이 표시됩니다. 품목 수 : 0 예외.
RAHULRSANNIDHI

9

LayoutManager를 확장하는 대안은보기의 크기를 수동으로 설정할 수 있습니다.

행 높이 당 항목 수 (모든 항목의 높이가 동일하고 구분 기호가 행에 포함 된 경우)

LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mListView.getLayoutParams();
params.height = mAdapter.getItemCount() * getResources().getDimensionPixelSize(R.dimen.row_height);
mListView.setLayoutParams(params);

여전히 해결 방법이지만 기본적인 경우에는 작동합니다.


6

여기에서 해결책을 찾았습니다 : https://code.google.com/p/android/issues/detail?id=74772

그것은 결코 내 해결책이 아닙니다. 방금 거기에서 복사했지만 수평 RecyclerView 및 wrap_content 높이를 구현할 때 도움이되는 사람에게 도움이되기를 바랍니다 (세로 및 wrap_content 너비에도 작동해야 함)

해결책은 LayoutManager를 확장하고 @yigit이 제안한대로 onMeasure 메소드를 재정의하는 것입니다.

링크가 죽는 경우의 코드는 다음과 같습니다.

public static class MyLinearLayoutManager extends LinearLayoutManager {

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

빙고! 수평으로 완벽하게 작동했지만 수직으로는 완벽하지 않음
H Raval

5

@ sinan-kozak의 솔루션을 사용했지만 몇 가지 버그가 수정되었습니다. 특히, 우리는 사용하지 않아야 View.MeasureSpec.UNSPECIFIED을 위해 모두 호출 할 때 폭과 높이를 measureScrapChild적절하게 아이에 감싸 인 텍스트를 고려하지 않을 것이다있다. 대신, 부모에서 가로 및 세로 모드를 통과하여 가로 및 세로 레이아웃 모두에서 작업 할 수 있습니다.

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++) {    
        if (getOrientation() == HORIZONTAL) {
            measureScrapChild(recycler, i,
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(heightSize, heightMode),
                mMeasuredDimension);

            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            measureScrapChild(recycler, i,
                View.MeasureSpec.makeMeasureSpec(widthSize, widthMode),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);

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

`


테스트를 거쳐 작동 RecyclerView하지만가에 중첩 된 경우에만 가능합니다 LinearLayout. 와 작동하지 않습니다 RelativeLayout.
user2968401

3

예, 모든 답변에 표시된 해결 방법이 맞습니다. 즉, 선형 레이아웃 관리자를 사용자 정의하여 런타임에 하위 항목의 높이를 동적으로 계산해야합니다. 그러나 모든 답변이 예상대로 작동하지 않습니다. 모든 방향을 지원하는 사용자 정의 레이아웃 관리자의 경우 아래 답변을 참조하십시오.

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

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

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

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(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("MyLinearLayoutManager", "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("MyLinearLayoutManager", "MyLinearLayoutManager 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("MyLinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
    }
}
}

3

Android 지원 라이브러리는 이제 WRAP_CONTENT 속성도 처리합니다. 이것을 gradle로 가져 오십시오.

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

그리고 끝났다!


2

Denis Nek의 작업을 기반으로 항목 너비의 합계가 컨테이너 크기보다 작 으면 잘 작동합니다. 그 외에는 recyclerview를 스크롤 할 수 없게 만들고 데이터의 하위 세트 만 표시합니다.

이 문제를 해결하기 위해 제공된 크기와 계산 된 크기의 최소값을 선택하도록 솔루션을 약간 수정했습니다. 아래를보십시오 :

package com.linkdev.gafi.adapters;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

import com.linkdev.gafi.R;

public class MyLinearLayoutManager extends LinearLayoutManager {

    public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout)    {
        super(context, orientation, reverseLayout);
        this.c = context;
    }


    private Context c;
    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:
        }

        int widthDesired = Math.min(widthSize,width);
        setMeasuredDimension(widthDesired, 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);
        }
    }}

1

나는 모든 솔루션을 시도했지만 매우 유용하지만 이것은 나에게만 잘 작동합니다.

public class  LinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

    public LinearLayoutManager(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++) {


            if (getOrientation() == HORIZONTAL) {

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

                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                measureScrapChild(recycler, i,
                        widthSpec,
                        View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                        mMeasuredDimension);
                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }

        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);
        recycler.bindViewToPosition(view, 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);
        }
    }
}

0

그리드 레이아웃으로 RecyclerView를 사용하여 내용을 간단히 감싸십시오.

이미지 : GridView 레이아웃으로 재활용

다음과 같이 GridLayoutManager를 사용하십시오.

RecyclerView.LayoutManager mRecyclerGrid=new GridLayoutManager(this,3,LinearLayoutManager.VERTICAL,false);
mRecyclerView.setLayoutManager(mRecyclerGrid);

행에 표시 할 항목 수를 설정할 수 있습니다 (3을 대체하십시오).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.