Android可拖动排序GridView实现

jopen 10年前

经常使用今日头条、网易新闻的同学们应该都会注意到用于管理多个频道的可拖动排序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();      }  }