实现可拖动排序的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;
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值