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

<p>废话不多说先上图,只有一张静态图,实现的是可拖拽的GridView。有很什么方便的GIF制作软件,推荐一下,另外我从windows Live writer上传blog中的代码样式变得好丑了:</p> <p><a href="http://static.oschina.net/uploads/img/201404/07180454_n3Zn.png"><img title="豌豆荚截图20140407112709" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="豌豆荚截图20140407112709" src="http://static.oschina.net/uploads/img/201404/07180455_9ROe.png" width="121" height="244" /></a> </p> <p>说到这里再推荐一篇同样是写可拖拽GridView的blog<a href="http://blog.csdn.net/xiaanming/article/details/17718579">Android 可拖拽的GridView效果实现, 长按可拖拽和item实时交换</a></p> <p>这里很好的讲解了拖拽的原理,也实现了item交换的效果。下面是他的步骤,和我要分析的这个开源项目有一些不同,我将比较来讲。</p> <p>先说一下推荐blog的思路:</p> <ol> <li>根据手指按下的X,Y坐标来获取我们在GridView上面点击的item </li> <li>手指按下的时候使用Handler和Runnable来实现一个定时器,假如定时时间为1000毫秒,在1000毫秒内,如果手指抬起了移除定时器,没有抬起并且手指点击在GridView的item所在的区域,则表示我们长按了GridView的item </li> <li>如果我们长按了item则隐藏item,然后使用WindowManager来添加一个item的镜像在屏幕用来代替刚刚隐藏的item </li> <li>当我们手指在屏幕移动的时候,更新item镜像的位置,然后在根据我们移动的X,Y的坐标来获取移动到GridView的哪一个位置 </li> <li>到GridView的item过多的时候,可能一屏幕显示不完,我们手指拖动item镜像到屏幕下方,要触发GridView想上滚动,同理,当我们手指拖动item镜像到屏幕上面,触发GridView向下滚动 </li> <li>GridView交换数据,刷新界面,移除item的镜像 </li> </ol> <p>DynamicGridView的思路:</p> <ol> <li>根据手指按下的X,Y坐标来获取我们在GridView上面点击的item </li> <li>在itemOnlongClickListener开启拖拽模式。隐藏item。 </li> <li>获取有拖拽item的内容的BitmapDrawable,并在重写dispatchDraw函数,将其显示出来,替换刚刚隐藏的item </li> <li>当我们手指在屏幕移动的时候,更新item BitmapDrawable的位置,然后在根据我们移动的X,Y的坐标来获取移动到GridView的哪一个位置 </li> <li>到GridView的item过多的时候,可能一屏幕显示不完,我们手指拖动item镜像到屏幕下方,要触发GridView想上滚动,同理,当我们手指拖动item镜像到屏幕上面,触发GridView向下滚动 </li> <li>GridView交换数据,改变隐藏Item的位置,刷新界面 </li> <li>当手指抬起后,将BitmapDrawable置为NULL,然后将隐藏的item显示 </li> </ol> <p>看起来两者的区别不大,但是上面只是一个思路,在多细节上的有很大的差异。这里交换数据是指将某一项数据从就得位置插入到新的位置,而不是前面blog中的将两个位置的数据交换。</p> <p>作者在判断是否符合交换条件和找到item上面花费了比较多的代码。代码比较多,再看代码之前,有几个关键点。</p> <blockquote> <p>1.GridView中的item视图从没有真正移动过,添加动画也不会改变item的真正位置</p> <p>2.假设没有BitmapDrawable即镜像不存在,也不考虑动画效果,我们就可以跟清楚的知道真正发生改变的是什么。我们的手指移到哪里,哪里的item视图便隐藏,原先的隐藏的视图可见。在这个过程中改变数据的位置。</p> <p>3.然后才是item交换的动画发生,此时data数据已经交换完成,并且调用了adapter.notifyDatasetchange()函数。</p> <p>画一个草图来表示一下。</p> <p>&#160;</p> <p><a href="http://static.oschina.net/uploads/img/201404/07180455_OsfB.png"><img title="之前" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="之前" src="http://static.oschina.net/uploads/img/201404/07180455_S9Qd.png" width="140" height="244" /></a>&#160; <a href="http://static.oschina.net/uploads/img/201404/07180455_j1aA.png"><img title="之后" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="之后" src="http://static.oschina.net/uploads/img/201404/07180456_qlRN.png" width="140" height="244" /></a> <a href="http://static.oschina.net/uploads/img/201404/07180456_UbSS.png"><img title="完成" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="完成" src="http://static.oschina.net/uploads/img/201404/07180456_HRge.png" width="140" height="244" /></a> </p> <p>图片上显示的是adapter提供的数据,虚线表示为不可见。</p> <p>第一张图,表示手指从5划入数据是9的item范围内,在数据层就是将5插入数据9之后,在视图上就是将镜像移动到数据为9的item范围内,并位置为4的视图设置为可见,位置为8的视图设置为不可见,等待下轮绘制。</p> <p>第二张图,在下一轮draw环节,若不加人动画效果,GridView就会这样显示,位置为8的视图不可见,当然这个时候你会看到有着一个随着手指移动的显示数据为5的镜像。</p> <p>第三张图,表示在显示第二张图的时候给Item视图添加的动画,比如会给显示数据7的位置为5的视图(绿色)设置这样的动画,从位置6(黄色)移过来。这样整体的效果就好像镜像让出一个位置,然后后面的视图一个接一个的补上。</p> <p>我根据这个开源项目完成了一个demo,觉得作者实现的有些地方很巧妙,比如动画,有些地方有些冗余,比如根据Id找到数据位置在找到的视图位置。</p> </blockquote> <div class="csharpcode"> <pre class="alt">package org.askerov.dynamicgid;</pre>

<pre>&#160;</pre>

<pre class="alt">import android.animation.*;</pre>

<pre>import android.annotation.TargetApi;</pre>

<pre class="alt">import android.content.Context;</pre>

<pre>import android.graphics.Bitmap;</pre>

<pre class="alt">import android.graphics.Canvas;</pre>

<pre>import android.graphics.Point;</pre>

<pre class="alt">import android.graphics.Rect;</pre>

<pre>import android.graphics.drawable.BitmapDrawable;</pre>

<pre class="alt">import android.os.Build;</pre>

<pre>import android.util.AttributeSet;</pre>

<pre class="alt">import android.util.DisplayMetrics;</pre>

<pre>import android.view.MotionEvent;</pre>

<pre class="alt">import android.view.View;</pre>

<pre>import android.view.ViewTreeObserver;</pre>

<pre class="alt">import android.view.animation.AccelerateDecelerateInterpolator;</pre>

<pre>import android.widget.AbsListView;</pre>

<pre class="alt">import android.widget.AdapterView;</pre>

<pre>import android.widget.GridView;</pre>

<pre class="alt">import android.widget.ListAdapter;</pre>

<pre>&#160;</pre>

<pre class="alt">import java.util.ArrayList;</pre>

<pre>import java.util.LinkedList;</pre>

<pre class="alt">import java.util.List;</pre>

<pre>&#160;</pre>

<pre class="alt">/**</pre>

<pre> * Author: alex askerov</pre>

<pre class="alt"> * Date: 9/6/13</pre>

<pre> * Time: 12:31 PM</pre>

<pre class="alt"> */</pre>

<pre>public class DynamicGridView extends GridView {</pre>

<pre class="alt"> private static final int INVALID_ID = AbstractDynamicGridAdapter.INVALID_ID;</pre>

<pre> /*</pre>

<pre class="alt"> * 动画持续时间</pre>

<pre> */</pre>

<pre class="alt"> private static final int MOVE_DURATION = 300;</pre>

<pre> private static final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 8;</pre>

<pre class="alt"> /*</pre>

<pre> * Item 镜像</pre>

<pre class="alt"> */</pre>

<pre> private BitmapDrawable mHoverCell;</pre>

<pre class="alt"> /*</pre>

<pre> * BitmapDrawable当前边界</pre>

<pre class="alt"> */</pre>

<pre> private Rect mHoverCellCurrentBounds;</pre>

<pre class="alt"> /*</pre>

<pre> * BitmapDrawable最初边界</pre>

<pre class="alt"> */</pre>

<pre> private Rect mHoverCellOriginalBounds;</pre>

<pre class="alt"> /*</pre>

<pre> * 这个,不过多解释,当前边界与最初边界的差值</pre>

<pre class="alt"> */</pre>

<pre> private int mTotalOffsetY = 0;</pre>

<pre class="alt"> private int mTotalOffsetX = 0;</pre>

<pre> /*</pre>

<pre class="alt"> * mDownX,mDown记录手指按下的位置</pre>

<pre> * mLastEventY,mLastEventX最后手指移动的位置</pre>

<pre class="alt"> * 每次更新完mHoverCellCurrentBounds位置后,会将mLastEventY,mLastEventX的值赋给mDownX,mDown</pre>

<pre> * 个人感觉作者在这一点上定义的变量有些重复</pre>

<pre class="alt"> */</pre>

<pre> private int mDownX = -1;</pre>

<pre class="alt"> private int mDownY = -1;</pre>

<pre> private int mLastEventY = -1;</pre>

<pre class="alt"> private int mLastEventX = -1;</pre>

<pre> /*</pre>

<pre class="alt"> * 作者自定义的adapter中为每一个data设置了一个Long型Id,在我自己写的demo中没有用到这个。</pre>

<pre> * 作者的思路是将每一项数据和一个Id绑定,通过Id可以找到这个数据现在的位置position,这个位置就是该项数据在视图中的位置</pre>

<pre class="alt"> */</pre>

<pre> private List<span class="kwrd">&lt;</span><span class="html">Long</span><span class="kwrd">&gt;</span> idList = new ArrayList<span class="kwrd">&lt;</span><span class="html">Long</span><span class="kwrd">&gt;</span>();</pre>

<pre class="alt">&#160;</pre>

<pre> /*</pre>

<pre class="alt"> * 记录按下的item对应数据的Id,这个值在一次完整的拖拽中不会改变</pre>

<pre> */</pre>

<pre class="alt"> private long mMobileItemId = INVALID_ID;</pre>

<pre> /*</pre>

<pre class="alt"> * </pre>

<pre> */</pre>

<pre class="alt"> private boolean mCellIsMobile = false;</pre>

<pre> private int mActivePointerId = INVALID_ID;</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean mIsMobileScrolling;</pre>

<pre class="alt"> private int mSmoothScrollAmountAtEdge = 0;</pre>

<pre> private boolean mIsWaitingForScrollFinish = false;</pre>

<pre class="alt"> private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;</pre>

<pre>&#160;</pre>

<pre class="alt"> private boolean mIsEditMode = false;</pre>

<pre> private List<span class="kwrd">&lt;</span><span class="html">ObjectAnimator</span><span class="kwrd">&gt;</span> mWobbleAnimators = new LinkedList<span class="kwrd">&lt;</span><span class="html">ObjectAnimator</span><span class="kwrd">&gt;</span>();</pre>

<pre class="alt"> private OnDropListener mDropListener;</pre>

<pre> private boolean mHoverAnimation;</pre>

<pre class="alt"> /*</pre>

<pre> * 当数据交换时item的动画</pre>

<pre class="alt"> */</pre>

<pre> private boolean mReorderAnimation;</pre>

<pre class="alt"> /*</pre>

<pre> * 当进入拖拽模式后,没有被选中的item会左右摇晃,mWobbleInEditMode表示是否要开启摇晃的动画模式</pre>

<pre class="alt"> */</pre>

<pre> private boolean mWobbleInEditMode = true;</pre>

<pre class="alt">&#160;</pre>

<pre> private OnItemLongClickListener mUserLongClickListener;</pre>

<pre class="alt"> /*</pre>

<pre> * 拖拽模式的入口</pre>

<pre class="alt"> */</pre>

<pre> private OnItemLongClickListener mLocalLongClickListener = new OnItemLongClickListener() {</pre>

<pre class="alt"> public boolean onItemLongClick(AdapterView<span class="kwrd">&lt;?&gt;</span> arg0, View arg1, int pos, long id) {</pre>

<pre> if (!isEnabled() || isEditMode())</pre>

<pre class="alt"> return false;</pre>

<pre> mTotalOffsetY = 0;</pre>

<pre class="alt"> mTotalOffsetX = 0;</pre>

<pre>&#160;</pre>

<pre class="alt"> int position = pointToPosition(mDownX, mDownY);</pre>

<pre> int itemNum = position - getFirstVisiblePosition();</pre>

<pre class="alt">&#160;</pre>

<pre> View selectedView = getChildAt(itemNum);</pre>

<pre class="alt"> //获取与该项数据绑定的Id</pre>

<pre> mMobileItemId = getAdapter().getItemId(position);</pre>

<pre class="alt"> //获取镜像 bitmapdrawable</pre>

<pre> mHoverCell = getAndAddHoverView(selectedView);</pre>

<pre class="alt"> if (isPostHoneycomb() &amp;&amp; selectedView != null)</pre>

<pre> selectedView.setVisibility(View.INVISIBLE);</pre>

<pre class="alt">&#160;</pre>

<pre> mCellIsMobile = true;</pre>

<pre class="alt"> //将当前Gridview可见的item所对应的数据的Id记为邻居</pre>

<pre> updateNeighborViewsForId(mMobileItemId);</pre>

<pre class="alt">&#160;</pre>

<pre> if (isPostHoneycomb() &amp;&amp; mWobbleInEditMode)</pre>

<pre class="alt"> startWobbleAnimation();</pre>

<pre>&#160;</pre>

<pre class="alt"> if (mUserLongClickListener != null)</pre>

<pre> mUserLongClickListener.onItemLongClick(arg0, arg1, pos, id);</pre>

<pre class="alt">&#160;</pre>

<pre> mIsEditMode = true;</pre>

<pre class="alt">&#160;</pre>

<pre> return true;</pre>

<pre class="alt"> }</pre>

<pre> };</pre>

<pre class="alt">&#160;</pre>

<pre> private OnItemClickListener mUserItemClickListener;</pre>

<pre class="alt"> private OnItemClickListener mLocalItemClickListener = new OnItemClickListener() {</pre>

<pre> @Override</pre>

<pre class="alt"> public void onItemClick(AdapterView<span class="kwrd">&lt;?&gt;</span> parent, View view, int position, long id) {</pre>

<pre> if (!isEditMode() &amp;&amp; isEnabled() &amp;&amp; mUserItemClickListener != null) {</pre>

<pre class="alt"> mUserItemClickListener.onItemClick(parent, view, position, id);</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre> };</pre>

<pre class="alt">&#160;</pre>

<pre> public DynamicGridView(Context context) {</pre>

<pre class="alt"> super(context);</pre>

<pre> init(context);</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public DynamicGridView(Context context, AttributeSet attrs) {</pre>

<pre> super(context, attrs);</pre>

<pre class="alt"> init(context);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> public DynamicGridView(Context context, AttributeSet attrs, int defStyle) {</pre>

<pre class="alt"> super(context, attrs, defStyle);</pre>

<pre> init(context);</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public void setOnDropListener(OnDropListener dropListener) {</pre>

<pre> this.mDropListener = dropListener;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public void startEditMode() {</pre>

<pre> mIsEditMode = true;</pre>

<pre class="alt"> if (isPostHoneycomb() &amp;&amp; mWobbleInEditMode)</pre>

<pre> startWobbleAnimation();</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public void stopEditMode() {</pre>

<pre> mIsEditMode = false;</pre>

<pre class="alt"> if (isPostHoneycomb() &amp;&amp; mWobbleInEditMode)</pre>

<pre> stopWobble(true);</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public boolean isEditMode() {</pre>

<pre> return mIsEditMode;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public boolean isWobbleInEditMode() {</pre>

<pre> return mWobbleInEditMode;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public void setWobbleInEditMode(boolean wobbleInEditMode) {</pre>

<pre> this.mWobbleInEditMode = wobbleInEditMode;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @Override</pre>

<pre> public void setOnItemLongClickListener(final OnItemLongClickListener listener) {</pre>

<pre class="alt"> mUserLongClickListener = listener;</pre>

<pre> super.setOnItemLongClickListener(mLocalLongClickListener);</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @Override</pre>

<pre> public void setOnItemClickListener(OnItemClickListener listener) {</pre>

<pre class="alt"> this.mUserItemClickListener = listener;</pre>

<pre> super.setOnItemClickListener(mLocalItemClickListener);</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre> private void startWobbleAnimation() {</pre>

<pre class="alt"> for (int i = 0; i <span class="kwrd">&lt;</span> getChildCount(); i++) {</pre>

<pre> View v = getChildAt(i);</pre>

<pre class="alt"> if (v != null &amp;&amp; Boolean.TRUE != v.getTag(R.id.dynamic_grid_wobble_tag)) {</pre>

<pre> if (i % 2 == 0)</pre>

<pre class="alt"> animateWobble(v);</pre>

<pre> else</pre>

<pre class="alt"> animateWobbleInverse(v);</pre>

<pre> v.setTag(R.id.dynamic_grid_wobble_tag, true);</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre> private void stopWobble(boolean resetRotation) {</pre>

<pre class="alt"> for (Animator wobbleAnimator : mWobbleAnimators) {</pre>

<pre> wobbleAnimator.cancel();</pre>

<pre class="alt"> }</pre>

<pre> mWobbleAnimators.clear();</pre>

<pre class="alt"> for (int i = 0; i <span class="kwrd">&lt;</span> getChildCount(); i++) {</pre>

<pre> View v = getChildAt(i);</pre>

<pre class="alt"> if (v != null) {</pre>

<pre> if (resetRotation) v.setRotation(0);</pre>

<pre class="alt"> v.setTag(R.id.dynamic_grid_wobble_tag, false);</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre class="alt"> private void restartWobble() {</pre>

<pre> stopWobble(false);</pre>

<pre class="alt"> startWobbleAnimation();</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> public void init(Context context) {</pre>

<pre class="alt"> //设置ScrollListener,当滚动后继续判断switch Item</pre>

<pre> setOnScrollListener(mScrollListener);</pre>

<pre class="alt"> DisplayMetrics metrics = context.getResources().getDisplayMetrics();</pre>

<pre> mSmoothScrollAmountAtEdge = (int) (SMOOTH_SCROLL_AMOUNT_AT_EDGE * metrics.density + 0.5f);</pre>

<pre class="alt">&#160;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre class="alt"> private void animateWobble(View v) {</pre>

<pre> ObjectAnimator animator = createBaseWobble(v);</pre>

<pre class="alt"> animator.setFloatValues(-2, 2);</pre>

<pre> animator.start();</pre>

<pre class="alt"> mWobbleAnimators.add(animator);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre class="alt"> private void animateWobbleInverse(View v) {</pre>

<pre> ObjectAnimator animator = createBaseWobble(v);</pre>

<pre class="alt"> animator.setFloatValues(2, -2);</pre>

<pre> animator.start();</pre>

<pre class="alt"> mWobbleAnimators.add(animator);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre>&#160;</pre>

<pre class="alt"> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre> private ObjectAnimator createBaseWobble(View v) {</pre>

<pre class="alt"> ObjectAnimator animator = new ObjectAnimator();</pre>

<pre> animator.setDuration(180);</pre>

<pre class="alt"> animator.setRepeatMode(ValueAnimator.REVERSE);</pre>

<pre> animator.setRepeatCount(ValueAnimator.INFINITE);</pre>

<pre class="alt"> animator.setPropertyName(&quot;rotation&quot;);</pre>

<pre> animator.setTarget(v);</pre>

<pre class="alt"> return animator;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre>&#160;</pre>

<pre class="alt"> private void reorderElements(int originalPosition, int targetPosition) {</pre>

<pre> getAdapterInterface().reorderItems(originalPosition, targetPosition);</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> private int getColumnCount() {</pre>

<pre> return getAdapterInterface().getColumnCount();</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> private AbstractDynamicGridAdapter getAdapterInterface() {</pre>

<pre> return ((AbstractDynamicGridAdapter) getAdapter());</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> /**</pre>

<pre> * Creates the hover cell with the appropriate bitmap and of appropriate</pre>

<pre class="alt"> * size. The hover cell's BitmapDrawable is drawn on top of the bitmap every</pre>

<pre> * single time an invalidate call is made.</pre>

<pre class="alt"> */</pre>

<pre> private BitmapDrawable getAndAddHoverView(View v) {</pre>

<pre class="alt">&#160;</pre>

<pre> int w = v.getWidth();</pre>

<pre class="alt"> int h = v.getHeight();</pre>

<pre> int top = v.getTop();</pre>

<pre class="alt"> int left = v.getLeft();</pre>

<pre>&#160;</pre>

<pre class="alt"> Bitmap b = getBitmapFromView(v);</pre>

<pre>&#160;</pre>

<pre class="alt"> BitmapDrawable drawable = new BitmapDrawable(getResources(), b);</pre>

<pre>&#160;</pre>

<pre class="alt"> mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);</pre>

<pre> mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);</pre>

<pre class="alt">&#160;</pre>

<pre> drawable.setBounds(mHoverCellCurrentBounds);</pre>

<pre class="alt">&#160;</pre>

<pre> return drawable;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> /**</pre>

<pre> * Returns a bitmap showing a screenshot of the view passed in.</pre>

<pre class="alt"> */</pre>

<pre> private Bitmap getBitmapFromView(View v) {</pre>

<pre class="alt"> Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);</pre>

<pre> Canvas canvas = new Canvas(bitmap);</pre>

<pre class="alt"> v.draw(canvas);</pre>

<pre> return bitmap;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt">&#160;</pre>

<pre> private void updateNeighborViewsForId(long itemId) {</pre>

<pre class="alt"> int draggedPos = getPositionForID(itemId);</pre>

<pre> for (int pos = getFirstVisiblePosition(); pos <span class="kwrd">&lt;</span>= getLastVisiblePosition(); pos++) {</pre>

<pre class="alt"> if (draggedPos != pos) {</pre>

<pre> idList.add(getId(pos));</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> /**</pre>

<pre> * Retrieves the position in the grid corresponding to <span class="kwrd">&lt;</span><span class="html">code</span><span class="kwrd">&gt;</span>itemId<span class="kwrd">&lt;/</span><span class="html">code</span><span class="kwrd">&gt;</span></pre>

<pre class="alt"> */</pre>

<pre> public int getPositionForID(long itemId) {</pre>

<pre class="alt"> View v = getViewForId(itemId);</pre>

<pre> if (v == null) {</pre>

<pre class="alt"> return -1;</pre>

<pre> } else {</pre>

<pre class="alt"> return getPositionForView(v);</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public View getViewForId(long itemId) {</pre>

<pre> int firstVisiblePosition = getFirstVisiblePosition();</pre>

<pre class="alt"> AbstractDynamicGridAdapter adapter = ((AbstractDynamicGridAdapter) getAdapter());</pre>

<pre> for (int i = 0; i <span class="kwrd">&lt;</span> getChildCount(); i++) {</pre>

<pre class="alt"> View v = getChildAt(i);</pre>

<pre> int position = firstVisiblePosition + i;</pre>

<pre class="alt"> long id = adapter.getItemId(position);</pre>

<pre> if (id == itemId) {</pre>

<pre class="alt"> return v;</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre> return null;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @Override</pre>

<pre> public boolean onTouchEvent(MotionEvent event) {</pre>

<pre class="alt"> switch (event.getAction() &amp; MotionEvent.ACTION_MASK) {</pre>

<pre> /*</pre>

<pre class="alt"> * 记下按下的位置和按下的触点Id,若mIsEditMode已开启,则更新选中的</pre>

<pre> * mMobileItemId和mCellIsMobile,以及邻居Id,这种情况只出现在长按开</pre>

<pre class="alt"> * 启拖拽模式后,在结束的时候没有调用StopEditMode()</pre>

<pre> */</pre>

<pre class="alt"> case MotionEvent.ACTION_DOWN:</pre>

<pre> mDownX = (int) event.getX();</pre>

<pre class="alt"> mDownY = (int) event.getY();</pre>

<pre> mActivePointerId = event.getPointerId(0);</pre>

<pre class="alt">&#160;</pre>

<pre> if (mIsEditMode &amp;&amp; isEnabled()) {</pre>

<pre class="alt"> layoutChildren();</pre>

<pre>&#160;</pre>

<pre class="alt"> mTotalOffsetY = 0;</pre>

<pre> mTotalOffsetX = 0;</pre>

<pre class="alt">&#160;</pre>

<pre> int position = pointToPosition(mDownX, mDownY);</pre>

<pre class="alt"> int itemNum = position - getFirstVisiblePosition();</pre>

<pre> View selectedView = getChildAt(itemNum);</pre>

<pre class="alt"> if (selectedView == null) {</pre>

<pre> return false;</pre>

<pre class="alt"> } else {</pre>

<pre> mMobileItemId = getAdapter().getItemId(position);</pre>

<pre class="alt"> mHoverCell = getAndAddHoverView(selectedView);</pre>

<pre> if (isPostHoneycomb())</pre>

<pre class="alt"> selectedView.setVisibility(View.INVISIBLE);</pre>

<pre> mCellIsMobile = true;</pre>

<pre class="alt"> updateNeighborViewsForId(mMobileItemId);</pre>

<pre> }</pre>

<pre class="alt"> } else if (!isEnabled()) {</pre>

<pre> return false;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> break;</pre>

<pre> /*</pre>

<pre class="alt"> * 核心功能在这里了,</pre>

<pre> * event.findPointerIndex() 关于多点触摸的知识,将在下面做出讲解</pre>

<pre class="alt"> * 更新镜像位置 mHoverCell.setBounds(mHoverCellCurrentBounds); invalidate();</pre>

<pre> * 判断是否满足交换数据的条件 handleCellSwitch();</pre>

<pre class="alt"> * 判断是否Scroll的条件handleMobileCellScroll();</pre>

<pre> */</pre>

<pre class="alt"> case MotionEvent.ACTION_MOVE:</pre>

<pre> if (mActivePointerId == INVALID_ID) {</pre>

<pre class="alt"> break;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> int pointerIndex = event.findPointerIndex(mActivePointerId);</pre>

<pre class="alt">&#160;</pre>

<pre> mLastEventY = (int) event.getY(pointerIndex);</pre>

<pre class="alt"> mLastEventX = (int) event.getX(pointerIndex);</pre>

<pre> int deltaY = mLastEventY - mDownY;</pre>

<pre class="alt"> int deltaX = mLastEventX - mDownX;</pre>

<pre>&#160;</pre>

<pre class="alt"> if (mCellIsMobile) {</pre>

<pre> mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left + deltaX + mTotalOffsetX,</pre>

<pre class="alt"> mHoverCellOriginalBounds.top + deltaY + mTotalOffsetY);</pre>

<pre> mHoverCell.setBounds(mHoverCellCurrentBounds);</pre>

<pre class="alt"> invalidate();</pre>

<pre> handleCellSwitch();</pre>

<pre class="alt"> mIsMobileScrolling = false;</pre>

<pre> handleMobileCellScroll();</pre>

<pre class="alt">&#160;</pre>

<pre> return false;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> break;</pre>

<pre> /*</pre>

<pre class="alt"> * 下面都是结束拖拽,应对不同的情况</pre>

<pre> */</pre>

<pre class="alt"> case MotionEvent.ACTION_UP:</pre>

<pre> touchEventsEnded();</pre>

<pre class="alt"> if (mDropListener != null) {</pre>

<pre> mDropListener.onActionDrop();</pre>

<pre class="alt"> }</pre>

<pre> break;</pre>

<pre class="alt"> case MotionEvent.ACTION_CANCEL:</pre>

<pre> touchEventsCancelled();</pre>

<pre class="alt"> if (mDropListener != null) {</pre>

<pre> mDropListener.onActionDrop();</pre>

<pre class="alt"> }</pre>

<pre> break;</pre>

<pre class="alt"> case MotionEvent.ACTION_POINTER_UP:</pre>

<pre> /* If a multitouch event took place and the original touch dictating</pre>

<pre class="alt"> * the movement of the hover cell has ended, then the dragging event</pre>

<pre> * ends and the hover cell is animated to its corresponding position</pre>

<pre class="alt"> * in the listview. */</pre>

<pre> pointerIndex = (event.getAction() &amp; MotionEvent.ACTION_POINTER_INDEX_MASK) <span class="kwrd">&gt;&gt;</span></pre>

<pre class="alt"> MotionEvent.ACTION_POINTER_INDEX_SHIFT;</pre>

<pre> final int pointerId = event.getPointerId(pointerIndex);</pre>

<pre class="alt"> if (pointerId == mActivePointerId) {</pre>

<pre> touchEventsEnded();</pre>

<pre class="alt"> }</pre>

<pre> break;</pre>

<pre class="alt"> default:</pre>

<pre> break;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> return super.onTouchEvent(event);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private void handleMobileCellScroll() {</pre>

<pre class="alt"> mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> /*</pre>

<pre class="alt"> * computeVerticalScrollOffset计算滑动thumb已滑动的距离</pre>

<pre> * computeVerticalScrollExtent计算thumb的高度</pre>

<pre class="alt"> * computeVerticalScrollRange计算thumb所表示的高度,一般为视图高度</pre>

<pre> */</pre>

<pre class="alt"> public boolean handleMobileCellScroll(Rect r) {</pre>

<pre> int offset = computeVerticalScrollOffset();</pre>

<pre class="alt"> int height = getHeight();</pre>

<pre> int extent = computeVerticalScrollExtent();</pre>

<pre class="alt"> int range = computeVerticalScrollRange();</pre>

<pre> int hoverViewTop = r.top;</pre>

<pre class="alt"> int hoverHeight = r.height();</pre>

<pre>&#160;</pre>

<pre class="alt"> if (hoverViewTop <span class="kwrd">&lt;</span>= 0 &amp;&amp; offset <span class="kwrd">&gt;</span> 0) {</pre>

<pre> smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);</pre>

<pre class="alt"> return true;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> if (hoverViewTop + hoverHeight <span class="kwrd">&gt;</span>= height &amp;&amp; (offset + extent) <span class="kwrd">&lt;</span> range) {</pre>

<pre class="alt"> smoothScrollBy(mSmoothScrollAmountAtEdge, 0);</pre>

<pre> return true;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> return false;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @Override</pre>

<pre class="alt"> public void setAdapter(ListAdapter adapter) {</pre>

<pre> super.setAdapter(adapter);</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> private void touchEventsEnded() {</pre>

<pre> final View mobileView = getViewForId(mMobileItemId);</pre>

<pre class="alt"> if (mCellIsMobile || mIsWaitingForScrollFinish) {</pre>

<pre> mCellIsMobile = false;</pre>

<pre class="alt"> mIsWaitingForScrollFinish = false;</pre>

<pre> mIsMobileScrolling = false;</pre>

<pre class="alt"> mActivePointerId = INVALID_ID;</pre>

<pre>&#160;</pre>

<pre class="alt"> // If the autoscroller has not completed scrolling, we need to wait for it to</pre>

<pre> // finish in order to determine the final location of where the hover cell</pre>

<pre class="alt"> // should be animated to.</pre>

<pre> if (mScrollState != OnScrollListener.SCROLL_STATE_IDLE) {</pre>

<pre class="alt"> mIsWaitingForScrollFinish = true;</pre>

<pre> return;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> mHoverCellCurrentBounds.offsetTo(mobileView.getLeft(), mobileView.getTop());</pre>

<pre>&#160;</pre>

<pre class="alt"> if (Build.VERSION.SDK_INT <span class="kwrd">&gt;</span> Build.VERSION_CODES.HONEYCOMB) {</pre>

<pre> animateBounds(mobileView);</pre>

<pre class="alt"> } else {</pre>

<pre> mHoverCell.setBounds(mHoverCellCurrentBounds);</pre>

<pre class="alt"> invalidate();</pre>

<pre> reset(mobileView);</pre>

<pre class="alt"> }</pre>

<pre> } else {</pre>

<pre class="alt"> touchEventsCancelled();</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre> private void animateBounds(final View mobileView) {</pre>

<pre class="alt"> TypeEvaluator<span class="kwrd">&lt;</span><span class="html">Rect</span><span class="kwrd">&gt;</span> sBoundEvaluator = new TypeEvaluator<span class="kwrd">&lt;</span><span class="html">Rect</span><span class="kwrd">&gt;</span>() {</pre>

<pre> public Rect evaluate(float fraction, Rect startValue, Rect endValue) {</pre>

<pre class="alt"> return new Rect(interpolate(startValue.left, endValue.left, fraction),</pre>

<pre> interpolate(startValue.top, endValue.top, fraction),</pre>

<pre class="alt"> interpolate(startValue.right, endValue.right, fraction),</pre>

<pre> interpolate(startValue.bottom, endValue.bottom, fraction));</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> public int interpolate(int start, int end, float fraction) {</pre>

<pre> return (int) (start + fraction * (end - start));</pre>

<pre class="alt"> }</pre>

<pre> };</pre>

<pre class="alt">&#160;</pre>

<pre>&#160;</pre>

<pre class="alt"> ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, &quot;bounds&quot;,</pre>

<pre> sBoundEvaluator, mHoverCellCurrentBounds);</pre>

<pre class="alt"> hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {</pre>

<pre> @Override</pre>

<pre class="alt"> public void onAnimationUpdate(ValueAnimator valueAnimator) {</pre>

<pre> invalidate();</pre>

<pre class="alt"> }</pre>

<pre> });</pre>

<pre class="alt"> hoverViewAnimator.addListener(new AnimatorListenerAdapter() {</pre>

<pre> @Override</pre>

<pre class="alt"> public void onAnimationStart(Animator animation) {</pre>

<pre> mHoverAnimation = true;</pre>

<pre class="alt"> updateEnableState();</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @Override</pre>

<pre class="alt"> public void onAnimationEnd(Animator animation) {</pre>

<pre> mHoverAnimation = false;</pre>

<pre class="alt"> updateEnableState();</pre>

<pre> reset(mobileView);</pre>

<pre class="alt"> }</pre>

<pre> });</pre>

<pre class="alt"> hoverViewAnimator.start();</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private void reset(View mobileView) {</pre>

<pre class="alt"> idList.clear();</pre>

<pre> mMobileItemId = INVALID_ID;</pre>

<pre class="alt"> mobileView.setVisibility(View.VISIBLE);</pre>

<pre> mHoverCell = null;</pre>

<pre class="alt"> if (!mIsEditMode &amp;&amp; isPostHoneycomb() &amp;&amp; mWobbleInEditMode)</pre>

<pre> stopWobble(true);</pre>

<pre class="alt"> if (mIsEditMode &amp;&amp; isPostHoneycomb() &amp;&amp; mWobbleInEditMode)</pre>

<pre> restartWobble();</pre>

<pre class="alt"> invalidate();</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private void updateEnableState() {</pre>

<pre class="alt"> setEnabled(!mHoverAnimation &amp;&amp; !mReorderAnimation);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> /**</pre>

<pre class="alt"> * Seems that GridView before HONEYCOMB not support stable id in proper way.</pre>

<pre> * That cause bugs on view recycle if we will animate or change visibility state for items.</pre>

<pre class="alt"> *</pre>

<pre> * @return</pre>

<pre class="alt"> */</pre>

<pre> private boolean isPostHoneycomb() {</pre>

<pre class="alt"> return Build.VERSION.SDK_INT <span class="kwrd">&gt;</span>= Build.VERSION_CODES.HONEYCOMB;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private void touchEventsCancelled() {</pre>

<pre class="alt"> View mobileView = getViewForId(mMobileItemId);</pre>

<pre> if (mCellIsMobile) {</pre>

<pre class="alt"> reset(mobileView);</pre>

<pre> }</pre>

<pre class="alt"> mCellIsMobile = false;</pre>

<pre> mIsMobileScrolling = false;</pre>

<pre class="alt"> mActivePointerId = INVALID_ID;</pre>

<pre>&#160;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> /*</pre>

<pre> * 处理数据交换</pre>

<pre class="alt"> */</pre>

<pre> private void handleCellSwitch() {</pre>

<pre class="alt"> </pre>

<pre> final int deltaY = mLastEventY - mDownY;</pre>

<pre class="alt"> final int deltaX = mLastEventX - mDownX;</pre>

<pre> //获取镜像的中心坐标</pre>

<pre class="alt"> final int deltaYTotal = mHoverCellOriginalBounds.centerY() + mTotalOffsetY + deltaY;</pre>

<pre> final int deltaXTotal = mHoverCellOriginalBounds.centerX() + mTotalOffsetX + deltaX;</pre>

<pre class="alt"> View mobileView = getViewForId(mMobileItemId);</pre>

<pre> View targetView = null;</pre>

<pre class="alt"> float vX = 0;</pre>

<pre> float vY = 0;</pre>

<pre class="alt"> //以下的工作便是判断是否满足改变数据位置的条件</pre>

<pre> Point mobileColumnRowPair = getColumnAndRowForView(mobileView);</pre>

<pre class="alt"> for (Long id : idList) {</pre>

<pre> View view = getViewForId(id);</pre>

<pre class="alt"> if (view != null) {</pre>

<pre> Point targetColumnRowPair = getColumnAndRowForView(view);</pre>

<pre class="alt"> if ((aboveRight(targetColumnRowPair, mobileColumnRowPair)</pre>

<pre> &amp;&amp; deltaYTotal <span class="kwrd">&lt;</span> view.getBottom() &amp;&amp; deltaXTotal <span class="kwrd">&gt;</span> view.getLeft()</pre>

<pre class="alt"> || aboveLeft(targetColumnRowPair, mobileColumnRowPair)</pre>

<pre> &amp;&amp; deltaYTotal <span class="kwrd">&lt;</span> view.getBottom() &amp;&amp; deltaXTotal <span class="kwrd">&lt;</span> view.getRight()</pre>

<pre class="alt"> || belowRight(targetColumnRowPair, mobileColumnRowPair)</pre>

<pre> &amp;&amp; deltaYTotal <span class="kwrd">&gt;</span> view.getTop() &amp;&amp; deltaXTotal <span class="kwrd">&gt;</span> view.getLeft()</pre>

<pre class="alt"> || belowLeft(targetColumnRowPair, mobileColumnRowPair)</pre>

<pre> &amp;&amp; deltaYTotal <span class="kwrd">&gt;</span> view.getTop() &amp;&amp; deltaXTotal <span class="kwrd">&lt;</span> view.getRight()</pre>

<pre class="alt"> || above(targetColumnRowPair, mobileColumnRowPair) &amp;&amp; deltaYTotal <span class="kwrd">&lt;</span> view.getBottom())</pre>

<pre> || below(targetColumnRowPair, mobileColumnRowPair) &amp;&amp; deltaYTotal <span class="kwrd">&gt;</span> view.getTop()</pre>

<pre class="alt"> || right(targetColumnRowPair, mobileColumnRowPair) &amp;&amp; deltaXTotal <span class="kwrd">&gt;</span> view.getLeft()</pre>

<pre> || left(targetColumnRowPair, mobileColumnRowPair) &amp;&amp; deltaXTotal <span class="kwrd">&lt;</span> view.getRight()) {</pre>

<pre class="alt"> float xDiff = Math.abs(DynamicGridUtils.getViewX(view) - DynamicGridUtils.getViewX(mobileView));</pre>

<pre> float yDiff = Math.abs(DynamicGridUtils.getViewY(view) - DynamicGridUtils.getViewY(mobileView));</pre>

<pre class="alt"> if (xDiff <span class="kwrd">&gt;</span>= vX &amp;&amp; yDiff <span class="kwrd">&gt;</span>= vY) {</pre>

<pre> vX = xDiff;</pre>

<pre class="alt"> vY = yDiff;</pre>

<pre> targetView = view;</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt"> if (targetView != null) {</pre>

<pre> final int originalPosition = getPositionForView(mobileView);</pre>

<pre class="alt"> int targetPosition = getPositionForView(targetView);</pre>

<pre>&#160;</pre>

<pre class="alt"> if (targetPosition == INVALID_POSITION) {</pre>

<pre> updateNeighborViewsForId(mMobileItemId);</pre>

<pre class="alt"> return;</pre>

<pre> }</pre>

<pre class="alt"> //改变数据位置</pre>

<pre> reorderElements(originalPosition, targetPosition);</pre>

<pre class="alt"> mDownY = mLastEventY;</pre>

<pre> mDownX = mLastEventX;</pre>

<pre class="alt"> mobileView.setVisibility(View.VISIBLE);</pre>

<pre> if (isPostHoneycomb()) {</pre>

<pre class="alt"> targetView.setVisibility(View.INVISIBLE);</pre>

<pre> }</pre>

<pre class="alt"> updateNeighborViewsForId(mMobileItemId);</pre>

<pre> final ViewTreeObserver observer = getViewTreeObserver();</pre>

<pre class="alt"> final int finalTargetPosition = targetPosition;</pre>

<pre> if (isPostHoneycomb() &amp;&amp; observer != null) {</pre>

<pre class="alt"> observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {</pre>

<pre> @Override</pre>

<pre class="alt"> public boolean onPreDraw() {</pre>

<pre> observer.removeOnPreDrawListener(this);</pre>

<pre class="alt"> mTotalOffsetY += deltaY;</pre>

<pre> mTotalOffsetX += deltaX;</pre>

<pre class="alt"> animateReorder(originalPosition, finalTargetPosition);</pre>

<pre> return true;</pre>

<pre class="alt"> }</pre>

<pre> });</pre>

<pre class="alt"> } else {</pre>

<pre> mTotalOffsetY += deltaY;</pre>

<pre class="alt"> mTotalOffsetX += deltaX;</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean belowLeft(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">&gt;</span> mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x <span class="kwrd">&lt;</span> mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean belowRight(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">&gt;</span> mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x <span class="kwrd">&gt;</span> mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean aboveLeft(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">&lt;</span> mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x <span class="kwrd">&lt;</span> mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean aboveRight(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">&lt;</span> mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x <span class="kwrd">&gt;</span> mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean above(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">&lt;</span> mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x == mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean below(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">&gt;</span> mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x == mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean right(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y == mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x <span class="kwrd">&gt;</span> mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private boolean left(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>

<pre class="alt"> return targetColumnRowPair.y == mobileColumnRowPair.y &amp;&amp; targetColumnRowPair.x <span class="kwrd">&lt;</span> mobileColumnRowPair.x;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private Point getColumnAndRowForView(View view) {</pre>

<pre class="alt"> int pos = getPositionForView(view);</pre>

<pre> int columns = getColumnCount();</pre>

<pre class="alt"> int column = pos % columns;</pre>

<pre> int row = pos / columns;</pre>

<pre class="alt"> return new Point(column, row);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> private long getId(int position) {</pre>

<pre class="alt"> return getAdapter().getItemId(position);</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre class="alt"> private void animateReorder(final int oldPosition, final int newPosition) {</pre>

<pre> boolean isForward = newPosition <span class="kwrd">&gt;</span> oldPosition;</pre>

<pre class="alt"> List<span class="kwrd">&lt;</span><span class="html">Animator</span><span class="kwrd">&gt;</span> resultList = new LinkedList<span class="kwrd">&lt;</span><span class="html">Animator</span><span class="kwrd">&gt;</span>();</pre>

<pre> if (isForward) {</pre>

<pre class="alt"> for (int pos = Math.min(oldPosition, newPosition); pos <span class="kwrd">&lt;</span> Math.max(oldPosition, newPosition); pos++) {</pre>

<pre> View view = getViewForId(getId(pos));</pre>

<pre class="alt"> if ((pos + 1) % getColumnCount() == 0) {</pre>

<pre> resultList.add(createTranslationAnimations(view, -view.getWidth() * (getColumnCount() - 1), 0, view.getHeight(), 0));</pre>

<pre class="alt"> } else {</pre>

<pre> resultList.add(createTranslationAnimations(view, view.getWidth(), 0, 0, 0));</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt"> } else {</pre>

<pre> for (int pos = Math.max(oldPosition, newPosition); pos <span class="kwrd">&gt;</span> Math.min(oldPosition, newPosition); pos--) {</pre>

<pre class="alt"> View view = getViewForId(getId(pos));</pre>

<pre> if ((pos + getColumnCount()) % getColumnCount() == 0) {</pre>

<pre class="alt"> resultList.add(createTranslationAnimations(view, view.getWidth() * (getColumnCount() - 1), 0, -view.getHeight(), 0));</pre>

<pre> } else {</pre>

<pre class="alt"> resultList.add(createTranslationAnimations(view, -view.getWidth(), 0, 0, 0));</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> AnimatorSet resultSet = new AnimatorSet();</pre>

<pre class="alt"> resultSet.playTogether(resultList);</pre>

<pre> resultSet.setDuration(MOVE_DURATION);</pre>

<pre class="alt"> resultSet.setInterpolator(new AccelerateDecelerateInterpolator());</pre>

<pre> resultSet.addListener(new AnimatorListenerAdapter() {</pre>

<pre class="alt"> @Override</pre>

<pre> public void onAnimationStart(Animator animation) {</pre>

<pre class="alt"> mReorderAnimation = true;</pre>

<pre> updateEnableState();</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @Override</pre>

<pre> public void onAnimationEnd(Animator animation) {</pre>

<pre class="alt"> mReorderAnimation = false;</pre>

<pre> updateEnableState();</pre>

<pre class="alt"> }</pre>

<pre> });</pre>

<pre class="alt"> resultSet.start();</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre>&#160;</pre>

<pre class="alt"> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre> private AnimatorSet createTranslationAnimations(View view, float startX, float endX, float startY, float endY) {</pre>

<pre class="alt"> ObjectAnimator animX = ObjectAnimator.ofFloat(view, &quot;translationX&quot;, startX, endX);</pre>

<pre> ObjectAnimator animY = ObjectAnimator.ofFloat(view, &quot;translationY&quot;, startY, endY);</pre>

<pre class="alt"> AnimatorSet animSetXY = new AnimatorSet();</pre>

<pre> animSetXY.playTogether(animX, animY);</pre>

<pre class="alt"> return animSetXY;</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @Override</pre>

<pre class="alt"> protected void dispatchDraw(Canvas canvas) {</pre>

<pre> super.dispatchDraw(canvas);</pre>

<pre class="alt"> if (mHoverCell != null) {</pre>

<pre> mHoverCell.draw(canvas);</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> /**</pre>

<pre class="alt"> * Interface provide callback for end of drag'n'drop event</pre>

<pre> */</pre>

<pre class="alt"> public interface OnDropListener {</pre>

<pre> /**</pre>

<pre class="alt"> * called when view been dropped</pre>

<pre> */</pre>

<pre class="alt"> void onActionDrop();</pre>

<pre>&#160;</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt">&#160;</pre>

<pre> /**</pre>

<pre class="alt"> * This scroll listener is added to the gridview in order to handle cell swapping</pre>

<pre> * when the cell is either at the top or bottom edge of the gridview. If the hover</pre>

<pre class="alt"> * cell is at either edge of the gridview, the gridview will begin scrolling. As</pre>

<pre> * scrolling takes place, the gridview continuously checks if new cells became visible</pre>

<pre class="alt"> * and determines whether they are potential candidates for a cell swap.</pre>

<pre> */</pre>

<pre class="alt"> private OnScrollListener mScrollListener = new OnScrollListener() {</pre>

<pre>&#160;</pre>

<pre class="alt"> private int mPreviousFirstVisibleItem = -1;</pre>

<pre> private int mPreviousVisibleItemCount = -1;</pre>

<pre class="alt"> private int mCurrentFirstVisibleItem;</pre>

<pre> private int mCurrentVisibleItemCount;</pre>

<pre class="alt"> private int mCurrentScrollState;</pre>

<pre>&#160;</pre>

<pre class="alt"> public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,</pre>

<pre> int totalItemCount) {</pre>

<pre class="alt"> mCurrentFirstVisibleItem = firstVisibleItem;</pre>

<pre> mCurrentVisibleItemCount = visibleItemCount;</pre>

<pre class="alt">&#160;</pre>

<pre> mPreviousFirstVisibleItem = (mPreviousFirstVisibleItem == -1) ? mCurrentFirstVisibleItem</pre>

<pre class="alt"> : mPreviousFirstVisibleItem;</pre>

<pre> mPreviousVisibleItemCount = (mPreviousVisibleItemCount == -1) ? mCurrentVisibleItemCount</pre>

<pre class="alt"> : mPreviousVisibleItemCount;</pre>

<pre>&#160;</pre>

<pre class="alt"> checkAndHandleFirstVisibleCellChange();</pre>

<pre> checkAndHandleLastVisibleCellChange();</pre>

<pre class="alt">&#160;</pre>

<pre> mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;</pre>

<pre class="alt"> mPreviousVisibleItemCount = mCurrentVisibleItemCount;</pre>

<pre> if (isPostHoneycomb() &amp;&amp; mWobbleInEditMode) {</pre>

<pre class="alt"> updateWobbleState(visibleItemCount);</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> @TargetApi(Build.VERSION_CODES.HONEYCOMB)</pre>

<pre> private void updateWobbleState(int visibleItemCount) {</pre>

<pre class="alt"> for (int i = 0; i <span class="kwrd">&lt;</span> visibleItemCount; i++) {</pre>

<pre> View child = getChildAt(i);</pre>

<pre class="alt">&#160;</pre>

<pre> if (child != null) {</pre>

<pre class="alt"> if (mMobileItemId != INVALID_ID &amp;&amp; Boolean.TRUE != child.getTag(R.id.dynamic_grid_wobble_tag)) {</pre>

<pre> if (i % 2 == 0)</pre>

<pre class="alt"> animateWobble(child);</pre>

<pre> else</pre>

<pre class="alt"> animateWobbleInverse(child);</pre>

<pre> child.setTag(R.id.dynamic_grid_wobble_tag, true);</pre>

<pre class="alt"> } else if (mMobileItemId == INVALID_ID &amp;&amp; child.getRotation() != 0) {</pre>

<pre> child.setRotation(0);</pre>

<pre class="alt"> child.setTag(R.id.dynamic_grid_wobble_tag, false);</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt">&#160;</pre>

<pre> @Override</pre>

<pre class="alt"> public void onScrollStateChanged(AbsListView view, int scrollState) {</pre>

<pre> mCurrentScrollState = scrollState;</pre>

<pre class="alt"> mScrollState = scrollState;</pre>

<pre> isScrollCompleted();</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> /**</pre>

<pre> * This method is in charge of invoking 1 of 2 actions. Firstly, if the gridview</pre>

<pre class="alt"> * is in a state of scrolling invoked by the hover cell being outside the bounds</pre>

<pre> * of the gridview, then this scrolling event is continued. Secondly, if the hover</pre>

<pre class="alt"> * cell has already been released, this invokes the animation for the hover cell</pre>

<pre> * to return to its correct position after the gridview has entered an idle scroll</pre>

<pre class="alt"> * state.</pre>

<pre> */</pre>

<pre class="alt"> private void isScrollCompleted() {</pre>

<pre> if (mCurrentVisibleItemCount <span class="kwrd">&gt;</span> 0 &amp;&amp; mCurrentScrollState == SCROLL_STATE_IDLE) {</pre>

<pre class="alt"> if (mCellIsMobile &amp;&amp; mIsMobileScrolling) {</pre>

<pre> handleMobileCellScroll();</pre>

<pre class="alt"> } else if (mIsWaitingForScrollFinish) {</pre>

<pre> touchEventsEnded();</pre>

<pre class="alt"> }</pre>

<pre> }</pre>

<pre class="alt"> }</pre>

<pre>&#160;</pre>

<pre class="alt"> /**</pre>

<pre> * Determines if the gridview scrolled up enough to reveal a new cell at the</pre>

<pre class="alt"> * top of the list. If so, then the appropriate parameters are updated.</pre>

<pre> */</pre>

<pre class="alt"> </pre>

</div> <style type="text/css"> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }</style>

转载于:https://my.oschina.net/speedinghzl/blog/219787

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值