GridLayoutManagerUtils.java

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.car.widget;

import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;

/**
 * Utility class that helps navigating in GridLayoutManager.
 *
 * <p>Assumes parameter {@code RecyclerView} uses {@link GridLayoutManager}.
 *
 * <p>Assumes the orientation of {@code GridLayoutManager} is vertical.
 */
class GridLayoutManagerUtils {
    private GridLayoutManagerUtils() {}

    /**
     * @param parent RecyclerView that uses GridLayoutManager as LayoutManager.
     * @return number of items in the first row in {@code RecyclerView}.
     */
    public static int getFirstRowItemCount(RecyclerView parent) {
        GridLayoutManager manager = (GridLayoutManager) parent.getLayoutManager();
        int itemCount = parent.getAdapter().getItemCount();
        int spanCount = manager.getSpanCount();

        int spanSum = 0;
        int pos = 0;
        while (pos < itemCount && spanSum < spanCount) {
            spanSum += manager.getSpanSizeLookup().getSpanSize(pos);
            pos += 1;
        }
        // pos will be either the first item in second row, or item count when items not fill
        // the first row.
        return pos;
    }

    /**
     * Returns the span index of an item.
     */
    public static int getSpanIndex(View item) {
        GridLayoutManager.LayoutParams layoutParams =
                ((GridLayoutManager.LayoutParams) item.getLayoutParams());
        return layoutParams.getSpanIndex();
    }

    /**
     * Returns the span size of an item. {@code item} must be already laid out.
     */
    public static int getSpanSize(View item) {
        GridLayoutManager.LayoutParams layoutParams =
                ((GridLayoutManager.LayoutParams) item.getLayoutParams());
        return layoutParams.getSpanSize();
    }

    /**
     * Returns the index of the last item that is on the same row as {@code index}.
     *
     * @param index index of child {@code View} in {@code parent}.
     * @param parent {@link RecyclerView} that contains the View {@code index} points to.
     */
    public static int getLastIndexOnSameRow(int index, RecyclerView parent) {
        int spanCount = ((GridLayoutManager) parent.getLayoutManager()).getSpanCount();
        int spanSum = GridLayoutManagerUtils.getSpanIndex(parent.getChildAt(index));
        for (int i = index; i < parent.getChildCount(); i++) {
            spanSum += GridLayoutManagerUtils.getSpanSize(parent.getChildAt(i));
            if (spanSum > spanCount) {
                // We have reached next row.

                // Implicit constraint by grid layout manager:
                // Initial spanSum + spanSize would not exceed spanCount, so it's safe to
                // subtract 1.
                return i - 1;
            }
        }
        // Still have not reached row end. Assuming the list only scrolls vertically, we are at
        // the last row.
        return parent.getChildCount() - 1;
    }
}