package org.tadpole.widget; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.tadpole.adapter.IDragGridAdapter; import org.tadpole.app.BoardPageItem; import org.tadpole.app.R; import org.tadpole.common.TLog; import org.w3c.dom.ls.LSException; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.TranslateAnimation; import android.widget.AdapterView; import android.widget.GridView; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.TextView; /** * * a gridview that item can be drag for sorting * * <br>========================== * <br> author:Zenip * <br> email:lxyczh@gmail.com * <br> create:2012-12-30下午8:55:52 * <br>========================== */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class DragGridView extends GridView { public static final int EVENT_START_DRAG = -1; public static final int EVENT_END_DRAG = -2; public static final int EVENT_SLIDING_PAGE = 0; public static final int PAGE_UNKNOWN = 5; private static final String TAG = "DragGridView"; private int dragPosition = AdapterView.INVALID_POSITION; private int dropPosition = AdapterView.INVALID_POSITION; private boolean mIsDragViewFromMe; private ArrayList<Runnable> runnableList = new ArrayList<Runnable>(); Animation AtoB, BtoA, DelDone; int stopCount = 0; private G_PageListener pageListener; private G_ItemChangeListener itemListener; private IDragGridAdapter mDragGridAdapter; private boolean isSwapAniRunning = false; private int moveAnimationCount = 0; private int mNumColumns; private int lastHitPostion = AdapterView.INVALID_POSITION; private int viewTag[] = new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }; private OnItemClickListener mItemClickListener; public DragGridView(Context context, AttributeSet attrs) { super(context, attrs); } public DragGridView(Context context) { super(context); } @Override @Deprecated public void setAdapter(ListAdapter adapter) { throw new RuntimeException("setDragGridAdapter(IDragAdapter adapter); must be called instead this"); } public void setDragGridAdapter(IDragGridAdapter adapter) { mDragGridAdapter = adapter; super.setAdapter(adapter); } protected void performLongClick(int x, int y) { int postion = pointToPosition((int) x, (int) y); if (postion != AdapterView.INVALID_POSITION) { TLog.debug(TAG, "onItemLongClick", ""); PagedView pagedView = getParentPagedView(); if (pagedView != null) { mIsDragViewFromMe = true; Configure.draggingPostion = postion; Configure.draggingPage = Configure.currentPage; dragPosition = dropPosition = postion; if (dragPosition == AdapterView.INVALID_POSITION) { } pagedView.startDrag(x, y, postion); IDragGridAdapter adapter = getDragGridAdapterByPage(Configure.currentPage); Configure.draggingItem = (BoardPageItem) adapter.getItem(dragPosition); Configure.draggingItem.hide = true; } } } public IDragGridAdapter getDragGridAdapter() { return mDragGridAdapter; } public DragGridView getDragGridView(int page) { View parentView = (View) DragGridView.this.getParent(); System.out.println("DragGridView.this.getParent() = " + DragGridView.this.getParent()); if (parentView != null) { PagedView pagedView = (PagedView) getParentPagedView(); View pageV = pagedView.getChildAt(page); DragGridView gridview = (DragGridView) pageV.findViewById(org.tadpole.app.R.id.grid_view_board_page); return gridview; } return null; } public IDragGridAdapter getDragGridAdapterByPage(int page) { DragGridView gridview = getDragGridView(page); return gridview.getDragGridAdapter(); } public PagedView getParentPagedView() { PagedView pagedView = null; View parentView = (View) DragGridView.this.getParent(); System.out.println("DragGridView.this.getParent() = " + DragGridView.this.getParent()); if (parentView != null) { pagedView = (PagedView) parentView.getParent().getParent(); } return pagedView; } /** * @description if gridview is editting, disable on item click listener */ @Override public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener listener) { mItemClickListener = listener; super.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (Configure.isEditMode == false) { mItemClickListener.onItemClick(parent, view, position, id); } } }); } /** * tranform @PagedView's motionEvent to @DragGridView motionEvent * and dispatch it in order to making click event available * * @param event * @return */ public boolean onParentTouchEvent(final MotionEvent event) { MotionEvent gridEvent = MotionEvent.obtain(event); TLog.debug(TAG, "onParentTouchEvent gridEvent = %s", gridEvent.toString()); gridEvent.setLocation(event.getX() - getDragGridOffsetLeft(), event.getY() - getYDragGridOffsetTop()); setupLongClickListener(gridEvent); this.dispatchTouchEvent(gridEvent); return true; } /** * 自定义长按事件 * * @param ev * @return */ public boolean onParentInterceptTouchEvent(final MotionEvent event) { Log.d(TAG, "onParentInterceptTouchEvent ev.getAction() = " + event.getAction() + ", isDragging = " + Configure.isDragging); return true; } public boolean setupLongClickListener(final MotionEvent event) { this.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { performLongClick((int) event.getX(), (int) event.getY()); return false; }; }); return true; } public int getYDragGridOffsetTop() { return this.getTop() + ((View) this.getParent()).getTop(); } public int getDragGridOffsetLeft() { return this.getLeft() + ((View) this.getParent()).getLeft(); } public boolean isMoveToNext(View iv_drag, int pageX) { return (pageX >= Configure.screenWidth - (iv_drag.getWidth() / 2)) && (pageX <= Configure.screenWidth) && !Configure.isChangingPage; } public boolean isMoveToPrev(View iv_drag, int pageX) { return (pageX >= 0) && (pageX <= 0 + iv_drag.getWidth() / 2) && !Configure.isChangingPage; } public void onDrag(View iv_drag, int pageX, int pageY, int gridX, int gridY) { if (isMoveToNext(iv_drag, pageX) || isMoveToPrev(iv_drag, pageX)) stopCount++; else stopCount = 0; if (stopCount > 10) { stopCount = 0; if (isMoveToNext(iv_drag, pageX)) { Configure.isChangingPage = true; DragGridView.this.onPage(EVENT_SLIDING_PAGE, ++Configure.currentPage); Configure.movePageNum++; } if (isMoveToPrev(iv_drag, pageX)) { Configure.isChangingPage = true; DragGridView.this.onPage(EVENT_SLIDING_PAGE, --Configure.currentPage); Configure.movePageNum--; } } onDragDoAnimation(gridX, gridY); } public void setPageListener(G_PageListener pageListener) { this.pageListener = pageListener; } public interface G_PageListener { void page(int cases, int page); } public void setOnItemChangeListener(G_ItemChangeListener pageListener) { this.itemListener = pageListener; } public interface G_ItemChangeListener { void change(int from, int to, int count); } public void notifyDataSetChanged() { if (mDragGridAdapter != null) { mDragGridAdapter.notifyDataSetChanged(); } } /** * * instruction。 * * @param cases * @param page */ private void onPage(int cases, int page) { TLog.debug(TAG, "cases=(%d) , page=(%d)", cases, page); if (pageListener != null) { pageListener.page(cases, page); } } public HashMap<Integer, Integer> getRealArrayHashMap() { HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>(); for (int i = 0; i < viewTag.length; i++) { hashMap.put(viewTag[i], i); } return hashMap; } private void onDragDoAnimation(int x, int y) { if (Configure.isChangingPage) { return; } // 获取目标绝对位置 int hitViewPosition = getAbsolutePostion(x, y); TLog.debug(TAG, "onDragDoAnimation lastHitPostion=%d hitViewPosition=%d, dragPosition=%d", lastHitPostion, hitViewPosition, dragPosition); if (isSwapAniRunning) { TLog.debug(TAG, "isSwapAniRunning"); return; } if (hitViewPosition == AdapterView.INVALID_POSITION) { TLog.debug(TAG, "hitViewPosition == AdapterView.INVALID_POSITION"); return; } if (lastHitPostion == hitViewPosition) { TLog.debug(TAG, "lastHitPostion == hitViewPosition"); return; } /** * 如果是跨页面拖动 */ if (dragPosition == AdapterView.INVALID_POSITION) { int movePostion = 0; int xSelfOffset = -4; int ySelfOffset = 4; if (Configure.currentPage < Configure.draggingPage) { movePostion = Configure.PAGE_SIZE - 1; xSelfOffset = 4; ySelfOffset = -4; } final View view = this.getChildAt(movePostion - getFirstVisiblePosition()); final View finalView = getParentPagedView().copyViewInAniLayer(view, view.getHeight(), view.getWidth()); Animation ani = getTransRelaAnimation(0, 0, xSelfOffset, ySelfOffset); ani.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { view.setVisibility(View.INVISIBLE); } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { ((ViewGroup) finalView.getParent()).removeView(finalView); } }); finalView.startAnimation(ani); dragPosition = movePostion; } if (viewTag[dragPosition] == hitViewPosition) { TLog.debug(TAG, "viewTag[dragPosition] == hitViewPosition"); // 当恰恰好偏移的是第一个 dropPosition = hitViewPosition; return; } TLog.debug(TAG, "onDragDoAnimation=%d, dragPosition=%d", hitViewPosition, viewTag[dragPosition]); lastHitPostion = hitViewPosition; HashMap<Integer, Integer> posToViewPos = getRealArrayHashMap(); TLog.debug(TAG, "viewTag[dragPosition]=%d, hitViewPosition=%d", viewTag[dragPosition], hitViewPosition); final int realDragPostion = viewTag[dragPosition]; dropPosition = hitViewPosition; // 往后移 if (realDragPostion > hitViewPosition) { // viewTag[Pos] 表示控件显示在界面上的位置。 // posToViewPos.get(pos) 表示控件在内部逻辑上实际的位置。 for (int i = hitViewPosition; i < realDragPostion; i++) { int pos = i - getFirstVisiblePosition(); System.out.println("pos ===== " + pos); final View iView = getChildAt(posToViewPos.get(pos)); if (iView != null) iView.startAnimation(getMoveAnimation(true, pos, (ViewGroup) iView)); } TLog.debug("", "hitViewPosition = %d, = %d", hitViewPosition, viewTag[dragPosition]); for (int j = hitViewPosition; j < realDragPostion; j++) { int pos = j - getFirstVisiblePosition(); System.out.println("pospospospos = " + pos); System.out.println("bbbbbbbbbbbbbbbbb = " + pos); viewTag[posToViewPos.get(pos)] = viewTag[posToViewPos.get(pos)] + 1; } } // 往前移 else { for (int i = hitViewPosition; i > realDragPostion; i--) { int pos = i - getFirstVisiblePosition(); final View iView = getChildAt(posToViewPos.get(pos)); if (iView != null) iView.startAnimation(getMoveAnimation(false, pos, (ViewGroup) iView)); } for (int i = hitViewPosition; i > realDragPostion; i--) { int pos = i - getFirstVisiblePosition(); System.out.println("viewTag[" + pos + "] = " + viewTag[pos]); viewTag[posToViewPos.get(pos)] = viewTag[posToViewPos.get(pos)] - 1; System.out.println("viewTag[" + pos + "] = " + viewTag[pos]); } } viewTag[dragPosition] = lastHitPostion; TLog.debug(TAG, "arr = %s", Arrays.toString(viewTag)); } public int getDropPostion() { return dropPosition; } public Rect getDropPostionRectInindow() { int xPer = dropPosition % mNumColumns; int yPer = dropPosition / mNumColumns; int windowX = this.getLeft() + xPer * cellWidth; int windowY = this.getRight() + yPer * cellHeight; Rect rect = new Rect(); rect.left = windowX; rect.top = windowY; rect.bottom = windowY + cellHeight; rect.right = windowY + cellWidth; return rect; } private int cellWidth = 0; private int cellHeight = 0; private int firstChildLeft = 0; private int firstChildRight = 0; /** * 碰撞检测 */ public int getAbsolutePostion(int x, int y) { if (cellWidth == 0) { View child = this.getChildAt(this.getFirstVisiblePosition()); cellWidth = child.getWidth(); cellHeight = child.getHeight(); firstChildLeft = child.getLeft(); firstChildRight = child.getTop(); } int xPos = (x - firstChildLeft) / cellWidth; int yPos = (y - firstChildRight) / cellHeight; int pos = yPos * mNumColumns + xPos; if (getChildAt(pos - getFirstVisiblePosition()) == null) { return AdapterView.INVALID_POSITION; } return pos; } public void resetMemberValue() { mIsDragViewFromMe = false; isSwapAniRunning = false; viewTag = new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }; lastHitPostion = AdapterView.INVALID_POSITION; lastHitPostion = dragPosition = dropPosition = AdapterView.INVALID_POSITION; } public void onDrop() { mIsDragViewFromMe = false; final int[] arr = viewTag; Runnable runnable = null; if (Configure.draggingPage == Configure.currentPage) { runnable = new Runnable() { @Override public void run() { if (Configure.draggingItem != null) { Configure.draggingItem.hide = false; Configure.draggingItem = null; } // 改变表格数 mDragGridAdapter.sortByPositions(arr); mDragGridAdapter.notifyDataSetChanged(); resetMemberValue(); Configure.draggingPage = Configure.DRAG_PAGE_INVALID; Configure.draggingPostion = Configure.DRAG_POSITION_INVALID; } }; } else { runnable = new Runnable() { @Override public void run() { if (Configure.draggingItem != null) { Configure.draggingItem.hide = false; Configure.draggingItem = null; } final BoardDataConfig boardData = Configure.boardData; final int draggingPostion = Configure.draggingPostion; final int currentPage = Configure.currentPage; final int draggingPage = Configure.draggingPage; List<BoardPageItem> curList = boardData.getPageItemList(currentPage); List<BoardPageItem> dragList = boardData.getPageItemList(draggingPage); BoardPageItem dragItem = dragList.get(draggingPostion); BoardPageItem curFirstItem = curList.get(dropPosition); List<BoardPageItem> totalList = boardData.getBoardItemList(); int from = totalList.indexOf(dragItem); int to = totalList.indexOf(curFirstItem); boardData.moveFromTo(from, to); getDragGridView(Configure.draggingPage).resetMemberValue(); resetMemberValue(); Configure.draggingPage = Configure.DRAG_PAGE_INVALID; Configure.draggingPostion = Configure.DRAG_POSITION_INVALID; getParentPagedView().refreshAllPageDragView(); } }; } TLog.debug(TAG, "DDDD moveAnimationCount=%d", moveAnimationCount); if (moveAnimationCount > 0) { runnableList.add(runnable); } else { runnable.run(); } } protected void dragPosToTailAndSort() { for (int i = 7; i > viewTag[dragPosition]; i--) { viewTag[i] = viewTag[i] - 1; } viewTag[dragPosition] = 7; mDragGridAdapter.sortByPositions(viewTag); } public void onLeave() { isSwapAniRunning = false; if (!mIsDragViewFromMe) { resetMemberValue(); if (moveAnimationCount > 0) { runnableList.add(new Runnable() { @Override public void run() { mDragGridAdapter.notifyDataSetChanged(); } }); } else { this.notifyDataSetChanged(); } } } public Animation getTransRelaAnimation(float toX, float toY) { TranslateAnimation go = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, toX, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, toY); go.setDuration(450); return go; } public Animation getTransRelaAnimation(float fromX, float fromY, float toX, float toY) { TranslateAnimation go = new TranslateAnimation(Animation.RELATIVE_TO_SELF, fromX, Animation.RELATIVE_TO_SELF, toX, Animation.RELATIVE_TO_SELF, fromY, Animation.RELATIVE_TO_SELF, toY); go.setDuration(600); return go; } @Override public void setNumColumns(int numColumns) { mNumColumns = numColumns; super.setNumColumns(numColumns); } public Animation getMoveAnimation(boolean isNext, int position, final View view) { Animation ani = null; int xSelfCount = 0; int ySelfCount = 0; if (isNext) { // 右边界的位置 if ((position % mNumColumns) == mNumColumns - 1) { xSelfCount = -(mNumColumns - 1); ySelfCount = 1; } else { xSelfCount = 1; ySelfCount = 0; } } else { // 左边界的位置 if ((position % mNumColumns) == 0) { xSelfCount = mNumColumns - 1; ySelfCount = -1; } else { xSelfCount = -1; ySelfCount = 0; } } ani = getTransRelaAnimation(xSelfCount, ySelfCount); final int finalXSelfCount = xSelfCount; final int finalYSelfCount = ySelfCount; ani.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { moveAnimationCount++; isSwapAniRunning = true; } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { int left = view.getLeft() + finalXSelfCount * view.getWidth(); int top = view.getTop() + finalYSelfCount * view.getHeight(); view.clearAnimation(); view.layout(left, top, left + view.getWidth(), top + view.getHeight()); view.setVisibility(View.VISIBLE); isSwapAniRunning = false; moveAnimationCount--; TLog.debug(TAG, "moveAnimationCount = %d", moveAnimationCount); for (int i = 0; i < runnableList.size(); i++) { runnableList.get(i).run(); } runnableList.clear(); } }); return ani; } /** * * instruction。 * * @param position * @param item * @param view */ public void delete(final int position, final BoardPageItem item, View view) { view.setVisibility(View.INVISIBLE); int nextPage = Configure.currentPage + 1; List curPageItemList = Configure.boardData.getPageItemList(Configure.currentPage); // do move animation for (int i = curPageItemList.size() - 1; i > position; i--) { int pos = i - getFirstVisiblePosition(); final View iView = getChildAt(pos); if (iView != null) iView.startAnimation(getMoveAnimation(false, pos, (ViewGroup) iView)); } if ((nextPage < Configure.boardData.getPageCount())) { BoardPageItem nextPageFistItem = Configure.boardData.getPageItemList(nextPage).get(0); final View newGridItemView = createGridItemView(LayoutInflater.from(getContext()), nextPageFistItem); View transTargetView = view; if (curPageItemList.size() == Configure.PAGE_SIZE) { transTargetView = getChildAt(Configure.PAGE_SIZE - 1 - getFirstVisiblePosition()); } int location[] = new int[2]; transTargetView.getLocationInWindow(location); TLog.debug(TAG, "transTargetView.getHeight() = %d, transTargetView.getWidth() = %d", transTargetView.getHeight(), transTargetView.getWidth()); getParentPagedView().addViewToAnimLayout(newGridItemView, location, transTargetView.getHeight(), transTargetView.getWidth()); newGridItemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TLog.debug(TAG, "newGridItemView.getHeight() = %d, newGridItemView.getWidth() = %d", newGridItemView.getHeight(), newGridItemView.getWidth()); } }); // move the first item of next page to cur page Animation transRelaAni = getTransRelaAnimation(2, -4, 0, 0); transRelaAni.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { List curPageItemList = Configure.boardData.getPageItemList(Configure.currentPage); curPageItemList.remove(position); getParentPagedView().refreshAllPageDragView(); ((ViewGroup) newGridItemView.getParent()).removeView(newGridItemView); } }); newGridItemView.startAnimation(transRelaAni); } else { Runnable runable = new Runnable() { @Override public void run() { List curPageItemList = Configure.boardData.getPageItemList(Configure.currentPage); curPageItemList.remove(position); getParentPagedView().refreshAllPageDragView(); } }; // has animtaion if (curPageItemList.size() - 1 > position) { runnableList.add(runable); } // no animation else { runable.run(); } } } public static View createGridItemView(LayoutInflater inflater, BoardPageItem item) { final View view = inflater.inflate(org.tadpole.app.R.layout.board_page_griditem, null); TextView textView = (TextView) view.findViewById(R.id.pageItemText); View deleteBtnView = view.findViewById(R.id.pageItemDeleteBtn); textView.setText(item.title); if (BoardPageItem.COLOR_BLUE.equals(item.color)) { textView.setBackgroundResource(R.drawable.blue); } else { textView.setBackgroundResource(R.drawable.red); } if (Configure.isEditMode) { textView.getBackground().setAlpha(220); deleteBtnView.setVisibility(View.VISIBLE); } else { textView.getBackground().setAlpha(255); deleteBtnView.setVisibility(View.INVISIBLE); } return view; } }