Import androidx LinearLayoutManager as BetterLinearLayoutManager
At version 1.2.1: From https://android.googlesource.com/platform/frameworks/support revision aaeb4835903077a52ade9c4b561ff34efa96a155 With changed package name, and modified to build outside of the androidx package. Change-Id: Id055880b3513305637f170df350611d20783931d
This commit is contained in:
parent
e7264de893
commit
a321d6a79a
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,101 @@
|
|||
package de.spiritcroc.recyclerview.widget;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Exposing/replicating some internal functions from RecylerView.LayoutManager
|
||||
*/
|
||||
public abstract class LayoutManager extends RecyclerView.LayoutManager {
|
||||
|
||||
|
||||
/*
|
||||
* Exposed things from RecyclerView.java
|
||||
*/
|
||||
|
||||
/**
|
||||
* The callback used for retrieving information about a RecyclerView and its children in the
|
||||
* horizontal direction.
|
||||
*/
|
||||
private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
|
||||
new ViewBoundsCheck.Callback() {
|
||||
@Override
|
||||
public View getChildAt(int index) {
|
||||
return LayoutManager.this.getChildAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParentStart() {
|
||||
return LayoutManager.this.getPaddingLeft();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParentEnd() {
|
||||
return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildStart(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildEnd(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback used for retrieving information about a RecyclerView and its children in the
|
||||
* vertical direction.
|
||||
*/
|
||||
private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
|
||||
new ViewBoundsCheck.Callback() {
|
||||
@Override
|
||||
public View getChildAt(int index) {
|
||||
return LayoutManager.this.getChildAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParentStart() {
|
||||
return LayoutManager.this.getPaddingTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParentEnd() {
|
||||
return LayoutManager.this.getHeight()
|
||||
- LayoutManager.this.getPaddingBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildStart(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildEnd(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility objects used to check the boundaries of children against their parent
|
||||
* RecyclerView.
|
||||
*
|
||||
* @see #isViewPartiallyVisible(View, boolean, boolean),
|
||||
* {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
|
||||
* and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
|
||||
*/
|
||||
ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
|
||||
ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 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 de.spiritcroc.recyclerview.widget;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.OrientationHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* A helper class to do scroll offset calculations.
|
||||
*/
|
||||
class ScrollbarHelper {
|
||||
|
||||
/**
|
||||
* @param startChild View closest to start of the list. (top or left)
|
||||
* @param endChild View closest to end of the list (bottom or right)
|
||||
*/
|
||||
static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
|
||||
View startChild, View endChild, RecyclerView.LayoutManager lm,
|
||||
boolean smoothScrollbarEnabled, boolean reverseLayout) {
|
||||
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
|
||||
|| endChild == null) {
|
||||
return 0;
|
||||
}
|
||||
final int minPosition = Math.min(lm.getPosition(startChild),
|
||||
lm.getPosition(endChild));
|
||||
final int maxPosition = Math.max(lm.getPosition(startChild),
|
||||
lm.getPosition(endChild));
|
||||
final int itemsBefore = reverseLayout
|
||||
? Math.max(0, state.getItemCount() - maxPosition - 1)
|
||||
: Math.max(0, minPosition);
|
||||
if (!smoothScrollbarEnabled) {
|
||||
return itemsBefore;
|
||||
}
|
||||
final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild)
|
||||
- orientation.getDecoratedStart(startChild));
|
||||
final int itemRange = Math.abs(lm.getPosition(startChild)
|
||||
- lm.getPosition(endChild)) + 1;
|
||||
final float avgSizePerRow = (float) laidOutArea / itemRange;
|
||||
|
||||
return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
|
||||
- orientation.getDecoratedStart(startChild)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startChild View closest to start of the list. (top or left)
|
||||
* @param endChild View closest to end of the list (bottom or right)
|
||||
*/
|
||||
static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation,
|
||||
View startChild, View endChild, RecyclerView.LayoutManager lm,
|
||||
boolean smoothScrollbarEnabled) {
|
||||
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
|
||||
|| endChild == null) {
|
||||
return 0;
|
||||
}
|
||||
if (!smoothScrollbarEnabled) {
|
||||
return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
|
||||
}
|
||||
final int extend = orientation.getDecoratedEnd(endChild)
|
||||
- orientation.getDecoratedStart(startChild);
|
||||
return Math.min(orientation.getTotalSpace(), extend);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startChild View closest to start of the list. (top or left)
|
||||
* @param endChild View closest to end of the list (bottom or right)
|
||||
*/
|
||||
static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation,
|
||||
View startChild, View endChild, RecyclerView.LayoutManager lm,
|
||||
boolean smoothScrollbarEnabled) {
|
||||
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
|
||||
|| endChild == null) {
|
||||
return 0;
|
||||
}
|
||||
if (!smoothScrollbarEnabled) {
|
||||
return state.getItemCount();
|
||||
}
|
||||
// smooth scrollbar enabled. try to estimate better.
|
||||
final int laidOutArea = orientation.getDecoratedEnd(endChild)
|
||||
- orientation.getDecoratedStart(startChild);
|
||||
final int laidOutRange = Math.abs(lm.getPosition(startChild)
|
||||
- lm.getPosition(endChild))
|
||||
+ 1;
|
||||
// estimate a size for full list.
|
||||
return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
|
||||
}
|
||||
|
||||
private ScrollbarHelper() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright 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 de.spiritcroc.recyclerview.widget;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* A utility class used to check the boundaries of a given view within its parent view based on
|
||||
* a set of boundary flags.
|
||||
*/
|
||||
class ViewBoundsCheck {
|
||||
|
||||
static final int GT = 1 << 0;
|
||||
static final int EQ = 1 << 1;
|
||||
static final int LT = 1 << 2;
|
||||
|
||||
|
||||
static final int CVS_PVS_POS = 0;
|
||||
/**
|
||||
* The child view's start should be strictly greater than parent view's start.
|
||||
*/
|
||||
static final int FLAG_CVS_GT_PVS = GT << CVS_PVS_POS;
|
||||
|
||||
/**
|
||||
* The child view's start can be equal to its parent view's start. This flag follows with GT
|
||||
* or LT indicating greater (less) than or equal relation.
|
||||
*/
|
||||
static final int FLAG_CVS_EQ_PVS = EQ << CVS_PVS_POS;
|
||||
|
||||
/**
|
||||
* The child view's start should be strictly less than parent view's start.
|
||||
*/
|
||||
static final int FLAG_CVS_LT_PVS = LT << CVS_PVS_POS;
|
||||
|
||||
|
||||
static final int CVS_PVE_POS = 4;
|
||||
/**
|
||||
* The child view's start should be strictly greater than parent view's end.
|
||||
*/
|
||||
static final int FLAG_CVS_GT_PVE = GT << CVS_PVE_POS;
|
||||
|
||||
/**
|
||||
* The child view's start can be equal to its parent view's end. This flag follows with GT
|
||||
* or LT indicating greater (less) than or equal relation.
|
||||
*/
|
||||
static final int FLAG_CVS_EQ_PVE = EQ << CVS_PVE_POS;
|
||||
|
||||
/**
|
||||
* The child view's start should be strictly less than parent view's end.
|
||||
*/
|
||||
static final int FLAG_CVS_LT_PVE = LT << CVS_PVE_POS;
|
||||
|
||||
|
||||
static final int CVE_PVS_POS = 8;
|
||||
/**
|
||||
* The child view's end should be strictly greater than parent view's start.
|
||||
*/
|
||||
static final int FLAG_CVE_GT_PVS = GT << CVE_PVS_POS;
|
||||
|
||||
/**
|
||||
* The child view's end can be equal to its parent view's start. This flag follows with GT
|
||||
* or LT indicating greater (less) than or equal relation.
|
||||
*/
|
||||
static final int FLAG_CVE_EQ_PVS = EQ << CVE_PVS_POS;
|
||||
|
||||
/**
|
||||
* The child view's end should be strictly less than parent view's start.
|
||||
*/
|
||||
static final int FLAG_CVE_LT_PVS = LT << CVE_PVS_POS;
|
||||
|
||||
|
||||
static final int CVE_PVE_POS = 12;
|
||||
/**
|
||||
* The child view's end should be strictly greater than parent view's end.
|
||||
*/
|
||||
static final int FLAG_CVE_GT_PVE = GT << CVE_PVE_POS;
|
||||
|
||||
/**
|
||||
* The child view's end can be equal to its parent view's end. This flag follows with GT
|
||||
* or LT indicating greater (less) than or equal relation.
|
||||
*/
|
||||
static final int FLAG_CVE_EQ_PVE = EQ << CVE_PVE_POS;
|
||||
|
||||
/**
|
||||
* The child view's end should be strictly less than parent view's end.
|
||||
*/
|
||||
static final int FLAG_CVE_LT_PVE = LT << CVE_PVE_POS;
|
||||
|
||||
static final int MASK = GT | EQ | LT;
|
||||
|
||||
final Callback mCallback;
|
||||
BoundFlags mBoundFlags;
|
||||
/**
|
||||
* The set of flags that can be passed for checking the view boundary conditions.
|
||||
* CVS in the flag name indicates the child view, and PV indicates the parent view.\
|
||||
* The following S, E indicate a view's start and end points, respectively.
|
||||
* GT and LT indicate a strictly greater and less than relationship.
|
||||
* Greater than or equal (or less than or equal) can be specified by setting both GT and EQ (or
|
||||
* LT and EQ) flags.
|
||||
* For instance, setting both {@link #FLAG_CVS_GT_PVS} and {@link #FLAG_CVS_EQ_PVS} indicate the
|
||||
* child view's start should be greater than or equal to its parent start.
|
||||
*/
|
||||
@IntDef(flag = true, value = {
|
||||
FLAG_CVS_GT_PVS, FLAG_CVS_EQ_PVS, FLAG_CVS_LT_PVS,
|
||||
FLAG_CVS_GT_PVE, FLAG_CVS_EQ_PVE, FLAG_CVS_LT_PVE,
|
||||
FLAG_CVE_GT_PVS, FLAG_CVE_EQ_PVS, FLAG_CVE_LT_PVS,
|
||||
FLAG_CVE_GT_PVE, FLAG_CVE_EQ_PVE, FLAG_CVE_LT_PVE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ViewBounds {}
|
||||
|
||||
ViewBoundsCheck(Callback callback) {
|
||||
mCallback = callback;
|
||||
mBoundFlags = new BoundFlags();
|
||||
}
|
||||
|
||||
static class BoundFlags {
|
||||
int mBoundFlags = 0;
|
||||
int mRvStart, mRvEnd, mChildStart, mChildEnd;
|
||||
|
||||
void setBounds(int rvStart, int rvEnd, int childStart, int childEnd) {
|
||||
mRvStart = rvStart;
|
||||
mRvEnd = rvEnd;
|
||||
mChildStart = childStart;
|
||||
mChildEnd = childEnd;
|
||||
}
|
||||
|
||||
void addFlags(@ViewBounds int flags) {
|
||||
mBoundFlags |= flags;
|
||||
}
|
||||
|
||||
void resetFlags() {
|
||||
mBoundFlags = 0;
|
||||
}
|
||||
|
||||
int compare(int x, int y) {
|
||||
if (x > y) {
|
||||
return GT;
|
||||
}
|
||||
if (x == y) {
|
||||
return EQ;
|
||||
}
|
||||
return LT;
|
||||
}
|
||||
|
||||
boolean boundsMatch() {
|
||||
if ((mBoundFlags & (MASK << CVS_PVS_POS)) != 0) {
|
||||
if ((mBoundFlags & (compare(mChildStart, mRvStart) << CVS_PVS_POS)) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mBoundFlags & (MASK << CVS_PVE_POS)) != 0) {
|
||||
if ((mBoundFlags & (compare(mChildStart, mRvEnd) << CVS_PVE_POS)) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mBoundFlags & (MASK << CVE_PVS_POS)) != 0) {
|
||||
if ((mBoundFlags & (compare(mChildEnd, mRvStart) << CVE_PVS_POS)) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mBoundFlags & (MASK << CVE_PVE_POS)) != 0) {
|
||||
if ((mBoundFlags & (compare(mChildEnd, mRvEnd) << CVE_PVE_POS)) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the first view starting from fromIndex to toIndex in views whose bounds lie within
|
||||
* its parent bounds based on the provided preferredBoundFlags. If no match is found based on
|
||||
* the preferred flags, and a nonzero acceptableBoundFlags is specified, the last view whose
|
||||
* bounds lie within its parent view based on the acceptableBoundFlags is returned. If no such
|
||||
* view is found based on either of these two flags, null is returned.
|
||||
* @param fromIndex The view position index to start the search from.
|
||||
* @param toIndex The view position index to end the search at.
|
||||
* @param preferredBoundFlags The flags indicating the preferred match. Once a match is found
|
||||
* based on this flag, that view is returned instantly.
|
||||
* @param acceptableBoundFlags The flags indicating the acceptable match if no preferred match
|
||||
* is found. If so, and if acceptableBoundFlags is non-zero, the
|
||||
* last matching acceptable view is returned. Otherwise, null is
|
||||
* returned.
|
||||
* @return The first view that satisfies acceptableBoundFlags or the last view satisfying
|
||||
* acceptableBoundFlags boundary conditions.
|
||||
*/
|
||||
View findOneViewWithinBoundFlags(int fromIndex, int toIndex,
|
||||
@ViewBounds int preferredBoundFlags,
|
||||
@ViewBounds int acceptableBoundFlags) {
|
||||
final int start = mCallback.getParentStart();
|
||||
final int end = mCallback.getParentEnd();
|
||||
final int next = toIndex > fromIndex ? 1 : -1;
|
||||
View acceptableMatch = null;
|
||||
for (int i = fromIndex; i != toIndex; i += next) {
|
||||
final View child = mCallback.getChildAt(i);
|
||||
final int childStart = mCallback.getChildStart(child);
|
||||
final int childEnd = mCallback.getChildEnd(child);
|
||||
mBoundFlags.setBounds(start, end, childStart, childEnd);
|
||||
if (preferredBoundFlags != 0) {
|
||||
mBoundFlags.resetFlags();
|
||||
mBoundFlags.addFlags(preferredBoundFlags);
|
||||
if (mBoundFlags.boundsMatch()) {
|
||||
// found a perfect match
|
||||
return child;
|
||||
}
|
||||
}
|
||||
if (acceptableBoundFlags != 0) {
|
||||
mBoundFlags.resetFlags();
|
||||
mBoundFlags.addFlags(acceptableBoundFlags);
|
||||
if (mBoundFlags.boundsMatch()) {
|
||||
acceptableMatch = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return acceptableMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified view lies within the boundary condition of its parent view.
|
||||
* @param child The child view to be checked.
|
||||
* @param boundsFlags The flag against which the child view and parent view are matched.
|
||||
* @return True if the view meets the boundsFlag, false otherwise.
|
||||
*/
|
||||
boolean isViewWithinBoundFlags(View child, @ViewBounds int boundsFlags) {
|
||||
mBoundFlags.setBounds(mCallback.getParentStart(), mCallback.getParentEnd(),
|
||||
mCallback.getChildStart(child), mCallback.getChildEnd(child));
|
||||
if (boundsFlags != 0) {
|
||||
mBoundFlags.resetFlags();
|
||||
mBoundFlags.addFlags(boundsFlags);
|
||||
return mBoundFlags.boundsMatch();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback provided by the user of this class in order to retrieve information about child and
|
||||
* parent boundaries.
|
||||
*/
|
||||
interface Callback {
|
||||
View getChildAt(int index);
|
||||
int getParentStart();
|
||||
int getParentEnd();
|
||||
int getChildStart(View view);
|
||||
int getChildEnd(View view);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue