Nested Recycler xem chiều cao không bao bọc nội dung của nó


176

Tôi có một ứng dụng quản lý bộ sưu tập sách (như danh sách phát).

Tôi muốn hiển thị danh sách bộ sưu tập với RecyclerView dọc và bên trong mỗi hàng, danh sách sách trong RecyclerView nằm ngang.

Khi tôi đặt layout_height của RecyclerView nằm ngang bên trong thành 300dp, nó được hiển thị chính xác nhưng khi tôi đặt nó thành quấn_content, nó không hiển thị gì cả. Tôi cần sử dụng quấn_content vì tôi muốn có thể thay đổi trình quản lý bố cục theo chương trình để chuyển đổi giữa màn hình dọc và ngang.

nhập mô tả hình ảnh ở đây

Bạn có biết tôi đang làm gì sai không?

Bố cục mảnh vỡ của tôi:

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

Bố cục phần tử bộ sưu tập:

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

Danh mục sách

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

Đây là Bộ sưu tập Bộ điều hợp của tôi:

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

Và cuối cùng, bộ chuyển đổi Sách:

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

Tại sao bạn cần sử dụng quấn_content để thay đổi cách bố trí giữa dọc và ngang?
AmaJayJB

1
Nếu tôi đặt layout_height thành một giá trị cụ thể (giả sử chiều cao của một phần tử cho trình quản lý bố cục ngang), nó chỉ hiển thị dòng đầu tiên của danh sách với trình quản lý bố cục dọc.
Twibit

Nhưng recyclerView có một scrollView được tích hợp sẵn, vì vậy bạn chỉ có thể cuộn qua các mục ... hoặc tôi đang thiếu điểm ở đây? Xin lỗi, cố gắng hiểu
AmaJayJB

Tôi muốn cuộn qua các mục bộ sưu tập nhưng không phải danh sách sách bên trong (ngoại trừ theo chiều ngang). Một cái gì đó như ExpandableListView.
Twibit

2
tôi đối mặt với câu hỏi tương tự với bạn, bạn đã giải quyết chưa?
VinceStyling

Câu trả lời:


46

Cập nhật

Nhiều vấn đề liên quan đến tính năng này trong phiên bản 23.2.0 đã được sửa trong 23.2.1, thay vào đó hãy cập nhật.

Với việc phát hành Thư viện hỗ trợ phiên bản 23.2, RecyclerViewgiờ đây đã hỗ trợ điều đó!

Cập nhật build.gradlelên:

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

hoặc bất kỳ phiên bản nào ngoài đó.

Bản phát hành này mang đến một tính năng mới thú vị cho API Trình quản lý: tự động đo lường! Điều này cho phép RecyclerView tự kích thước dựa trên kích thước của nội dung của nó. Điều này có nghĩa là các tình huống trước đây không có sẵn, chẳng hạn như sử dụng WRAP_CONTENT cho kích thước của RecyclerView, hiện có thể. Bây giờ bạn sẽ tìm thấy tất cả các Trình quản lý được tích hợp sẵn hỗ trợ đo tự động.

Điều này có thể được vô hiệu hóa thông qua setAutoMeasurementEnabled()nếu cần. Kiểm tra chi tiết tại đây .


4
Hãy cẩn thận! Dưới đây là các lỗi mới: stackoverflow.com/questions/35619022/ Kiếm
Denis Nek

Điều đó có giải quyết được lỗi ở trên không?
razzledazzle 7/03/2016

1
Điều này không hoạt động đúng luôn luôn thấy khoảng trống ở giữa mỗi mục
RAHULRSANNIDHI

2
Vui lòng cẩn thận chiều cao của bố trí cha mẹ chứa chế độ xem trình tái chế con phải là nội dung gói.
RAHULRSANNIDHI

2
Phiên bản 23.2.1RecyclerView dường như đã sửa một số lỗi. Hoạt động rất tốt cho chúng tôi. Nếu không, bạn thậm chí có thể thử với24.0.0-alpha1
Joaquin Iurchuk 17/03/2016

124

@ user2302510 giải pháp hoạt động không tốt như bạn mong đợi. Giải pháp đầy đủ cho cả định hướng và thay đổi dữ liệu động là:

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
Giải pháp của bạn hoạt động khá tốt nhưng dường như không tính đến ItemDecor (chẳng hạn như dấu phân cách).
Twibit

11
Không chắc chắn tại sao, nhưng cuộn dường như không hoạt động. Tôi đã thử mã này với bố cục cuộn theo chiều dọc ... Khi các mục nhỏ hơn chiều cao của màn hình, mọi thứ đều hoạt động tốt. Tuy nhiên, khi có nhiều mục hơn có thể được hiển thị trên màn hình, thì việc ghi danh không hoạt động.
Justin

1
Điều này không hoạt động với hình ảnh động thay đổi nội dung - kích thước của snapserview thay vì tăng / thu nhỏ một cách trơn tru.
zyamys

36
Đây là một phiên bản tinh chỉnh của lớp dường như hoạt động và thiếu các vấn đề mà các giải pháp khác có: github.com/serso/android-linear-layout-manager/blob/master/lib/iêu
se.solovyev

2
Tôi đang sử dụng recyclerview này trong scrollview và tạo nội dung bọc recylerview bằng mã này. Nhưng cuộn scrollview không trơn tru. Giải pháp là stackoverflow.com/a/32283439/3736955
Jemshit Iskenderov

39

Mã ở trên không hoạt động tốt khi bạn cần làm cho các mục của mình "quấn_content", bởi vì nó đo cả chiều cao và chiều rộng của vật phẩm với Số đoSpec.UNSPECifyED. Sau một số rắc rối tôi đã sửa đổi giải pháp đó để bây giờ các mục có thể mở rộng. Sự khác biệt duy nhất là nó cung cấp cho cha mẹ thước đo chiều cao hoặc chiều rộng phụ thuộc vào hướng bố trí.

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

Cảm ơn bạn! Giải pháp này đã làm việc tốt nhất cho tôi. Denis 'dường như không đo đủ và tầm nhìn vẫn có thể cuộn được. Sinan chỉ tỏ ra cư xử đúng mực khi có một đứa con. Lưu ý: lượt xem của con tôi được sử dụngwrap_content
kassim 10/03/2015

Để cho phép cuộn khi nó chạm vào giới hạn của chế độ xem, tôi phải đặt vàoswitch (heightMode) { case View.MeasureSpec.AT_MOST: if (height < heightSize) break; case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.UNSPECIFIED: }
kassim

Giải pháp tuyệt vời. Điều này làm việc hoàn hảo. Bên cạnh đó, thay vì sử dụng một hàm tạo, tôi chỉ tạo một biến cá thể riêng và một phương thức setter / getter với hằng số int ORIENTATION (Chỉ trong trường hợp người khác gặp sự cố) và sau đó sử dụng nó để kiểm tra hướng đã qua .
PGMacDesign

1
Tôi đã gặp vấn đề với Lollipop trở lên, ScrollView bao bọc RecyclerView hoạt động không chính xác (không có lỗi) khi bạn thử cuộn trên RecyclerView. Để giải quyết vấn đề này, bạn cần tạo NotScrollableRecyclerView (tùy chỉnh) và ghi đè onInterceptTouchEvent, các phương thức onTouchEvent không gọi siêu triển khai của chúng và bạn nên trả về false trong cả hai phương thức.
ultraon

Điều này hoạt động nhưng tôi không hiểu tại sao nó lại phức tạp đến mức không cần thiết khi chỉ cần đo chiều cao của nó sau khi tính chiều cao của trẻ em ...
Joe Maher

22

Trình quản lý bố cục hiện tại chưa hỗ trợ gói nội dung.

Bạn có thể tạo một Trình quản lý bố cục mới mở rộng phương thức hiện có và ghi đè phương thức onMeasure để đo cho nội dung gói.


1
Đó không phải là những gì tôi mong đợi nhưng nó có vẻ là sự thật. Xem: RecyclerView.LayoutManager & LinearLayoutManager
Twibit

@ yiğit, RecyclerView có hỗ trợ bọc nội dung theo mặc định không?
Sinan Kozak

3
Nó phụ thuộc vào LayoutManager chứ không phải RecyclerView. Đó là trong lộ trình của chúng tôi.
Yiğit

@yigit Điều này không làm cho setHasFixedSize(boolean)vô dụng với các trình quản lý bố cục mặc định? Dù sao, thật tuyệt khi biết nó trên lộ trình. Hy vọng nó ra sớm.
goncalossilva

Đúng, vì LM không thể có kích thước cố định nếu kích thước của nó phụ thuộc vào nội dung bộ điều hợp. Sự khác biệt chính là liệu RV xử lý cập nhật trước onMeasure hay trước onLayout. Ngoài ra, với thay đổi kích thước, khá khó để thực hiện các hoạt ảnh dự đoán do LM cần phải biết kích thước cuối cùng để tự đo chính xác trong khi bố cục trước sẽ xảy ra sau khi đo (giống như quay ngược thời gian).
số

22

Như @ yiğit đã đề cập, bạn cần ghi đè onMeasure (). Cả @ user2302510 và @DenisNek đều có câu trả lời hay nhưng nếu bạn muốn hỗ trợ ItemDecoration, bạn có thể sử dụng trình quản lý bố cục tùy chỉnh này.

Và các câu trả lời khác không thể cuộn khi có nhiều mục hơn có thể được hiển thị trên màn hình. Cái này đang sử dụng hàm giả định mặc định của onMeasure () khi có nhiều mục hơn kích thước màn hình.

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

Và nếu bạn muốn sử dụng nó với GridLayoutManager, chỉ cần mở rộng nó từ GridLayoutManager và thay đổi

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

đến

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

4
Đây là giải pháp hoạt động chính xác, tôi đã thử nghiệm tất cả những thứ khác trong chuỗi này và tất cả chúng đều có vấn đề
wasyl

Giải pháp này làm việc rất tốt cho tôi quá. Vấn đề duy nhất là nó không giải thích đúng về văn bản được bao bọc ở trẻ. Xem câu trả lời của tôi ở trên cho giải pháp cho vấn đề đó.
Bishbulb

Điều này không hoạt động cho Gridlayout (vâng, tôi đã áp dụng các thay đổi). Đầu tiên nó có vẻ hoạt động, sau đó tôi thay đổi hướng qua lại và chiều cao được thay đổi so với chiều cao ban đầu. Ngoài ra, chế độ xem không hoạt động như mong đợi (ví dụ: onclicks không hoạt động trên hầu hết trong số chúng trong khi với trình điều khiển lưới đơn giản mà chúng hoạt động).
Csabi

Làm thế nào chúng ta có thể đạt được điều tương tự gridlayoutmanagerstaggeredgridlayoutmanagerxem xét số lượng nhịp trong tâm trí?
Amrut Bidri

MyLinearLayoutManager này có được áp dụng cho chế độ xem tái chế con hay cho phụ huynh không? hoặc cả hai?
CommonSenseCode

21

CẬP NHẬT tháng 3 năm 2016

Bởi Thư viện hỗ trợ Android 23.2.1 của phiên bản thư viện hỗ trợ. Vì vậy, tất cả WRAP_CONTENT sẽ hoạt động chính xác.

Vui lòng cập nhật phiên bản của một thư viện trong tập tin gradle.

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

Điều này cho phép RecyclerView tự kích thước dựa trên kích thước của nội dung của nó. Điều này có nghĩa là các tình huống trước đây không có sẵn, chẳng hạn như sử dụng WRAP_CONTENT cho kích thước của RecyclerView, hiện có thể .

bạn sẽ được yêu cầu gọi setAutoMeasureEnables (đúng)

Đã sửa lỗi liên quan đến các phương pháp đo-spec khác nhau trong bản cập nhật

Kiểm tra https://developer.android.com/topic/lologists/support-l Library / tính năng.html


14

Câu trả lời này dựa trên giải pháp được đưa ra bởi Denis Nek. Nó giải quyết vấn đề không lấy đồ trang trí như bộ chia vào tài khoản.

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
Cuối cùng, điều duy nhất hoạt động chính xác. Công việc tuyệt vời Cảm ơn!
falc0nit3

3
Tôi đang nhận java.lang.IndexOutOfBoundException: Vị trí mục không hợp lệ 0 (0). Số mục: 0 ngoại lệ.
RAHULRSANNIDHI

9

Một cách khác để mở rộng LayoutManager có thể chỉ là đặt kích thước của chế độ xem theo cách thủ công.

Số mục trên mỗi chiều cao hàng (nếu tất cả các mục có cùng chiều cao và dấu phân cách được bao gồm trên hàng)

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

Vẫn là một cách giải quyết, nhưng đối với các trường hợp cơ bản, nó hoạt động.


6

Ở đây tôi đã tìm thấy một giải pháp: https://code.google.com.vn/p/android/issues/detail?id=74772

Đó không phải là giải pháp của tôi. Tôi vừa sao chép nó từ đó, nhưng tôi hy vọng nó sẽ giúp được ai đó nhiều như nó đã giúp tôi khi thực hiện RecyclerView ngang và chiều cao quấn_content (cũng nên hoạt động cho chiều dọc và chiều rộng quấn_content)

Giải pháp là mở rộng LayoutManager và ghi đè phương thức onMeasure của nó như @yigit đề xuất.

Đây là mã trong trường hợp liên kết chết:

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

chơi lô tô! hoạt động hoàn hảo với phương ngang nhưng không theo chiều dọc
H Raval

5

Sử dụng giải pháp từ @ sinan-kozak, ngoại trừ sửa một vài lỗi. Cụ thể, chúng ta không nên sử dụng View.MeasureSpec.UNSPECIFIEDcho cả chiều rộng và chiều cao khi gọi measureScrapChildvì điều đó sẽ không giải thích chính xác cho văn bản được bọc trong trẻ. Thay vào đó, chúng ta sẽ chuyển qua các chế độ chiều rộng và chiều cao từ cha mẹ, điều này sẽ cho phép mọi thứ hoạt động cho cả bố cục ngang và dọc.

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

`


Tôi đã thử nghiệm và nó hoạt động, nhưng chỉ khi RecyclerViewđược lồng trong a LinearLayout. Không làm việc với RelativeLayout.
dùng2968401

3

Có, cách giải quyết được hiển thị trong tất cả các câu trả lời là chính xác, đó là chúng ta cần tùy chỉnh trình quản lý bố cục tuyến tính để tính toán chiều cao của các mục con của nó một cách linh hoạt trong thời gian chạy. Nhưng tất cả các câu trả lời không hoạt động như mong đợi. Vui lòng trả lời bên dưới cho trình quản lý bố cục tùy chỉnh với tất cả các hỗ trợ định hướng.

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

Thư viện hỗ trợ Android hiện cũng xử lý thuộc tính WRAP_CONTENT. Chỉ cần nhập cái này trong lớp của bạn.

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

Và thực hiện!


2

Dựa trên công việc của Denis Nek, nó hoạt động tốt nếu tổng chiều rộng của vật phẩm nhỏ hơn kích thước của container. ngoài ra, nó sẽ làm cho recyclerview không thể cuộn và chỉ hiển thị tập hợp con của dữ liệu.

Để giải quyết vấn đề này, tôi đã sửa đổi giải pháp một cách đơn giản để nó chọn tối thiểu kích thước được cung cấp và kích thước tính toán. xem bên dưới:

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

Tôi đã thử tất cả các giải pháp, chúng rất hữu ích nhưng điều này chỉ hoạt động tốt với tôi

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

Chỉ cần bọc nội dung bằng RecyclerView với Grid Layout

Hình ảnh: Recycler dưới dạng bố cục GridView

Chỉ cần sử dụng GridLayoutManager như thế này:

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

Bạn có thể đặt số lượng mục sẽ xuất hiện trên một hàng (thay thế 3).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.