Android实现可拖拽的ListView

来源:互联网 发布:新东方网络课程百度云 编辑:程序博客网 时间:2024/05/29 10:18


通过继承ListView实现可拖拽的ListView,先说说实现拖拽的原理吧,实现拖拽需要考虑三个问题:第一怎么确定你在拖拽listview里面的item的时候就是你手指当前选中的item;第二实现拖拽的效果,就是有一个浮动的层跟随你的手指在移动;第三你放开手指时怎么把你拖拽的这个item放到当前listView的位置(也就是说改变item的位置)。明白了这三个问题就比较好实现了。

里面会涉及到一些比较重要的方法调用,首先是pointToPosition(int x, int y)这方方法Android 官方的解释是” Maps a point to a position in the list”我把它理解为通过xy的位置来确定这个listView里面这个item的位置。有了这个方法就解决了第一和第三个问题了。接下来我们可以通过WindowManager来解决第二个问题,然后通过pointToPosition方法就可以获取你手指按下时的item这个item其实就是你listview里面的item,这样的就可以把这个item设置为是WindowManagerview,这样的话拖动的层的效果就模拟出来了,接下来是怎么让这个WindowManager跟随你的手指在移动。这个时候会涉及到WindowManager里面的updateViewLayout(view, layoutparams)来刷新WindowManager的位置,这样就实现了WindowManager会跟随你的手指在移动。最后就剩下你放下手指的时候怎么让你拖拽的item插入到listview里面,这个插入的动作其实包含了移除和插入这两个动作。这个时候你可能会问在某个位置插入这个item需要”position””item”两个参数,position我们可以通过pointToPosition方法来获取,然后要插入的“item”其实是你adapter里面数据。因为我们上面的一系列动作都是在listview里面完成的,但是在我们重写listview的时候是还没有给listview设置adapter是吧,这个问题的我们通过在重写listview的类中自定义一个接口,然后你在activity里面初始化listview数据的时候实现这个接口。接口里面只有一个方法,方法里面的两个参数一个是你开始拖拽的的item的位置,另一个是你拖拽移动之后之后的item的位置。下面我们看看效果吧:

Android实现可拖拽的ListViewAndroid实现可拖拽的ListViewAndroid实现可拖拽的ListView下面我们看看代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
        privatefinal float mAlpha = 0.9f;
    //拖动的view
    privateImageView mDragView;
    privateContext mContext;
    privateWindowManager mWindowManager;
    privateWindowManager.LayoutParams mLayoutParams;
    //开始拖动时的位置
    privateint mDragStartPosition;
    //当前的位置
    privateint mDragCurrentPostion;
    //在滑动的时候,手的移动要大于这个返回的距离值才开始移动控件
    privateint mScaledTouchSlop;
    //当前位置距离边界的位置
    privateint mDragOffsetX;
    privateint mDragOffSetY;
    //移动的位置
    privateint mDragPointX;
    privateint mDragPointY;
    //边界
    privateint mUpperBound;
        privateint mLowerBound;
    privateDropViewListener mDropViewListener;
     
    publicDragListView(Context context) {
        super(context);
        mContext = context;
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
    }
 
    publicDragListView(Context context, AttributeSet attr) {
        super(context, attr);
        mContext = context;
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
    }

我们在onInterceptTouchEvent方法里面作初始化动作:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Override
    publicboolean onInterceptTouchEvent(MotionEvent ev) {
        //ev.getX()相对于控件本身左上角,ev.getRawX()相对于容器圆点位置
        switch(ev.getAction()) {
            caseMotionEvent.ACTION_DOWN:
                finalint x = (int) ev.getX();//相对于空间本身
                finalint y = (int) ev.getY();
                finalint itemNum = pointToPosition(x, y);
                if(itemNum == AdapterView.INVALID_POSITION){
                    break;
                }
                finalViewGroup item = (ViewGroup) getChildAt(itemNum - getFirstVisiblePosition());
                mDragPointX = x - item.getLeft();
                mDragPointY = y - item.getTop();
                mDragOffsetX = ((int) ev.getRawX()) - x;
                mDragOffSetY = ((int) ev.getRawY()) - y;
                 
                //长按
                item.setOnLongClickListener(newOnLongClickListener() {
                    @Override
                    publicboolean onLongClick(View v) {
                        //计算边界
                        finalint height = getHeight();
                        mUpperBound = Math.min(y - mScaledTouchSlop, height / 3);
                        mLowerBound = Math.max(y + mScaledTouchSlop, height * 23);
                        mDragCurrentPostion = mDragStartPosition = itemNum;
                         
                        item.setDrawingCacheEnabled(true);
                        Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
                        startDragging(bitmap, x, y);
                        returntrue;
                    }
                });
                break;
        }
        returnsuper.onInterceptTouchEvent(ev);
    }

这里面我们就记录了拖拽的当前和开始时位置mDragCurrentPostion和 mDragStartPosition,并通过startDragging来初始化mWindowManager,在startDragging方法里面调用的stopDragging方法其实是一个内存释放的过程,这个方法做的事情就是释放内存空间。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
privatevoid startDragging(Bitmap bitm, intx, inty){
        stopDragging();
         
        mLayoutParams = newWindowManager.LayoutParams();
        mLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
        mLayoutParams.x = x - mDragPointX + mDragOffsetX;
        mLayoutParams.y = y - mDragPointY + mDragOffSetY;
        mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
        mLayoutParams.format = PixelFormat.TRANSLUCENT;
        mLayoutParams.windowAnimations = 0;
         
        ImageView imageView = newImageView(mContext);
        imageView.setImageBitmap(bitm);
        imageView.setBackgroundResource(R.drawable.tab_item_bg);
        imageView.setPadding(0000);
        mWindowManager.addView(imageView, mLayoutParams);
        mDragView = imageView;
    }
 
privatevoid stopDragging(){
        if(mDragView != null){
            mWindowManager.removeView(mDragView);
            mDragView.setImageDrawable(null);
            mDragView = null;
        }
    }

我们在onInterceptTouchEvent方法记录了拖拽的开始位置和当前位置,并且初始化了windowmanager,接下来我们通过onTouchEvent方法来实现让windowmanager跟随你的手指移动

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Override
    publicboolean onTouchEvent(MotionEvent ev) {
        if(mDragView != null&& mDragCurrentPostion != INVALID_POSITION && mDropViewListener != null){
            switch(ev.getAction()) {
                caseMotionEvent.ACTION_UP:
                    //int y = (int) ev.getY();
                    stopDragging();
                    //数据交换
                    if(mDragCurrentPostion >= 0&& mDragCurrentPostion < getCount()){
                        mDropViewListener.drop(mDragStartPosition, mDragCurrentPostion);
                    }
                    break;
                caseMotionEvent.ACTION_MOVE:
                    intx = (int) ev.getX();
                    inty = (int) ev.getY();
                    dragView(x, y);
                    if(y >= getHeight() / 3) {
                        mUpperBound = getHeight() / 3;
                    }
                    if(y <= getHeight() * 23) {
                        mLowerBound = getHeight() * 23;
                    }
                     intspeed = 0;
                    if(y > mLowerBound) {
                        if(getLastVisiblePosition() < getCount() - 1) {
                            speed = y > (getHeight() + mLowerBound) / 2164;
                        else{
                            speed = 1;
                        }
                    elseif (y < mUpperBound) {
                        speed = y < mUpperBound / 2? -16: -4;
                        if(getFirstVisiblePosition() == 0
                                && getChildAt(0).getTop() >= getPaddingTop()) {
                            speed = 0;
                        }
                    }
                    if(speed != 0) {
                        smoothScrollBy(speed, 30);
                    }
                    break;
            }
            returntrue;
        }
        returnsuper.onTouchEvent(ev);
    }

其中dragView方法就是根据你手指移动来改变windowmanager的位置,在移动的时候我们需要考虑的另外一个问题是你的listview滚动条的时候,这个时候我们需要考虑边界,不然会抛出空指针异常。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
privatevoid dragView(intx, inty){
        if(mDragView != null){
            mLayoutParams.alpha = mAlpha;
            mLayoutParams.y = y - mDragPointY + mDragOffSetY;
            mLayoutParams.x = x - mDragPointX + mDragOffsetX;
            mWindowManager.updateViewLayout(mDragView, mLayoutParams);
        }
        inttempPosition = pointToPosition(0, y);
        if(tempPosition != INVALID_POSITION){
            mDragCurrentPostion = tempPosition;
        }
         
        //滚动
        intscrollY = 0;
        if(y < mUpperBound){
            scrollY = 8;
        }elseif(y > mLowerBound){
            scrollY = -8;
        }
         
        if(scrollY != 0){
            inttop = getChildAt(mDragCurrentPostion - getFirstVisiblePosition()).getTop();
            setSelectionFromTop(mDragCurrentPostion, top + scrollY);
        }
    }

 这样的话就完成了拖拽的效果,剩下的是你放开手指,然后把你拖拽的item插入到listview的列表里面,我们定义了一个接口

?
1
2
3
4
5
6
7
publicvoid setDropViewListener(DropViewListener listener){
        this.mDropViewListener = listener;
    }
     
    publicinterface DropViewListener {
        voiddrop(intfrom, intto);
    }

这样的话,你在初始化listview给listView设置adapter的时候需要实现DropViewListener接口,from和to两个参数分别是你拖拽时的最初位置和你移动后的位置,这样的话你在activity里面就可以实现数据插入了。

注意:
但是如果你的这listview的item包含了复选框的话,这个时候listview的onitemclicklistener事件就会失效,也就是说你不能通过你平时处理listview的onitemclicklistener的方法一样处理,你也可以通过自定义接口来模拟onitemclicklistener事件。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 觉得老公对自己不够关心体贴怎么办 孕期老公不知道关心不体贴怎么办? 中国和伊朗做贸易美国制裁怎么办 土地被村民霸占村长解决不了怎么办 村支书霸占群众选举村长的票怎么办 苹果手机自带的音乐软件删了怎么办 不小心把手机系统软件删了怎么办 佳能打印机打相片是打不全怎么办 卖家说我寄回去的包是假的怎么办 辞职后原单位没把档案给转出怎么办 天下行以租代购要起诉我怎么办 顺丰收件人电话地址都写错了怎么办 领导问任务完不成怎么办怎么回答 我和我老婆感情出现问题了怎么办 手机坏了手机浏览器里照片怎么办 不小心把电脑ie浏览器删了怎么办 华为手机自带浏览器中病毒了怎么办 手机打开浏览器提示中病毒了怎么办 手机卸载了浏览器和软件商店怎么办 房东在我租房期间贴房子出租怎么办 额头上毛孔大 还有些黑怎么办 孩子学跳舞老师压的疼怎么办 步步高点读机电池坏了怎么办 电脑连接受限制或无连接怎么办 电脑无线网受限制或无连接怎么办 电脑网络受限制或无连接怎么办 3 证书报考大专证书丢了怎么办 广工期末考试被老师警告了怎么办 我在农村不当队长我能力不够怎么办 我在农村不当队长我当代表怎么办 北京本地人社保中间断了一年怎么办 我想把大哥的孩子带出国要怎么办 美国有亲戚想让孩子出国怎么办 连接温控器的线断了怎么办 植发后好多原生发都脱落了怎么办 4岁宝宝支体能力差怎么办 当与游客发生矛盾时员工怎么办 顾客与同事发生矛盾你该怎么办 当一个人犯下的过错难以原谅怎么办 开车把别人撞成植物人了该怎么办 结婚证丢了孩子上不上学了怎么办