正确地动画删除ListView中的行?

时间:2013-08-05 02:08:38

标签: android android-listview android-animation

问题:

(1)向列表视图中的行添加触摸侦听器,以便轻扫。

(2)滑动动画播放

(3)在后端删除行,

(4)动画播放没有任何闪烁或急动。通过“闪烁”我的意思是在动画结束后简要显示删除的行。

我怀疑动画监听器发生了一些时髦的事情,所以我最终做了以下事情(按照给定的顺序完成):

  1. 通过将setFillAfter和setFillenabled设置为true来制作动画并使其保持不变
  2. 动画结束时使视图不可见
  3. 删除数据库中的行
  4. 重置动画
  5. 重新加载列表视图
  6. 使视图可见(但等待300毫秒)
  7. 结果删除没有急动或闪烁的行但是现在因为额外的300毫秒等待而感觉迟钝。 (我也不确定这种延迟是否适用于所有设备。)

    更新:我应该指出300毫秒的延迟是它的工作原理。这很奇怪,因为到那时动画被重置并且listview具有最新数据。应该没有理由让视图显示使旧行简要显示,对吧?

    我也尝试使用ViewPropertyAnimator(根据Using animation on a ViewPager and setFillAfter)但由于某种原因,onAnimationEnd侦听器在动画的每个步骤中被调用。

    我还读到我们应该实现一个自定义视图并覆盖它的onAnimationEnd监听器。 (但是,我还没有尝试过这种方法。)

    更新:刚尝试在最后添加一个额外的虚拟动画(根据Android Animation Flicker)。但是,这不起作用

    我的测试手机运行Ice Cream Sandwich。我的应用程序的目标是姜饼和之后。

    那么什么是正确的解决方案?我这样做是错误的吗?

    以下是代码:

     @Override
    public boolean onTouch(final View view, MotionEvent event)
    {
        //...
    
        switch(action) {
            //...
            case MotionEvent.ACTION_MOVE:
                // ...
                if (//check for fling
                {
                    view.clearAnimation();
                    //animation = standard translate animation
                    animation.setAnimationListener(new AnimationListener() {
                        //
    
                        @Override
                        public void onAnimationEnd(Animation animation)
                        {
                            view.setVisibility(View.INVISIBLE);
                            flingListener.onFling(cursorPosition, view, velocity);
                        }
    
                        //...
                    });
                    view.startAnimation(animation);
                }
                break;
          //
     }
    

    “狡猾的倾听者”:

        @Override
        public void onFling(int position, final View view, float velocity)
        {
            //delete row -- its actually a Loader
            //the following code runs in the Loader's onLoadFinished
            view.clearAnimation();
            adapter.notifyDataSetChanged();
            adapter.swapCursor(null);
    
            //reload listview -- it's actually a Loader
            //the following code runs in the Loader's onLoadFinished
            adapter.swapCursor(cursor);
            view.postDelayed(new Runnable() {
                @Override
                public void run()
                {
                    view.setVisibility(View.VISIBLE);
                }
            }, 300);
        }
    

    更新:在比较Chet Haase的代码之后,我们做了类似的事情,但有一些重要的区别:(1)他使用onPreDraw监听器和ListView树观察器进行实际删除,(2)他不仅从数组中删除了行,还从listview中删除了该行。在模仿他的代码后,它仍然无法正常工作。现在的问题是Loader ---我使用Loader异步删除行。 Loader似乎强制对ListView进行额外的绘制调用... 之前在后端删除了行。这是闪烁的(另一个)原因。仍然没有想出一个解决方法。

2 个答案:

答案 0 :(得分:2)

Chet Haase(谷歌工程师)在这个主题上汇集了一个非常好的DevBytes,我建议观察/从他的来源获取想法。如果您使用NineOldAndroids,我认为这将向后兼容GB。

在这里查看:

http://graphics-geek.blogspot.com/2013/06/devbytes-animating-listview-deletion.html

答案 1 :(得分:2)

正如我在评论中所指出的,Chet代码的问题在于其设计用于同步数据访问。一旦开始异步删除行,他的代码就会失败。

我通过将Chet的代码与这个答案结合起来找到了闪烁问题的解决方案:CursorAdapter backed ListView delete animation "flickers" on delete

以异步方式正确执行行删除的解决方案是:

  1. 为ListView树观察器创建onPreDraw侦听器以防止闪烁。此侦听器中的所有代码都在列表视图重新绘制之前运行,从而防止闪烁。
  2. 找到一种方法来删除列表视图中的行(但尚未在数据库中)。有两种方法(见CursorAdapter backed ListView delete animation "flickers" on delete):
    1. 创建一个AbstractCursor包装器,忽略要删除的行并将其交换为真实游标。 OR
    2. 将要删除的行标记为“已染色”,并在重绘行时对其进行适当操作。
  3. 在数据库中删除real for real行(异步)。
  4. 一些使用AbstractCursor包装器的伪代码(技术上称为“代理”):

        //Called when you swipe a row to delete
        @Override
        public void onFling(final int positionToRemove, final View view)
        {
            final ViewTreeObserver observer = listView.getViewTreeObserver();
            observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
            {
                public boolean onPreDraw()
                {
                    observer.removeOnPreDrawListener(this);
    
                    //remove the row from the matrix cursor
                    CursorProxy newCursor = new CursorProxy(cursor,
                                                            positionToRemove);
                    swapCursor(newCursor);
                    //delete row in database
                 }
            }
      }