blob: b7ce10cb6994b6dc8a2530340c1f1ddb18873309 [file] [log] [blame]
/*
* Copyright (C) 2019 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 com.android.car.ui.pagedrecyclerview.decorations.grid;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.IntDef;
import androidx.recyclerview.widget.RecyclerView;
import java.lang.annotation.Retention;
/** Adds an offset to the top of a RecyclerView with a GridLayoutManager or its subclass. */
public class GridOffsetItemDecoration extends RecyclerView.ItemDecoration {
private int mOffsetPx;
private Drawable mOffsetDrawable;
private int mNumColumns;
@OffsetPosition
private final int mOffsetPosition;
/** The possible values for setScrollbarPosition. */
@IntDef({
OffsetPosition.START,
OffsetPosition.END,
})
@Retention(SOURCE)
public @interface OffsetPosition {
/** Position the offset to the start of the screen. */
int START = 0;
/** Position offset to the end of the screen. */
int END = 1;
}
/**
* Constructor that takes in the size of the offset to be added to the top of the RecyclerView.
*
* @param offsetPx The size of the offset to be added to the top of the RecyclerView in pixels
* @param numColumns The number of columns in the grid of the RecyclerView
* @param offsetPosition Position where offset needs to be applied.
*/
public GridOffsetItemDecoration(int offsetPx, int numColumns, int offsetPosition) {
this.mOffsetPx = offsetPx;
this.mNumColumns = numColumns;
this.mOffsetPosition = offsetPosition;
}
/**
* Constructor that takes in a {@link Drawable} to be drawn at the top of the RecyclerView.
*
* @param offsetDrawable The {@code Drawable} to be added to the top of the RecyclerView
* @param numColumns The number of columns in the grid of the RecyclerView
*/
public GridOffsetItemDecoration(Drawable offsetDrawable, int numColumns, int offsetPosition) {
this.mOffsetDrawable = offsetDrawable;
this.mNumColumns = numColumns;
this.mOffsetPosition = offsetPosition;
}
public void setNumOfColumns(int numberOfColumns) {
mNumColumns = numberOfColumns;
}
/**
* Determines the size and the location of the offset to be added to the top of the
* RecyclerView.
*
* @param outRect The {@link Rect} of offsets to be added around the child view
* @param view The child view to be decorated with an offset
* @param parent The RecyclerView onto which dividers are being added
* @param state The current RecyclerView.State of the RecyclerView
*/
@Override
public void getItemOffsets(
Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (mOffsetPosition == OffsetPosition.START) {
boolean childIsInTopRow = parent.getChildAdapterPosition(view) < mNumColumns;
if (childIsInTopRow) {
if (mOffsetPx > 0) {
outRect.top = mOffsetPx;
} else if (mOffsetDrawable != null) {
outRect.top = mOffsetDrawable.getIntrinsicHeight();
}
}
return;
}
int childCount = state.getItemCount();
int lastRowChildCount = getLastRowChildCount(childCount);
boolean childIsInBottomRow =
parent.getChildAdapterPosition(view) >= childCount - lastRowChildCount;
if (childIsInBottomRow) {
if (mOffsetPx > 0) {
outRect.bottom = mOffsetPx;
} else if (mOffsetDrawable != null) {
outRect.bottom = mOffsetDrawable.getIntrinsicHeight();
}
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOffsetDrawable == null) {
return;
}
int parentLeft = parent.getPaddingLeft();
int parentRight = parent.getWidth() - parent.getPaddingRight();
if (mOffsetPosition == OffsetPosition.START) {
int parentTop = parent.getPaddingTop();
int offsetDrawableBottom = parentTop + mOffsetDrawable.getIntrinsicHeight();
mOffsetDrawable.setBounds(parentLeft, parentTop, parentRight, offsetDrawableBottom);
mOffsetDrawable.draw(c);
return;
}
int childCount = state.getItemCount();
int lastRowChildCount = getLastRowChildCount(childCount);
int offsetDrawableTop = 0;
int offsetDrawableBottom = 0;
for (int i = childCount - lastRowChildCount; i < childCount; i++) {
View child = parent.getChildAt(i);
offsetDrawableTop = child.getBottom();
offsetDrawableBottom = offsetDrawableTop + mOffsetDrawable.getIntrinsicHeight();
}
mOffsetDrawable.setBounds(parentLeft, offsetDrawableTop, parentRight, offsetDrawableBottom);
mOffsetDrawable.draw(c);
}
private int getLastRowChildCount(int itemCount) {
int lastRowChildCount = itemCount % mNumColumns;
if (lastRowChildCount == 0) {
lastRowChildCount = mNumColumns;
}
return lastRowChildCount;
}
}