fix AdapterHelper's OpReorderer

This commit is contained in:
侯歌 2024-05-23 15:47:43 +08:00
parent 6ab9e07fd5
commit bfe3889e49
12 changed files with 145 additions and 66 deletions

View File

@ -36,6 +36,7 @@ public:
TextView*textView = ((MyAdapter::ViewHolder&)holder).textView;
textView->setText(item);
textView->setId(position);
LOGD("%p:%d",textView,position);
textView->setOnClickListener([position](View&v){
RecyclerView*rv = (RecyclerView*)v.getParent();
RecyclerView::LayoutManager*mgr = rv->getLayoutManager();
@ -88,6 +89,7 @@ int main(int argc,const char*argv[]){
r=[&](){
adapter->remove(4);
adapter->notifyItemRemoved(4);
w->postDelayed(r,3000);
};
w->postDelayed(r,8000);
app.exec();

View File

@ -32,12 +32,12 @@ Keyboard::Key::Key(void*parent,int x,int y,Context*context,const AttributeSet&at
this->x = x;
this->y = y;
Keyboard::Row*row=(Keyboard::Row*)parent;
Keyboard*keyboard =row->parent;
Keyboard*keyboard = row->parent;
width = getDimensionOrFraction(attrs,"keyWidth" , keyboard->mDisplayWidth, row->defaultWidth );
height= getDimensionOrFraction(attrs,"keyHeight", keyboard->mDisplayHeight, row->defaultHeight);
gap = getDimensionOrFraction(attrs,"horizontalGap", keyboard->mDisplayWidth, row->defaultHorizontalGap);
edgeFlags =row->rowEdgeFlags | attrs.getInt("keyEdgeFlags",edgeFlagKVS,0);
this->x+=gap;
this->x += gap;
const std::string resicon=attrs.getString("keyIcon");
icon = resicon.empty()?nullptr:context->getDrawable(resicon);
label = attrs.getString("keyLabel");
@ -50,18 +50,19 @@ Keyboard::Key::Key(void*parent,int x,int y,Context*context,const AttributeSet&at
std::wstring ws=TextUtils::utf8tounicode(label);
codes.push_back(ws[0]);
}
LOGV("Key[%x]%s(%d,%d,%d,%d) gap=%d",codes[0],label.c_str(),x,y,width,height,gap);
}
Keyboard::Key::Key(void*p){
parent =p;
Row*row=(Row*)parent;
sticky =modifier=0;
parent = p;
Row*row= (Row*)parent;
sticky = modifier = 0;
x= y = gap =0;
width = row->defaultWidth;
height = row->defaultHeight;
edgeFlags= row->rowEdgeFlags;
width = row->defaultWidth;
height = row->defaultHeight;
edgeFlags = row->rowEdgeFlags;
on = false;
pressed=false;
pressed = false;
}
void Keyboard::Key::onPressed() {
@ -90,10 +91,10 @@ int Keyboard::Key::parseCSV(const std::string& value,std::vector<int>& codes){
}
bool Keyboard::Key::isInside(int x, int y) {
bool leftEdge = (edgeFlags & EDGE_LEFT) > 0;
bool rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
bool topEdge = (edgeFlags & EDGE_TOP) > 0;
bool bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
const bool leftEdge = (edgeFlags & EDGE_LEFT) > 0;
const bool rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
const bool topEdge = (edgeFlags & EDGE_TOP) > 0;
const bool bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
if ((x >= this->x || (leftEdge && x <= this->x + this->width))
&& (x < this->x + this->width || (rightEdge && x >= this->x))
&& (y >= this->y || (topEdge && y <= this->y + this->height))
@ -105,8 +106,8 @@ bool Keyboard::Key::isInside(int x, int y) {
}
int Keyboard::Key::squaredDistanceFrom(int x, int y){
int xDist = this->x + width / 2 - x;
int yDist = this->y + height / 2 - y;
const int xDist = this->x + width / 2 - x;
const int yDist = this->y + height / 2 - y;
return xDist * xDist + yDist * yDist;
}
@ -255,7 +256,7 @@ static void endTag(void *userData, const XML_Char *name){
keyboard->getModifierKeys().push_back(key);
}
}else if(0==strcmp(name,"Row")){
pd->y += row->defaultHeight+row->verticalGap;
pd->y += (row->defaultHeight + row->verticalGap);
pd->minWidth = std::max(pd->x,pd->minWidth);
pd->x = 0;
if(row->mode == pd->keyboardMode)
@ -293,11 +294,11 @@ void Keyboard::loadKeyboard(Context*context,const std::string&resid){
} while(len!=0);
}
XML_ParserFree(parser);
mTotalHeight= pd.y-mDefaultVerticalGap;
mTotalHeight= pd.y - mDefaultVerticalGap;
mTotalWidth = pd.minWidth;
mProximityThreshold = mDefaultWidth*.6f;//SEARCH_DISTANCE;
mProximityThreshold*= mProximityThreshold;
LOGD("%s endof loadkeyboard %d rows %d keys gaps=%d,%d parsed size=%dx%d display=%dx%d",
LOGD("%s endof loadkeyboard %d rows %d keys gaps=%d,%d parsed.Size=%dx%d display=%dx%d",
resid.c_str(),rows.size(),mKeys.size(), getHorizontalGap(),getVerticalGap(),
mTotalWidth,mTotalHeight,mDisplayWidth,mDisplayHeight);
}
@ -435,7 +436,7 @@ void Keyboard::computeNearestNeighbors() {
std::vector<int> Keyboard::getNearestKeys(int x, int y){
if (mGridNeighbors.size() ==0) computeNearestNeighbors();
if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
const int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
if (index < GRID_SIZE) {
return mGridNeighbors[index];
}

View File

@ -4,6 +4,7 @@
#include <fstream>
#include <string.h>
#include <limits.h>
#include <float.h>
namespace cdroid{
@ -680,13 +681,13 @@ void KeyboardView::SwipeTracker::addPoint(float x,float y,long time){
}
void KeyboardView::SwipeTracker::computeCurrentVelocity(int units){
//computeCurrentVelocity(units,FLT_MAX);
computeCurrentVelocity(units,FLT_MAX);
}
void KeyboardView::SwipeTracker::computeCurrentVelocity(int units,float maxVelocity){
float oldestX = mPastX[0];//pastX[0];
float oldestY = mPastY[0];//pastY[0];
long oldestTime = mPastTime[0];//pastTime[0];
float oldestX = mPastX[0];
float oldestY = mPastY[0];
long oldestTime = mPastTime[0];
float accumX = 0;
float accumY = 0;
int N=0;

View File

@ -9,7 +9,11 @@ AdapterHelper::AdapterHelper(Callback callback)
AdapterHelper::AdapterHelper(Callback callback, bool disableRecycler) {
mCallback = callback;
mDisableRecycler = disableRecycler;
mOpReorderer = nullptr;//new OpReorderer(this);
OpReorderer::Callback cbk;
cbk.obtainUpdateOp = std::bind(&AdapterHelper::obtainUpdateOp,this,std::placeholders::_1,
std::placeholders::_2,std::placeholders::_3,std::placeholders::_4);
cbk.recycleUpdateOp= std::bind(&AdapterHelper::recycleUpdateOp,this,std::placeholders::_1);
mOpReorderer = new OpReorderer(cbk);
mUpdateOpPool = new Pools::SimplePool<UpdateOp>(UpdateOp::POOL_SIZE);
}
@ -19,7 +23,6 @@ AdapterHelper::~AdapterHelper(){
}
AdapterHelper& AdapterHelper::addUpdateOp(const std::vector<UpdateOp*>&ops) {
//Collections.addAll(mPendingUpdates, ops);
mPendingUpdates.insert(mPendingUpdates.end(),ops.begin(),ops.end());
return *this;
}
@ -164,19 +167,19 @@ void AdapterHelper::dispatchAndUpdateViewHolders(UpdateOp* op) {
if (op->cmd == UpdateOp::ADD || op->cmd == UpdateOp::MOVE) {
throw "should not dispatch add or move for pre layout";
}
/*if (_DEBUG) {
LOGD("dispatch (pre)" + op);
Log.d(TAG, "postponed state before:");
for (UpdateOp updateOp : mPostponedList) {
Log.d(TAG, updateOp.toString());
if (_DEBUG) {
LOGD("dispatch (pre)%p" ,op);
LOGD("postponed state before:");
for (UpdateOp* updateOp : mPostponedList) {
LOGD("%s",updateOp->toString().c_str());
}
Log.d(TAG, "----");
}*/
LOGD("----");
}
// handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial
// TODO Since move ops are pushed to end, we should not need this anymore
int tmpStart = updatePositionWithPostponed(op->positionStart, op->cmd);
LOGD_IF(_DEBUG,"pos:%d,updatedPos:%d",op->positionStart,tmpStart);
LOGD_IF(_DEBUG,"pos:%d,updatedPos:%d mPostponedList.size=%d",op->positionStart,tmpStart,mPostponedList.size());
int tmpCnt = 1;
int offsetPositionForPartial = op->positionStart;
int positionMultiplier;
@ -221,7 +224,7 @@ void AdapterHelper::dispatchAndUpdateViewHolders(UpdateOp* op) {
LOGD("post dispatch");
LOGD("postponed state after:");
for (UpdateOp* updateOp : mPostponedList) {
//Log.d(TAG, updateOp.toString());
LOGD("%s",updateOp->toString().c_str());
}
LOGD("----");
}
@ -297,14 +300,14 @@ int AdapterHelper::updatePositionWithPostponed(int pos, int cmd) {
}
}
}
/*if (_DEBUG) {
Log.d(TAG, "dispath (step" + i + ")");
Log.d(TAG, "postponed state:" + i + ", pos:" + pos);
if (_DEBUG) {
LOGD("dispath (step %d)",i);
LOGD("postponed state:%d, pos:%d",i,pos);
for (UpdateOp* updateOp : mPostponedList) {
Log.d(TAG, updateOp.toString());
LOGD(updateOp->toString().c_str());
}
Log.d(TAG, "----");
}*/
LOGD("----");
}
}
for (int i = mPostponedList.size() - 1; i >= 0; i--) {
UpdateOp* op = mPostponedList.at(i);
@ -346,10 +349,8 @@ void AdapterHelper::applyAdd(UpdateOp* op) {
}
void AdapterHelper::postponeAndUpdateViewHolders(UpdateOp* op) {
if (_DEBUG) {
LOGD("postponing %d",op->cmd);
}
mPostponedList.push_back(op);//add(op);
LOGD_IF(_DEBUG,"postponing op->%p:%d mPostponedList.size=%d",op,op->cmd,mPostponedList.size());
switch (op->cmd) {
case UpdateOp::ADD:
mCallback.offsetPositionsForAdd(op->positionStart, op->itemCount);
@ -560,9 +561,9 @@ void AdapterHelper::recycleUpdateOpsAndClearList(std::vector<UpdateOp*>& ops) {
///////////////////////////////////////////////////////////////////////////////////////////////////
AdapterHelper::UpdateOp::UpdateOp(){
cmd =0;
itemCount=0;
payload =nullptr;
cmd = 0;
itemCount = 0;
payload = nullptr;
}
AdapterHelper::UpdateOp::UpdateOp(int cmd, int positionStart, int itemCount, Object* payload) {
@ -578,4 +579,13 @@ int AdapterHelper::UpdateOp::hashCode() {
result = 31 * result + itemCount;
return result;
}
const std::string AdapterHelper::UpdateOp::toString()const{
std::ostringstream oss;
const char*cmds[]={"add","rm","up","mv"};
const int idx = __builtin_clz(cmd);
oss<<cmds[idx]<<",s:"<<positionStart<<" c:"<<itemCount;
return oss.str();
}
}/*endof namespace*/

View File

@ -23,6 +23,7 @@ public:
UpdateOp();
UpdateOp(int cmd, int positionStart, int itemCount, Object* payload);
int hashCode();
const std::string toString()const;
};
struct Callback {
std::function<RecyclerView::ViewHolder*(int)> findViewHolder;//(int position)

View File

@ -149,24 +149,43 @@ bool DefaultItemAnimator::animateAdd(RecyclerView::ViewHolder& holder) {
return true;
}
void DefaultItemAnimator::onAddAnimationStart(RecyclerView::ViewHolder*holder,Animator& animator,bool isReverse){
dispatchAddStarting(*holder);
}
void DefaultItemAnimator::onAddAnimationCancel(RecyclerView::ViewHolder*holder,Animator& animator,bool isReverse){
holder->itemView->setAlpha(1);
}
void DefaultItemAnimator::onAddAnimationEnd(RecyclerView::ViewHolder*holder,Animator& animator,bool isReverse){
View* view = holder->itemView;
ViewPropertyAnimator& animation = view->animate();
animation.setListener({});
dispatchAddFinished(*holder);
auto it = std::find(mAddAnimations.begin(), mAddAnimations.end(),holder);
mAddAnimations.erase(it);//mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
}
void DefaultItemAnimator::animateAddImpl(RecyclerView::ViewHolder& holder) {
View* view = holder.itemView;
ViewPropertyAnimator& animation = view->animate();
mAddAnimations.push_back(&holder);//add(holder);
Animator::AnimatorListener al;
al.onAnimationStart = [this,&holder](Animator&animator,bool isReverse){
al.onAnimationStart = std::bind(&DefaultItemAnimator::onAddAnimationStart,this,&holder,std::placeholders::_1,std::placeholders::_2);
/*[this,&holder](Animator&animator,bool isReverse){
dispatchAddStarting(holder);
};
al.onAnimationCancel=[&holder](Animator&){
};*/
//al.onAnimationCancel= std::bind(&DefaultItemAnimator::onAddAnimationCancel,this,&holder,std::placeholders::_1,std::placeholders::_2);
/*[&holder](Animator&){
holder.itemView->setAlpha(1);
};
al.onAnimationEnd = [this,&holder,&animation](Animator& animator,bool isReverse){
};*/
al.onAnimationEnd = std::bind(&DefaultItemAnimator::onAddAnimationEnd,this,&holder,std::placeholders::_1,std::placeholders::_2);
/*[this,&holder,&animation](Animator& animator,bool isReverse){
animation.setListener({});
dispatchAddFinished(holder);
auto it = std::find(mAddAnimations.begin(), mAddAnimations.end(),&holder);
mAddAnimations.erase(it);//mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
};
};*/
animation.alpha(1).setDuration(getAddDuration()).setListener(al).start();
}

View File

@ -43,6 +43,9 @@ protected:
int fromX, int fromY, int toX, int toY);
};
protected:
void onAddAnimationStart(RecyclerView::ViewHolder*,Animator& animator,bool isReverse);
void onAddAnimationCancel(RecyclerView::ViewHolder*,Animator& animator,bool isReverse);
void onAddAnimationEnd(RecyclerView::ViewHolder*,Animator& animator,bool isReverse);
void onRemoveAnimationStart(RecyclerView::ViewHolder*,Animator& animator,bool isReverse);
void onRemoveAnimationEnd(RecyclerView::ViewHolder*,Animator& animator,bool isReverse);
void onMoveAnimationStart(RecyclerView::ViewHolder*,Animator& animator,bool isReverse);

View File

@ -9,6 +9,7 @@ GridLayoutManager::GridLayoutManager(Context* context,const AttributeSet& attrs)
mSpanSizeLookup = new DefaultSpanSizeLookup();
setSpanCount(properties->spanCount);
delete properties;
mPendingSpanCountChange = false;
}
GridLayoutManager::GridLayoutManager(Context* context, int spanCount)

View File

@ -14,6 +14,7 @@ LinearLayoutManager::LinearLayoutManager(Context* context,int orientation,bool r
mLayoutState = nullptr;
mOrientationHelper = nullptr;
mLastStackFromEnd = false;
mStackFromEnd = false;
mRecycleChildrenOnDetach = true;
mAnchorInfo = new AnchorInfo();
mLayoutChunkResult = new LayoutChunkResult();

View File

@ -157,7 +157,7 @@ public:
int findLastCompletelyVisibleItemPosition();
View* onFocusSearchFailed(View* focused, int focusDirection,
RecyclerView::Recycler& recycler, RecyclerView::State& state);
bool supportsPredictiveItemAnimations();
bool supportsPredictiveItemAnimations()override;
void prepareForDrop(View* view,View* target, int x, int y);
};

View File

@ -136,6 +136,7 @@ void RecyclerView::initRecyclerView(){
mDataSetHasChangedAfterLayout = false;
mEatenAccessibilityChangeFlags =0;
mInterceptRequestLayoutDepth = 0;
mAdapterUpdateDuringMeasure = false;
mState = new State();
mAdapterHelper = nullptr;
mViewInfoStore = new ViewInfoStore();
@ -332,7 +333,7 @@ void RecyclerView::initChildrenHelper() {
if (!vh->isTmpDetached() && !vh->shouldIgnore()) {
LOGE("Called attach on a child which is not detached: %p",vh);
}
LOGD_IF(_DEBUG,"reAttach %p",vh);
LOGD_IF(_DEBUG,"reAttach %p %p:%d",vh,vh->itemView,vh->itemView->getId());
vh->clearTmpDetachFlag();
}
attachViewToParent(child, index, layoutParams);
@ -346,7 +347,7 @@ void RecyclerView::initChildrenHelper() {
if (vh->isTmpDetached() && !vh->shouldIgnore()) {
LOGE("called detach on an already detached child %p",vh);
}
LOGD_IF(_DEBUG,"tmpDetach =%p",vh);
LOGD_IF(_DEBUG,"tmpDetach =%p %p:%d",vh,vh->itemView,vh->itemView->getId());
vh->addFlags(ViewHolder::FLAG_TMP_DETACHED);
}
}
@ -3795,7 +3796,24 @@ bool RecyclerView::Recycler::tryBindViewHolderByDeadline(ViewHolder& holder, int
// abort - we have a deadline we can't meet
return false;
}
// Holders being bound should be either fully attached or fully detached.
// We don't want to bind with views that are temporarily detached, because that
// creates a situation in which they are unable to reason about their attach state
// properly.
// For example, isAttachedToWindow will return true, but the itemView will lack a
// parent. This breaks, among other possible issues, anything involving traversing
// the view tree, such as ViewTreeLifecycleOwner.
// Thus, we temporarily reattach any temp-detached holders for the bind operation.
// See https://issuetracker.google.com/265347515 for additional details on problems
// resulting from this
bool reattachedForBind = false;
if (holder.isTmpDetached()) {
mRV->attachViewToParent(holder.itemView, mRV->getChildCount(), holder.itemView->getLayoutParams());
reattachedForBind = true;
}
mRV->mAdapter->bindViewHolder(holder, offsetPosition);
if(reattachedForBind)
mRV->detachViewFromParent(holder.itemView);
long endBindNs = mRV->getNanoTime();
mRecyclerPool->factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
attachAccessibilityDelegateOnBind(holder);
@ -4045,6 +4063,27 @@ void RecyclerView::Recycler::recycleView(View* view) {
holder->clearReturnedFromScrapFlag();
}
recycleViewHolderInternal(*holder);
// If the ViewHolder is running ItemAnimator, we want the recycleView() in scroll pass
// to stop the ItemAnimator and put ViewHolder back in cache or Pool.
// There are three situations:
// 1. If the custom Adapter clears ViewPropertyAnimator in view detach like the
// leanback (TV) app does, the ItemAnimator is likely to be stopped and
// recycleViewHolderInternal will succeed.
// 2. If the custom Adapter clears ViewPropertyAnimator, but the ItemAnimator uses
// "pending runnable" and ViewPropertyAnimator has not started yet, the ItemAnimator
// on the view will not be cleared. See b/73552923.
// 3. If the custom Adapter does not clear ViewPropertyAnimator in view detach, the
// ItemAnimator will not be cleared.
// Since both 2&3 lead to failure of recycleViewHolderInternal(), we just explicitly end
// the ItemAnimator, the callback of ItemAnimator.endAnimations() will recycle the View.
//
// Note the order: we must call endAnimation() after recycleViewHolderInternal()
// to avoid recycle twice. If ViewHolder isRecyclable is false,
// recycleViewHolderInternal() will not recycle it, endAnimation() will reset
// isRecyclable flag and recycle the view.
if (mRV->mItemAnimator && !holder->isRecyclable()) {
mRV->mItemAnimator->endAnimation(*holder);
}
}
void RecyclerView::Recycler::recycleViewInternal(View* view) {
@ -6253,10 +6292,10 @@ void RecyclerView::ViewHolder::setIsRecyclable(bool recyclable) {
mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
if (mIsRecyclableCount < 0) {
mIsRecyclableCount = 0;
LOGE_IF(_DEBUG,"isRecyclable decremented below 0: "
"unmatched pair of setIsRecyable() calls for %p" , this);
LOGE("isRecyclable decremented below 0: "
"unmatched pair of setIsRecyable() calls for %p" , this);
LOGE_IF(_DEBUG,"isRecyclable decremented to %d is below 0: "
"unmatched pair of setIsRecyable() calls for %p" , mIsRecyclableCount,this);
LOGE("isRecyclable decremented to %d is below 0: "
"unmatched pair of setIsRecyable() calls for %p" ,mIsRecyclableCount, this);
} else if (!recyclable && mIsRecyclableCount == 1) {
mFlags |= FLAG_NOT_RECYCLABLE;
} else if (recyclable && mIsRecyclableCount == 0) {
@ -7035,7 +7074,8 @@ bool RecyclerView::ItemAnimator::canReuseUpdatedViewHolder(ViewHolder& viewHolde
void RecyclerView::ItemAnimator::dispatchAnimationsFinished() {
const int count = mFinishedListeners.size();
for (int i = 0; i < count; ++i) {
mFinishedListeners.at(i)();//.onAnimationsFinished();
auto ls = mFinishedListeners.at(i);
if(ls)ls();//.onAnimationsFinished();
}
mFinishedListeners.clear();
}

View File

@ -29,12 +29,12 @@ StaggeredGridLayoutManager::StaggeredGridLayoutManager(int spanCount, int orient
StaggeredGridLayoutManager::~StaggeredGridLayoutManager(){
delete mPendingSavedState;
delete mPrimaryOrientation;
delete mSecondaryOrientation;
delete mRemainingSpans;
delete mAnchorInfo;
delete mLazySpanLookup;
delete mLayoutState;
delete mPrimaryOrientation;
delete mSecondaryOrientation;
delete mRemainingSpans;
delete mAnchorInfo;
delete mLazySpanLookup;
delete mLayoutState;
}
void StaggeredGridLayoutManager::initLayoutManager(){