【问题标题】:Correctly animate removing row in ListView?正确地为 ListView 中的删除行设置动画?
【发布时间】:2013-08-05 02:08:38
【问题描述】:

问题:

(1) 为列表视图中的行添加一个触摸监听器,以便在滑动时。

(2) 滑动动画播放

(3) 行在后端被删除,并且

(4) 动画播放时没有任何闪烁或抖动。 “闪烁”是指动画完成后删除的行会短暂显示。

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

  1. 通过将 setFillAfter 和 setFillenabled 设置为 true 来制作动画并使其持久化
  2. 动画结束时使视图不可见
  3. 删除数据库中的行
  4. 重置动画
  5. 重新加载列表视图
  6. 使视图可见(但再等待 300 毫秒)

结果删除该行时没有抖动或闪烁,但由于额外的 300 毫秒等待,它现在感觉迟缓。 (我也不确定这种延迟是否适用于所有设备。)

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

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

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

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

我的测试手机运行 Ice Cream Sandwich。我的应用面向 Gingerbread 及之后的应用。

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

代码如下:

 @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)他不仅从数组中删除了该行,还从列表视图中删除了该行。模仿他的代码后,还是不行。现在的问题是加载器——我使用加载器异步删除行。 Loader 似乎强制对 ListView 进行额外的绘制调用...该行在后端被删除之前。这是闪烁的(另一个)原因。不过还没有找到解决方法。

【问题讨论】:

  • 请在投票前阅读。您提供的链接仅适用于 Android 4.1 及更高版本。我需要姜饼和之后。如果你读过,你就会意识到
  • 你不要检查这个graphics-geek.blogspot.in。它也有姜饼的代码。我不会投反对票。我将其作为副本关闭。 developer.android.com/shareables/devbytes/…。我没有测试过代码。但它应该可以工作,因为 Cher haase 是一名谷歌工程师。
  • 我赞成你的问题,但我仍然认为它是重复的,我不知道我是否可以回滚(封闭投票)因为我已经将它标记为重复。
  • 啊,好的,谢谢。我看到姜饼代码并进行调查。谢谢

标签: android android-listview android-animation


【解决方案1】:

Chet Haase(Google 工程师)针对这个主题编写了一个非常好的 DevBytes,我建议您观看/从他的来源中获取想法。如果您使用 NineOldAndroids,我认为这将向后兼容 GB。

在这里查看:

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

【讨论】:

  • 不幸的是,它之所以有效,是因为它依赖于 Android 4.1 的 withEndAction() 功能。不幸的是,早期版本中的等效功能(动画侦听器)已损坏。因此,我的问题。
  • 是的,这就是我要查看 nineoldandroids.com 的原因,它提供了动画 api 的向后兼容版本。
  • 是的,推出原生动画代码来使用 Nineoldandroids,你很好。
  • 我试图避免将另一个库用于应该很简单的东西。但如果我想不通,肯定会调查
【解决方案2】:

正如我在 cmets 中指出的,Chet 代码的问题在于它是为同步数据访问而设计的。一旦你开始异步删除行,他的代码就会失败。

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

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

  1. 为 ListView 树观察器创建一个 onPreDraw 监听器以防止闪烁。此侦听器中的所有代码在列表视图重新绘制之前运行,防止闪烁。
  2. 找到一种方法来删除列表视图中的行(但还没有在数据库中)。有两种方法(参见CursorAdapter backed ListView delete animation "flickers" on delete):
    1. 创建一个 AbstractCursor 包装器,它忽略要删除的行并将其换成真正的游标。或
    2. 将要删除的行标记为“染色”,并在重绘行时对其进行适当的操作。
  3. 在数据库中删除该行(异步)。

一些使用 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
             }
        }
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    • 2012-09-17
    • 1970-01-01
    • 2014-03-07
    • 1970-01-01
    • 2019-07-28
    • 1970-01-01
    相关资源
    最近更新 更多