android ListView和GridView拖拽移位具体实现及拓展

关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例:

   1354182517_7951.jpg

2012-12-4 18:16 上传
下载附件 (49.49 KB)
      1354182533_6959.jpg
2012-12-4 18:16 上传
下载附件 (48.27 KB)
      1354182611_4700.jpg
2012-12-4 18:16 上传
下载附件 (117.06 KB)

首先说一下:拖拽ListView的item就不应该可以任意移动,只应该在ListView所在的范围内,而网易的你看看我都可以移动到状态栏了,虽然你做了处理,但是用户体验我个人感觉不好,在看看百度的,不仅控制了移动范围,更不错的百度的移动起来会时时的换位,看起来相当的形象,所以我认为这样相当的棒.

说明一点,我没有那么有才,我也是看别人代码,然后自己整理下.在这里就简单记载一下.

首先对touch事件的处理,从应用中,我们可以得出,在我们点击后面拖拉图标后,就会创建一个item的影像视图.并且可以移动该影像,而此时的ListView不应该有touch事件.

01
02
03
04
05
06
07
08
09
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
/***
     * touch事件拦截
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 按下
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            int x = ( int ) ev.getX(); // 获取相对与ListView的x坐标
            int y = ( int ) ev.getY(); // 获取相应与ListView的y坐标
            dragSrcPosition = dragPosition = pointToPosition(x, y);
            // 无效不进行处理
            if (dragPosition == AdapterView.INVALID_POSITION) {
                return super .onInterceptTouchEvent(ev);
            }
            // 获取当前位置的视图(可见状态)
            ViewGroup itemView = (ViewGroup) getChildAt(dragPosition
                    - getFirstVisiblePosition());
            // 获取到的dragPoint其实就是在你点击指定item项中的高度.
            dragPoint = y - itemView.getTop();
            // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).
            dragOffset = ( int ) (ev.getRawY() - y);
            // 获取可拖拽的图标
            View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);
            // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)
            if (dragger != null && x > dragger.getLeft() - 20 ) {
                upScrollBounce = getHeight() / 3 ; // 取得向上滚动的边际,大概为该控件的1/3
                downScrollBounce = getHeight() * 2 / 3 ; // 取得向下滚动的边际,大概为该控件的2/3
                itemView.setDrawingCacheEnabled( true ); // 开启cache.
                Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache()); // 根据cache创建一个新的bitmap对象.
                startDrag(bm, y); // 初始化影像
            }
            // return false;
        }
        return super .onInterceptTouchEvent(ev);
    }


这个方法的作用很简单:当我们摁下的如果是可拖拽的图标,那么进行初始化该Item的映像试图.

而在这里如果大家对WindowManager和WindowManager.LayoutParams不熟悉的朋友先去参考下这篇文章,要对WindowManager有一定的了解,简单的会应用.

接下来我们看onTouchEvent事件:


01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
     * 触摸事件处理
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // item的view不为空,且获取的dragPosition有效
        if (dragImageView != null && dragPosition != INVALID_POSITION) {
            int action = ev.getAction();
            switch (action) {
            case MotionEvent.ACTION_UP:
                int upY = ( int ) ev.getY();
                stopDrag();
                onDrop(upY);
                break ;
            case MotionEvent.ACTION_MOVE:
                int moveY = ( int ) ev.getY();
                onDrag(moveY);
                break ;
            case MotionEvent.ACTION_DOWN:
                break ;
            default :
                break ;
            }
            return true ; // 取消ListView滑动.
        }
        return super .onTouchEvent(ev);
    }

简单说明: 首先在Touch中,我们要进行判断,是否点击的是拖动图标,如果是的话,那么对ACTION_MOVE and ACTION_UP相应事件进行处理,并且返回true or false.作用:取消ListView自身的Touch事件.如果不是的话,执行ListView 本身的Touch事件.

大致就介绍这么多,具体的实现,还是大家看源码吧,我注释的还算清晰,只要大家仔细看的话,一定可以掌握的,为什么这么说呢,技术只有在掌握了情况下才可以进行拓展.

对了,提醒大家要理解这三句话:


getRawX()和getRawY():获得的是相对屏幕的位置.

getX()和getY():获得的永远是相对view的触摸位置 坐标(这两个值不会超过view的长度和宽度)。

getLeft , getTop, getBottom,getRight, 这个指的是该控件相对于父控件的距离.

源码:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package com.jj.drag;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import com.jj.drag.MainActivity.DragListAdapter;
/***
* 自定义拖拽ListView
*
* @author zhangjia
*
*/
public class DragListView extends ListView {
    private WindowManager windowManager; // windows窗口控制类
    private WindowManager.LayoutParams windowParams; // 用于控制拖拽项的显示的参数
    private int scaledTouchSlop; // 判断滑动的一个距离,scroll的时候会用到(24)
    private ImageView dragImageView; // 被拖拽的项(item),其实就是一个ImageView
    private int dragSrcPosition; // 手指拖动项原始在列表中的位置
    private int dragPosition; // 手指点击准备拖动的时候,当前拖动项在列表中的位置.
    private int dragPoint; // 在当前数据项中的位置
    private int dragOffset; // 当前视图和屏幕的距离(这里只使用了y方向上)
    private int upScrollBounce; // 拖动的时候,开始向上滚动的边界
    private int downScrollBounce; // 拖动的时候,开始向下滚动的边界
    private final static int step = 1 ; // ListView 滑动步伐.
    private int current_Step; // 当前步伐.
    /***
     * 构造方法
     *
     * @param context
     * @param attrs
     */
    public DragListView(Context context, AttributeSet attrs) {
        super (context, attrs);
    }
    /***
     * touch事件拦截
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 按下
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            int x = ( int ) ev.getX(); // 获取相对与ListView的x坐标
            int y = ( int ) ev.getY(); // 获取相应与ListView的y坐标
            dragSrcPosition = dragPosition = pointToPosition(x, y);
            // 无效不进行处理
            if (dragPosition == AdapterView.INVALID_POSITION) {
                return super .onInterceptTouchEvent(ev);
            }
            // 获取当前位置的视图(可见状态)
            ViewGroup itemView = (ViewGroup) getChildAt(dragPosition
                    - getFirstVisiblePosition());
            // 获取到的dragPoint其实就是在你点击指定item项中的高度.
            dragPoint = y - itemView.getTop();
            // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).
            dragOffset = ( int ) (ev.getRawY() - y);
            // 获取可拖拽的图标
            View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);
            // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)
            if (dragger != null && x > dragger.getLeft() - 20 ) {
                upScrollBounce = getHeight() / 3 ; // 取得向上滚动的边际,大概为该控件的1/3
                downScrollBounce = getHeight() * 2 / 3 ; // 取得向下滚动的边际,大概为该控件的2/3
                itemView.setDrawingCacheEnabled( true ); // 开启cache.
                Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache()); // 根据cache创建一个新的bitmap对象.
                startDrag(bm, y); // 初始化影像
            }
        }
        return super .onInterceptTouchEvent(ev);
    }
    /**
     * 触摸事件处理
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // item的view不为空,且获取的dragPosition有效
        if (dragImageView != null && dragPosition != INVALID_POSITION) {
            int action = ev.getAction();
            switch (action) {
            case MotionEvent.ACTION_UP:
                int upY = ( int ) ev.getY();
                stopDrag();
                onDrop(upY);
                break ;
            case MotionEvent.ACTION_MOVE:
                int moveY = ( int ) ev.getY();
                onDrag(moveY);
                break ;
            case MotionEvent.ACTION_DOWN:
                break ;
            default :
                break ;
            }
            return true ; // 取消ListView滑动.
        }
        return super .onTouchEvent(ev);
    }
    /**
     * 准备拖动,初始化拖动项的图像
     *
     * @param bm
     * @param y
     */
    private void startDrag(Bitmap bm, int y) {
        // stopDrag();
        /***
         * 初始化window.
         */
        windowParams = new WindowManager.LayoutParams();
        windowParams.gravity = Gravity.TOP;
        windowParams.x = 0 ;
        windowParams.y = y - dragPoint + dragOffset;
        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( "window" );
        windowManager.addView(imageView, windowParams);
        dragImageView = imageView;
    }
    /**
     * 拖动执行,在Move方法中执行
     *
     * @param y
     */
    public void onDrag( int y) {
        int drag_top = y - dragPoint; // 拖拽view的top值不能<0,否则则出界.
        if (dragImageView != null && drag_top >= 0 ) {
            windowParams.alpha = 0 .5f; // 透明度
            windowParams.y = y - dragPoint + dragOffset; // 移动y值.//记得要加上dragOffset,windowManager计算的是整个屏幕.(标题栏和状态栏都要算上)
            windowManager.updateViewLayout(dragImageView, windowParams); // 时时移动.
        }
        // 为了避免滑动到分割线的时候,返回-1的问题
        int tempPosition = pointToPosition( 0 , y);
        if (tempPosition != INVALID_POSITION) {
            dragPosition = tempPosition;
        }
        doScroller(y);
    }
    /***
     * ListView的移动.
     * 要明白移动原理:当映像移动到下端的时候,ListView向上滑动,当映像移动到上端的时候,ListView要向下滑动。正好和实际的相反.
     *
     */
    public void doScroller( int y) {
        Log.e( "jj" , "y=" + y);
        Log.e( "jj" , "upScrollBounce=" + upScrollBounce);
        // ListView需要下滑
        if (y < upScrollBounce) {
            current_Step = step + (upScrollBounce - y) / 10 ; // 时时步伐
        } // ListView需要上滑
        else if (y > downScrollBounce) {
            current_Step = -(step + (y - downScrollBounce)) / 10 ; // 时时步伐
        } else {
            current_Step = 0 ;
        }
        // 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position)
        View view = getChildAt(dragPosition - getFirstVisiblePosition());
        // 真正滚动的方法setSelectionFromTop()
        setSelectionFromTop(dragPosition, view.getTop() + current_Step);
    }
    /**
     * 停止拖动,删除影像
     */
    public void stopDrag() {
        if (dragImageView != null ) {
            windowManager.removeView(dragImageView);
            dragImageView = null ;
        }
    }
    /**
     * 拖动放下的时候
     *
     * @param y
     */
    public void onDrop( int y) {
        // 为了避免滑动到分割线的时候,返回-1的问题
        int tempPosition = pointToPosition( 0 , y);
        if (tempPosition != INVALID_POSITION) {
            dragPosition = tempPosition;
        }
        // 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置)
        if (y < getChildAt( 0 ).getTop()) {
            // 超出上边界
            dragPosition = 0 ;
            // 如果拖动超过最后一项的最下边那么就防止在最下边
        } else if (y > getChildAt(getChildCount() - 1 ).getBottom()) {
            // 超出下边界
            dragPosition = getAdapter().getCount() - 1 ;
        }
        // 数据交换
        if (dragPosition < getAdapter().getCount()) {
            DragListAdapter adapter = (DragListAdapter) getAdapter();
            adapter.update(dragSrcPosition, dragPosition);
        }
    }
}

下面我说下适配器:
01
02
03
04
05
06
07
08
09
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/***
     * 自定义适配器
     *
     * @author zhangjia
     *
     */
    class DragListAdapter extends BaseAdapter {
        private ArrayList<String> arrayTitles;
        private ArrayList<Integer> arrayDrawables;
        private Context context;
        public DragListAdapter(Context context, ArrayList<String> arrayTitles,
                ArrayList<Integer> arrayDrawables) {
            this .context = context;
            this .arrayTitles = arrayTitles;
            this .arrayDrawables = arrayDrawables;
        }
        @Override
        public View getView( int position, View convertView, ViewGroup parent) {
            View view = convertView;
            /***
             * 在这里尽可能每次都进行实例化新的,这样在拖拽ListView的时候不会出现错乱.
             * 具体原因不明,不过这样经过测试,目前没有发现错乱。虽说效率不高,但是做拖拽LisView足够了。
             */
            view = LayoutInflater.from(context).inflate(
                    R.layout.drag_list_item, null );
            TextView textView = (TextView) view
                    .findViewById(R.id.tv_drag_list_item_text);
            ImageView imageView = (ImageView) view
                    .findViewById(R.id.iv_drag_list_item_1);
            imageView.setImageResource(arrayDrawables.get(position));
            textView.setText(arrayTitles.get(position));
            return view;
        }
        /***
         * 动态修改ListVIiw的方位.
         *
         * @param start
         *            点击移动的position
         * @param down
         *            松开时候的position
         */
        public void update( int start, int down) {
            // 获取删除的东东.
            String title = arrayTitles.get(start);
            int drawable_id = arrayDrawables.get(start);
            arrayTitles.remove(start); // 删除该项
            arrayDrawables.remove(start); // 删除该项
            arrayTitles.add(down, title); // 添加删除项
            arrayDrawables.add(down, drawable_id); // 添加删除项
            notifyDataSetChanged(); // 刷新ListView
        }
        @Override
        public int getCount() {
            return Title.length;
        }
        @Override
        public Object getItem( int position) {
            return Title[position];
        }
        @Override
        public long getItemId( int position) {
            return position;
        }
    }

这里不过多解释了,相信大家都看的明白.如果疑问请留言.

展示下运行效果:

1354206807_6198.jpg

2012-12-4 18:16 上传
下载附件 (303.75 KB)

效果看起来还行吧,如果觉得不错的话,记得要赞一个哦.


下面我们接着修改,模拟百度嘛,谁让百度这么牛叉呢.

思路:点中拖拉图标的时候,每次移动只要dragPosition发生改变,也就是我移动到了下一个位置,那么此时我就进行交换执行update.并且除了第一次移动外,在每次交换后要除去映射源的显示,这样用户觉得这里的空位就是就是为我准备的,比较人性化.

实现起来并不复杂,前提是你得掌握上面的操作.

源码如下;



001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
package com.jj.drag;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import com.jj.drag.MainActivity.DragListAdapter;
public class DragListView extends ListView {
    private WindowManager windowManager; // windows窗口控制类
    private WindowManager.LayoutParams windowParams; // 用于控制拖拽项的显示的参数
    private int scaledTouchSlop; // 判断滑动的一个距离,scroll的时候会用到(24)
    private ImageView dragImageView; // 被拖拽的项(item),其实就是一个ImageView
    private int dragSrcPosition; // 手指拖动项原始在列表中的位置
    private int dragPosition; // 手指点击准备拖动的时候,当前拖动项在列表中的位置.
    private int dragPoint; // 在当前数据项中的位置
    private int dragOffset; // 当前视图和屏幕的距离(这里只使用了y方向上)
    private int upScrollBounce; // 拖动的时候,开始向上滚动的边界
    private int downScrollBounce; // 拖动的时候,开始向下滚动的边界
    private final static int step = 1 ; // ListView 滑动步伐.
    private int current_Step; // 当前步伐.
    private int temChangId; // 临时交换id
    private boolean isLock; // 是否上锁.
    public void setLock( boolean isLock) {
        this .isLock = isLock;
    }
    public DragListView(Context context, AttributeSet attrs) {
        super (context, attrs);
        scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }
    /***
     * touch事件拦截 在这里我进行相应拦截,
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 按下
        if (ev.getAction() == MotionEvent.ACTION_DOWN && !isLock) {
            int x = ( int ) ev.getX(); // 获取相对与ListView的x坐标
            int y = ( int ) ev.getY(); // 获取相应与ListView的y坐标
            temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y);
            // 无效不进行处理
            if (dragPosition == AdapterView.INVALID_POSITION) {
                return super .onInterceptTouchEvent(ev);
            }
            // 获取当前位置的视图(可见状态)
            ViewGroup itemView = (ViewGroup) getChildAt(dragPosition
                    - getFirstVisiblePosition());
            // 获取到的dragPoint其实就是在你点击指定item项中的高度.
            dragPoint = y - itemView.getTop();
            // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).
            dragOffset = ( int ) (ev.getRawY() - y);
            // 获取可拖拽的图标
            View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);
            // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)
            if (dragger != null && x > dragger.getLeft() - 20 ) {
                upScrollBounce = getHeight() / 3 ; // 取得向上滚动的边际,大概为该控件的1/3
                downScrollBounce = getHeight() * 2 / 3 ; // 取得向下滚动的边际,大概为该控件的2/3
                itemView.setBackgroundColor(Color.BLUE);
                itemView.setDrawingCacheEnabled( true ); // 开启cache.
                Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache()); // 根据cache创建一个新的bitmap对象.
                startDrag(bm, y); // 初始化影像
            }
            return false ;
        }
        return super .onInterceptTouchEvent(ev);
    }
    /**
     * 触摸事件处理
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // item的view不为空,且获取的dragPosition有效
        if (dragImageView != null && dragPosition != INVALID_POSITION
                && !isLock) {
            int action = ev.getAction();
            switch (action) {
            case MotionEvent.ACTION_UP:
                int upY = ( int ) ev.getY();
                stopDrag();
                onDrop(upY);
                break ;
            case MotionEvent.ACTION_MOVE:
                int moveY = ( int ) ev.getY();
                onDrag(moveY);
                break ;
            case MotionEvent.ACTION_DOWN:
                break ;
            default :
                break ;
            }
            return true ; // 取消ListView滑动.
        }
        return super .onTouchEvent(ev);
    }
    /**
     * 准备拖动,初始化拖动项的图像
     *
     * @param bm
     * @param y
     */
    private void startDrag(Bitmap bm, int y) {
        // stopDrag();
        /***
         * 初始化window.
         */
        windowParams = new WindowManager.LayoutParams();
        windowParams.gravity = Gravity.TOP;
        windowParams.x = 0 ;
        windowParams.y = y - dragPoint + dragOffset;
        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( "window" );
        windowManager.addView(imageView, windowParams);
        dragImageView = imageView;
    }
    /**
     * 拖动执行,在Move方法中执行
     *
     * @param y
     */
    public void onDrag( int y) {
        int drag_top = y - dragPoint; // 拖拽view的top值不能<0,否则则出界.
        if (dragImageView != null && drag_top >= 0 ) {
            windowParams.alpha = 0 .5f;
            windowParams.y = y - dragPoint + dragOffset;
            windowManager.updateViewLayout(dragImageView, windowParams); // 时时移动.
        }
        // 为了避免滑动到分割线的时候,返回-1的问题
        int tempPosition = pointToPosition( 0 , y);
        if (tempPosition != INVALID_POSITION) {
            dragPosition = tempPosition;
        }
        onChange(y); // 时时交换
        doScroller(y); // listview移动.
    }
    /***
     * ListView的移动.
     * 要明白移动原理:当我移动到下端的时候,ListView向上滑动,当我移动到上端的时候,ListView要向下滑动。正好和实际的相反.
     *
     */
    public void doScroller( int y) {
        // Log.e("jj", "y=" + y);
        // Log.e("jj", "upScrollBounce=" + upScrollBounce);
        // ListView需要下滑
        if (y < upScrollBounce) {
            current_Step = step + (upScrollBounce - y) / 10 ; // 时时步伐
        } // ListView需要上滑
        else if (y > downScrollBounce) {
            current_Step = -(step + (y - downScrollBounce)) / 10 ; // 时时步伐
        } else {
            current_Step = 0 ;
        }
        // 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position)
        View view = getChildAt(dragPosition - getFirstVisiblePosition());
        // 真正滚动的方法setSelectionFromTop()
        setSelectionFromTop(dragPosition, view.getTop() + current_Step);
    }
    /**
     * 停止拖动,删除影像
     */
    public void stopDrag() {
        if (dragImageView != null ) {
            windowManager.removeView(dragImageView);
            dragImageView = null ;
        }
    }
    /***
     * 拖动时时change
     */
    private void onChange( int y) {
        // 数据交换
        if (dragPosition < getAdapter().getCount()) {
            DragListAdapter adapter = (DragListAdapter) getAdapter();
            adapter.isHidden = false ;
            if (dragPosition != temChangId) {
                adapter.update(temChangId, dragPosition);
                temChangId = dragPosition; // 将点击最初所在位置position付给临时的,用于判断是否换位.
            }
        }
        // 为了避免滑动到分割线的时候,返回-1的问题
        int tempPosition = pointToPosition( 0 , y);
        if (tempPosition != INVALID_POSITION) {
            dragPosition = tempPosition;
        }
        // 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置)
        if (y < getChildAt( 0 ).getTop()) {
            // 超出上边界
            dragPosition = 0 ;
            // 如果拖动超过最后一项的最下边那么就防止在最下边
        } else if (y > getChildAt(getChildCount() - 1 ).getBottom()) {
            // 超出下边界
            dragPosition = getAdapter().getCount() - 1 ;
        }
    }
    /**
     * 拖动放下的时候
     *
     * @param y
     */
    public void onDrop( int y) {
        // 数据交换
        if (dragPosition < getAdapter().getCount()) {
            DragListAdapter adapter = (DragListAdapter) getAdapter();
            adapter.isHidden = false ;
            adapter.notifyDataSetChanged(); // 刷新.
        }
    }
}

因为我们要时时交换位置,所以将原先的拖动方法onDrop方法移动到onChange中.具体的还是看源码吧.

另外的就是对适配器的修改,因为你要对特殊的item进行隐藏之类的操作,这些代码我就不写了,我会将案例上传网上,不懂的可以下载源码.

好了还是我们来观看下效果吧.

1354244742_6656.jpg

2012-12-4 18:16 上传
下载附件 (339.28 KB)

怎么样,这个效果看起来要比上面那个效果更人性化点吧,我的操作或许有点快,不信的话,你自己手机体验一下吧.

关于ListView拖拽就说到这里,如有不足请大家自己创新.


下面我们接着对GridView的拖拽简单说明.因为这些在项目中我们都会用到,所以既然做到就做全面点吧.好了大家接着往下看吧.

首先说明,原理一样,都是拖动映像,记录拖动位置,然后调用notifyDataSetChanged更新UI.

而GridView不同的是你要根据x,y值共同获取点击的position和移动至的position,而ListView因为不涉及x坐标.

嗯,最初的原始移动我就不给大家展示了,效果也不是很友好,我直接展示时时更新的那种方法.效果类是与上面那个时时更新ListView一样。

原理也一样.下面我们直接看代码吧.


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package com.jj.draggrid;
import java.util.logging.Handler;
import com.jj.draggrid.MainActivity.DragGridAdapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;
/***
* 自定义拖拽GridView
*
* @author zhangjia
*
*/
public class DragGridView extends GridView {
    private WindowManager windowManager; // windows窗口控制类
    private WindowManager.LayoutParams windowParams; // 用于控制拖拽项的显示的参数
    private int scaledTouchSlop; // 判断滑动的一个距离,scroll的时候会用到(24)
    private ImageView dragImageView; // 被拖拽的项(item),其实就是一个ImageView
    private int dragSrcPosition; // 手指拖动项原始在列表中的位置
    private int dragPosition; // 手指点击准备拖动的时候,当前拖动项在列表中的位置.
    private int dragPointX; // 在当前数据项中的位置
    private int dragPointY; // 在当前数据项中的位置
    private int dragOffsetX; // 当前视图和屏幕的距离(这里只使用了x方向上)
    private int dragOffsetY; // 当前视图和屏幕的距离(这里只使用了y方向上)
    private int upScrollBounce; // 拖动的时候,开始向上滚动的边界
    private int downScrollBounce; // 拖动的时候,开始向下滚动的边界
    private int temChangId; // 临时交换id
    private boolean isDoTouch = false ; // touch是否可用
    private boolean isHide = false ; // 是否隐藏
    private Handler handler;
    public void setDoTouch( boolean isDoTouch) {
        this .isDoTouch = isDoTouch;
    }
    public DragGridView(Context context, AttributeSet attrs) {
        super (context, attrs);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            int x = ( int ) ev.getX();
            int y = ( int ) ev.getY();
            temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y);
            if (dragPosition == AdapterView.INVALID_POSITION) {
                return super .onInterceptTouchEvent(ev);
            }
            ViewGroup itemView = (ViewGroup) getChildAt(dragPosition
                    - getFirstVisiblePosition());
            dragPointX = x - itemView.getLeft();
            dragPointY = y - itemView.getTop();
            dragOffsetX = ( int ) (ev.getRawX() - x);
            dragOffsetY = ( int ) (ev.getRawY() - y);
            View dragger = itemView.findViewById(R.id.drag_grid_item);
            /***
             * 判断是否选中拖动图标
             */
            if (dragger != null && dragPointX > dragger.getLeft()
                    && dragPointX < dragger.getRight()
                    && dragPointY > dragger.getTop()
                    && dragPointY < dragger.getBottom() + 20 ) {
                upScrollBounce = getHeight() / 4 ;
                downScrollBounce = getHeight() * 3 / 4 ;
                itemView.setDrawingCacheEnabled( true );
                Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());
                startDrag(bm, x, y); // 初始话映像
                dragger.setVisibility(View.INVISIBLE); // 隐藏该项.
            }
        }
        return super .onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (dragImageView != null && dragPosition != INVALID_POSITION
                && isDoTouch) {
            int action = ev.getAction();
            switch (action) {
            /***
             *
             */
            case MotionEvent.ACTION_UP:
                int upX = ( int ) ev.getX();
                int upY = ( int ) ev.getY();
                stopDrag(); // 删除映像
                onDrop(upX, upY); // 松开
                // isDoTouch = false;
                break ;
            /***
             * 拖拽item
             *
             */
            case MotionEvent.ACTION_MOVE:
                int moveX = ( int ) ev.getX();
                int moveY = ( int ) ev.getY();
                onDrag(moveX, moveY); // 拖拽
                break ;
            case MotionEvent.ACTION_DOWN:
                int downX = ( int ) ev.getX();
                int downY = ( int ) ev.getY();
                onHide(downX, downY); // 隐藏该项
                break ;
            default :
                break ;
            }
            return true ;
        }
        return super .onTouchEvent(ev);
    }
    /**
     * 准备拖动,初始化拖动项的图像
     *
     * @param bm
     * @param y
     */
    public void startDrag(Bitmap bm, int x, int y) {
        windowParams = new WindowManager.LayoutParams();
        windowParams.gravity = Gravity.TOP | Gravity.LEFT;
        windowParams.x = x - dragPointX + dragOffsetX;
        windowParams.y = y - dragPointY + dragOffsetY;
        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.windowAnimations = 0 ;
        ImageView imageView = new ImageView(getContext());
        imageView.setImageBitmap(bm);
        windowManager = (WindowManager) getContext().getSystemService( "window" );
        windowManager.addView(imageView, windowParams);
        dragImageView = imageView;
    }
    /***
     * 拖动时时change
     */
    private void onChange( int x, int y) {
        // 获取适配器
        DragGridAdapter adapter = (DragGridAdapter) getAdapter();
        // 数据交换
        if (dragPosition < getAdapter().getCount()) {
            // 不相等的情况下要进行换位,相等的情况下说明正在移动
            if (dragPosition != temChangId) {
                adapter.update(temChangId, dragPosition); // 进行换位
                temChangId = dragPosition; // 将点击最初所在位置position付给临时的,用于判断是否换位.
            }
        }
        // 为了避免滑动到分割线的时候,返回-1的问题
        int tempPosition = pointToPosition(x, y);
        if (tempPosition != INVALID_POSITION) {
            dragPosition = tempPosition;
        }
    }
    /***
     * 拖动执行,在Move方法中执行
     *
     * @param x
     * @param y
     */
    public void onDrag( int x, int y) {
        // 移动
        if (dragImageView != null ) {
            windowParams.alpha = 0 .8f;
            windowParams.x = x - dragPointX + dragOffsetX;
            windowParams.y = y - dragPointY + dragOffsetY;
            windowManager.updateViewLayout(dragImageView, windowParams);
        }
        onChange(x, y); // 时时交换
        // 滚动
        if (y < upScrollBounce || y > downScrollBounce) {
            // 使用setSelection来实现滚动
            setSelection(dragPosition);
        }
    }
    /***
     * 隐藏该选项
     */
    private void onHide( int x, int y) {
        // 获取适配器
        DragGridAdapter adapter = (DragGridAdapter) getAdapter();
        // 为了避免滑动到分割线的时候,返回-1的问题
        int tempPosition = pointToPosition(x, y);
        if (tempPosition != INVALID_POSITION) {
            dragPosition = tempPosition;
        }
        adapter.setIsHidePosition(dragPosition);
    }
    /**
     * 停止拖动,删除影像
     */
    public void stopDrag() {
        if (dragImageView != null ) {
            windowManager.removeView(dragImageView);
            dragImageView = null ;
        }
    }
    /***
     * 拖动放下的时候
     *
     * @param x
     * @param y
     */
    public void onDrop( int x, int y) {
        DragGridAdapter adapter = (DragGridAdapter) getAdapter();
        adapter.setIsHidePosition(- 1 ); // 不进行隐藏
    }
}

相信大家只要ListView拖拽弄白后,这个GridView也会轻易弄出来,其实拖拽就是对坐标的考察。

向大家展示一下效果:

1354518890_6212.jpg

2012-12-4 18:16 上传
下载附件 (203.68 KB)

但是有个不足的地方,网上一些例子都是长按可以拖拽,而点击则执行点击事件.其实实现起来也不是很复杂,可是在实现的过程中,遇到了诡异纠结的问题,郁闷了一天,结果目前先放弃,以后哪天在搞搞吧.纠结的问题就是错位.

我说下我的思路:首先,我们在自定义GridView中创建一个控制是否可以Touch拖拽的变量,而这个变量的值我们通过对GridView的setOnItemClickListener和setOnItemLongClickListener来获取,

如:


01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
gv_main.setOnItemClickListener( new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                gv_main.setDoTouch( false );
                Toast.makeText(MainActivity. this ,
                        adapter.getItemId(position) + "" , 1 ).show();
            }
        });
        gv_main.setOnItemLongClickListener( new OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view,
                    int position, long id) {
                gv_main.setDoTouch( true );
                return true ;
            }
        });

这样我们就实现了长按可以拖拽的效果了,可是遇到个变态的问题,不过这个思路没有错,肯定可以实现.

就先说到这里,其实通过这个例子,我们还可以拓展实现ListView上滑动的时候,到达Title时,Title停留在顶部,当下一个Titile滑动到这里的时候,那么代替前面那个TItle.网上有写应该就是这么搞的,具体实现不知道,不过这种方案可以实现,



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值