Android可拖动排序GridView实现
jopen
10年前
经常使用今日头条、网易新闻的同学们应该都会注意到用于管理多个频道的可拖动排序GridView,下面介绍一下可拖动的DragGridView 的实现方法。代码放在GitHub上https://github.com/zhaoyu87/DragGridView,需要的同学可以下载
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(); } }