转载地址:http://www.jianshu.com/p/7be162740a95


效果图

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


wangyixinwen.gif [Android] 仿网易新闻客户端分类排序_第1张图片
gridviewsort.gif

如何实现拖拽一个Item

WindowManager添加一个ImageView,并且将这个ImageView的显示图片设置成被拖拽item的截图,截图可以通过ViewgetDrawingCache获得。拖拽的时候,隐藏原始的item。处理触摸事件的ActionMove,调整ImageView的位置,跟随手指移动。在ActionUp的时候removeView

GridView

 @Override    public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l)    {        // 至少有两个item的时候,才有排序        if (getChildCount() >= 2)        {            mView = view;            // 在调用getDrawingCache必须先调用            view.setDrawingCacheEnabled(true);            // 获取截图并设置            Bitmap bitmap = view.getDrawingCache();            mDragItemView.setImageBitmap(bitmap);            // 设置拖拽的imageview的params            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);            // 设置拖拽imageview的中心位于长按点击点            mDragItemLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  //不接受按键事件                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE  // 不接收触摸事件                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON   // 保持常亮                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; // place the window within the entire screen, ignoring decorations around the border (such as the status bar)            mDragItemLayoutParams.format = PixelFormat.TRANSLUCENT;            mDragItemLayoutParams.windowAnimations = 0;            // 往WindowManager中添加拖拽的View            mWindowManager.addView(mDragItemView, mDragItemLayoutParams);            ((GridViewSortAdapter) getAdapter()).init();            ((GridViewSortAdapter) getAdapter()).hideView(i);            Log.d(TAG, "long click = " + i);            mDragStarted = true;        }        return true;    }
@Overridepublic 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);               // 更新params               mWindowManager.updateViewLayout(mDragItemView, mDragItemLayoutParams);               // ......           }           break;       case MotionEvent.ACTION_UP:           // ......           break;   }   return super.onTouchEvent(ev);}

如何实现隐藏拖拽的Item

在开始拖拽的时候,把隐藏的item的position告诉Adapter,调用AdapternotifyDataSetChanged刷新数据,在getView方法中判断当前的构建的item的position是不是需要隐藏的position是的话就设置viewinVisible

GridView

@Overridepublic boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l){    // ......    ((GridViewSortAdapter) getAdapter()).hideView(i);    // ......}

GridViewSortAdapter

public void hideView(int item){    // ......    mStartHideItemPosition = item;    notifyDataSetChanged();}
private int mStartHideItemPosition = AdapterView.INVALID_POSITION;@Overridepublic 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之上

要想在拖拽到其他item上面时互换位置,那必须得知道当前拖拽到了哪一个item之上。GrideView提供了一个方法叫pointToPosition,可以在处理触摸事件的ACTION_MOVE时,获取手指触摸的x,y来得到当前拖拽到item之上的position。这里需要注意的一点是,在拖拽的过程,同一个item的position是不会变的,除非调用了AdapternotifyDataSetChangedposition才会重新计算。比如position为2的item,在拖拽的过程无论怎么动画移动位置,他的position都是2,知道一次拖拽结束,ActionUp的时候,会调用notifyDataSetChanged

GridView

@Override public boolean onTouchEvent(MotionEvent ev) {    case MotionEvent.ACTION_MOVE:    if (mDragStarted)    {        // ......        int position = pointToPosition((int) ev.getX(), (int) ev.getY());        // ......    }    break;}

如何实现动画

一个item需要水平以及垂直需要移动的距离可以事先先计算出来,其实水平距离不管怎么样一定会是GridView一个单元格的宽度加上水平间距,垂直距离无论如何都是一个单元格的高度加上垂直距离,宽度非常好取,高度的话,这里默认item 的高度和单元格的高度相同。

GridViewSortAdapter

View view = mGridView.getChildAt(0);mTranslateX = view.getWidth() + mHorizontalSpace;mTranslateY = view.getHeight() + mVerticalSpace;

当拖拽到其他item之上时,开始动画

SortGridView

if (position != AdapterView.INVALID_POSITION && !((GridViewSortAdapter) getAdapter()).isInAnimation()){         Log.d(TAG, "position = " + position);         ((GridViewSortAdapter) getAdapter()).swap(position);}

GridSortAdapter

public void swap(int position){    mAnimatorSetList.clear();    int r_p = mPositionList.indexOf(position);    Log.d(TAG, "r_p = " + r_p);    if (mCurrentHideItemPosition < r_p)    {        for (int i = mCurrentHideItemPosition + 1; i <= r_p; i++)        {            View v = mGridView.getChildAt(mPositionList.get(i));            if (i % mColsNum == 0 && i > 0)            {                startMoveAnimation(v, v.getTranslationX() + mTranslateX * (mColsNum - 1), v.getTranslationY() -                        mTranslateY);            }            else            {                startMoveAnimation(v, v.getTranslationX() - mTranslateX, 0);            }        }    }    else if (mCurrentHideItemPosition > r_p)    {        for (int i = r_p; i < mCurrentHideItemPosition; i++)        {            View v = mGridView.getChildAt(mPositionList.get(i));            if ((i + 1) % mColsNum == 0)            {                startMoveAnimation(v, v.getTranslationX() - mTranslateX * (mColsNum - 1), v.getTranslationY() + mTranslateY);            }            else            {                startMoveAnimation(v, v.getTranslationX() + mTranslateX, 0);            }        }    }    resetPositionList();    int value = mPositionList.get(mStartHideItemPosition);    if (mStartHideItemPosition < r_p)    {        mPositionList.add(r_p + 1, value);        mPositionList.remove(mStartHideItemPosition);    }    else if (mStartHideItemPosition > r_p)    {        mPositionList.add(r_p, value);        mPositionList.remove(mStartHideItemPosition + 1);    }    mCurrentHideItemPosition = r_p;}public boolean isInAnimation(){    return mInAnimation;}private void resetPositionList(){    mPositionList.clear();    for (int i = 0; i < mGridView.getChildCount(); i++)    {        mPositionList.add(i);    }}private void startMoveAnimation(View myView, float x, float y){    AnimatorSet set = new AnimatorSet();    set.playTogether(            ObjectAnimator.ofFloat(myView, "translationX", myView.getTranslationX(), x),            ObjectAnimator.ofFloat(myView, "translationY", myView.getTranslationY(), y)    );    set.addListener(new Animator.AnimatorListener()    {        @Override        public void onAnimationStart(Animator animator)        {            mInAnimation = true;        }        @Override        public void onAnimationEnd(Animator animator)        {            mInAnimation = false;        }        @Override        public void onAnimationCancel(Animator animator)        {        }        @Override        public void onAnimationRepeat(Animator animator)        {        }    });    mAnimatorSetList.add(set);    set.setDuration(150).start();}

这里我主要解释一下代码中 mPositionList这个列表的作用,之前说过一次拖拽的时候,item的position是不会变化的。

假设有一组数据

a b c de f g hi j k l

此时mPositionList的内容就是 0 1 2 3 4 5 6 7 8 9 10 11 12
现在将c拖拽到g上,拖拽完成之后的数据应该是,未释放手指

a b d ef g c hi 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那么似曾相识啊

[Android] 仿网易新闻客户端分类排序_第2张图片
gridviewex.gif

源码地址

https://github.com/jiahuanyu/android-example-code/tree/master/app/src/main/java/com/github/jiahuanyu/example/ui/dragsortgird



文/Abbott(简书作者)
原文链接:http://www.jianshu.com/p/7be162740a95
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

更多相关文章

  1. android常驻通知栏点击事件
  2. Android Wear 交互表面设计及Android 双击事件监测实现
  3. Android GridView根据Item的行数设置高度
  4. Android 如何提前知道fling velocity的距离、时间 根据滑动距离
  5. Android ListView/ListActivity点击长按事件
  6. 关于android软键盘enter键的替换与事件监听

随机推荐

  1. android中图片倒影、圆角效果重绘
  2. Android全屏显示 无标题栏、全屏、设置为
  3. Android(安卓)Adapter
  4. Android(安卓)Settings中快速搜索流程
  5. SAX解析XML文件
  6. android 学习五 设置应用程序全屏(没有状
  7. Android(安卓)Android使用JSON与服务器交
  8. 修改 android版本 签名问题
  9. Android(安卓)keyevent 中的各个值
  10. Android(安卓)getDecorView用途——屏幕