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

作者:l_215851356 时间:2023-12-19 13:07:52 

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

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

gridviewsort.gif

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

如何实现拖拽一个Item

用WindowManager添加一个ImageView,并且将这个ImageView的显示图片设置成被拖拽item的截图,截图可以通过View的getDrawingCache获得。拖拽的时候,隐藏原始的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;
 }
@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);
       // 更新params
       mWindowManager.updateViewLayout(mDragItemView, mDragItemLayoutParams);
       // ......
     }
     break;
   case MotionEvent.ACTION_UP:
     // ......
     break;
 }
 return super.onTouchEvent(ev);
}

如何实现隐藏拖拽的Item

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

GridView


@Override
public 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;
@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之上

要想在拖拽到其他item上面时互换位置,那必须得知道当前拖拽到了哪一个item之上。GrideView提供了一个方法叫pointToPosition,可以在处理触摸事件的ACTION_MOVE时,获取手指触摸的x,y来得到当前拖拽到item之上的position。这里需要注意的一点是,在拖拽的过程,同一个item的position是不会变的,除非调用了Adapter的notifyDataSetChanged,position才会重新计算。比如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 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那么似曾相识啊

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

gridviewex.gif

源码地址

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

以上所述是小编给大家介绍的Android 仿网易新闻客户端分类排序功能网站的支持!

来源:http://blog.csdn.net/l_215851356/article/details/53884004

标签:android,网易,分类,排序
0
投稿

猜你喜欢

  • Java Map接口及其实现类原理解析

    2022-06-04 22:54:29
  • spring boot org.junit.jupiter.api不存在的解决

    2023-07-11 18:34:16
  • 解析ScrollView--仿QQ空间标题栏渐变

    2021-06-20 22:44:54
  • Android对话框使用方法详解

    2023-11-09 03:37:51
  • C#敏感词过滤实现方法

    2022-11-28 12:00:31
  • Java 深入探讨设计模式之原型模式篇

    2023-11-16 17:37:59
  • Java利用MessageFormat实现短信模板的匹配

    2023-02-19 11:41:54
  • android studio编译jar包或者aar包的方法教程详解

    2023-06-18 17:22:32
  • Android 校验email是否合法实现代码

    2021-06-02 05:18:24
  • 一篇文章带你入门Springboot沙箱环境支付宝支付(附源码)

    2021-06-26 23:21:16
  • Android使用多线程进行网络聊天室通信

    2022-05-11 18:56:36
  • 深入浅析java中flyway使用简介

    2022-06-05 20:45:45
  • 从Hello World开始理解GraphQL背后处理及执行过程

    2023-06-04 00:25:53
  • java中sleep方法和wait方法的五个区别

    2023-08-27 18:37:23
  • JAVA泛型的继承和实现、擦除原理解析

    2023-03-12 19:37:09
  • springboot实现多文件上传功能

    2022-05-31 22:49:10
  • 浅谈java实现背包算法(0-1背包问题)

    2022-04-28 15:23:43
  • 详解Spring Boot集成MyBatis(注解方式)

    2023-10-03 17:45:47
  • Java并发LinkedBlockingQueue源码分析

    2022-09-01 08:35:30
  • Java零基础入门数组

    2023-05-21 00:09:16
  • asp之家 软件编程 m.aspxhome.com