2015-05-03 11:48:36 +00:00
|
|
|
/*
|
2019-05-14 12:08:05 +00:00
|
|
|
* Copyright 2018 The Android Open Source Project
|
2015-05-03 11:48:36 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2019-05-14 12:08:05 +00:00
|
|
|
package androidx.recyclerview.widget;
|
2015-05-03 11:48:36 +00:00
|
|
|
|
|
|
|
import android.util.Log;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper class to manage children.
|
|
|
|
* <p>
|
|
|
|
* It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods
|
|
|
|
* provided by this class. <b>Regular</b> methods are the ones that replicate ViewGroup methods
|
|
|
|
* like getChildAt, getChildCount etc. These methods ignore hidden children.
|
|
|
|
* <p>
|
|
|
|
* When RecyclerView needs direct access to the view group children, it can call unfiltered
|
|
|
|
* methods like get getUnfilteredChildCount or getUnfilteredChildAt.
|
|
|
|
*/
|
|
|
|
class ChildHelper {
|
|
|
|
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
|
|
|
|
private static final String TAG = "ChildrenHelper";
|
|
|
|
|
|
|
|
final Callback mCallback;
|
|
|
|
|
|
|
|
final Bucket mBucket;
|
|
|
|
|
|
|
|
final List<View> mHiddenViews;
|
|
|
|
|
|
|
|
ChildHelper(Callback callback) {
|
|
|
|
mCallback = callback;
|
|
|
|
mBucket = new Bucket();
|
|
|
|
mHiddenViews = new ArrayList<View>();
|
|
|
|
}
|
|
|
|
|
2015-09-24 20:52:02 +00:00
|
|
|
/**
|
|
|
|
* Marks a child view as hidden
|
|
|
|
*
|
|
|
|
* @param child View to hide.
|
|
|
|
*/
|
|
|
|
private void hideViewInternal(View child) {
|
|
|
|
mHiddenViews.add(child);
|
|
|
|
mCallback.onEnteredHiddenState(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unmarks a child view as hidden.
|
|
|
|
*
|
|
|
|
* @param child View to hide.
|
|
|
|
*/
|
|
|
|
private boolean unhideViewInternal(View child) {
|
|
|
|
if (mHiddenViews.remove(child)) {
|
|
|
|
mCallback.onLeftHiddenState(child);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-30 02:07:02 +00:00
|
|
|
protected int getHiddenChildCount() {
|
|
|
|
return mHiddenViews.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
public View getHiddenChildAt(int index) {
|
|
|
|
if (index < 0 || index >= mHiddenViews.size()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return mHiddenViews.get(index);
|
|
|
|
}
|
|
|
|
|
2015-05-03 11:48:36 +00:00
|
|
|
/**
|
|
|
|
* Adds a view to the ViewGroup
|
|
|
|
*
|
|
|
|
* @param child View to add.
|
|
|
|
* @param hidden If set to true, this item will be invisible from regular methods.
|
|
|
|
*/
|
|
|
|
void addView(View child, boolean hidden) {
|
|
|
|
addView(child, -1, hidden);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a view to the ViewGroup at an index
|
|
|
|
*
|
|
|
|
* @param child View to add.
|
|
|
|
* @param index Index of the child from the regular perspective (excluding hidden views).
|
|
|
|
* ChildHelper offsets this index to actual ViewGroup index.
|
|
|
|
* @param hidden If set to true, this item will be invisible from regular methods.
|
|
|
|
*/
|
|
|
|
void addView(View child, int index, boolean hidden) {
|
|
|
|
final int offset;
|
|
|
|
if (index < 0) {
|
|
|
|
offset = mCallback.getChildCount();
|
|
|
|
} else {
|
|
|
|
offset = getOffset(index);
|
|
|
|
}
|
|
|
|
mBucket.insert(offset, hidden);
|
|
|
|
if (hidden) {
|
2015-09-24 20:52:02 +00:00
|
|
|
hideViewInternal(child);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
mCallback.addView(child, offset);
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private int getOffset(int index) {
|
|
|
|
if (index < 0) {
|
|
|
|
return -1; //anything below 0 won't work as diff will be undefined.
|
|
|
|
}
|
|
|
|
final int limit = mCallback.getChildCount();
|
|
|
|
int offset = index;
|
|
|
|
while (offset < limit) {
|
|
|
|
final int removedBefore = mBucket.countOnesBefore(offset);
|
|
|
|
final int diff = index - (offset - removedBefore);
|
|
|
|
if (diff == 0) {
|
|
|
|
while (mBucket.get(offset)) { // ensure this offset is not hidden
|
2018-07-30 02:07:02 +00:00
|
|
|
offset++;
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
return offset;
|
|
|
|
} else {
|
|
|
|
offset += diff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the provided View from underlying RecyclerView.
|
|
|
|
*
|
|
|
|
* @param view The view to remove.
|
|
|
|
*/
|
|
|
|
void removeView(View view) {
|
|
|
|
int index = mCallback.indexOfChild(view);
|
|
|
|
if (index < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mBucket.remove(index)) {
|
2015-09-24 20:52:02 +00:00
|
|
|
unhideViewInternal(view);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
mCallback.removeViewAt(index);
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "remove View off:" + index + "," + this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the view at the provided index from RecyclerView.
|
|
|
|
*
|
|
|
|
* @param index Index of the child from the regular perspective (excluding hidden views).
|
|
|
|
* ChildHelper offsets this index to actual ViewGroup index.
|
|
|
|
*/
|
|
|
|
void removeViewAt(int index) {
|
|
|
|
final int offset = getOffset(index);
|
|
|
|
final View view = mCallback.getChildAt(offset);
|
|
|
|
if (view == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mBucket.remove(offset)) {
|
2015-09-24 20:52:02 +00:00
|
|
|
unhideViewInternal(view);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
mCallback.removeViewAt(offset);
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the child at provided index.
|
|
|
|
*
|
|
|
|
* @param index Index of the child to return in regular perspective.
|
|
|
|
*/
|
|
|
|
View getChildAt(int index) {
|
|
|
|
final int offset = getOffset(index);
|
|
|
|
return mCallback.getChildAt(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes all views from the ViewGroup including the hidden ones.
|
|
|
|
*/
|
|
|
|
void removeAllViewsUnfiltered() {
|
|
|
|
mBucket.reset();
|
2015-09-24 20:52:02 +00:00
|
|
|
for (int i = mHiddenViews.size() - 1; i >= 0; i--) {
|
|
|
|
mCallback.onLeftHiddenState(mHiddenViews.get(i));
|
|
|
|
mHiddenViews.remove(i);
|
|
|
|
}
|
2015-05-03 11:48:36 +00:00
|
|
|
mCallback.removeAllViews();
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "removeAllViewsUnfiltered");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This can be used to find a disappearing view by position.
|
|
|
|
*
|
|
|
|
* @param position The adapter position of the item.
|
2017-03-30 23:58:05 +00:00
|
|
|
* @return A hidden view with a valid ViewHolder that matches the position.
|
2015-05-03 11:48:36 +00:00
|
|
|
*/
|
2017-03-30 23:58:05 +00:00
|
|
|
View findHiddenNonRemovedView(int position) {
|
2015-05-03 11:48:36 +00:00
|
|
|
final int count = mHiddenViews.size();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
final View view = mHiddenViews.get(i);
|
|
|
|
RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
|
2017-03-30 23:58:05 +00:00
|
|
|
if (holder.getLayoutPosition() == position
|
|
|
|
&& !holder.isInvalid()
|
|
|
|
&& !holder.isRemoved()) {
|
2015-05-03 11:48:36 +00:00
|
|
|
return view;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attaches the provided view to the underlying ViewGroup.
|
|
|
|
*
|
|
|
|
* @param child Child to attach.
|
|
|
|
* @param index Index of the child to attach in regular perspective.
|
|
|
|
* @param layoutParams LayoutParams for the child.
|
|
|
|
* @param hidden If set to true, this item will be invisible to the regular methods.
|
|
|
|
*/
|
|
|
|
void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams,
|
|
|
|
boolean hidden) {
|
|
|
|
final int offset;
|
|
|
|
if (index < 0) {
|
|
|
|
offset = mCallback.getChildCount();
|
|
|
|
} else {
|
|
|
|
offset = getOffset(index);
|
|
|
|
}
|
|
|
|
mBucket.insert(offset, hidden);
|
|
|
|
if (hidden) {
|
2015-09-24 20:52:02 +00:00
|
|
|
hideViewInternal(child);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
mCallback.attachViewToParent(child, offset, layoutParams);
|
|
|
|
if (DEBUG) {
|
2018-07-30 02:07:02 +00:00
|
|
|
Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + ","
|
|
|
|
+ "h:" + hidden + ", " + this);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the number of children that are not hidden.
|
|
|
|
*
|
|
|
|
* @return Number of children that are not hidden.
|
|
|
|
* @see #getChildAt(int)
|
|
|
|
*/
|
|
|
|
int getChildCount() {
|
|
|
|
return mCallback.getChildCount() - mHiddenViews.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the total number of children.
|
|
|
|
*
|
|
|
|
* @return The total number of children including the hidden views.
|
|
|
|
* @see #getUnfilteredChildAt(int)
|
|
|
|
*/
|
|
|
|
int getUnfilteredChildCount() {
|
|
|
|
return mCallback.getChildCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a child by ViewGroup offset. ChildHelper won't offset this index.
|
|
|
|
*
|
|
|
|
* @param index ViewGroup index of the child to return.
|
|
|
|
* @return The view in the provided index.
|
|
|
|
*/
|
|
|
|
View getUnfilteredChildAt(int index) {
|
|
|
|
return mCallback.getChildAt(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Detaches the view at the provided index.
|
|
|
|
*
|
|
|
|
* @param index Index of the child to return in regular perspective.
|
|
|
|
*/
|
|
|
|
void detachViewFromParent(int index) {
|
|
|
|
final int offset = getOffset(index);
|
|
|
|
mBucket.remove(offset);
|
|
|
|
mCallback.detachViewFromParent(offset);
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "detach view from parent " + index + ", off:" + offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the index of the child in regular perspective.
|
|
|
|
*
|
|
|
|
* @param child The child whose index will be returned.
|
|
|
|
* @return The regular perspective index of the child or -1 if it does not exists.
|
|
|
|
*/
|
|
|
|
int indexOfChild(View child) {
|
|
|
|
final int index = mCallback.indexOfChild(child);
|
|
|
|
if (index == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (mBucket.get(index)) {
|
|
|
|
if (DEBUG) {
|
|
|
|
throw new IllegalArgumentException("cannot get index of a hidden child");
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// reverse the index
|
|
|
|
return index - mBucket.countOnesBefore(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether a View is visible to LayoutManager or not.
|
|
|
|
*
|
|
|
|
* @param view The child view to check. Should be a child of the Callback.
|
|
|
|
* @return True if the View is not visible to LayoutManager
|
|
|
|
*/
|
|
|
|
boolean isHidden(View view) {
|
|
|
|
return mHiddenViews.contains(view);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Marks a child view as hidden.
|
|
|
|
*
|
|
|
|
* @param view The view to hide.
|
|
|
|
*/
|
|
|
|
void hide(View view) {
|
|
|
|
final int offset = mCallback.indexOfChild(view);
|
|
|
|
if (offset < 0) {
|
|
|
|
throw new IllegalArgumentException("view is not a child, cannot hide " + view);
|
|
|
|
}
|
|
|
|
if (DEBUG && mBucket.get(offset)) {
|
|
|
|
throw new RuntimeException("trying to hide same view twice, how come ? " + view);
|
|
|
|
}
|
|
|
|
mBucket.set(offset);
|
2015-09-24 20:52:02 +00:00
|
|
|
hideViewInternal(view);
|
2015-05-03 11:48:36 +00:00
|
|
|
if (DEBUG) {
|
2018-07-30 02:07:02 +00:00
|
|
|
Log.d(TAG, "hiding child " + view + " at offset " + offset + ", " + this);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-16 12:26:32 +00:00
|
|
|
/**
|
|
|
|
* Moves a child view from hidden list to regular list.
|
|
|
|
* Calling this method should probably be followed by a detach, otherwise, it will suddenly
|
|
|
|
* show up in LayoutManager's children list.
|
|
|
|
*
|
|
|
|
* @param view The hidden View to unhide
|
|
|
|
*/
|
|
|
|
void unhide(View view) {
|
|
|
|
final int offset = mCallback.indexOfChild(view);
|
|
|
|
if (offset < 0) {
|
|
|
|
throw new IllegalArgumentException("view is not a child, cannot hide " + view);
|
|
|
|
}
|
|
|
|
if (!mBucket.get(offset)) {
|
|
|
|
throw new RuntimeException("trying to unhide a view that was not hidden" + view);
|
|
|
|
}
|
|
|
|
mBucket.clear(offset);
|
|
|
|
unhideViewInternal(view);
|
|
|
|
}
|
|
|
|
|
2015-05-03 11:48:36 +00:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return mBucket.toString() + ", hidden list:" + mHiddenViews.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a view from the ViewGroup if it is hidden.
|
|
|
|
*
|
|
|
|
* @param view The view to remove.
|
|
|
|
* @return True if the View is found and it is hidden. False otherwise.
|
|
|
|
*/
|
|
|
|
boolean removeViewIfHidden(View view) {
|
|
|
|
final int index = mCallback.indexOfChild(view);
|
|
|
|
if (index == -1) {
|
2015-09-24 20:52:02 +00:00
|
|
|
if (unhideViewInternal(view) && DEBUG) {
|
2015-05-03 11:48:36 +00:00
|
|
|
throw new IllegalStateException("view is in hidden list but not in view group");
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (mBucket.get(index)) {
|
|
|
|
mBucket.remove(index);
|
2015-09-24 20:52:02 +00:00
|
|
|
if (!unhideViewInternal(view) && DEBUG) {
|
2015-05-03 11:48:36 +00:00
|
|
|
throw new IllegalStateException(
|
|
|
|
"removed a hidden view but it is not in hidden views list");
|
|
|
|
}
|
|
|
|
mCallback.removeViewAt(index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bitset implementation that provides methods to offset indices.
|
|
|
|
*/
|
|
|
|
static class Bucket {
|
|
|
|
|
2018-07-30 02:07:02 +00:00
|
|
|
static final int BITS_PER_WORD = Long.SIZE;
|
2015-05-03 11:48:36 +00:00
|
|
|
|
2018-07-30 02:07:02 +00:00
|
|
|
static final long LAST_BIT = 1L << (Long.SIZE - 1);
|
2015-05-03 11:48:36 +00:00
|
|
|
|
|
|
|
long mData = 0;
|
|
|
|
|
2018-07-30 02:07:02 +00:00
|
|
|
Bucket mNext;
|
2015-05-03 11:48:36 +00:00
|
|
|
|
|
|
|
void set(int index) {
|
|
|
|
if (index >= BITS_PER_WORD) {
|
|
|
|
ensureNext();
|
2018-07-30 02:07:02 +00:00
|
|
|
mNext.set(index - BITS_PER_WORD);
|
2015-05-03 11:48:36 +00:00
|
|
|
} else {
|
|
|
|
mData |= 1L << index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ensureNext() {
|
2018-07-30 02:07:02 +00:00
|
|
|
if (mNext == null) {
|
|
|
|
mNext = new Bucket();
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear(int index) {
|
|
|
|
if (index >= BITS_PER_WORD) {
|
2018-07-30 02:07:02 +00:00
|
|
|
if (mNext != null) {
|
|
|
|
mNext.clear(index - BITS_PER_WORD);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mData &= ~(1L << index);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean get(int index) {
|
|
|
|
if (index >= BITS_PER_WORD) {
|
|
|
|
ensureNext();
|
2018-07-30 02:07:02 +00:00
|
|
|
return mNext.get(index - BITS_PER_WORD);
|
2015-05-03 11:48:36 +00:00
|
|
|
} else {
|
|
|
|
return (mData & (1L << index)) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
mData = 0;
|
2018-07-30 02:07:02 +00:00
|
|
|
if (mNext != null) {
|
|
|
|
mNext.reset();
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void insert(int index, boolean value) {
|
|
|
|
if (index >= BITS_PER_WORD) {
|
|
|
|
ensureNext();
|
2018-07-30 02:07:02 +00:00
|
|
|
mNext.insert(index - BITS_PER_WORD, value);
|
2015-05-03 11:48:36 +00:00
|
|
|
} else {
|
|
|
|
final boolean lastBit = (mData & LAST_BIT) != 0;
|
|
|
|
long mask = (1L << index) - 1;
|
|
|
|
final long before = mData & mask;
|
|
|
|
final long after = ((mData & ~mask)) << 1;
|
|
|
|
mData = before | after;
|
|
|
|
if (value) {
|
|
|
|
set(index);
|
|
|
|
} else {
|
|
|
|
clear(index);
|
|
|
|
}
|
2018-07-30 02:07:02 +00:00
|
|
|
if (lastBit || mNext != null) {
|
2015-05-03 11:48:36 +00:00
|
|
|
ensureNext();
|
2018-07-30 02:07:02 +00:00
|
|
|
mNext.insert(0, lastBit);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean remove(int index) {
|
|
|
|
if (index >= BITS_PER_WORD) {
|
|
|
|
ensureNext();
|
2018-07-30 02:07:02 +00:00
|
|
|
return mNext.remove(index - BITS_PER_WORD);
|
2015-05-03 11:48:36 +00:00
|
|
|
} else {
|
|
|
|
long mask = (1L << index);
|
|
|
|
final boolean value = (mData & mask) != 0;
|
|
|
|
mData &= ~mask;
|
|
|
|
mask = mask - 1;
|
|
|
|
final long before = mData & mask;
|
|
|
|
// cannot use >> because it adds one.
|
|
|
|
final long after = Long.rotateRight(mData & ~mask, 1);
|
|
|
|
mData = before | after;
|
2018-07-30 02:07:02 +00:00
|
|
|
if (mNext != null) {
|
|
|
|
if (mNext.get(0)) {
|
2015-05-03 11:48:36 +00:00
|
|
|
set(BITS_PER_WORD - 1);
|
|
|
|
}
|
2018-07-30 02:07:02 +00:00
|
|
|
mNext.remove(0);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int countOnesBefore(int index) {
|
2018-07-30 02:07:02 +00:00
|
|
|
if (mNext == null) {
|
2015-05-03 11:48:36 +00:00
|
|
|
if (index >= BITS_PER_WORD) {
|
|
|
|
return Long.bitCount(mData);
|
|
|
|
}
|
|
|
|
return Long.bitCount(mData & ((1L << index) - 1));
|
|
|
|
}
|
|
|
|
if (index < BITS_PER_WORD) {
|
|
|
|
return Long.bitCount(mData & ((1L << index) - 1));
|
|
|
|
} else {
|
2018-07-30 02:07:02 +00:00
|
|
|
return mNext.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2018-07-30 02:07:02 +00:00
|
|
|
return mNext == null ? Long.toBinaryString(mData)
|
|
|
|
: mNext.toString() + "xx" + Long.toBinaryString(mData);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-30 02:07:02 +00:00
|
|
|
interface Callback {
|
2015-05-03 11:48:36 +00:00
|
|
|
|
|
|
|
int getChildCount();
|
|
|
|
|
|
|
|
void addView(View child, int index);
|
|
|
|
|
|
|
|
int indexOfChild(View view);
|
|
|
|
|
|
|
|
void removeViewAt(int index);
|
|
|
|
|
|
|
|
View getChildAt(int offset);
|
|
|
|
|
|
|
|
void removeAllViews();
|
|
|
|
|
|
|
|
RecyclerView.ViewHolder getChildViewHolder(View view);
|
|
|
|
|
|
|
|
void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams);
|
|
|
|
|
|
|
|
void detachViewFromParent(int offset);
|
2015-09-24 20:52:02 +00:00
|
|
|
|
|
|
|
void onEnteredHiddenState(View child);
|
|
|
|
|
|
|
|
void onLeftHiddenState(View child);
|
2015-05-03 11:48:36 +00:00
|
|
|
}
|
|
|
|
}
|