为RecyclerView创建可变列大小布局管理器

时间:2016-03-05 11:54:19

标签: android android-recyclerview

我正在尝试创建一个布局管理器,它会水平放置它的孩子,直到它遇到回收器视图的宽度。如果它到达边缘,它应该将孩子排列在下一行。

例如假设回收者视图中有4个项目 - jaritem1item2item3item4item1的视图正在彼此相邻排列。还剩下一些空间。但item2视图无法适应该宽度。所以item3's转到下一行。但是,留下的差距现在应该在item3item1之间平均分配。

item2

这应该成为

| <item1><item2><--gap-->|
|<-----item3---->        |

如果| <--item1--> <--item2-->| |<-----item3----> | 视图适合item4's之后的空格,则应将其放置在那里。

item3


这不可能通过| <--item1--> <--item2--> | |<-----item3----><-item4->| GridLayoutManager实现,因为它们不会考虑单个项目的宽度变化。

要编写自定义布局管理器,我觉得我应该覆盖布局管理器的StaggeredGridLayoutManager方法。但是我在这一点上有点卡住了。我不知道该如何解决这个问题。任何帮助,将不胜感激。 感谢。

1 个答案:

答案 0 :(得分:0)

我有类似的问题,但使用GridLayout解决了这个问题。应该从头开始创建所有视图的缺点:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

        <android.support.v7.widget.GridLayout
            android:id="@+id/bubble_grid"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="vertical"
            android:scrollbarStyle="outsideOverlay"
            android:layout_gravity="top|left"
            app:orientation="vertical"
            app:columnCount="@integer/grid_cells">
        </android.support.v7.widget.GridLayout>

    </ScrollView>

这是一个适配器:

public class GridAdapter {
    /**
     * Indicates maximum filled row in current column, column is the index of arr, row is the value
     * [224444555550000000]
     * It equals to:
     * [***********-------]
     * [***********-------]
     * [--*********-------]
     * [--*********-------]
     * [------*****-------]
     * [------------------]
     */
    private final int[] mRowMatrix;

    public GridAdapter(GridLayout gridLayout) {
        mGridLayout = gridLayout;
        mPlacedList = new ArrayList<>(10);
        mUnplacedList = new LinkedList<>();

        mNumColumns = ResHelper.getInteger(R.integer.grid_cells);
        mMinCells = ResHelper.getInteger(R.integer.min_cells);
        mMaxCells = ResHelper.getInteger(R.integer.max_cells);
        mRowMatrix = new int[mNumColumns];
    }

    public void notifyDataSetChanged() {

        while (mUnplacedList.size() > 0) {
            final int toCol = findColWithMinRow();
            final int gapSize = findGapSizeForCol(toCol);
            final CustomView view = findAppropriate(gapSize);
            if (view == null) {
                final int filledRow = toCol > 0
                        ? (toCol + gapSize < mRowMatrix.length ? Math.min(mRowMatrix[toCol - 1], mRowMatrix[toCol + gapSize]) : mRowMatrix[toCol - 1])
                        : mRowMatrix[gapSize];
                for (int j = toCol, jCount = toCol + gapSize; j < jCount; j++) {
                    mRowMatrix[j] = filledRow;
                }
            } else {
                placeView(view, toCol, mRowMatrix[toCol]);
            }
        }
    }

    /**
     * Put view in certain column and row in the gridlayout
     */
    private void placeView(CustomView view, int toCol, int toRow) {
        final int gridSize = view.getGridSize();
        final GridLayout.LayoutParams params = new GridLayout.LayoutParams();
        params.width = params.height = gridSize * mCellSize;
        params.columnSpec = GridLayout.spec(toCol, gridSize);
        params.rowSpec = GridLayout.spec(toRow, gridSize);

        mPlacedList.add(view);
        mUnplacedList.remove(view);
        mGridLayout.addView(view, params);

        final int filledRow = toRow + gridSize;
        for (int j = toCol, count = toCol + gridSize; j < count; j++) {
            mRowMap[j] = filledRow;
        }
    }

    /**
     * Find empty gap which starts from toCol
     * [*********************]
     * [******------*****----] here col = 6, size = 3
     * [******------*****----]
     * [******------*****----]
     * [*****************----]
     * @param toCol
     * @return
     */
    private int findGapSizeForCol(int toCol) {
        final int fromRow = mRowMatrix[toCol];
        int i = toCol;
        for (; i < mNumColumns; i++) {
            if (fromRow != mRowMatrix[i]) {
                break;
            }
        }
        return i - toCol;
    }

    /**
     * Find column with minimum filled row
     * [*********************]
     * [*****************----] here col = 17
     * [******------*****----]
     * [******------*****----]
     * @return
     */
    private int findColWithMinRow() {
        int minRow = Integer.MAX_VALUE, minCol = 0;
        for (int i = 0, count = mRowMatrix.length; i < count; i++) {
            if (minRow > mRowMatrix[i]) {
                minRow = mRowMatrix[i];
                minCol = i;
            }
        }
        return minCol;
    }

    /**
     * Find customView with appropriate size for the empty gap
     */
    private CustomView findAppropriate(int size) {
        for (int j = mUnplacedList.size() - 1; j >= 0; j--) {
            if (mUnplacedList.get(j).getGridSize() <= size) {
                return mUnplacedList.get(j);
            }
        }
        return null;
    }

}