1. 程式人生 > >實現可拖動排序的ListView-DragListView

實現可拖動排序的ListView-DragListView

  • 專案 中要用到拖動排序的效果,於是百度到網上的做法,github上開源框架被我pass, 為了一個小功能匯入一庫太不划算。然後看到這篇
    http://blog.csdn.net/jj120522/article/details/8240407,可能是博主原始碼給的不全,看到好多人要原始碼。其實看懂後,很容易的就能補全差的東西。

  • 既然用到,自己一定要去實現一遍,只有這樣才能真正學到。我對這個控制元件做了些整理和優化, 這樣使用時和普通的ListView就沒有什麼區別了。

    實現步驟:
    1. 按下時, 創建出item的虛影。
    2. 拖動時, 移動虛影,同步進行item的交換和listview的上下滑動
    3. 放下時, 移除虛影, 顯示出當前的item.

    優化:
    1. 移動時item的隱藏, 這樣adapter中就不用去處理隱藏問題
    2. itemView.setDrawingCacheEnabled 方法 開啟cache ,建立bitmap後一定要關閉cache, 這樣解決拖動時的錯亂問題,item就可以 複用了。
    3. 上下出界問題

下面是DragListView的原始碼

public class DragListView extends ListView {

    private int dragViewId;
    private WindowManager.LayoutParams windowParams;
    private
WindowManager windowManager; private ImageView dragImageView; private int offsetScreenTop; //距離螢幕頂部的位置 private int offsetViewTop; //手指按下位置距離item頂部的位置 private int dragPosition; private int srcY; //用於判斷滑動方向 public DragListView(Context context) { super(context); } public
DragListView(Context context, AttributeSet attrs) { super(context, attrs); } public void setDragViewId(int dragViewId) { this.dragViewId = dragViewId; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { int x = (int) ev.getX(); int y = (int) ev.getY(); int rawY = (int) ev.getRawY(); int currentPostion = dragPosition = pointToPosition(x, y); if (currentPostion == AdapterView.INVALID_POSITION) { return super.onInterceptTouchEvent(ev); } //getChildAt是獲取可見位置的item ViewGroup itemView = (ViewGroup) getChildAt(currentPostion - getFirstVisiblePosition()); offsetScreenTop = rawY - y; offsetViewTop = y - itemView.getTop(); // 獲取可拖拽的圖示 View dragger = itemView.findViewById(dragViewId); if (dragger != null && x > dragger.getLeft()) { itemView.setDrawingCacheEnabled(true);// 開啟cache. Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根據cache建立一個新的bitmap物件. itemView.setDrawingCacheEnabled(false);// 一定關閉cache,否則複用會出現錯亂 startDrag(bm, y); } } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { if (dragImageView != null) { int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: srcY = y; break; case MotionEvent.ACTION_MOVE: onDrag(y); getChildAt(dragPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE); break; case MotionEvent.ACTION_UP: stopDrag(); getChildAt(dragPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE); break; } return true; } return super.onTouchEvent(ev); } private void startDrag(Bitmap bm, int y) { /*** * 初始化window. */ windowParams = new WindowManager.LayoutParams(); windowParams.gravity = Gravity.TOP; windowParams.x = 0; windowParams.y = y - offsetViewTop + offsetScreenTop; windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT; windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT; windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE// 不需獲取焦點 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE// 不需接受觸控事件 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON// 保持裝置常開,並保持亮度不變。 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;// 窗口占滿整個螢幕,忽略周圍的裝飾邊框(例如狀態列)。此視窗需考慮到裝飾邊框的內容。 // windowParams.format = PixelFormat.TRANSLUCENT;// 預設為不透明,這裡設成透明效果. windowParams.windowAnimations = 0;// 視窗所使用的動畫設定 ImageView imageView = new ImageView(getContext()); imageView.setImageBitmap(bm); windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); windowManager.addView(imageView, windowParams); dragImageView = imageView; } /** * 拖動景象 * * @param y */ private void onDrag(int y) { int offsetTop = y - offsetViewTop; //頂部不能出界 if (dragImageView != null && offsetTop >= 0 && offsetTop <= getChildAt(getChildCount() - 1).getTop()) { windowParams.alpha = 0.8f;// 透明度 windowParams.y = y - offsetViewTop + offsetScreenTop;// 移動y值.//記得要加上dragOffset,windowManager計算的是整個螢幕.(標題欄和狀態列都要算上) windowManager.updateViewLayout(dragImageView, windowParams);// 時時移動. } onChange(y); scrollListView(y); } /** * 同步滑動ListView * @param y */ private void scrollListView(int y) { View view = getChildAt(dragPosition - getFirstVisiblePosition()); int offsetY = srcY - y; if (y < getHeight() / 3 && y < srcY) { //listview向上滑 setSelectionFromTop(dragPosition, offsetY + view.getTop()); } else if (y > getHeight() / 3 * 2 && y > srcY) { //listview向下滑 setSelectionFromTop(dragPosition, offsetY + view.getTop()); } srcY = y; } /** * 同步改變item的位置 * @param y */ private void onChange(int y) { int currentPostion = pointToPosition(0, y); if (currentPostion == AdapterView.INVALID_POSITION) { currentPostion = dragPosition; } if (dragPosition != currentPostion) { DragAdapter adapter = (DragAdapter) getAdapter(); adapter.change(dragPosition, currentPostion); swich(dragPosition, currentPostion); } dragPosition = currentPostion; } /*** * 切換隱藏的位置 */ private void swich(int start, int end) { getChildAt(start - getFirstVisiblePosition()).setVisibility(View.VISIBLE); getChildAt(end - getFirstVisiblePosition()).setVisibility(View.INVISIBLE); } /** * 停止拖動,刪除影像 */ public void stopDrag() { if (dragImageView != null) { windowManager.removeView(dragImageView); dragImageView = null; } } }

這樣實現DragAdapter 只用實現一個update方法

public abstract class DragAdapter extends BaseAdapter {

    private List<String> data;

    public DragAdapter(List<String> data) {
        this.data = data;
    }

    /**
     * 交換位置
     *
     * @param start
     * @param end
     */
    public void change(int start, int end) {
        String srcData = data.get(start);
        data.remove(srcData);
        data.add(end, srcData);
        notifyDataSetChanged();
    }
}

在Activity中使用除了要設定一個拖動按鈕的id, 其它和ListView沒有任何區別了

public class DragListViewActivity extends Activity {
    private DragListView dragListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drag_list_view);

        dragListView = (DragListView) findViewById(R.id.dragListView);
        dragListView.setDragViewId(R.id.tvDrag);
        List<String> data = initData();
        MyAdapter dragAdapter = new MyAdapter(data,this);
        dragListView.setAdapter(dragAdapter);
    }

    private List<String> initData() {
        List<String> data = new ArrayList<String>();
        for (int i = 0; i < 20; i++) {
            data.add("標題xxx" + i);
        }
        return data;
    }

    private class MyAdapter extends DragAdapter {

        private List<String> data;
        private Context context;

        public MyAdapter(List<String> data, Context context) {
            super(data);
            this.data = data;
            this.context = context;
        }


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

        @Override
        public Object getItem(int position) {
            return null;
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Holder holder = null;
            if (convertView == null) {
                holder = new Holder();
                convertView = View.inflate(context, R.layout.list_view_item, null);
                holder.tvDrag = (TextView) convertView.findViewById(R.id.tvDrag);
                holder.tvName = (TextView) convertView.findViewById(R.id.tvName);
                convertView.setTag(holder);
            } else {
                holder = (Holder) convertView.getTag();
            }
            holder.tvName.setText(data.get(position));

            return convertView;
        }

        private class Holder {
            TextView tvName;
            TextView tvDrag;
        }
    }

}