仿网易新闻客户端分类排序

4,099 阅读2分钟
原文链接: www.jianshu.com

效果图

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


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
紧接着,继续拖拽ce上,你会发现调用pointToPosition方法得到的position是5,但是e现在的索引是4
因此你只需要调用

mPositionList.indexOf(pointToPosition(x,y))

就能得到真实的item的position

其他

如果把GridView的列数变成1那么似曾相识啊


gridviewex.gif

源码地址

github.com/jiahuanyu/a…