您的位置:首页 > 其它

开源项目学习与分析系列——DynamicGridView

2014-04-07 18:04 381 查看
废话不多说先上图,只有一张静态图,实现的是可拖拽的GridView。有很什么方便的GIF制作软件,推荐一下,另外我从windowsLivewriter上传blog中的代码样式变得好丑了:





说到这里再推荐一篇同样是写可拖拽GridView的blogAndroid可拖拽的GridView效果实现,长按可拖拽和item实时交换

这里很好的讲解了拖拽的原理,也实现了item交换的效果。下面是他的步骤,和我要分析的这个开源项目有一些不同,我将比较来讲。

先说一下推荐blog的思路:

根据手指按下的X,Y坐标来获取我们在GridView上面点击的item

手指按下的时候使用Handler和Runnable来实现一个定时器,假如定时时间为1000毫秒,在1000毫秒内,如果手指抬起了移除定时器,没有抬起并且手指点击在GridView的item所在的区域,则表示我们长按了GridView的item

如果我们长按了item则隐藏item,然后使用WindowManager来添加一个item的镜像在屏幕用来代替刚刚隐藏的item

当我们手指在屏幕移动的时候,更新item镜像的位置,然后在根据我们移动的X,Y的坐标来获取移动到GridView的哪一个位置

到GridView的item过多的时候,可能一屏幕显示不完,我们手指拖动item镜像到屏幕下方,要触发GridView想上滚动,同理,当我们手指拖动item镜像到屏幕上面,触发GridView向下滚动

GridView交换数据,刷新界面,移除item的镜像

DynamicGridView的思路:

根据手指按下的X,Y坐标来获取我们在GridView上面点击的item

在itemOnlongClickListener开启拖拽模式。隐藏item。

获取有拖拽item的内容的BitmapDrawable,并在重写dispatchDraw函数,将其显示出来,替换刚刚隐藏的item

当我们手指在屏幕移动的时候,更新itemBitmapDrawable的位置,然后在根据我们移动的X,Y的坐标来获取移动到GridView的哪一个位置

到GridView的item过多的时候,可能一屏幕显示不完,我们手指拖动item镜像到屏幕下方,要触发GridView想上滚动,同理,当我们手指拖动item镜像到屏幕上面,触发GridView向下滚动

GridView交换数据,改变隐藏Item的位置,刷新界面

当手指抬起后,将BitmapDrawable置为NULL,然后将隐藏的item显示

看起来两者的区别不大,但是上面只是一个思路,在多细节上的有很大的差异。这里交换数据是指将某一项数据从就得位置插入到新的位置,而不是前面blog中的将两个位置的数据交换。

作者在判断是否符合交换条件和找到item上面花费了比较多的代码。代码比较多,再看代码之前,有几个关键点。

1.GridView中的item视图从没有真正移动过,添加动画也不会改变item的真正位置

2.假设没有BitmapDrawable即镜像不存在,也不考虑动画效果,我们就可以跟清楚的知道真正发生改变的是什么。我们的手指移到哪里,哪里的item视图便隐藏,原先的隐藏的视图可见。在这个过程中改变数据的位置。

3.然后才是item交换的动画发生,此时data数据已经交换完成,并且调用了adapter.notifyDatasetchange()函数。

画一个草图来表示一下。









图片上显示的是adapter提供的数据,虚线表示为不可见。

第一张图,表示手指从5划入数据是9的item范围内,在数据层就是将5插入数据9之后,在视图上就是将镜像移动到数据为9的item范围内,并位置为4的视图设置为可见,位置为8的视图设置为不可见,等待下轮绘制。

第二张图,在下一轮draw环节,若不加人动画效果,GridView就会这样显示,位置为8的视图不可见,当然这个时候你会看到有着一个随着手指移动的显示数据为5的镜像。

第三张图,表示在显示第二张图的时候给Item视图添加的动画,比如会给显示数据7的位置为5的视图(绿色)设置这样的动画,从位置6(黄色)移过来。这样整体的效果就好像镜像让出一个位置,然后后面的视图一个接一个的补上。

我根据这个开源项目完成了一个demo,觉得作者实现的有些地方很巧妙,比如动画,有些地方有些冗余,比如根据Id找到数据位置在找到的视图位置。

packageorg.askerov.dynamicgid;

importandroid.animation.*;
importandroid.annotation.TargetApi;
importandroid.content.Context;
importandroid.graphics.Bitmap;
importandroid.graphics.Canvas;
importandroid.graphics.Point;
importandroid.graphics.Rect;
importandroid.graphics.drawable.BitmapDrawable;
importandroid.os.Build;
importandroid.util.AttributeSet;
importandroid.util.DisplayMetrics;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewTreeObserver;
importandroid.view.animation.AccelerateDecelerateInterpolator;
importandroid.widget.AbsListView;
importandroid.widget.AdapterView;
importandroid.widget.GridView;
importandroid.widget.ListAdapter;

importjava.util.ArrayList;
importjava.util.LinkedList;
importjava.util.List;

/**
*Author:alexaskerov
*Date:9/6/13
*Time:12:31PM
*/
publicclassDynamicGridViewextendsGridView{
privatestaticfinalintINVALID_ID=AbstractDynamicGridAdapter.INVALID_ID;
/*
*动画持续时间
*/
privatestaticfinalintMOVE_DURATION=300;
privatestaticfinalintSMOOTH_SCROLL_AMOUNT_AT_EDGE=8;
/*
*Item镜像
*/
privateBitmapDrawablemHoverCell;
/*
*BitmapDrawable当前边界
*/
privateRectmHoverCellCurrentBounds;
/*
*BitmapDrawable最初边界
*/
privateRectmHoverCellOriginalBounds;
/*
*这个,不过多解释,当前边界与最初边界的差值
*/
privateintmTotalOffsetY=0;
privateintmTotalOffsetX=0;
/*
*mDownX,mDown记录手指按下的位置
*mLastEventY,mLastEventX最后手指移动的位置
*每次更新完mHoverCellCurrentBounds位置后,会将mLastEventY,mLastEventX的值赋给mDownX,mDown
*个人感觉作者在这一点上定义的变量有些重复
*/
privateintmDownX=-1;
privateintmDownY=-1;
privateintmLastEventY=-1;
privateintmLastEventX=-1;
/*
*作者自定义的adapter中为每一个data设置了一个Long型Id,在我自己写的demo中没有用到这个。
*作者的思路是将每一项数据和一个Id绑定,通过Id可以找到这个数据现在的位置position,这个位置就是该项数据在视图中的位置
*/
privateList<Long>idList=newArrayList<Long>();

/*
*记录按下的item对应数据的Id,这个值在一次完整的拖拽中不会改变
*/
privatelongmMobileItemId=INVALID_ID;
/*
*
*/
privatebooleanmCellIsMobile=false;
privateintmActivePointerId=INVALID_ID;

privatebooleanmIsMobileScrolling;
privateintmSmoothScrollAmountAtEdge=0;
privatebooleanmIsWaitingForScrollFinish=false;
privateintmScrollState=OnScrollListener.SCROLL_STATE_IDLE;

privatebooleanmIsEditMode=false;
privateList<ObjectAnimator>mWobbleAnimators=newLinkedList<ObjectAnimator>();
privateOnDropListenermDropListener;
privatebooleanmHoverAnimation;
/*
*当数据交换时item的动画
*/
privatebooleanmReorderAnimation;
/*
*当进入拖拽模式后,没有被选中的item会左右摇晃,mWobbleInEditMode表示是否要开启摇晃的动画模式
*/
privatebooleanmWobbleInEditMode=true;

privateOnItemLongClickListenermUserLongClickListener;
/*
*拖拽模式的入口
*/
privateOnItemLongClickListenermLocalLongClickListener=newOnItemLongClickListener(){
publicbooleanonItemLongClick(AdapterView

arg0,Viewarg1,intpos,longid){
if(!isEnabled()||isEditMode())
returnfalse;
mTotalOffsetY=0;
mTotalOffsetX=0;

intposition=pointToPosition(mDownX,mDownY);
intitemNum=position-getFirstVisiblePosition();

ViewselectedView=getChildAt(itemNum);
//获取与该项数据绑定的Id
mMobileItemId=getAdapter().getItemId(position);
//获取镜像bitmapdrawable
mHoverCell=getAndAddHoverView(selectedView);
if(isPostHoneycomb()&&selectedView!=null)
selectedView.setVisibility(View.INVISIBLE);

mCellIsMobile=true;
//将当前Gridview可见的item所对应的数据的Id记为邻居
updateNeighborViewsForId(mMobileItemId);

if(isPostHoneycomb()&&mWobbleInEditMode)
startWobbleAnimation();

if(mUserLongClickListener!=null)
mUserLongClickListener.onItemLongClick(arg0,arg1,pos,id);

mIsEditMode=true;

returntrue;
}
};

privateOnItemClickListenermUserItemClickListener;
privateOnItemClickListenermLocalItemClickListener=newOnItemClickListener(){
@Override
publicvoidonItemClick(AdapterView

parent,Viewview,intposition,longid){
if(!isEditMode()&&isEnabled()&&mUserItemClickListener!=null){
mUserItemClickListener.onItemClick(parent,view,position,id);
}
}
};

publicDynamicGridView(Contextcontext){
super(context);
init(context);
}

publicDynamicGridView(Contextcontext,AttributeSetattrs){
super(context,attrs);
init(context);
}

publicDynamicGridView(Contextcontext,AttributeSetattrs,intdefStyle){
super(context,attrs,defStyle);
init(context);
}

publicvoidsetOnDropListener(OnDropListenerdropListener){
this.mDropListener=dropListener;
}

publicvoidstartEditMode(){
mIsEditMode=true;
if(isPostHoneycomb()&&mWobbleInEditMode)
startWobbleAnimation();
}

publicvoidstopEditMode(){
mIsEditMode=false;
if(isPostHoneycomb()&&mWobbleInEditMode)
stopWobble(true);
}

publicbooleanisEditMode(){
returnmIsEditMode;
}

publicbooleanisWobbleInEditMode(){
returnmWobbleInEditMode;
}

publicvoidsetWobbleInEditMode(booleanwobbleInEditMode){
this.mWobbleInEditMode=wobbleInEditMode;
}

@Override
publicvoidsetOnItemLongClickListener(finalOnItemLongClickListenerlistener){
mUserLongClickListener=listener;
super.setOnItemLongClickListener(mLocalLongClickListener);
}

@Override
publicvoidsetOnItemClickListener(OnItemClickListenerlistener){
this.mUserItemClickListener=listener;
super.setOnItemClickListener(mLocalItemClickListener);
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidstartWobbleAnimation(){
for(inti=0;i<getChildCount();i++){
Viewv=getChildAt(i);
if(v!=null&&Boolean.TRUE!=v.getTag(R.id.dynamic_grid_wobble_tag)){
if(i%2==0)
animateWobble(v);
else
animateWobbleInverse(v);
v.setTag(R.id.dynamic_grid_wobble_tag,true);
}
}
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidstopWobble(booleanresetRotation){
for(AnimatorwobbleAnimator:mWobbleAnimators){
wobbleAnimator.cancel();
}
mWobbleAnimators.clear();
for(inti=0;i<getChildCount();i++){
Viewv=getChildAt(i);
if(v!=null){
if(resetRotation)v.setRotation(0);
v.setTag(R.id.dynamic_grid_wobble_tag,false);
}
}
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidrestartWobble(){
stopWobble(false);
startWobbleAnimation();
}

publicvoidinit(Contextcontext){
//设置ScrollListener,当滚动后继续判断switchItem
setOnScrollListener(mScrollListener);
DisplayMetricsmetrics=context.getResources().getDisplayMetrics();
mSmoothScrollAmountAtEdge=(int)(SMOOTH_SCROLL_AMOUNT_AT_EDGE*metrics.density+0.5f);

}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidanimateWobble(Viewv){
ObjectAnimatoranimator=createBaseWobble(v);
animator.setFloatValues(-2,2);
animator.start();
mWobbleAnimators.add(animator);
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidanimateWobbleInverse(Viewv){
ObjectAnimatoranimator=createBaseWobble(v);
animator.setFloatValues(2,-2);
animator.start();
mWobbleAnimators.add(animator);
}


@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privateObjectAnimatorcreateBaseWobble(Viewv){
ObjectAnimatoranimator=newObjectAnimator();
animator.setDuration(180);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setPropertyName("rotation");
animator.setTarget(v);
returnanimator;
}


privatevoidreorderElements(intoriginalPosition,inttargetPosition){
getAdapterInterface().reorderItems(originalPosition,targetPosition);
}

privateintgetColumnCount(){
returngetAdapterInterface().getColumnCount();
}

privateAbstractDynamicGridAdaptergetAdapterInterface(){
return((AbstractDynamicGridAdapter)getAdapter());
}

/**
*Createsthehovercellwiththeappropriatebitmapandofappropriate
*size.Thehovercell'sBitmapDrawableisdrawnontopofthebitmapevery
*singletimeaninvalidatecallismade.
*/
privateBitmapDrawablegetAndAddHoverView(Viewv){

intw=v.getWidth();
inth=v.getHeight();
inttop=v.getTop();
intleft=v.getLeft();

Bitmapb=getBitmapFromView(v);

BitmapDrawabledrawable=newBitmapDrawable(getResources(),b);

mHoverCellOriginalBounds=newRect(left,top,left+w,top+h);
mHoverCellCurrentBounds=newRect(mHoverCellOriginalBounds);

drawable.setBounds(mHoverCellCurrentBounds);

returndrawable;
}

/**
*Returnsabitmapshowingascreenshotoftheviewpassedin.
*/
privateBitmapgetBitmapFromView(Viewv){
Bitmapbitmap=Bitmap.createBitmap(v.getWidth(),v.getHeight(),Bitmap.Config.ARGB_8888);
Canvascanvas=newCanvas(bitmap);
v.draw(canvas);
returnbitmap;
}


privatevoidupdateNeighborViewsForId(longitemId){
intdraggedPos=getPositionForID(itemId);
for(intpos=getFirstVisiblePosition();pos<=getLastVisiblePosition();pos++){
if(draggedPos!=pos){
idList.add(getId(pos));
}
}
}

/**
*Retrievesthepositioninthegridcorrespondingto<code>itemId

code>
*/
publicintgetPositionForID(longitemId){
Viewv=getViewForId(itemId);
if(v==null){
return-1;
}else{
returngetPositionForView(v);
}
}

publicViewgetViewForId(longitemId){
intfirstVisiblePosition=getFirstVisiblePosition();
AbstractDynamicGridAdapteradapter=((AbstractDynamicGridAdapter)getAdapter());
for(inti=0;i<getChildCount();i++){
Viewv=getChildAt(i);
intposition=firstVisiblePosition+i;
longid=adapter.getItemId(position);
if(id==itemId){
returnv;
}
}
returnnull;
}

@Override
publicbooleanonTouchEvent(MotionEventevent){
switch(event.getAction()&MotionEvent.ACTION_MASK){
/*
*记下按下的位置和按下的触点Id,若mIsEditMode已开启,则更新选中的
*mMobileItemId和mCellIsMobile,以及邻居Id,这种情况只出现在长按开
*启拖拽模式后,在结束的时候没有调用StopEditMode()
*/
caseMotionEvent.ACTION_DOWN:
mDownX=(int)event.getX();
mDownY=(int)event.getY();
mActivePointerId=event.getPointerId(0);

if(mIsEditMode&&isEnabled()){
layoutChildren();

mTotalOffsetY=0;
mTotalOffsetX=0;

intposition=pointToPosition(mDownX,mDownY);
intitemNum=position-getFirstVisiblePosition();
ViewselectedView=getChildAt(itemNum);
if(selectedView==null){
returnfalse;
}else{
mMobileItemId=getAdapter().getItemId(position);
mHoverCell=getAndAddHoverView(selectedView);
if(isPostHoneycomb())
selectedView.setVisibility(View.INVISIBLE);
mCellIsMobile=true;
updateNeighborViewsForId(mMobileItemId);
}
}elseif(!isEnabled()){
returnfalse;
}

break;
/*
*核心功能在这里了,
*event.findPointerIndex()关于多点触摸的知识,将在下面做出讲解
*更新镜像位置mHoverCell.setBounds(mHoverCellCurrentBounds);invalidate();
*判断是否满足交换数据的条件handleCellSwitch();
*判断是否Scroll的条件handleMobileCellScroll();
*/
caseMotionEvent.ACTION_MOVE:
if(mActivePointerId==INVALID_ID){
break;
}

intpointerIndex=event.findPointerIndex(mActivePointerId);

mLastEventY=(int)event.getY(pointerIndex);
mLastEventX=(int)event.getX(pointerIndex);
intdeltaY=mLastEventY-mDownY;
intdeltaX=mLastEventX-mDownX;

if(mCellIsMobile){
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left+deltaX+mTotalOffsetX,
mHoverCellOriginalBounds.top+deltaY+mTotalOffsetY);
mHoverCell.setBounds(mHoverCellCurrentBounds);
invalidate();
handleCellSwitch();
mIsMobileScrolling=false;
handleMobileCellScroll();

returnfalse;
}

break;
/*
*下面都是结束拖拽,应对不同的情况
*/
caseMotionEvent.ACTION_UP:
touchEventsEnded();
if(mDropListener!=null){
mDropListener.onActionDrop();
}
break;
caseMotionEvent.ACTION_CANCEL:
touchEventsCancelled();
if(mDropListener!=null){
mDropListener.onActionDrop();
}
break;
caseMotionEvent.ACTION_POINTER_UP:
/*Ifamultitoucheventtookplaceandtheoriginaltouchdictating
*themovementofthehovercellhasended,thenthedraggingevent
*endsandthehovercellisanimatedtoitscorrespondingposition
*inthelistview.*/
pointerIndex=(event.getAction()&MotionEvent.ACTION_POINTER_INDEX_MASK)>>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
finalintpointerId=event.getPointerId(pointerIndex);
if(pointerId==mActivePointerId){
touchEventsEnded();
}
break;
default:
break;
}

returnsuper.onTouchEvent(event);
}

privatevoidhandleMobileCellScroll(){
mIsMobileScrolling=handleMobileCellScroll(mHoverCellCurrentBounds);
}

/*
*computeVerticalScrollOffset计算滑动thumb已滑动的距离
*computeVerticalScrollExtent计算thumb的高度
*computeVerticalScrollRange计算thumb所表示的高度,一般为视图高度
*/
publicbooleanhandleMobileCellScroll(Rectr){
intoffset=computeVerticalScrollOffset();
intheight=getHeight();
intextent=computeVerticalScrollExtent();
intrange=computeVerticalScrollRange();
inthoverViewTop=r.top;
inthoverHeight=r.height();

if(hoverViewTop<=0&&offset>0){
smoothScrollBy(-mSmoothScrollAmountAtEdge,0);
returntrue;
}

if(hoverViewTop+hoverHeight>=height&&(offset+extent)<range){
smoothScrollBy(mSmoothScrollAmountAtEdge,0);
returntrue;
}

returnfalse;
}

@Override
publicvoidsetAdapter(ListAdapteradapter){
super.setAdapter(adapter);
}

privatevoidtouchEventsEnded(){
finalViewmobileView=getViewForId(mMobileItemId);
if(mCellIsMobile||mIsWaitingForScrollFinish){
mCellIsMobile=false;
mIsWaitingForScrollFinish=false;
mIsMobileScrolling=false;
mActivePointerId=INVALID_ID;

//Iftheautoscrollerhasnotcompletedscrolling,weneedtowaitforitto
//finishinordertodeterminethefinallocationofwherethehovercell
//shouldbeanimatedto.
if(mScrollState!=OnScrollListener.SCROLL_STATE_IDLE){
mIsWaitingForScrollFinish=true;
return;
}

mHoverCellCurrentBounds.offsetTo(mobileView.getLeft(),mobileView.getTop());

if(Build.VERSION.SDK_INT>Build.VERSION_CODES.HONEYCOMB){
animateBounds(mobileView);
}else{
mHoverCell.setBounds(mHoverCellCurrentBounds);
invalidate();
reset(mobileView);
}
}else{
touchEventsCancelled();
}
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidanimateBounds(finalViewmobileView){
TypeEvaluator<Rect>sBoundEvaluator=newTypeEvaluator<Rect>(){
publicRectevaluate(floatfraction,RectstartValue,RectendValue){
returnnewRect(interpolate(startValue.left,endValue.left,fraction),
interpolate(startValue.top,endValue.top,fraction),
interpolate(startValue.right,endValue.right,fraction),
interpolate(startValue.bottom,endValue.bottom,fraction));
}

publicintinterpolate(intstart,intend,floatfraction){
return(int)(start+fraction*(end-start));
}
};


ObjectAnimatorhoverViewAnimator=ObjectAnimator.ofObject(mHoverCell,"bounds",
sBoundEvaluator,mHoverCellCurrentBounds);
hoverViewAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){
@Override
publicvoidonAnimationUpdate(ValueAnimatorvalueAnimator){
invalidate();
}
});
hoverViewAnimator.addListener(newAnimatorListenerAdapter(){
@Override
publicvoidonAnimationStart(Animatoranimation){
mHoverAnimation=true;
updateEnableState();
}

@Override
publicvoidonAnimationEnd(Animatoranimation){
mHoverAnimation=false;
updateEnableState();
reset(mobileView);
}
});
hoverViewAnimator.start();
}

privatevoidreset(ViewmobileView){
idList.clear();
mMobileItemId=INVALID_ID;
mobileView.setVisibility(View.VISIBLE);
mHoverCell=null;
if(!mIsEditMode&&isPostHoneycomb()&&mWobbleInEditMode)
stopWobble(true);
if(mIsEditMode&&isPostHoneycomb()&&mWobbleInEditMode)
restartWobble();
invalidate();
}

privatevoidupdateEnableState(){
setEnabled(!mHoverAnimation&&!mReorderAnimation);
}

/**
*SeemsthatGridViewbeforeHONEYCOMBnotsupportstableidinproperway.
*Thatcausebugsonviewrecycleifwewillanimateorchangevisibilitystateforitems.
*
*@return
*/
privatebooleanisPostHoneycomb(){
returnBuild.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB;
}

privatevoidtouchEventsCancelled(){
ViewmobileView=getViewForId(mMobileItemId);
if(mCellIsMobile){
reset(mobileView);
}
mCellIsMobile=false;
mIsMobileScrolling=false;
mActivePointerId=INVALID_ID;

}

/*
*处理数据交换
*/
privatevoidhandleCellSwitch(){

finalintdeltaY=mLastEventY-mDownY;
finalintdeltaX=mLastEventX-mDownX;
//获取镜像的中心坐标
finalintdeltaYTotal=mHoverCellOriginalBounds.centerY()+mTotalOffsetY+deltaY;
finalintdeltaXTotal=mHoverCellOriginalBounds.centerX()+mTotalOffsetX+deltaX;
ViewmobileView=getViewForId(mMobileItemId);
ViewtargetView=null;
floatvX=0;
floatvY=0;
//以下的工作便是判断是否满足改变数据位置的条件
PointmobileColumnRowPair=getColumnAndRowForView(mobileView);
for(Longid:idList){
Viewview=getViewForId(id);
if(view!=null){
PointtargetColumnRowPair=getColumnAndRowForView(view);
if((aboveRight(targetColumnRowPair,mobileColumnRowPair)
&&deltaYTotal<view.getBottom()&&deltaXTotal>view.getLeft()
||aboveLeft(targetColumnRowPair,mobileColumnRowPair)
&&deltaYTotal<view.getBottom()&&deltaXTotal<view.getRight()
||belowRight(targetColumnRowPair,mobileColumnRowPair)
&&deltaYTotal>view.getTop()&&deltaXTotal>view.getLeft()
||belowLeft(targetColumnRowPair,mobileColumnRowPair)
&&deltaYTotal>view.getTop()&&deltaXTotal<view.getRight()
||above(targetColumnRowPair,mobileColumnRowPair)&&deltaYTotal<view.getBottom())
||below(targetColumnRowPair,mobileColumnRowPair)&&deltaYTotal>view.getTop()
||right(targetColumnRowPair,mobileColumnRowPair)&&deltaXTotal>view.getLeft()
||left(targetColumnRowPair,mobileColumnRowPair)&&deltaXTotal<view.getRight()){
floatxDiff=Math.abs(DynamicGridUtils.getViewX(view)-DynamicGridUtils.getViewX(mobileView));
floatyDiff=Math.abs(DynamicGridUtils.getViewY(view)-DynamicGridUtils.getViewY(mobileView));
if(xDiff>=vX&&yDiff>=vY){
vX=xDiff;
vY=yDiff;
targetView=view;
}
}
}
}
if(targetView!=null){
finalintoriginalPosition=getPositionForView(mobileView);
inttargetPosition=getPositionForView(targetView);

if(targetPosition==INVALID_POSITION){
updateNeighborViewsForId(mMobileItemId);
return;
}
//改变数据位置
reorderElements(originalPosition,targetPosition);
mDownY=mLastEventY;
mDownX=mLastEventX;
mobileView.setVisibility(View.VISIBLE);
if(isPostHoneycomb()){
targetView.setVisibility(View.INVISIBLE);
}
updateNeighborViewsForId(mMobileItemId);
finalViewTreeObserverobserver=getViewTreeObserver();
finalintfinalTargetPosition=targetPosition;
if(isPostHoneycomb()&&observer!=null){
observer.addOnPreDrawListener(newViewTreeObserver.OnPreDrawListener(){
@Override
publicbooleanonPreDraw(){
observer.removeOnPreDrawListener(this);
mTotalOffsetY+=deltaY;
mTotalOffsetX+=deltaX;
animateReorder(originalPosition,finalTargetPosition);
returntrue;
}
});
}else{
mTotalOffsetY+=deltaY;
mTotalOffsetX+=deltaX;
}
}
}

privatebooleanbelowLeft(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y>mobileColumnRowPair.y&&targetColumnRowPair.x<mobileColumnRowPair.x;
}

privatebooleanbelowRight(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y>mobileColumnRowPair.y&&targetColumnRowPair.x>mobileColumnRowPair.x;
}

privatebooleanaboveLeft(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y<mobileColumnRowPair.y&&targetColumnRowPair.x<mobileColumnRowPair.x;
}

privatebooleanaboveRight(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y<mobileColumnRowPair.y&&targetColumnRowPair.x>mobileColumnRowPair.x;
}

privatebooleanabove(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y<mobileColumnRowPair.y&&targetColumnRowPair.x==mobileColumnRowPair.x;
}

privatebooleanbelow(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y>mobileColumnRowPair.y&&targetColumnRowPair.x==mobileColumnRowPair.x;
}

privatebooleanright(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y==mobileColumnRowPair.y&&targetColumnRowPair.x>mobileColumnRowPair.x;
}

privatebooleanleft(PointtargetColumnRowPair,PointmobileColumnRowPair){
returntargetColumnRowPair.y==mobileColumnRowPair.y&&targetColumnRowPair.x<mobileColumnRowPair.x;
}

privatePointgetColumnAndRowForView(Viewview){
intpos=getPositionForView(view);
intcolumns=getColumnCount();
intcolumn=pos%columns;
introw=pos/columns;
returnnewPoint(column,row);
}

privatelonggetId(intposition){
returngetAdapter().getItemId(position);
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidanimateReorder(finalintoldPosition,finalintnewPosition){
booleanisForward=newPosition>oldPosition;
List<Animator>resultList=newLinkedList<Animator>();
if(isForward){
for(intpos=Math.min(oldPosition,newPosition);pos<Math.max(oldPosition,newPosition);pos++){
Viewview=getViewForId(getId(pos));
if((pos+1)%getColumnCount()==0){
resultList.add(createTranslationAnimations(view,-view.getWidth()*(getColumnCount()-1),0,view.getHeight(),0));
}else{
resultList.add(createTranslationAnimations(view,view.getWidth(),0,0,0));
}
}
}else{
for(intpos=Math.max(oldPosition,newPosition);pos>Math.min(oldPosition,newPosition);pos--){
Viewview=getViewForId(getId(pos));
if((pos+getColumnCount())%getColumnCount()==0){
resultList.add(createTranslationAnimations(view,view.getWidth()*(getColumnCount()-1),0,-view.getHeight(),0));
}else{
resultList.add(createTranslationAnimations(view,-view.getWidth(),0,0,0));
}
}
}

AnimatorSetresultSet=newAnimatorSet();
resultSet.playTogether(resultList);
resultSet.setDuration(MOVE_DURATION);
resultSet.setInterpolator(newAccelerateDecelerateInterpolator());
resultSet.addListener(newAnimatorListenerAdapter(){
@Override
publicvoidonAnimationStart(Animatoranimation){
mReorderAnimation=true;
updateEnableState();
}

@Override
publicvoidonAnimationEnd(Animatoranimation){
mReorderAnimation=false;
updateEnableState();
}
});
resultSet.start();
}


@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privateAnimatorSetcreateTranslationAnimations(Viewview,floatstartX,floatendX,floatstartY,floatendY){
ObjectAnimatoranimX=ObjectAnimator.ofFloat(view,"translationX",startX,endX);
ObjectAnimatoranimY=ObjectAnimator.ofFloat(view,"translationY",startY,endY);
AnimatorSetanimSetXY=newAnimatorSet();
animSetXY.playTogether(animX,animY);
returnanimSetXY;
}

@Override
protectedvoiddispatchDraw(Canvascanvas){
super.dispatchDraw(canvas);
if(mHoverCell!=null){
mHoverCell.draw(canvas);
}
}

/**
*Interfaceprovidecallbackforendofdrag'n'dropevent
*/
publicinterfaceOnDropListener{
/**
*calledwhenviewbeendropped
*/
voidonActionDrop();

}


/**
*Thisscrolllistenerisaddedtothegridviewinordertohandlecellswapping
*whenthecelliseitheratthetoporbottomedgeofthegridview.Ifthehover
*cellisateitheredgeofthegridview,thegridviewwillbeginscrolling.As
*scrollingtakesplace,thegridviewcontinuouslychecksifnewcellsbecamevisible
*anddetermineswhethertheyarepotentialcandidatesforacellswap.
*/
privateOnScrollListenermScrollListener=newOnScrollListener(){

privateintmPreviousFirstVisibleItem=-1;
privateintmPreviousVisibleItemCount=-1;
privateintmCurrentFirstVisibleItem;
privateintmCurrentVisibleItemCount;
privateintmCurrentScrollState;

publicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,
inttotalItemCount){
mCurrentFirstVisibleItem=firstVisibleItem;
mCurrentVisibleItemCount=visibleItemCount;

mPreviousFirstVisibleItem=(mPreviousFirstVisibleItem==-1)?mCurrentFirstVisibleItem
:mPreviousFirstVisibleItem;
mPreviousVisibleItemCount=(mPreviousVisibleItemCount==-1)?mCurrentVisibleItemCount
:mPreviousVisibleItemCount;

checkAndHandleFirstVisibleCellChange();
checkAndHandleLastVisibleCellChange();

mPreviousFirstVisibleItem=mCurrentFirstVisibleItem;
mPreviousVisibleItemCount=mCurrentVisibleItemCount;
if(isPostHoneycomb()&&mWobbleInEditMode){
updateWobbleState(visibleItemCount);
}
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
privatevoidupdateWobbleState(intvisibleItemCount){
for(inti=0;i<visibleItemCount;i++){
Viewchild=getChildAt(i);

if(child!=null){
if(mMobileItemId!=INVALID_ID&&Boolean.TRUE!=child.getTag(R.id.dynamic_grid_wobble_tag)){
if(i%2==0)
animateWobble(child);
else
animateWobbleInverse(child);
child.setTag(R.id.dynamic_grid_wobble_tag,true);
}elseif(mMobileItemId==INVALID_ID&&child.getRotation()!=0){
child.setRotation(0);
child.setTag(R.id.dynamic_grid_wobble_tag,false);
}
}

}
}

@Override
publicvoidonScrollStateChanged(AbsListViewview,intscrollState){
mCurrentScrollState=scrollState;
mScrollState=scrollState;
isScrollCompleted();
}

/**
*Thismethodisinchargeofinvoking1of2actions.Firstly,ifthegridview
*isinastateofscrollinginvokedbythehovercellbeingoutsidethebounds
*ofthegridview,thenthisscrollingeventiscontinued.Secondly,ifthehover
*cellhasalreadybeenreleased,thisinvokestheanimationforthehovercell
*toreturntoitscorrectpositionafterthegridviewhasenteredanidlescroll
*state.
*/
privatevoidisScrollCompleted(){
if(mCurrentVisibleItemCount>0&&mCurrentScrollState==SCROLL_STATE_IDLE){
if(mCellIsMobile&&mIsMobileScrolling){
handleMobileCellScroll();
}elseif(mIsWaitingForScrollFinish){
touchEventsEnded();
}
}
}

/**
*Determinesifthegridviewscrolledupenoughtorevealanewcellatthe
*topofthelist.Ifso,thentheappropriateparametersareupdated.
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: