<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> </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>  <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>css
<pre> </pre>html
<pre class="alt">import android.animation.*;</pre>java
<pre>import android.annotation.TargetApi;</pre>android
<pre class="alt">import android.content.Context;</pre>canvas
<pre>import android.graphics.Bitmap;</pre>windows
<pre class="alt">import android.graphics.Canvas;</pre>app
<pre>import android.graphics.Point;</pre>ide
<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> </pre>
<pre class="alt">import java.util.ArrayList;</pre>
<pre>import java.util.LinkedList;</pre>
<pre class="alt">import java.util.List;</pre>
<pre> </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"><</span><span class="html">Long</span><span class="kwrd">></span> idList = new ArrayList<span class="kwrd"><</span><span class="html">Long</span><span class="kwrd">></span>();</pre>
<pre class="alt"> </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"> </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> </pre>
<pre class="alt"> private boolean mIsEditMode = false;</pre>
<pre> private List<span class="kwrd"><</span><span class="html">ObjectAnimator</span><span class="kwrd">></span> mWobbleAnimators = new LinkedList<span class="kwrd"><</span><span class="html">ObjectAnimator</span><span class="kwrd">></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"> </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"><?></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> </pre>
<pre class="alt"> int position = pointToPosition(mDownX, mDownY);</pre>
<pre> int itemNum = position - getFirstVisiblePosition();</pre>
<pre class="alt"> </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() && selectedView != null)</pre>
<pre> selectedView.setVisibility(View.INVISIBLE);</pre>
<pre class="alt"> </pre>
<pre> mCellIsMobile = true;</pre>
<pre class="alt"> //將當前Gridview可見的item所對應的數據的Id記爲鄰居</pre>
<pre> updateNeighborViewsForId(mMobileItemId);</pre>
<pre class="alt"> </pre>
<pre> if (isPostHoneycomb() && mWobbleInEditMode)</pre>
<pre class="alt"> startWobbleAnimation();</pre>
<pre> </pre>
<pre class="alt"> if (mUserLongClickListener != null)</pre>
<pre> mUserLongClickListener.onItemLongClick(arg0, arg1, pos, id);</pre>
<pre class="alt"> </pre>
<pre> mIsEditMode = true;</pre>
<pre class="alt"> </pre>
<pre> return true;</pre>
<pre class="alt"> }</pre>
<pre> };</pre>
<pre class="alt"> </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"><?></span> parent, View view, int position, long id) {</pre>
<pre> if (!isEditMode() && isEnabled() && mUserItemClickListener != null) {</pre>
<pre class="alt"> mUserItemClickListener.onItemClick(parent, view, position, id);</pre>
<pre> }</pre>
<pre class="alt"> }</pre>
<pre> };</pre>
<pre class="alt"> </pre>
<pre> public DynamicGridView(Context context) {</pre>
<pre class="alt"> super(context);</pre>
<pre> init(context);</pre>
<pre class="alt"> }</pre>
<pre> </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"> </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> </pre>
<pre class="alt"> public void setOnDropListener(OnDropListener dropListener) {</pre>
<pre> this.mDropListener = dropListener;</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> public void startEditMode() {</pre>
<pre> mIsEditMode = true;</pre>
<pre class="alt"> if (isPostHoneycomb() && mWobbleInEditMode)</pre>
<pre> startWobbleAnimation();</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> public void stopEditMode() {</pre>
<pre> mIsEditMode = false;</pre>
<pre class="alt"> if (isPostHoneycomb() && mWobbleInEditMode)</pre>
<pre> stopWobble(true);</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> public boolean isEditMode() {</pre>
<pre> return mIsEditMode;</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> public boolean isWobbleInEditMode() {</pre>
<pre> return mWobbleInEditMode;</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> public void setWobbleInEditMode(boolean wobbleInEditMode) {</pre>
<pre> this.mWobbleInEditMode = wobbleInEditMode;</pre>
<pre class="alt"> }</pre>
<pre> </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> </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> </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"><</span> getChildCount(); i++) {</pre>
<pre> View v = getChildAt(i);</pre>
<pre class="alt"> if (v != null && 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> </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"><</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"> </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"> </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"> </pre>
<pre> }</pre>
<pre class="alt"> </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"> </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"> </pre>
<pre> </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("rotation");</pre>
<pre> animator.setTarget(v);</pre>
<pre class="alt"> return animator;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> </pre>
<pre class="alt"> private void reorderElements(int originalPosition, int targetPosition) {</pre>
<pre> getAdapterInterface().reorderItems(originalPosition, targetPosition);</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> private int getColumnCount() {</pre>
<pre> return getAdapterInterface().getColumnCount();</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> private AbstractDynamicGridAdapter getAdapterInterface() {</pre>
<pre> return ((AbstractDynamicGridAdapter) getAdapter());</pre>
<pre class="alt"> }</pre>
<pre> </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"> </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> </pre>
<pre class="alt"> Bitmap b = getBitmapFromView(v);</pre>
<pre> </pre>
<pre class="alt"> BitmapDrawable drawable = new BitmapDrawable(getResources(), b);</pre>
<pre> </pre>
<pre class="alt"> mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);</pre>
<pre> mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);</pre>
<pre class="alt"> </pre>
<pre> drawable.setBounds(mHoverCellCurrentBounds);</pre>
<pre class="alt"> </pre>
<pre> return drawable;</pre>
<pre class="alt"> }</pre>
<pre> </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> </pre>
<pre class="alt"> </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"><</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> </pre>
<pre class="alt"> /**</pre>
<pre> * Retrieves the position in the grid corresponding to <span class="kwrd"><</span><span class="html">code</span><span class="kwrd">></span>itemId<span class="kwrd"></</span><span class="html">code</span><span class="kwrd">></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> </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"><</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> </pre>
<pre class="alt"> @Override</pre>
<pre> public boolean onTouchEvent(MotionEvent event) {</pre>
<pre class="alt"> switch (event.getAction() & 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"> </pre>
<pre> if (mIsEditMode && isEnabled()) {</pre>
<pre class="alt"> layoutChildren();</pre>
<pre> </pre>
<pre class="alt"> mTotalOffsetY = 0;</pre>
<pre> mTotalOffsetX = 0;</pre>
<pre class="alt"> </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> </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"> </pre>
<pre> int pointerIndex = event.findPointerIndex(mActivePointerId);</pre>
<pre class="alt"> </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> </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"> </pre>
<pre> return false;</pre>
<pre class="alt"> }</pre>
<pre> </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() & MotionEvent.ACTION_POINTER_INDEX_MASK) <span class="kwrd">>></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> </pre>
<pre class="alt"> return super.onTouchEvent(event);</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private void handleMobileCellScroll() {</pre>
<pre class="alt"> mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);</pre>
<pre> }</pre>
<pre class="alt"> </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> </pre>
<pre class="alt"> if (hoverViewTop <span class="kwrd"><</span>= 0 && offset <span class="kwrd">></span> 0) {</pre>
<pre> smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);</pre>
<pre class="alt"> return true;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> if (hoverViewTop + hoverHeight <span class="kwrd">></span>= height && (offset + extent) <span class="kwrd"><</span> range) {</pre>
<pre class="alt"> smoothScrollBy(mSmoothScrollAmountAtEdge, 0);</pre>
<pre> return true;</pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> return false;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> @Override</pre>
<pre class="alt"> public void setAdapter(ListAdapter adapter) {</pre>
<pre> super.setAdapter(adapter);</pre>
<pre class="alt"> }</pre>
<pre> </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> </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> </pre>
<pre class="alt"> mHoverCellCurrentBounds.offsetTo(mobileView.getLeft(), mobileView.getTop());</pre>
<pre> </pre>
<pre class="alt"> if (Build.VERSION.SDK_INT <span class="kwrd">></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> </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"><</span><span class="html">Rect</span><span class="kwrd">></span> sBoundEvaluator = new TypeEvaluator<span class="kwrd"><</span><span class="html">Rect</span><span class="kwrd">></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> </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"> </pre>
<pre> </pre>
<pre class="alt"> ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, "bounds",</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"> </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"> </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 && isPostHoneycomb() && mWobbleInEditMode)</pre>
<pre> stopWobble(true);</pre>
<pre class="alt"> if (mIsEditMode && isPostHoneycomb() && mWobbleInEditMode)</pre>
<pre> restartWobble();</pre>
<pre class="alt"> invalidate();</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private void updateEnableState() {</pre>
<pre class="alt"> setEnabled(!mHoverAnimation && !mReorderAnimation);</pre>
<pre> }</pre>
<pre class="alt"> </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">></span>= Build.VERSION_CODES.HONEYCOMB;</pre>
<pre> }</pre>
<pre class="alt"> </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> </pre>
<pre class="alt"> }</pre>
<pre> </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> && deltaYTotal <span class="kwrd"><</span> view.getBottom() && deltaXTotal <span class="kwrd">></span> view.getLeft()</pre>
<pre class="alt"> || aboveLeft(targetColumnRowPair, mobileColumnRowPair)</pre>
<pre> && deltaYTotal <span class="kwrd"><</span> view.getBottom() && deltaXTotal <span class="kwrd"><</span> view.getRight()</pre>
<pre class="alt"> || belowRight(targetColumnRowPair, mobileColumnRowPair)</pre>
<pre> && deltaYTotal <span class="kwrd">></span> view.getTop() && deltaXTotal <span class="kwrd">></span> view.getLeft()</pre>
<pre class="alt"> || belowLeft(targetColumnRowPair, mobileColumnRowPair)</pre>
<pre> && deltaYTotal <span class="kwrd">></span> view.getTop() && deltaXTotal <span class="kwrd"><</span> view.getRight()</pre>
<pre class="alt"> || above(targetColumnRowPair, mobileColumnRowPair) && deltaYTotal <span class="kwrd"><</span> view.getBottom())</pre>
<pre> || below(targetColumnRowPair, mobileColumnRowPair) && deltaYTotal <span class="kwrd">></span> view.getTop()</pre>
<pre class="alt"> || right(targetColumnRowPair, mobileColumnRowPair) && deltaXTotal <span class="kwrd">></span> view.getLeft()</pre>
<pre> || left(targetColumnRowPair, mobileColumnRowPair) && deltaXTotal <span class="kwrd"><</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">></span>= vX && yDiff <span class="kwrd">></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> </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() && 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"> </pre>
<pre> private boolean belowLeft(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">></span> mobileColumnRowPair.y && targetColumnRowPair.x <span class="kwrd"><</span> mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private boolean belowRight(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">></span> mobileColumnRowPair.y && targetColumnRowPair.x <span class="kwrd">></span> mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private boolean aboveLeft(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y <span class="kwrd"><</span> mobileColumnRowPair.y && targetColumnRowPair.x <span class="kwrd"><</span> mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private boolean aboveRight(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y <span class="kwrd"><</span> mobileColumnRowPair.y && targetColumnRowPair.x <span class="kwrd">></span> mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private boolean above(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y <span class="kwrd"><</span> mobileColumnRowPair.y && targetColumnRowPair.x == mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private boolean below(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y <span class="kwrd">></span> mobileColumnRowPair.y && targetColumnRowPair.x == mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private boolean right(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y == mobileColumnRowPair.y && targetColumnRowPair.x <span class="kwrd">></span> mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </pre>
<pre> private boolean left(Point targetColumnRowPair, Point mobileColumnRowPair) {</pre>
<pre class="alt"> return targetColumnRowPair.y == mobileColumnRowPair.y && targetColumnRowPair.x <span class="kwrd"><</span> mobileColumnRowPair.x;</pre>
<pre> }</pre>
<pre class="alt"> </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"> </pre>
<pre> private long getId(int position) {</pre>
<pre class="alt"> return getAdapter().getItemId(position);</pre>
<pre> }</pre>
<pre class="alt"> </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">></span> oldPosition;</pre>
<pre class="alt"> List<span class="kwrd"><</span><span class="html">Animator</span><span class="kwrd">></span> resultList = new LinkedList<span class="kwrd"><</span><span class="html">Animator</span><span class="kwrd">></span>();</pre>
<pre> if (isForward) {</pre>
<pre class="alt"> for (int pos = Math.min(oldPosition, newPosition); pos <span class="kwrd"><</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">></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"> </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> </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"> </pre>
<pre> </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, "translationX", startX, endX);</pre>
<pre> ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY", 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"> </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"> </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> </pre>
<pre class="alt"> }</pre>
<pre> </pre>
<pre class="alt"> </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> </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> </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"> </pre>
<pre> mPreviousFirstVisibleItem = (mPreviousFirstVisibleItem == -1) ? mCurrentFirstVisibleItem</pre>
<pre class="alt"> : mPreviousFirstVisibleItem;</pre>
<pre> mPreviousVisibleItemCount = (mPreviousVisibleItemCount == -1) ? mCurrentVisibleItemCount</pre>
<pre class="alt"> : mPreviousVisibleItemCount;</pre>
<pre> </pre>
<pre class="alt"> checkAndHandleFirstVisibleCellChange();</pre>
<pre> checkAndHandleLastVisibleCellChange();</pre>
<pre class="alt"> </pre>
<pre> mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;</pre>
<pre class="alt"> mPreviousVisibleItemCount = mCurrentVisibleItemCount;</pre>
<pre> if (isPostHoneycomb() && mWobbleInEditMode) {</pre>
<pre class="alt"> updateWobbleState(visibleItemCount);</pre>
<pre> }</pre>
<pre class="alt"> }</pre>
<pre> </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"><</span> visibleItemCount; i++) {</pre>
<pre> View child = getChildAt(i);</pre>
<pre class="alt"> </pre>
<pre> if (child != null) {</pre>
<pre class="alt"> if (mMobileItemId != INVALID_ID && 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 && 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> </pre>
<pre class="alt"> }</pre>
<pre> }</pre>
<pre class="alt"> </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> </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">></span> 0 && mCurrentScrollState == SCROLL_STATE_IDLE) {</pre>
<pre class="alt"> if (mCellIsMobile && 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> </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>