mirror of
https://gitee.com/houstudio/Cdroid.git
synced 2024-12-04 05:10:06 +08:00
add carousellayoutmanager
This commit is contained in:
parent
dbf5d80dea
commit
bb6d5076f7
@ -6,22 +6,22 @@ namespace cdroid{
|
||||
#define MAX_LEVEL 10000
|
||||
RotateDrawable::RotateState::RotateState()
|
||||
:DrawableWrapperState(){
|
||||
mFromDegrees=.0;
|
||||
mToDegrees=360.0;
|
||||
mPivotX=mPivotY=0.5;
|
||||
mPivotXRel=mPivotYRel=true;
|
||||
mCurrentDegrees=0;
|
||||
mFromDegrees =.0;
|
||||
mToDegrees = 360.0;
|
||||
mPivotX = mPivotY = 0.5;
|
||||
mPivotXRel = mPivotYRel = true;
|
||||
mCurrentDegrees = 0;
|
||||
}
|
||||
|
||||
RotateDrawable::RotateState::RotateState(const RotateState& orig)
|
||||
:DrawableWrapperState(orig){
|
||||
mFromDegrees=orig.mFromDegrees;
|
||||
mToDegrees =orig.mToDegrees;
|
||||
mPivotX =orig.mPivotX;
|
||||
mPivotY =orig.mPivotY;
|
||||
mPivotXRel=orig.mPivotXRel;
|
||||
mPivotYRel=orig.mPivotYRel;
|
||||
mCurrentDegrees=orig.mCurrentDegrees;
|
||||
mFromDegrees= orig.mFromDegrees;
|
||||
mToDegrees = orig.mToDegrees;
|
||||
mPivotX = orig.mPivotX;
|
||||
mPivotY = orig.mPivotY;
|
||||
mPivotXRel= orig.mPivotXRel;
|
||||
mPivotYRel= orig.mPivotYRel;
|
||||
mCurrentDegrees = orig.mCurrentDegrees;
|
||||
}
|
||||
|
||||
RotateDrawable*RotateDrawable::RotateState::newDrawable(){
|
||||
@ -30,12 +30,12 @@ RotateDrawable*RotateDrawable::RotateState::newDrawable(){
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
std::shared_ptr<DrawableWrapper::DrawableWrapperState> RotateDrawable::mutateConstantState(){
|
||||
mState=std::make_shared<RotateState>(*mState);
|
||||
mState = std::make_shared<RotateState>(*mState);
|
||||
return mState;
|
||||
}
|
||||
|
||||
RotateDrawable::RotateDrawable(std::shared_ptr<RotateState>state):DrawableWrapper(state){
|
||||
mState=state;
|
||||
mState = state;
|
||||
}
|
||||
|
||||
RotateDrawable::RotateDrawable(Drawable*d)
|
||||
@ -129,22 +129,21 @@ static inline float sdot(float a,float b,float c,float d){
|
||||
void RotateDrawable::draw(Canvas& canvas) {
|
||||
Drawable*d = getDrawable();
|
||||
const Rect& bounds = getBounds();
|
||||
int w = bounds.width;
|
||||
int h = bounds.height;
|
||||
float px = mState->mPivotXRel ? (w * mState->mPivotX) : mState->mPivotX;
|
||||
float py = mState->mPivotYRel ? (h * mState->mPivotY) : mState->mPivotY;
|
||||
|
||||
const int w = bounds.width;
|
||||
const int h = bounds.height;
|
||||
const float px = mState->mPivotXRel ? (w * mState->mPivotX) : mState->mPivotX;
|
||||
const float py = mState->mPivotYRel ? (h * mState->mPivotY) : mState->mPivotY;
|
||||
#if 1
|
||||
d->setBounds(bounds);
|
||||
canvas.save();
|
||||
canvas.translate(px,py);
|
||||
canvas.rotate_degrees(mState->mCurrentDegrees);
|
||||
Rect rd = d->getBounds();
|
||||
rd.offset(-w/2,-h/2);
|
||||
d->setBounds(rd);
|
||||
Rect rect = bounds;
|
||||
rect.offset(-w/2,-h/2);
|
||||
d->setBounds(rect);
|
||||
d->draw(canvas);
|
||||
rd.offset(w/2,h/2);
|
||||
d->setBounds(rd);
|
||||
rect.offset(w/2,h/2);
|
||||
d->setBounds(rect);
|
||||
//LOGD("pos=%d,%d/%.f,%.f level=%d degress=%d",bounds.left,bounds.top,px,py,getLevel(),int(mState->mCurrentDegrees));
|
||||
canvas.restore();
|
||||
#else
|
||||
|
752
src/gui/widgetEx/recyclerview/carousellayoutmanager.cc
Normal file
752
src/gui/widgetEx/recyclerview/carousellayoutmanager.cc
Normal file
@ -0,0 +1,752 @@
|
||||
#include <widgetEx/recyclerview/carousellayoutmanager.h>
|
||||
#include <widgetEx/recyclerview/linearsmoothscroller.h>
|
||||
|
||||
//REF[https://github.com/Azoft/CarouselLayoutManager]
|
||||
|
||||
namespace cdroid{
|
||||
|
||||
CarouselLayoutManager::ItemTransformation::ItemTransformation(float scaleX, float scaleY, float translationX, float translationY) {
|
||||
mScaleX = scaleX;
|
||||
mScaleY = scaleY;
|
||||
mTranslationX = translationX;
|
||||
mTranslationY = translationY;
|
||||
}
|
||||
|
||||
CarouselLayoutManager::CarouselLayoutManager(int orientation)
|
||||
:CarouselLayoutManager(orientation, CIRCLE_LAYOUT){
|
||||
}
|
||||
|
||||
CarouselLayoutManager::CarouselLayoutManager(int orientation, bool circleLayout) {
|
||||
if (HORIZONTAL != orientation && VERTICAL != orientation) {
|
||||
throw "orientation should be HORIZONTAL or VERTICAL";
|
||||
}
|
||||
mOrientation = orientation;
|
||||
mCircleLayout = circleLayout;
|
||||
mPendingScrollPosition = INVALID_POSITION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change circle layout type
|
||||
*/
|
||||
void CarouselLayoutManager::setCircleLayout(bool circleLayout) {
|
||||
if (mCircleLayout != circleLayout) {
|
||||
mCircleLayout = circleLayout;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup {@link CarouselLayoutManager.PostLayoutListener} for this LayoutManager.
|
||||
* Its methods will be called for each visible view item after general LayoutManager layout finishes. <br />
|
||||
* <br />
|
||||
* Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting.
|
||||
*
|
||||
* @param postLayoutListener listener for item layout changes. Can be null.
|
||||
*/
|
||||
void CarouselLayoutManager::setPostLayoutListener(PostLayoutListener postLayoutListener) {
|
||||
mViewPostLayout = postLayoutListener;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup maximum visible (layout) items on each side of the center item.
|
||||
* Basically during scrolling there can be more visible items (+1 item on each side), but in idle state this is the only reached maximum.
|
||||
*
|
||||
* @param maxVisibleItems should be great then 0, if bot an {@link IllegalAccessException} will be thrown
|
||||
*/
|
||||
void CarouselLayoutManager::setMaxVisibleItems(int maxVisibleItems) {
|
||||
if (0 > maxVisibleItems) {
|
||||
throw "maxVisibleItems can't be less then 0";
|
||||
}
|
||||
mLayoutHelper->mMaxVisibleItems = maxVisibleItems;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current setup for maximum visible items.
|
||||
* @see #setMaxVisibleItems(int)
|
||||
*/
|
||||
int CarouselLayoutManager::getMaxVisibleItems() const{
|
||||
return mLayoutHelper->mMaxVisibleItems;
|
||||
}
|
||||
|
||||
RecyclerView::LayoutParams* CarouselLayoutManager::generateDefaultLayoutParams() const{
|
||||
return new RecyclerView::LayoutParams(ViewGroup::LayoutParams::WRAP_CONTENT, ViewGroup::LayoutParams::WRAP_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current layout orientation
|
||||
* @see #VERTICAL
|
||||
* @see #HORIZONTAL
|
||||
*/
|
||||
int CarouselLayoutManager::getOrientation() const{
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
bool CarouselLayoutManager::canScrollHorizontally() const{
|
||||
return 0 != getChildCount() && HORIZONTAL == mOrientation;
|
||||
}
|
||||
|
||||
bool CarouselLayoutManager::canScrollVertically() const{
|
||||
return 0 != getChildCount() && VERTICAL == mOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current layout center item
|
||||
*/
|
||||
int CarouselLayoutManager::getCenterItemPosition() const{
|
||||
return mCenterItemPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onCenterItemSelectionListener listener that will trigger when ItemSelectionChanges. can't be null
|
||||
*/
|
||||
void CarouselLayoutManager::addOnItemSelectionListener(OnCenterItemSelectionListener onCenterItemSelectionListener) {
|
||||
mOnCenterItemSelectionListeners.push_back(onCenterItemSelectionListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onCenterItemSelectionListener listener that was previously added by {@link #addOnItemSelectionListener(OnCenterItemSelectionListener)}
|
||||
*/
|
||||
void CarouselLayoutManager::removeOnItemSelectionListener(OnCenterItemSelectionListener onCenterItemSelectionListener) {
|
||||
auto it = std::find(mOnCenterItemSelectionListeners.begin(),
|
||||
mOnCenterItemSelectionListeners.end(),onCenterItemSelectionListener);
|
||||
mOnCenterItemSelectionListeners.erase(it);//remove(onCenterItemSelectionListener);
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::scrollToPosition(int position) {
|
||||
FATAL_IF(position<0,"position can't be less then 0. position is : %d" ,position);
|
||||
mPendingScrollPosition = position;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
class CarouselLayoutManager::CarouselLinearSmoothScroller:public LinearSmoothScroller{
|
||||
private:
|
||||
CarouselLayoutManager*mLM;
|
||||
public:
|
||||
CarouselLinearSmoothScroller(Context*ctx,CarouselLayoutManager*lm):LinearSmoothScroller(ctx),mLM(lm){
|
||||
}
|
||||
int calculateDyToMakeVisible(View* view, int snapPreference) override{
|
||||
if (!mLM->canScrollVertically()) {
|
||||
return 0;
|
||||
}
|
||||
return mLM->getOffsetForCurrentView(*view);
|
||||
}
|
||||
int calculateDxToMakeVisible(View* view, int snapPreference) override{
|
||||
if (!mLM->canScrollHorizontally()) {
|
||||
return 0;
|
||||
}
|
||||
return mLM->getOffsetForCurrentView(*view);
|
||||
}
|
||||
};
|
||||
|
||||
void CarouselLayoutManager::smoothScrollToPosition(RecyclerView& recyclerView, RecyclerView::State& state, int position) {
|
||||
LinearSmoothScroller* linearSmoothScroller = new CarouselLinearSmoothScroller(recyclerView.getContext(),this);
|
||||
linearSmoothScroller->setTargetPosition(position);
|
||||
startSmoothScroll(linearSmoothScroller);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T signum(T x) {
|
||||
return (T(0) < x) - (x < T(0));
|
||||
}
|
||||
|
||||
bool CarouselLayoutManager::computeScrollVectorForPosition(int targetPosition,PointF*pt) {
|
||||
float directionDistance = getScrollDirection(targetPosition);
|
||||
//noinspection NumericCastThatLosesPrecision
|
||||
int direction = (int) -signum(directionDistance);
|
||||
if(pt)pt->x = pt->y = 0;
|
||||
if (HORIZONTAL == mOrientation) {
|
||||
pt->x = direction;
|
||||
} else {
|
||||
pt->y = direction;
|
||||
}
|
||||
return getChildCount()>0;
|
||||
}
|
||||
|
||||
float CarouselLayoutManager::getScrollDirection(int targetPosition) {
|
||||
float currentScrollPosition = makeScrollPositionInRange0ToCount(getCurrentScrollPosition(), mItemsCount);
|
||||
|
||||
if (mCircleLayout) {
|
||||
float t1 = currentScrollPosition - targetPosition;
|
||||
float t2 = std::abs(t1) - mItemsCount;
|
||||
if (std::abs(t1) > std::abs(t2)) {
|
||||
return signum(t1) * t2;
|
||||
} else {
|
||||
return t1;
|
||||
}
|
||||
} else {
|
||||
return currentScrollPosition - targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
int CarouselLayoutManager::scrollVerticallyBy(int dy, RecyclerView::Recycler& recycler, RecyclerView::State& state) {
|
||||
if (HORIZONTAL == mOrientation) {
|
||||
return 0;
|
||||
}
|
||||
return scrollBy(dy, recycler, state);
|
||||
}
|
||||
|
||||
int CarouselLayoutManager::scrollHorizontallyBy(int dx, RecyclerView::Recycler& recycler, RecyclerView::State& state) {
|
||||
if (VERTICAL == mOrientation) {
|
||||
return 0;
|
||||
}
|
||||
return scrollBy(dx, recycler, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from {@link #scrollHorizontallyBy(int, RecyclerView.Recycler, RecyclerView.State)} and
|
||||
* {@link #scrollVerticallyBy(int, RecyclerView.Recycler, RecyclerView.State)} to calculate needed scroll that is allowed. <br />
|
||||
* <br />
|
||||
* This method may do relayout work.
|
||||
*
|
||||
* @param diff distance that we want to scroll by
|
||||
* @param recycler Recycler to use for fetching potentially cached views for a position
|
||||
* @param state Transient state of RecyclerView
|
||||
* @return distance that we actually scrolled by
|
||||
*/
|
||||
int CarouselLayoutManager::scrollBy(int diff, RecyclerView::Recycler& recycler, RecyclerView::State& state) {
|
||||
if ((INT_MIN == mDecoratedChildWidth) || (INT_MIN == mDecoratedChildHeight)) {
|
||||
return 0;
|
||||
}
|
||||
if (0 == getChildCount() || 0 == diff) {
|
||||
return 0;
|
||||
}
|
||||
int resultScroll;
|
||||
if (mCircleLayout) {
|
||||
resultScroll = diff;
|
||||
|
||||
mLayoutHelper->mScrollOffset += resultScroll;
|
||||
|
||||
int maxOffset = getScrollItemSize() * mItemsCount;
|
||||
while (0 > mLayoutHelper->mScrollOffset) {
|
||||
mLayoutHelper->mScrollOffset += maxOffset;
|
||||
}
|
||||
while (mLayoutHelper->mScrollOffset > maxOffset) {
|
||||
mLayoutHelper->mScrollOffset -= maxOffset;
|
||||
}
|
||||
|
||||
mLayoutHelper->mScrollOffset -= resultScroll;
|
||||
} else {
|
||||
int maxOffset = getMaxScrollOffset();
|
||||
|
||||
if (0 > mLayoutHelper->mScrollOffset + diff) {
|
||||
resultScroll = -mLayoutHelper->mScrollOffset; //to make it 0
|
||||
} else if (mLayoutHelper->mScrollOffset + diff > maxOffset) {
|
||||
resultScroll = maxOffset - mLayoutHelper->mScrollOffset; //to make it maxOffset
|
||||
} else {
|
||||
resultScroll = diff;
|
||||
}
|
||||
}
|
||||
if (0 != resultScroll) {
|
||||
mLayoutHelper->mScrollOffset += resultScroll;
|
||||
fillData(recycler, state);
|
||||
}
|
||||
return resultScroll;
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::onMeasure(RecyclerView::Recycler& recycler, RecyclerView::State& state, int widthSpec, int heightSpec) {
|
||||
mDecoratedChildSizeInvalid = true;
|
||||
LayoutManager::onMeasure(recycler, state, widthSpec, heightSpec);
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::onAdapterChanged(RecyclerView::Adapter* oldAdapter, RecyclerView::Adapter* newAdapter) {
|
||||
LayoutManager::onAdapterChanged(oldAdapter, newAdapter);
|
||||
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::onLayoutChildren(RecyclerView::Recycler& recycler, RecyclerView::State& state) {
|
||||
if (0 == state.getItemCount()) {
|
||||
removeAndRecycleAllViews(recycler);
|
||||
selectItemCenterPosition(INVALID_POSITION);
|
||||
return;
|
||||
}
|
||||
|
||||
detachAndScrapAttachedViews(recycler);
|
||||
|
||||
if ((INT_MIN == mDecoratedChildWidth) || mDecoratedChildSizeInvalid) {
|
||||
std::vector<RecyclerView::ViewHolder*> scrapList = recycler.getScrapList();
|
||||
|
||||
bool shouldRecycle;
|
||||
View* view;
|
||||
if (scrapList.empty()) {
|
||||
shouldRecycle = true;
|
||||
int itemsCount = state.getItemCount();
|
||||
view = recycler.getViewForPosition(mPendingScrollPosition == INVALID_POSITION ?
|
||||
0 : std::max(0, std::min(itemsCount - 1, mPendingScrollPosition)) );
|
||||
addView(view);
|
||||
} else {
|
||||
shouldRecycle = false;
|
||||
view = scrapList.at(0)->itemView;
|
||||
}
|
||||
measureChildWithMargins(view, 0, 0);
|
||||
|
||||
int decoratedChildWidth = getDecoratedMeasuredWidth(view);
|
||||
int decoratedChildHeight = getDecoratedMeasuredHeight(view);
|
||||
if (shouldRecycle) {
|
||||
detachAndScrapView(view, recycler);
|
||||
}
|
||||
|
||||
if ((INT_MIN != mDecoratedChildWidth) && ((mDecoratedChildWidth != decoratedChildWidth) || (mDecoratedChildHeight != decoratedChildHeight))) {
|
||||
if ((INVALID_POSITION == mPendingScrollPosition) && (nullptr == mPendingCarouselSavedState)) {
|
||||
mPendingScrollPosition = mCenterItemPosition;
|
||||
}
|
||||
}
|
||||
|
||||
mDecoratedChildWidth = decoratedChildWidth;
|
||||
mDecoratedChildHeight = decoratedChildHeight;
|
||||
mDecoratedChildSizeInvalid = false;
|
||||
}
|
||||
|
||||
if (INVALID_POSITION != mPendingScrollPosition) {
|
||||
int itemsCount = state.getItemCount();
|
||||
mPendingScrollPosition = 0 == itemsCount ? INVALID_POSITION : std::max(0, std::min(itemsCount - 1, mPendingScrollPosition));
|
||||
}
|
||||
if (INVALID_POSITION != mPendingScrollPosition) {
|
||||
mLayoutHelper->mScrollOffset = calculateScrollForSelectingPosition(mPendingScrollPosition, state);
|
||||
mPendingScrollPosition = INVALID_POSITION;
|
||||
mPendingCarouselSavedState = nullptr;
|
||||
} else if (mPendingCarouselSavedState) {
|
||||
mLayoutHelper->mScrollOffset = calculateScrollForSelectingPosition(mPendingCarouselSavedState->mCenterItemPosition, state);
|
||||
mPendingCarouselSavedState = nullptr;
|
||||
} else if (state.didStructureChange() && INVALID_POSITION != mCenterItemPosition) {
|
||||
mLayoutHelper->mScrollOffset = calculateScrollForSelectingPosition(mCenterItemPosition, state);
|
||||
}
|
||||
|
||||
fillData(recycler, state);
|
||||
}
|
||||
|
||||
int CarouselLayoutManager::calculateScrollForSelectingPosition(int itemPosition, RecyclerView::State& state) {
|
||||
if (itemPosition == INVALID_POSITION) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fixedItemPosition = itemPosition < state.getItemCount() ? itemPosition : state.getItemCount() - 1;
|
||||
return fixedItemPosition * (VERTICAL == mOrientation ? mDecoratedChildHeight : mDecoratedChildWidth);
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::fillData(RecyclerView::Recycler& recycler, RecyclerView::State& state) {
|
||||
float currentScrollPosition = getCurrentScrollPosition();
|
||||
|
||||
generateLayoutOrder(currentScrollPosition, state);
|
||||
detachAndScrapAttachedViews(recycler);
|
||||
recyclerOldViews(recycler);
|
||||
|
||||
int width = getWidthNoPadding();
|
||||
int height = getHeightNoPadding();
|
||||
if (VERTICAL == mOrientation) {
|
||||
fillDataVertical(recycler, width, height);
|
||||
} else {
|
||||
fillDataHorizontal(recycler, width, height);
|
||||
}
|
||||
|
||||
recycler.clear();
|
||||
|
||||
detectOnItemSelectionChanged(currentScrollPosition, state);
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::detectOnItemSelectionChanged(float currentScrollPosition, RecyclerView::State& state) {
|
||||
float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, state.getItemCount());
|
||||
int centerItem = std::round(absCurrentScrollPosition);
|
||||
|
||||
if (mCenterItemPosition != centerItem) {
|
||||
mCenterItemPosition = centerItem;
|
||||
/*new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
selectItemCenterPosition(centerItem);
|
||||
}
|
||||
});*/
|
||||
}
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::selectItemCenterPosition(int centerItem) {
|
||||
for (OnCenterItemSelectionListener onCenterItemSelectionListener : mOnCenterItemSelectionListeners) {
|
||||
onCenterItemSelectionListener(centerItem);
|
||||
}
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::fillDataVertical(RecyclerView::Recycler& recycler, int width, int height) {
|
||||
int start = (width - mDecoratedChildWidth) / 2;
|
||||
int end = start + mDecoratedChildWidth;
|
||||
|
||||
int centerViewTop = (height - mDecoratedChildHeight) / 2;
|
||||
|
||||
for (int i = 0, count = mLayoutHelper->mLayoutOrder.size(); i < count; ++i) {
|
||||
LayoutOrder* layoutOrder = mLayoutHelper->mLayoutOrder[i];
|
||||
int offset = getCardOffsetByPositionDiff(layoutOrder->mItemPositionDiff);
|
||||
int top = centerViewTop + offset;
|
||||
int bottom = top + mDecoratedChildHeight;
|
||||
fillChildItem(start, top, end, bottom, *layoutOrder, recycler, i);
|
||||
}
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::fillDataHorizontal(RecyclerView::Recycler& recycler, int width, int height) {
|
||||
int top = (height - mDecoratedChildHeight) / 2;
|
||||
int bottom = top + mDecoratedChildHeight;
|
||||
|
||||
int centerViewStart = (width - mDecoratedChildWidth) / 2;
|
||||
|
||||
for (int i = 0, count = mLayoutHelper->mLayoutOrder.size(); i < count; ++i) {
|
||||
LayoutOrder* layoutOrder = mLayoutHelper->mLayoutOrder[i];
|
||||
int offset = getCardOffsetByPositionDiff(layoutOrder->mItemPositionDiff);
|
||||
int start = centerViewStart + offset;
|
||||
int end = start + mDecoratedChildWidth;
|
||||
fillChildItem(start, top, end, bottom, *layoutOrder, recycler, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CarouselLayoutManager::fillChildItem(int start, int top, int end, int bottom,
|
||||
LayoutOrder& layoutOrder, RecyclerView::Recycler& recycler, int i) {
|
||||
View* view = bindChild(layoutOrder.mItemAdapterPosition, recycler);
|
||||
view->setElevation(i);
|
||||
ItemTransformation* transformation = nullptr;
|
||||
if (mViewPostLayout) {
|
||||
transformation = mViewPostLayout(*view, layoutOrder.mItemPositionDiff, mOrientation, layoutOrder.mItemAdapterPosition);
|
||||
}
|
||||
if (nullptr == transformation) {
|
||||
view->layout(start, top, end, bottom);
|
||||
} else {
|
||||
view->layout(std::round(start + transformation->mTranslationX), std::round(top + transformation->mTranslationY),
|
||||
std::round(end + transformation->mTranslationX), std::round(bottom + transformation->mTranslationY));
|
||||
|
||||
view->setScaleX(transformation->mScaleX);
|
||||
view->setScaleY(transformation->mScaleY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current scroll position of center item. this value can be in any range if it is cycle layout.
|
||||
* if this is not, that then it is in [0, {@link #mItemsCount - 1}]
|
||||
*/
|
||||
float CarouselLayoutManager::getCurrentScrollPosition() {
|
||||
int fullScrollSize = getMaxScrollOffset();
|
||||
if (0 == fullScrollSize) {
|
||||
return 0;
|
||||
}
|
||||
return 1.0f * mLayoutHelper->mScrollOffset / getScrollItemSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return maximum scroll value to fill up all items in layout. Generally this is only needed for non cycle layouts.
|
||||
*/
|
||||
int CarouselLayoutManager::getMaxScrollOffset() {
|
||||
return getScrollItemSize() * (mItemsCount - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Because we can support old Android versions, we should layout our children in specific order to make our center view in the top of layout
|
||||
* (this item should layout last). So this method will calculate layout order and fill up {@link #mLayoutHelper} object.
|
||||
* This object will be filled by only needed to layout items. Non visible items will not be there.
|
||||
*
|
||||
* @param currentScrollPosition current scroll position this is a value that indicates position of center item
|
||||
* (if this value is int, then center item is really in the center of the layout, else it is near state).
|
||||
* Be aware that this value can be in any range is it is cycle layout
|
||||
* @param state Transient state of RecyclerView
|
||||
* @see #getCurrentScrollPosition()
|
||||
*/
|
||||
void CarouselLayoutManager::generateLayoutOrder(float currentScrollPosition, RecyclerView::State& state) {
|
||||
mItemsCount = state.getItemCount();
|
||||
float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, mItemsCount);
|
||||
int centerItem = std::round(absCurrentScrollPosition);
|
||||
|
||||
if (mCircleLayout && 1 < mItemsCount) {
|
||||
int layoutCount = std::min(mLayoutHelper->mMaxVisibleItems * 2 + 1, mItemsCount);
|
||||
|
||||
mLayoutHelper->initLayoutOrder(layoutCount);
|
||||
|
||||
int countLayoutHalf = layoutCount / 2;
|
||||
// before center item
|
||||
for (int i = 1; i <= countLayoutHalf; ++i) {
|
||||
int position = static_cast<int>(std::round(absCurrentScrollPosition - i + mItemsCount)) % mItemsCount;
|
||||
mLayoutHelper->setLayoutOrder(countLayoutHalf - i, position, centerItem - absCurrentScrollPosition - i);
|
||||
}
|
||||
// after center item
|
||||
for (int i = layoutCount - 1; i >= countLayoutHalf + 1; --i) {
|
||||
int position = static_cast<int>(std::round(absCurrentScrollPosition - i + layoutCount)) % mItemsCount;
|
||||
mLayoutHelper->setLayoutOrder(i - 1, position, centerItem - absCurrentScrollPosition + layoutCount - i);
|
||||
}
|
||||
mLayoutHelper->setLayoutOrder(layoutCount - 1, centerItem, centerItem - absCurrentScrollPosition);
|
||||
|
||||
} else {
|
||||
int firstVisible = std::max(centerItem - mLayoutHelper->mMaxVisibleItems, 0);
|
||||
int lastVisible = std::min(centerItem + mLayoutHelper->mMaxVisibleItems, mItemsCount - 1);
|
||||
int layoutCount = lastVisible - firstVisible + 1;
|
||||
|
||||
mLayoutHelper->initLayoutOrder(layoutCount);
|
||||
|
||||
for (int i = firstVisible; i <= lastVisible; ++i) {
|
||||
if (i == centerItem) {
|
||||
mLayoutHelper->setLayoutOrder(layoutCount - 1, i, i - absCurrentScrollPosition);
|
||||
} else if (i < centerItem) {
|
||||
mLayoutHelper->setLayoutOrder(i - firstVisible, i, i - absCurrentScrollPosition);
|
||||
} else {
|
||||
mLayoutHelper->setLayoutOrder(layoutCount - (i - centerItem) - 1, i, i - absCurrentScrollPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CarouselLayoutManager::getWidthNoPadding() {
|
||||
return getWidth() - getPaddingStart() - getPaddingEnd();
|
||||
}
|
||||
|
||||
int CarouselLayoutManager::getHeightNoPadding() {
|
||||
return getHeight() - getPaddingEnd() - getPaddingStart();
|
||||
}
|
||||
|
||||
View* CarouselLayoutManager::bindChild(int position, RecyclerView::Recycler& recycler) {
|
||||
View* view = recycler.getViewForPosition(position);
|
||||
|
||||
addView(view);
|
||||
measureChildWithMargins(view, 0, 0);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::recyclerOldViews(RecyclerView::Recycler& recycler) {
|
||||
auto scrapList = recycler.getScrapList();
|
||||
for (RecyclerView::ViewHolder* viewHolder : scrapList) {
|
||||
int adapterPosition = viewHolder->getAdapterPosition();
|
||||
bool found = false;
|
||||
for (LayoutOrder* layoutOrder : mLayoutHelper->mLayoutOrder) {
|
||||
if (layoutOrder->mItemAdapterPosition == adapterPosition) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
recycler.recycleView(viewHolder->itemView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during {@link #fillData(RecyclerView.Recycler, RecyclerView.State)} to calculate item offset from layout center line. <br />
|
||||
* <br />
|
||||
* Returns {@link #convertItemPositionDiffToSmoothPositionDiff(float)} * (size off area above center item when it is on the center). <br />
|
||||
* Sign is: plus if this item is bellow center line, minus if not<br />
|
||||
* <br />
|
||||
* ----- - area above it<br />
|
||||
* ||||| - center item<br />
|
||||
* ----- - area bellow it (it has the same size as are above center item)<br />
|
||||
*
|
||||
* @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line.
|
||||
* if this is 1 then this item is bellow the layout center line in the full item size distance.
|
||||
* @return offset in scroll px coordinates.
|
||||
*/
|
||||
int CarouselLayoutManager::getCardOffsetByPositionDiff(float itemPositionDiff) {
|
||||
double smoothPosition = convertItemPositionDiffToSmoothPositionDiff(itemPositionDiff);
|
||||
|
||||
int dimenDiff;
|
||||
if (VERTICAL == mOrientation) {
|
||||
dimenDiff = (getHeightNoPadding() - mDecoratedChildHeight) / 2;
|
||||
} else {
|
||||
dimenDiff = (getWidthNoPadding() - mDecoratedChildWidth) / 2;
|
||||
}
|
||||
//noinspection NumericCastThatLosesPrecision
|
||||
return (int) std::round(signum(itemPositionDiff) * dimenDiff * smoothPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during {@link #getCardOffsetByPositionDiff(float)} for better item movement. <br/>
|
||||
* Current implementation speed up items that are far from layout center line and slow down items that are close to this line.
|
||||
* This code is full of maths. If you want to make items move in a different way, probably you should override this method.<br />
|
||||
* Please see code comments for better explanations.
|
||||
*
|
||||
* @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line.
|
||||
* if this is 1 then this item is bellow the layout center line in the full item size distance.
|
||||
* @return smooth position offset. needed for scroll calculation and better user experience.
|
||||
* @see #getCardOffsetByPositionDiff(float)
|
||||
*/
|
||||
double CarouselLayoutManager::convertItemPositionDiffToSmoothPositionDiff(float itemPositionDiff) {
|
||||
// generally item moves the same way above center and bellow it. So we don't care about diff sign.
|
||||
float absIemPositionDiff = std::abs(itemPositionDiff);
|
||||
|
||||
// we detect if this item is close for center or not. We use (1 / maxVisibleItem) ^ (1/3) as close definer.
|
||||
if (absIemPositionDiff > std::pow(1.0f / mLayoutHelper->mMaxVisibleItems, 1.0f / 3)) {
|
||||
// this item is far from center line, so we should make it move like square root function
|
||||
return std::pow(absIemPositionDiff / mLayoutHelper->mMaxVisibleItems, 1 / 2.0f);
|
||||
} else {
|
||||
// this item is close from center line. we should slow it down and don't make it speed up very quick.
|
||||
// so square function in range of [0, (1/maxVisible)^(1/3)] is quite good in it;
|
||||
return std::pow(absIemPositionDiff, 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return full item size
|
||||
*/
|
||||
int CarouselLayoutManager::getScrollItemSize() {
|
||||
if (VERTICAL == mOrientation) {
|
||||
return mDecoratedChildHeight;
|
||||
} else {
|
||||
return mDecoratedChildWidth;
|
||||
}
|
||||
}
|
||||
|
||||
Parcelable* CarouselLayoutManager::onSaveInstanceState() {
|
||||
if (mPendingCarouselSavedState) {
|
||||
return new CarouselSavedState(*mPendingCarouselSavedState);
|
||||
}
|
||||
CarouselSavedState* savedState = new CarouselSavedState(LayoutManager::onSaveInstanceState());
|
||||
savedState->mCenterItemPosition = mCenterItemPosition;
|
||||
return savedState;
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::onRestoreInstanceState(Parcelable& state) {
|
||||
if (dynamic_cast<CarouselSavedState*>(&state)) {
|
||||
mPendingCarouselSavedState = (CarouselSavedState*) &state;
|
||||
LayoutManager::onRestoreInstanceState(*mPendingCarouselSavedState->mSuperState);
|
||||
} else {
|
||||
LayoutManager::onRestoreInstanceState(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Scroll offset from nearest item from center
|
||||
*/
|
||||
int CarouselLayoutManager::getOffsetCenterView() {
|
||||
return std::round(getCurrentScrollPosition()) * getScrollItemSize() - mLayoutHelper->mScrollOffset;
|
||||
}
|
||||
|
||||
int CarouselLayoutManager::getOffsetForCurrentView(View& view) {
|
||||
int targetPosition = getPosition(&view);
|
||||
float directionDistance = getScrollDirection(targetPosition);
|
||||
|
||||
return std::round(directionDistance * getScrollItemSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that make scroll in range of [0, count). Generally this method is needed only for cycle layout.
|
||||
*
|
||||
* @param currentScrollPosition any scroll position range.
|
||||
* @param count adapter items count
|
||||
* @return good scroll position in range of [0, count)
|
||||
*/
|
||||
float CarouselLayoutManager::makeScrollPositionInRange0ToCount(float currentScrollPosition, int count) {
|
||||
float absCurrentScrollPosition = currentScrollPosition;
|
||||
while (0 > absCurrentScrollPosition) {
|
||||
absCurrentScrollPosition += count;
|
||||
}
|
||||
while (std::round(absCurrentScrollPosition) >= count) {
|
||||
absCurrentScrollPosition -= count;
|
||||
}
|
||||
return absCurrentScrollPosition;
|
||||
}
|
||||
#if 0
|
||||
public abstract static class PostLayoutListener {
|
||||
public ItemTransformation transformChild(
|
||||
@NonNull View child,
|
||||
float itemPositionToCenterDiff,
|
||||
int orientation,
|
||||
int itemPositionInAdapter
|
||||
) {
|
||||
return transformChild(child, itemPositionToCenterDiff, orientation);
|
||||
}
|
||||
|
||||
public ItemTransformation transformChild(
|
||||
@NonNull View child,
|
||||
float itemPositionToCenterDiff,
|
||||
int orientation
|
||||
) {
|
||||
throw new IllegalStateException("at least one transformChild should be implemented");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CarouselLayoutManager::LayoutHelper::LayoutHelper(int maxVisibleItems) {
|
||||
mMaxVisibleItems = maxVisibleItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before any fill calls. Needed to recycle old items and init new array list. Generally this list is an array an it is reused.
|
||||
*
|
||||
* @param layoutCount items count that will be layout
|
||||
*/
|
||||
void CarouselLayoutManager::LayoutHelper::initLayoutOrder(int layoutCount) {
|
||||
if (mLayoutOrder.size() != layoutCount) {
|
||||
if (mLayoutOrder.size()) {
|
||||
recycleItems(mLayoutOrder);
|
||||
}
|
||||
mLayoutOrder.resize(layoutCount);//mLayoutOrder= new LayoutOrder[layoutCount];
|
||||
fillLayoutOrder();
|
||||
}
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::LayoutHelper::setLayoutOrder(int arrayPosition, int itemAdapterPosition, float itemPositionDiff) {
|
||||
LayoutOrder* item = mLayoutOrder[arrayPosition];
|
||||
item->mItemAdapterPosition = itemAdapterPosition;
|
||||
item->mItemPositionDiff = itemPositionDiff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks is this screen Layout has this adapterPosition view in layout
|
||||
*
|
||||
* @param adapterPosition adapter position of item for future data filling logic
|
||||
* @return true is adapterItem is in layout
|
||||
*/
|
||||
bool CarouselLayoutManager::LayoutHelper::hasAdapterPosition(int adapterPosition) {
|
||||
if (mLayoutOrder.size()) {
|
||||
for (LayoutOrder* layoutOrder : mLayoutOrder) {
|
||||
if (layoutOrder->mItemAdapterPosition == adapterPosition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::LayoutHelper::recycleItems(const std::vector<LayoutOrder*>&layoutOrders) {
|
||||
for (LayoutOrder* layoutOrder : layoutOrders) {
|
||||
//noinspection ObjectAllocationInLoop
|
||||
mReusedItems.push_back(layoutOrder);//add(new WeakReference<>(layoutOrder));
|
||||
}
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::LayoutHelper::fillLayoutOrder() {
|
||||
for (int i = 0, length = mLayoutOrder.size(); i < length; ++i) {
|
||||
if (nullptr == mLayoutOrder[i]) {
|
||||
mLayoutOrder[i] = createLayoutOrder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CarouselLayoutManager::LayoutOrder* CarouselLayoutManager::LayoutHelper::createLayoutOrder() {
|
||||
/*Iterator<LayoutOrder*> iterator = mReusedItems.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
WeakReference<LayoutOrder> layoutOrderWeakReference = iterator.next();
|
||||
LayoutOrder* layoutOrder = layoutOrderWeakReference.get();
|
||||
iterator.remove();
|
||||
if (nullptr != layoutOrder) {
|
||||
return layoutOrder;
|
||||
}
|
||||
}*/
|
||||
return new LayoutOrder();
|
||||
}
|
||||
|
||||
CarouselLayoutManager::CarouselSavedState::CarouselSavedState(Parcelable* superState) {
|
||||
//mSuperState = superState;
|
||||
}
|
||||
|
||||
CarouselLayoutManager::CarouselSavedState::CarouselSavedState(Parcel& in) {
|
||||
//mSuperState = in.readParcelable(Parcelable.class.getClassLoader());
|
||||
mCenterItemPosition = in.readInt();
|
||||
}
|
||||
|
||||
CarouselLayoutManager::CarouselSavedState::CarouselSavedState(CarouselSavedState& other) {
|
||||
//mSuperState = other.mSuperState;
|
||||
mCenterItemPosition = other.mCenterItemPosition;
|
||||
}
|
||||
|
||||
int CarouselLayoutManager::CarouselSavedState::describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CarouselLayoutManager::CarouselSavedState::writeToParcel(Parcel& parcel, int i) {
|
||||
//parcel.writeParcelable(mSuperState, i);
|
||||
parcel.writeInt(mCenterItemPosition);
|
||||
}
|
||||
}/*endof namespace*/
|
208
src/gui/widgetEx/recyclerview/carousellayoutmanager.h
Normal file
208
src/gui/widgetEx/recyclerview/carousellayoutmanager.h
Normal file
@ -0,0 +1,208 @@
|
||||
#ifndef __CAROUSEL_LAYOUTMANAGER_H__
|
||||
#define __CAROUSEL_LAYOUTMANAGER_H__
|
||||
#include <widgetEx/recyclerview/recyclerview.h>
|
||||
#include <widgetEx/recyclerview/orientationhelper.h>
|
||||
|
||||
namespace cdroid{
|
||||
|
||||
class CarouselLayoutManager :public RecyclerView::LayoutManager{
|
||||
public:
|
||||
static constexpr int HORIZONTAL = OrientationHelper::HORIZONTAL;
|
||||
static constexpr int VERTICAL = OrientationHelper::VERTICAL;
|
||||
|
||||
static constexpr int INVALID_POSITION = -1;
|
||||
static constexpr int MAX_VISIBLE_ITEMS = 3;
|
||||
|
||||
typedef CallbackBase<void,int>OnCenterItemSelectionListener;
|
||||
struct ItemTransformation {
|
||||
float mScaleX;
|
||||
float mScaleY;
|
||||
float mTranslationX;
|
||||
float mTranslationY;
|
||||
ItemTransformation(float scaleX, float scaleY, float translationX, float translationY);
|
||||
};
|
||||
/**
|
||||
* Called after child layout finished. Generally you can do any translation and scaling work here.
|
||||
*
|
||||
* @param child view that was layout
|
||||
* @param itemPositionToCenterDiff view center line difference to layout center.
|
||||
* if > 0 then this item is bellow layout center line, else if not
|
||||
* @param orientation layoutManager orientation {@link #getLayoutDirection()}
|
||||
* @param itemPositionInAdapter item position inside adapter for this layout pass
|
||||
*/
|
||||
typedef CallbackBase<ItemTransformation*,View&,float,int,int>PostLayoutListener;
|
||||
private:
|
||||
static constexpr bool CIRCLE_LAYOUT = false;
|
||||
struct LayoutOrder;
|
||||
class LayoutHelper;
|
||||
class CarouselLinearSmoothScroller;
|
||||
friend CarouselLinearSmoothScroller;
|
||||
int mDecoratedChildWidth;
|
||||
int mDecoratedChildHeight;
|
||||
int mOrientation;
|
||||
int mPendingScrollPosition;
|
||||
bool mDecoratedChildSizeInvalid;
|
||||
bool mCircleLayout;
|
||||
|
||||
LayoutHelper* mLayoutHelper;// = new LayoutHelper(MAX_VISIBLE_ITEMS);
|
||||
PostLayoutListener mViewPostLayout;
|
||||
std::vector<OnCenterItemSelectionListener> mOnCenterItemSelectionListeners;
|
||||
int mCenterItemPosition = INVALID_POSITION;
|
||||
int mItemsCount;
|
||||
|
||||
private:
|
||||
int calculateScrollForSelectingPosition(int itemPosition, RecyclerView::State& state);
|
||||
void fillData(RecyclerView::Recycler& recycler, RecyclerView::State& state);
|
||||
void detectOnItemSelectionChanged(float currentScrollPosition, RecyclerView::State& state);
|
||||
void selectItemCenterPosition(int centerItem);
|
||||
void fillDataVertical(RecyclerView::Recycler& recycler, int width, int height);
|
||||
void fillDataHorizontal(RecyclerView::Recycler& recycler, int width, int height);
|
||||
void fillChildItem(int start, int top, int end, int bottom, LayoutOrder& layoutOrder, RecyclerView::Recycler& recycler, int i);
|
||||
/**
|
||||
* @return current scroll position of center item. this value can be in any range if it is cycle layout.
|
||||
* if this is not, that then it is in [0, {@link #mItemsCount - 1}]
|
||||
*/
|
||||
float getCurrentScrollPosition();
|
||||
/**
|
||||
* @return maximum scroll value to fill up all items in layout. Generally this is only needed for non cycle layouts.
|
||||
*/
|
||||
int getMaxScrollOffset();
|
||||
float getScrollDirection(int targetPosition);
|
||||
void generateLayoutOrder(float currentScrollPosition,RecyclerView::State& state);
|
||||
View* bindChild(int position, RecyclerView::Recycler& recycler);
|
||||
void recyclerOldViews(RecyclerView::Recycler& recycler);
|
||||
static float makeScrollPositionInRange0ToCount(float currentScrollPosition, int count);
|
||||
protected:
|
||||
class CarouselSavedState;
|
||||
CarouselSavedState* mPendingCarouselSavedState;
|
||||
int scrollBy(int diff,RecyclerView::Recycler& recycler, RecyclerView::State& state);
|
||||
int getCardOffsetByPositionDiff(float itemPositionDiff);
|
||||
double convertItemPositionDiffToSmoothPositionDiff(float itemPositionDiff);
|
||||
int getScrollItemSize();
|
||||
int getOffsetCenterView();
|
||||
int getOffsetForCurrentView(View& view);
|
||||
public:
|
||||
/**
|
||||
* @param orientation should be {@link #VERTICAL} or {@link #HORIZONTAL}
|
||||
*/
|
||||
CarouselLayoutManager(int orientation);
|
||||
CarouselLayoutManager(int orientation, bool circleLayout);
|
||||
/**
|
||||
* Change circle layout type
|
||||
*/
|
||||
void setCircleLayout(bool circleLayout);
|
||||
/**
|
||||
* Setup {@link CarouselLayoutManager::PostLayoutListener} for this LayoutManager.
|
||||
* Its methods will be called for each visible view item after general LayoutManager layout finishes. <br />
|
||||
* <br />
|
||||
* Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting.
|
||||
*
|
||||
* @param postLayoutListener listener for item layout changes. Can be null.
|
||||
*/
|
||||
void setPostLayoutListener(PostLayoutListener postLayoutListener);
|
||||
|
||||
/**
|
||||
* Setup maximum visible (layout) items on each side of the center item.
|
||||
* Basically during scrolling there can be more visible items (+1 item on each side), but in idle state this is the only reached maximum.
|
||||
*
|
||||
* @param maxVisibleItems should be great then 0, if bot an {@link IllegalAccessException} will be thrown
|
||||
*/
|
||||
void setMaxVisibleItems(int maxVisibleItems);
|
||||
|
||||
/**
|
||||
* @return current setup for maximum visible items.
|
||||
* @see #setMaxVisibleItems(int)
|
||||
*/
|
||||
int getMaxVisibleItems()const;
|
||||
|
||||
RecyclerView::LayoutParams* generateDefaultLayoutParams()const override;
|
||||
|
||||
/**
|
||||
* @return current layout orientation
|
||||
* @see #VERTICAL
|
||||
* @see #HORIZONTAL
|
||||
*/
|
||||
int getOrientation()const;
|
||||
|
||||
bool canScrollHorizontally()const override;
|
||||
bool canScrollVertically()const override;
|
||||
|
||||
/**
|
||||
* @return current layout center item
|
||||
*/
|
||||
int getCenterItemPosition()const;
|
||||
|
||||
/**
|
||||
* @param onCenterItemSelectionListener listener that will trigger when ItemSelectionChanges. can't be null
|
||||
*/
|
||||
void addOnItemSelectionListener(OnCenterItemSelectionListener onCenterItemSelectionListener);
|
||||
|
||||
/**
|
||||
* @param onCenterItemSelectionListener listener that was previously added by {@link #addOnItemSelectionListener(OnCenterItemSelectionListener)}
|
||||
*/
|
||||
void removeOnItemSelectionListener(OnCenterItemSelectionListener onCenterItemSelectionListener);
|
||||
|
||||
void scrollToPosition(int position) override;
|
||||
|
||||
void smoothScrollToPosition(RecyclerView& recyclerView, RecyclerView::State& state, int position)override;
|
||||
bool computeScrollVectorForPosition(int targetPosition,PointF*)override;
|
||||
int scrollVerticallyBy(int dy, RecyclerView::Recycler& recycler, RecyclerView::State& state)override;
|
||||
int scrollHorizontallyBy(int dx, RecyclerView::Recycler& recycler, RecyclerView::State& state)override;
|
||||
void onMeasure(RecyclerView::Recycler& recycler, RecyclerView::State& state, int widthSpec, int heightSpec)override;
|
||||
void onAdapterChanged(RecyclerView::Adapter* oldAdapter, RecyclerView::Adapter* newAdapter)override;
|
||||
void onLayoutChildren(RecyclerView::Recycler& recycler,RecyclerView::State& state)override;
|
||||
|
||||
int getWidthNoPadding();
|
||||
int getHeightNoPadding();
|
||||
Parcelable* onSaveInstanceState()override;
|
||||
void onRestoreInstanceState(Parcelable& state)override;
|
||||
|
||||
/**
|
||||
* This interface methods will be called for each visible view item after general LayoutManager layout finishes. <br />
|
||||
* <br />
|
||||
* Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting.
|
||||
*/
|
||||
|
||||
};/*end carousellayoutmanager*/
|
||||
|
||||
struct CarouselLayoutManager::LayoutOrder {
|
||||
int mItemAdapterPosition;
|
||||
float mItemPositionDiff;
|
||||
friend LayoutHelper;
|
||||
};
|
||||
|
||||
class CarouselLayoutManager::LayoutHelper {
|
||||
private :
|
||||
friend CarouselLayoutManager;
|
||||
int mMaxVisibleItems;
|
||||
int mScrollOffset;
|
||||
std::vector<LayoutOrder*> mLayoutOrder;
|
||||
std::vector<LayoutOrder*> mReusedItems;
|
||||
private:
|
||||
void recycleItems(const std::vector<LayoutOrder*>& layoutOrders);
|
||||
void fillLayoutOrder();
|
||||
LayoutOrder* createLayoutOrder();
|
||||
public:
|
||||
LayoutHelper(int maxVisibleItems);
|
||||
|
||||
void initLayoutOrder(int layoutCount);
|
||||
void setLayoutOrder(int arrayPosition, int itemAdapterPosition, float itemPositionDiff);
|
||||
bool hasAdapterPosition(int adapterPosition);
|
||||
};
|
||||
|
||||
class CarouselLayoutManager::CarouselSavedState :public Parcelable {
|
||||
private:
|
||||
friend CarouselLayoutManager;
|
||||
Parcelable* mSuperState;
|
||||
int mCenterItemPosition;
|
||||
CarouselSavedState(Parcel& in);
|
||||
protected:
|
||||
CarouselSavedState(Parcelable* superState);
|
||||
CarouselSavedState(CarouselSavedState& other);
|
||||
public:
|
||||
int describeContents();
|
||||
void writeToParcel(Parcel& parcel, int i);
|
||||
};
|
||||
|
||||
}/*endof namespace*/
|
||||
#endif/*__CAROUSEL_LAYOUTMANAGER_H__*/
|
@ -133,7 +133,7 @@ void ChildHelper::attachViewToParent(View* child, int index, ViewGroup::LayoutPa
|
||||
LOGD_IF(_DEBUG,"attach view to parent index:%d off:%d h:%d ",index,offset,hidden);
|
||||
}
|
||||
|
||||
int ChildHelper::getChildCount(){
|
||||
int ChildHelper::getChildCount()const{
|
||||
return mCallback.getChildCount() - mHiddenViews.size();
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
View* findHiddenNonRemovedView(int position);
|
||||
void attachViewToParent(View* child, int index, ViewGroup::LayoutParams* layoutParams,
|
||||
bool hidden);
|
||||
int getChildCount();
|
||||
int getChildCount()const;
|
||||
int getUnfilteredChildCount();
|
||||
View* getUnfilteredChildAt(int index);
|
||||
void detachViewFromParent(int index);
|
||||
|
@ -35,8 +35,8 @@ public:
|
||||
int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
|
||||
snapPreference);
|
||||
|
||||
int calculateDyToMakeVisible(View* view, int snapPreference);
|
||||
int calculateDxToMakeVisible(View* view, int snapPreference);
|
||||
virtual int calculateDyToMakeVisible(View* view, int snapPreference);
|
||||
virtual int calculateDxToMakeVisible(View* view, int snapPreference);
|
||||
};
|
||||
}/*endof namespace*/
|
||||
#endif/*__LINEAR_SMOOTH_SCROLLER_H__*/
|
||||
|
@ -317,14 +317,13 @@ void RecyclerView::initChildrenHelper() {
|
||||
return getChildViewHolderInt(view);
|
||||
};
|
||||
|
||||
cbk.attachViewToParent=[this](View* child, int index,
|
||||
ViewGroup::LayoutParams* layoutParams) {
|
||||
cbk.attachViewToParent=[this](View* child, int index, ViewGroup::LayoutParams* layoutParams) {
|
||||
ViewHolder* vh = getChildViewHolderInt(child);
|
||||
if (vh != nullptr) {
|
||||
if (!vh->isTmpDetached() && !vh->shouldIgnore()) {
|
||||
LOGE("Called attach on a child which is not detached: %p",vh);
|
||||
}
|
||||
LOGD("reAttach %p",vh);
|
||||
LOGD_IF(_DEBUG,"reAttach %p",vh);
|
||||
vh->clearTmpDetachFlag();
|
||||
}
|
||||
attachViewToParent(child, index, layoutParams);
|
||||
@ -338,7 +337,7 @@ void RecyclerView::initChildrenHelper() {
|
||||
if (vh->isTmpDetached() && !vh->shouldIgnore()) {
|
||||
LOGE("called detach on an already detached child %p",vh);
|
||||
}
|
||||
LOGD("tmpDetach =%p",vh);
|
||||
LOGD_IF(_DEBUG,"tmpDetach =%p",vh);
|
||||
vh->addFlags(ViewHolder::FLAG_TMP_DETACHED);
|
||||
}
|
||||
}
|
||||
@ -1880,7 +1879,7 @@ bool RecyclerView::onTouchEvent(MotionEvent& e) {
|
||||
mVelocityTracker->computeCurrentVelocity(1000, mMaxFlingVelocity);
|
||||
const float xvel = bCanScrollHorizontally ? -mVelocityTracker->getXVelocity(mScrollPointerId) : 0;
|
||||
const float yvel = bCanScrollVertically ? -mVelocityTracker->getYVelocity(mScrollPointerId) : 0;
|
||||
LOGD("xvel=%.2f yvel=%.2f",xvel,yvel);
|
||||
LOGV("xvel=%.2f yvel=%.2f",xvel,yvel);
|
||||
if (!(((xvel != 0) || (yvel != 0)) && fling((int) xvel, (int) yvel))) {
|
||||
setScrollState(SCROLL_STATE_IDLE);
|
||||
}
|
||||
@ -5184,7 +5183,7 @@ void RecyclerView::LayoutManager::removeAndRecycleViewAt(int index,Recycler& rec
|
||||
recycler.recycleView(view);
|
||||
}
|
||||
|
||||
int RecyclerView::LayoutManager::getChildCount() {
|
||||
int RecyclerView::LayoutManager::getChildCount() const{
|
||||
return mChildHelper ? mChildHelper->getChildCount() : 0;
|
||||
}
|
||||
|
||||
|
@ -704,7 +704,7 @@ public:
|
||||
void detachAndScrapViewAt(int index,Recycler& recycler);
|
||||
void removeAndRecycleView(View* child, Recycler& recycler);
|
||||
void removeAndRecycleViewAt(int index,Recycler& recycler);
|
||||
int getChildCount();
|
||||
int getChildCount()const;
|
||||
View* getChildAt(int index);
|
||||
int getWidthMode();
|
||||
int getHeightMode();
|
||||
|
@ -1736,7 +1736,7 @@ bool StaggeredGridLayoutManager::checkLayoutParams(const RecyclerView::LayoutPar
|
||||
return dynamic_cast<const LayoutParams*>(lp);
|
||||
}
|
||||
|
||||
int StaggeredGridLayoutManager::getOrientation() {
|
||||
int StaggeredGridLayoutManager::getOrientation() const{
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ public:
|
||||
RecyclerView::LayoutParams* generateLayoutParams(Context* c,const AttributeSet& attrs)const override;
|
||||
RecyclerView::LayoutParams* generateLayoutParams(const ViewGroup::LayoutParams& lp)const override;
|
||||
bool checkLayoutParams(const RecyclerView::LayoutParams* lp)const override;
|
||||
int getOrientation();
|
||||
int getOrientation()const;
|
||||
View* onFocusSearchFailed(View* focused, int direction, RecyclerView::Recycler& recycler, RecyclerView::State& state);
|
||||
};/*StaggeredGridLayoutManager*/
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user