效果图
先来看看网易新闻客户端以及自己实现的效果图,效果当然还是网易的好

wangyixinwen.gif

gridviewsort.gif
使用注意事项
一开始的思路并不是想把这个做成通用库,因此只有在item的宽度和高度撑满GridView的一个单元格的宽度和高度时候,才能使用。本文讲个原理,代码大家自己理解着修改。
思路
如何实现拖拽一个Item
这个比较容易想到,就是与类似360啊,什么的桌面上面有个小球可以拖来拖去的玩意原理是一样的,使用WindowManager去添加一个ImageView,并且将这个ImageView赋值拖拽item的Bitmap截图。
核心代码如下
@Override
public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l)
{
view.setDrawingCacheEnabled(true);
mView = view;
Bitmap bitmap = view.getDrawingCache();
mDragItemLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
mDragItemLayoutParams.width = bitmap.getWidth();
mDragItemLayoutParams.height = bitmap.getHeight();
mDragItemLayoutParams.x = (mDownX - mDragItemLayoutParams.width / 2);
mDragItemLayoutParams.y = (mDownY - mDragItemLayoutParams.height / 2);
mDragItemLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
mDragItemLayoutParams.format = PixelFormat.TRANSLUCENT;
mDragItemLayoutParams.windowAnimations = 0;
mDragItemView.setImageBitmap(bitmap);
mWindowManager.addView(mDragItemView, mDragItemLayoutParams);
// ......
}
@Override
public boolean onTouchEvent(MotionEvent ev)
{
switch (ev.getAction() & ev.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
mDownX = (int) ev.getRawX();
mDownY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (mDragStarted)
{
mDragItemLayoutParams.x = (int) (ev.getRawX() - mDragItemView.getWidth() / 2);
mDragItemLayoutParams.y = (int) (ev.getRawY() - mDragItemView.getHeight() / 2);
mWindowManager.updateViewLayout(mDragItemView, mDragItemLayoutParams);
// ......
}
break;
case MotionEvent.ACTION_UP:
// ......
break;
}
return super.onTouchEvent(ev);
}
如何实现隐藏拖拽的Item
在开始拖拽的时候,把隐藏的item的position告诉Adapter
@Override
public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l)
{
// ......
((GridViewSortAdapter) getAdapter()).hideView(i);
// ......
}
public void hideView(int item)
{
// ......
mStartHideItemPosition = item;
notifyDataSetChanged();
}
private int mStartHideItemPosition = AdapterView.INVALID_POSITION;
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder = null;
if (convertView == null)
{
convertView = LayoutInflater.from(mContext).inflate(R.layout.view_item_grid_view_sort, null);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.view_item_grid_view_sort_title);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(mTypeTitle.get(position));
if (mStartHideItemPosition == position)
{
convertView.setVisibility(View.INVISIBLE);
}
else
{
convertView.setVisibility(View.VISIBLE);
}
return convertView;
}
如何知道拖拽到了第几个item之上
GrideView提供了一个方法叫pointToPosition,可以通过ACTION_MOVE的x,y去得到当前拖拽到了第几个item之上
@Override
public boolean onTouchEvent(MotionEvent ev)
{
case MotionEvent.ACTION_MOVE:
if (mDragStarted)
{
// ......
int position = pointToPosition((int) ev.getX(), (int) ev.getY());
// ......
}
break;
}
如何实现动画
首先将一个item需要移动的距离,包括垂直方向以及水平方向的距离计算出来,然后在AcitonMove的时候去判断,如何进行动画。
动画很好实现,这里我采用了属性动画去实现,低版本的API可以使用开源的nineoldandroid开源动画库。
这里我主要解释一下代码中 mPositionList这个列表的作用。
只有调用了notifyDataSetChanged之后,列表中的item的顺序才会重新排列
假设有一组数据
a b c d
e f g h
i j k l
此时mPositionList的内容就是 0 1 2 3 4 5 6 7 8 9 10 11 12
现在将c拖拽到g上,拖拽完成之后的数据应该是,未释放手指
a b d e
f g c h
i j k l
此时mPositionList的内容就是 0 1 2 4 5 6 7 3 8 9 10 11 12
紧接着,继续拖拽c到e上,你会发现调用pointToPosition方法得到的position是5,但是e现在的索引是4
因此你只需要调用
mPositionList.indexOf(pointToPosition(x,y))
就能得到真实的item的position
其他
如果把GridView的列数变成1那么似曾相识啊

gridviewex.gif