這幾天我們要求實現仿支付寶首頁全部應用管理功能(長按gridview圖標拖動切換item的位置),我在網上找到了自定義的代碼略加改動實現了效果,現在我將代碼貼出來,由于我也是很少接觸自定義的東西很多東西也沒有弄明白供大家參考,歡迎大家批評指正。
使用自定義的方式這里需要使用的適配器是有限制的,這是實現的基礎的Adapter然后在設置適配器的時候需要繼承BaseAdapter再實現
DragGridBaseAdapter接口public interface DragGridBaseAdapter { /** * 重新排列數據 * @param oldPosition * @param newPosition */ public void reorderItems(int oldPosition, int newPosition); /** * 設置某個item隱藏 * @param hidePosition */ public void setHideItem(int hidePosition); /** * 刪除某個item * @param removePosition */ public void removeItem(int removePosition);}下面我就貼出我寫的適配器的代碼,僅供大家參考
class MeAppAdapter extends BaseAdapter implements DragGridBaseAdapter { List<HashMap<String, Object>> melist; PRivate LayoutInflater mInflater; boolean isShowIcans; private int mHidePosition=-1; public MeAppAdapter(List<HashMap<String, Object>> melist) { this.melist = melist; mInflater = LayoutInflater.from(MoreServerActivity.this); } public void setIsShowIcans(boolean isShowIcans) { this.isShowIcans = isShowIcans; notifyDataSetChanged(); } @Override public int getCount() { return melist != null ? melist.size() : 0; } @Override public Object getItem(int position) { return melist.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup viewGroup) { convertView = mInflater.inflate(R.layout.me_moreapp, null); ImageView me_appimg = (ImageView) convertView.findViewById(R.id.me_appimg); ImageView me_app_charge = (ImageView) convertView.findViewById(R.id.me_app_charge); TextView me_app_text = (TextView) convertView.findViewById(R.id.me_app_text); AutoRelativeLayout me_app_item = (AutoRelativeLayout) convertView.findViewById(R.id.me_app_item); me_appimg.setImageResource(R.mipmap.parknavi); me_app_text.setText((CharSequence) melist.get(position).get("item_text")); me_app_charge.setVisibility(isShowIcans ? View.VISIBLE : View.GONE); me_app_charge.setImageResource(R.mipmap.reduce); if (isShowIcans) { me_app_item.setBackgroundResource(R.drawable.gridview_line); } else { me_app_item.setBackgroundResource(R.drawable.gridview_noline); } me_app_charge.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { parklist.add(melist.get(position)); melist.remove(position); meAppAdapter.notifyDataSetChanged(); moreParkAppAdapter.notifyDataSetChanged(); } }); if (position == mHidePosition) { convertView.setVisibility(View.INVISIBLE); } return convertView; } @Override public void reorderItems(int oldPosition, int newPosition) { HashMap<String, Object> temp = melist.get(oldPosition); if (oldPosition < newPosition) { for (int i = oldPosition; i < newPosition; i++) { Collections.swap(melist, i, i + 1); } } else if (oldPosition > newPosition) { for (int i = oldPosition; i > newPosition; i--) { Collections.swap(melist, i, i - 1); } } melist.set(newPosition, temp); } @Override public void setHideItem(int hidePosition) { this.mHidePosition = hidePosition; notifyDataSetChanged(); } @Override public void removeItem(int removePosition) { melist.remove(removePosition); notifyDataSetChanged(); }}其他都非常簡單,這是自定義GridView的核心代碼
public class DragGridView extends GridView { /** * DragGridView的item長按響應的時間, 默認是1000毫秒,也可以自行設置 */ private long dragResponseMS = 1000; /** * 是否可以拖拽,默認不可以 */ private boolean isDrag = false; private int mDownX; private int mDownY; private int moveX; private int moveY; /** * 正在拖拽的position */ private int mDragPosition; /** * 剛開始拖拽的item對應的View */ private View mStartDragItemView = null; /** * 用于拖拽的鏡像,這里直接用一個ImageView */ private ImageView mDragImageView; /** * 震動器 */ private Vibrator mVibrator; private WindowManager mWindowManager; /** * item鏡像的布局參數 */ private WindowManager.LayoutParams mWindowLayoutParams; /** * 我們拖拽的item對應的Bitmap */ private Bitmap mDragBitmap; /** * 按下的點到所在item的上邊緣的距離 */ private int mPoint2ItemTop; /** * 按下的點到所在item的左邊緣的距離 */ private int mPoint2ItemLeft; /** * DragGridView距離屏幕頂部的偏移量 */ private int mOffset2Top; /** * DragGridView距離屏幕左邊的偏移量 */ private int mOffset2Left; /** * 狀態欄的高度 */ private int mStatusHeight; /** * DragGridView自動向下滾動的邊界值 */ private int mDownScrollBorder; /** * DragGridView自動向上滾動的邊界值 */ private int mUpScrollBorder; /** * DragGridView自動滾動的速度 */ private static final int speed = 20; /** * item的移動動畫是否結束 */ private boolean mAnimationEnd = true; private DragGridBaseAdapter mDragAdapter; /** * GridView的列數 */ private int mNumColumns; /** * 當GridView的numColumns設置為AUTO_FIT,我們需要計算GirdView具體的列數 */ private int mColumnWidth; /** * GridView是否設置了numColumns為具體的數字 */ private boolean mNumColumnsSet; private int mHorizontalSpacing; private int mVerticalSpacing; public DragGridView(Context context) { this(context, null); } public DragGridView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DragGridView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mStatusHeight = getStatusHeight(context); //獲取狀態欄的高度 if (!mNumColumnsSet) { mNumColumns = AUTO_FIT; } } /** * 刪除item的動畫效果 * * @param position */ public void removeItemAnimation(final int position) { mDragAdapter.removeItem(position); final ViewTreeObserver observer = getViewTreeObserver(); observer.addOnPreDrawListener(new OnPreDrawListener() { @Override public boolean onPreDraw() { observer.removeOnPreDrawListener(this); animateReorder(position, getLastVisiblePosition() + 1); return true; } }); } private Handler mHandler = new Handler(); //用來處理是否為長按的Runnable private Runnable mLongClickRunnable = new Runnable() { @Override public void run() { isDrag = true; //設置可以拖拽 //mVibrator.vibrate(50); //震動一下(這里我不需要震動效果,所以我將其注釋掉了,如果大家需要可以將其放開) mStartDragItemView.setVisibility(View.INVISIBLE);//隱藏該item //根據我們按下的點顯示item鏡像 createDragImage(mDragBitmap, mDownX, mDownY); } }; @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); if (adapter instanceof DragGridBaseAdapter) { mDragAdapter = (DragGridBaseAdapter) adapter; } else { throw new IllegalStateException("the adapter must be implements DragGridAdapter"); } } /** * 獲取列數 */ @Override public void setNumColumns(int numColumns) { super.setNumColumns(numColumns); mNumColumnsSet = true; this.mNumColumns = numColumns; } /** * 獲取設置的列寬 */ @Override public void setColumnWidth(int columnWidth) { super.setColumnWidth(columnWidth); mColumnWidth = columnWidth; } /** * 獲取水平方向的間隙 */ @Override public void setHorizontalSpacing(int horizontalSpacing) { super.setHorizontalSpacing(horizontalSpacing); this.mHorizontalSpacing = horizontalSpacing; } /** * 獲取豎直方向的間隙 */ @Override public void setVerticalSpacing(int verticalSpacing) { super.setVerticalSpacing(verticalSpacing); this.mVerticalSpacing = verticalSpacing; } /** * 若列數設置為AUTO_FIT,我們在這里面計算具體的列數 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mNumColumns == AUTO_FIT) { int numFittedColumns; if (mColumnWidth > 0) { int gridWidth = Math.max(MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(), 0); numFittedColumns = gridWidth / mColumnWidth; if (numFittedColumns > 0) { while (numFittedColumns != 1) { if (numFittedColumns * mColumnWidth + (numFittedColumns - 1) * mHorizontalSpacing > gridWidth) { numFittedColumns--; } else { break; } } } else { numFittedColumns = 1; } } else { numFittedColumns = 2; } mNumColumns = numFittedColumns; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** * 設置響應拖拽的毫秒數,默認是1000毫秒 * * @param dragResponseMS */ public void setDragResponseMS(long dragResponseMS) { this.dragResponseMS = dragResponseMS; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = (int) ev.getX(); mDownY = (int) ev.getY(); //根據按下的X,Y坐標獲取所點擊item的position mDragPosition = pointToPosition(mDownX, mDownY); if (mDragPosition == AdapterView.INVALID_POSITION) { return super.dispatchTouchEvent(ev); } //使用Handler延遲dragResponseMS執行mLongClickRunnable mHandler.postDelayed(mLongClickRunnable, dragResponseMS); //根據position獲取該item所對應的View mStartDragItemView = getChildAt(mDragPosition - getFirstVisiblePosition()); //下面這幾個距離大家可以參考我的博客上面的圖來理解下 mPoint2ItemTop = mDownY - mStartDragItemView.getTop(); mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft(); mOffset2Top = (int) (ev.getRawY() - mDownY); mOffset2Left = (int) (ev.getRawX() - mDownX); //獲取DragGridView自動向上滾動的偏移量,小于這個值,DragGridView向下滾動 mDownScrollBorder = getHeight() / 5; //獲取DragGridView自動向下滾動的偏移量,大于這個值,DragGridView向上滾動 mUpScrollBorder = getHeight() * 4 / 5; //開啟mDragItemView繪圖緩存 mStartDragItemView.setDrawingCacheEnabled(true); //獲取mDragItemView在緩存中的Bitmap對象 mDragBitmap = Bitmap.createBitmap(mStartDragItemView.getDrawingCache()); //這一步很關鍵,釋放繪圖緩存,避免出現重復的鏡像 mStartDragItemView.destroyDrawingCache(); break; case MotionEvent.ACTION_MOVE: int moveX = (int) ev.getX(); int moveY = (int) ev.getY(); //如果我們在按下的item上面移動,只要不超過item的邊界我們就不移除mRunnable if (!isTouchInItem(mStartDragItemView, moveX, moveY)) { mHandler.removeCallbacks(mLongClickRunnable); } break; case MotionEvent.ACTION_UP: mHandler.removeCallbacks(mLongClickRunnable); mHandler.removeCallbacks(mScrollRunnable); break; } return super.dispatchTouchEvent(ev); } /** * 是否點擊在GridView的item上面 * * @param x * @param y * @return */ private boolean isTouchInItem(View dragView, int x, int y) { if (dragView == null) { return false; } int leftOffset = dragView.getLeft(); int topOffset = dragView.getTop(); if (x < leftOffset || x > leftOffset + dragView.getWidth()) { return false; } if (y < topOffset || y > topOffset + dragView.getHeight()) { return false; } return true; } @Override public boolean onTouchEvent(MotionEvent ev) { if (isDrag && mDragImageView != null) { switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: moveX = (int) ev.getX(); moveY = (int) ev.getY(); //拖動item onDragItem(moveX, moveY); break; case MotionEvent.ACTION_UP: onStopDrag(); isDrag = false; break; } return true; } return super.onTouchEvent(ev); } /** * 創建拖動的鏡像 * * @param bitmap * @param downX 按下的點相對父控件的X坐標 * @param downY 按下的點相對父控件的X坐標 */ private void createDragImage(Bitmap bitmap, int downX, int downY) { mWindowLayoutParams = new WindowManager.LayoutParams(); mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; //圖片之外的其他地方透明 mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left; mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top - mStatusHeight; mWindowLayoutParams.alpha = 0.55f; //透明度 mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; mDragImageView = new ImageView(getContext()); mDragImageView.setImageBitmap(bitmap); mWindowManager.addView(mDragImageView, mWindowLayoutParams); } /** * 從界面上面移動拖動鏡像 */ private void removeDragImage() { if (mDragImageView != null) { mWindowManager.removeView(mDragImageView); mDragImageView = null; } } /** * 拖動item,在里面實現了item鏡像的位置更新,item的相互交換以及GridView的自行滾動 * * @param * @param */ private void onDragItem(int moveX, int moveY) { mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left; mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top - mStatusHeight; mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams); //更新鏡像的位置 onSwapItem(moveX, moveY); //GridView自動滾動 mHandler.post(mScrollRunnable); } /** * 當moveY的值大于向上滾動的邊界值,觸發GridView自動向上滾動 * 當moveY的值小于向下滾動的邊界值,觸發GridView自動向下滾動 * 否則不進行滾動 */ private Runnable mScrollRunnable = new Runnable() { @Override public void run() { int scrollY; if (getFirstVisiblePosition() == 0 || getLastVisiblePosition() == getCount() - 1) { mHandler.removeCallbacks(mScrollRunnable); } if (moveY > mUpScrollBorder) { scrollY = speed; mHandler.postDelayed(mScrollRunnable, 25); } else if (moveY < mDownScrollBorder) { scrollY = -speed; mHandler.postDelayed(mScrollRunnable, 25); } else { scrollY = 0; mHandler.removeCallbacks(mScrollRunnable); } smoothScrollBy(scrollY, 10); } }; /** * 交換item,并且控制item之間的顯示與隱藏效果 * * @param moveX * @param moveY */ private void onSwapItem(int moveX, int moveY) { //獲取我們手指移動到的那個item的position final int tempPosition = pointToPosition(moveX, moveY); //假如tempPosition 改變了并且tempPosition不等于-1,則進行交換 if (tempPosition != mDragPosition && tempPosition != AdapterView.INVALID_POSITION && mAnimationEnd) { /** * 交換item */ mDragAdapter.reorderItems(mDragPosition, tempPosition); /** * 設置新到的位置隱藏 */ mDragAdapter.setHideItem(tempPosition); final ViewTreeObserver observer = getViewTreeObserver(); observer.addOnPreDrawListener(new OnPreDrawListener() { @Override public boolean onPreDraw() { observer.removeOnPreDrawListener(this); animateReorder(mDragPosition, tempPosition); mDragPosition = tempPosition; return true; } }); } } /** * 創建移動動畫 * * @param view * @param startX * @param endX * @param startY * @param endY * @return */ private AnimatorSet createTranslationAnimations(View view, float startX, float endX, float startY, float endY) { ObjectAnimator animX = ObjectAnimator.ofFloat(view, "translationX", startX, endX); ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY", startY, endY); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); return animSetXY; } /** * item的交換動畫效果 * * @param oldPosition * @param newPosition */ private void animateReorder(final int oldPosition, final int newPosition) { boolean isForward = newPosition > oldPosition; List<Animator> resultList = new LinkedList<Animator>(); if (isForward) { for (int pos = oldPosition; pos < newPosition; pos++) { View view = getChildAt(pos - getFirstVisiblePosition()); if ((pos + 1) % mNumColumns == 0) { resultList.add(createTranslationAnimations(view, -(view.getWidth() + mHorizontalSpacing) * (mNumColumns - 1), 0, view.getHeight() + mVerticalSpacing, 0)); } else { resultList.add(createTranslationAnimations(view, view.getWidth() + mHorizontalSpacing, 0, 0, 0)); } } } else { for (int pos = oldPosition; pos > newPosition; pos--) { View view = getChildAt(pos - getFirstVisiblePosition()); if ((pos) % mNumColumns == 0) { resultList.add(createTranslationAnimations(view, (view.getWidth() + mHorizontalSpacing) * (mNumColumns - 1), 0, -view.getHeight() - mVerticalSpacing, 0)); } else { resultList.add(createTranslationAnimations(view, -view.getWidth() - mHorizontalSpacing, 0, 0, 0)); } } } AnimatorSet resultSet = new AnimatorSet(); resultSet.playTogether(resultList); resultSet.setDuration(300); resultSet.setInterpolator(new AccelerateDecelerateInterpolator()); resultSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mAnimationEnd = false; } @Override public void onAnimationEnd(Animator animation) { mAnimationEnd = true; } }); resultSet.start(); } /** * 停止拖拽我們將之前隱藏的item顯示出來,并將鏡像移除 */ private void onStopDrag() { View view = getChildAt(mDragPosition - getFirstVisiblePosition()); if (view != null) { view.setVisibility(View.VISIBLE); } mDragAdapter.setHideItem(-1); removeDragImage(); } /** * 獲取狀態欄的高度 * * @param context * @return */ private static int getStatusHeight(Context context) { int statusHeight = 0; Rect localRect = new Rect(); ((Activity) context).getWindow().getDecorView().getWindowVisibleDisplayFrame(localRect); statusHeight = localRect.top; if (0 == statusHeight) { Class<?> localClass; try { localClass = Class.forName("com.android.internal.R$dimen"); Object localObject = localClass.newInstance(); int i5 = Integer.parseInt(localClass.getField("status_bar_height").get(localObject).toString()); statusHeight = context.getResources().getDimensionPixelSize(i5); } catch (Exception e) { e.printStackTrace(); } } return statusHeight; }}
新聞熱點
疑難解答