Nagram/TMessagesProj/src/main/java/androidx/recyclerview/widget/ViewBoundsCheck.java
2019-05-14 15:08:05 +03:00

270 lines
9.7 KiB
Java

/*
* 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 androidx.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);
}
}