欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-22 21:17:52
先来看看网易新闻客户端以及自己实现的效果图,效果当然还是网易的好 gridviewsort.gif 如何实现拖拽一个item 用windowmanager添加...

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

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

源码地址

以上所述是小编给大家介绍的android 仿网易新闻客户端分类排序功能,希望对大家有所帮助