Android可拖動排序GridView實現

jopen 10年前發布 | 97K 次閱讀 Android Android開發 移動開發

經常使用今日頭條、網易新聞的同學們應該都會注意到用于管理多個頻道的可拖動排序GridView,下面介紹一下可拖動的DragGridView 的實現方法。代碼放在GitHub上https://github.com/zhaoyu87/DragGridView,需要的同學可以下載

Android可拖動排序GridView實現

DragGridView繼承自GridView,當長按選中某個item進行拖動,放手更新GridView順序:

1.重寫onTouchEvent響應拖動事件:被按下時記錄按下坐標;拖動時更新被拖動視圖顯示;放開時更新排序

2.設置OnItemLongClickListener:響應長按選取拖動item,獲取被選中item bitmap,添加到窗口顯示

3.通過view.getDrawingCache()獲取被選中item的bitmap,用于繪制拖動的view

4.使用WindowManager來向窗口添加view,更新view顯示。關于WindowManagerService對窗口的組織方式,博 客http://blog.csdn.net/luoshengyang/article/details/8498908有介紹,可以參考。

5. MotionEvent中的getX()為相對于容器的坐標,這里就是GridView;getRawX()為相對于整個屏幕的坐標

DragGridView實現如下,注釋中有更詳細的解釋

public class DragGridView extends GridView {
    private static final int DRAG_IMG_SHOW = 1;
    private static final int DRAG_IMG_NOT_SHOW = 0;
    private static final String LOG_TAG = "DragGridView";
    private static final float AMP_FACTOR = 1.2f;

    /**被拖動的視圖*/
    private ImageView dragImageView;
    private WindowManager.LayoutParams dragImageViewParams;
    private WindowManager windowManager;
    private boolean isViewOnDrag = false;

    /**前一次拖動的位置*/
    private int preDraggedOverPositon = AdapterView.INVALID_POSITION;
    private int downRawX;
    private int downRawY;

    /**長按選中拖動item*/
    private OnItemLongClickListener onLongClickListener = new OnItemLongClickListener(){

        @Override
        //長按item開始拖動
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {

            //記錄長按item位置
            preDraggedOverPositon = position;

            //獲取被長按item的drawing cache
            view.destroyDrawingCache();
            view.setDrawingCacheEnabled(true);
            //通過被長按item,獲取拖動item的bitmap
            Bitmap dragBitmap = Bitmap.createBitmap(view.getDrawingCache());

            //設置拖動item的參數
            dragImageViewParams.gravity = Gravity.TOP | Gravity.LEFT;
            //設置拖動item為原item 1.2倍
            dragImageViewParams.width = (int)(AMP_FACTOR*dragBitmap.getWidth());
            dragImageViewParams.height = (int)(AMP_FACTOR*dragBitmap.getHeight());
            //設置觸摸點為繪制拖動item的中心
            dragImageViewParams.x = (downRawX - dragImageViewParams.width/2);
            dragImageViewParams.y = (downRawY - dragImageViewParams.height/2);
            dragImageViewParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
            dragImageViewParams.format = PixelFormat.TRANSLUCENT;
            dragImageViewParams.windowAnimations = 0;

            //dragImageView為被拖動item的容器,清空上一次的顯示
            if((int)dragImageView.getTag() == DRAG_IMG_SHOW) {
                windowManager.removeView(dragImageView);
                dragImageView.setTag(DRAG_IMG_NOT_SHOW);
            }

            //設置本次被長按的item
            dragImageView.setImageBitmap(dragBitmap);

            //添加拖動item到屏幕
            windowManager.addView(dragImageView, dragImageViewParams);
            dragImageView.setTag(DRAG_IMG_SHOW);
            isViewOnDrag = true;

            //設置被長按item不顯示
            ((GridViewAdapter)getAdapter()).hideView(position);
            return true;
        }
    };

    public DragGridView(Context context) {
        super(context);
        initView();
    }

    public DragGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public DragGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    public void initView() {
        setOnItemLongClickListener(onLongClickListener);
        //初始化顯示被拖動item的image view
        dragImageView = new ImageView(getContext());
        dragImageView.setTag(DRAG_IMG_NOT_SHOW);
        //初始化用于設置dragImageView的參數對象
        dragImageViewParams = new WindowManager.LayoutParams();
        //獲取窗口管理對象,用于后面向窗口中添加dragImageView
        windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    }


    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        //被按下時記錄按下的坐標
        if(ev.getAction() == MotionEvent.ACTION_DOWN) {
            //獲取觸摸點相對于屏幕的坐標
            downRawX = (int)ev.getRawX();
            downRawY = (int)ev.getRawY();
        }
        //dragImageView處于被拖動時,更新dragImageView位置
        else if((ev.getAction() == MotionEvent.ACTION_MOVE) && (isViewOnDrag == true)) {
            Log.i(LOG_TAG, "" + ev.getRawX() + " " + ev.getRawY());
            //設置觸摸點為dragImageView中心
            dragImageViewParams.x = (int)(ev.getRawX() - dragImageView.getWidth()/2);
            dragImageViewParams.y = (int)(ev.getRawY() - dragImageView.getHeight()/2);
            //更新窗口顯示
            windowManager.updateViewLayout(dragImageView, dragImageViewParams);
            //獲取當前觸摸點的item position
            int currDraggedPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
            //如果當前停留位置item不等于上次停留位置的item,交換本次和上次停留的item
            if((currDraggedPosition != AdapterView.INVALID_POSITION) && (currDraggedPosition != preDraggedOverPositon)) {
                ((GridViewAdapter)getAdapter()).swapView(preDraggedOverPositon, currDraggedPosition);
                preDraggedOverPositon = currDraggedPosition;
            }
        }
        //釋放dragImageView
        else if((ev.getAction() == MotionEvent.ACTION_UP) && (isViewOnDrag == true)) {
            ((GridViewAdapter)getAdapter()).showHideView();
            if((int)dragImageView.getTag() == DRAG_IMG_SHOW) {
                windowManager.removeView(dragImageView);
                dragImageView.setTag(DRAG_IMG_NOT_SHOW);
            }
            isViewOnDrag = false;
        }
        return super.onTouchEvent(ev);
    }
}

GridViewAdapter繼承自BaseAdapter:

1.提供showHideView、hideView兩個方法用于顯示和隱藏選中Item的Text

2.提供swapView方法用于拖動過程中更新GridView中item顯示

public class GridViewAdapter extends BaseAdapter {
    private Context context;
    private List<String> strList;
    private int hidePosition = AdapterView.INVALID_POSITION;

    public GridViewAdapter(Context context, List<String> strList) {
        this.context = context;
        this.strList = strList;
    }

    @Override
    public int getCount() {
        return strList.size();
    }

    @Override
    public String getItem(int position) {
        return strList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView view;
        if(convertView == null) {
            view = new TextView(context);
        }
        else {
            view = (TextView)convertView;
        }

        //hide時隱藏Text
        if(position != hidePosition) {
            view.setText(strList.get(position));
        }
        else {
            view.setText("");
        }
        view.setId(position);

        return view;
    }

    public void hideView(int pos) {
        hidePosition = pos;
        notifyDataSetChanged();
    }

    public void showHideView() {
        hidePosition = AdapterView.INVALID_POSITION;
        notifyDataSetChanged();
    }

    public void removeView(int pos) {
        strList.remove(pos);
        notifyDataSetChanged();
    }

    //更新拖動時的gridView
    public void swapView(int draggedPos, int destPos) {
        //從前向后拖動,其他item依次前移
        if(draggedPos < destPos) {
            strList.add(destPos+1, getItem(draggedPos));
            strList.remove(draggedPos);
        }
        //從后向前拖動,其他item依次后移
        else if(draggedPos > destPos) {
            strList.add(destPos, getItem(draggedPos));
            strList.remove(draggedPos+1);
        }
        hidePosition = destPos;
        notifyDataSetChanged();
    }
}

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!