正确动画ListView中删除行?正确、动画、ListView

2023-09-04 08:10:08 作者:一抹嫣红

问题:

(1)增添一抹监听器排在列表视图,使上刷卡。

(2)刷卡动画播放

(3)的行中的后端被删除,以及

(4)动画播放,没有任何闪烁或跳跃。通过忽悠我的意思是删除的行短暂显示动画结束后。

我怀疑,一些时髦正在发生与动画的听众,所以我最后做以下(在给定的顺序完成):

请动画,并使其通过设置setFillAfter坚持和setFillenabled为true 请视图不可见,当动画结束 删除数据库中的行 重置动画 刷新列表视图 请在视图中可见的(但再等待300毫秒)

结果删除了该行不急动或闪烁,但现在感觉呆滞,因为额外的300毫秒的等待。 (我也不知道这是否延迟工作在所有设备上。)

更新:我要指出的是,300毫秒的延迟是什么使得它的工作。这是奇怪的,因为到那个时候动画被重置和列表视图有最新的数据。不应该有任何理由使视图中可见,使旧行短暂显示,对吧?

我也尝试使用ViewPropertyAnimator(按Using动画在ViewPager和setFillAfter ),但由于某些原因,onAnimationEnd监听器被称为在的每次的动画步骤。

我也看了,我们应该实现一个自定义视图并覆盖其onAnimationEnd听众。 (不过,我没有尝试过这种方法呢。)

更新:只是试图在末尾加上一个额外的虚拟动画(按照 Android的动画闪烁 )。但是,这并不正常工作

我测试的手机上运行冰淇淋三明治。我的应用程序的目标是姜饼之后。

那么,什么是正确的解决方案?难道我这做了错误的方式?

这里的code:

  @覆盖
公共布尔onTouch(最终查看视图,MotionEvent事件)
{
    // ...

    开关(动作){
        // ...
        案例MotionEvent.ACTION_MOVE:
            // ...
            如果(//检查一扔
            {
                view.clearAnimation();
                //动画=标准翻译动画
                animation.setAnimationListener(新AnimationListener(){
                    //

                    @覆盖
                    公共无效onAnimationEnd(动画动画)
                    {
                        view.setVisibility(View.INVISIBLE);
                        flingListener.onFling(cursorPosition,查看,速度);
                    }

                    // ...
                });
                view.startAnimation(动画);
            }
            打破;
      //
 }
 

该一扔监听器:

  @覆盖
    公共无效onFling(INT位置,最终意见认为,浮法速度)
    {
        //删除行 - 它实际上是一个装载机
        //以下code,在装载机的运行onLoadFinished
        view.clearAnimation();
        adapter.notifyDataSetChanged();
        adapter.swapCursor(空);

        //重载列表视图 - 它实际上是一个装载机
        //以下code,在装载机的运行onLoadFinished
        adapter.swapCursor(光标);
        view.postDelayed(新的Runnable(){
            @覆盖
            公共无效的run()
            {
                view.setVisibility(View.VISIBLE);
            }
        },300);
    }
 

更新:的比较后切特·哈泽的code,我们都在做类似的事情,有一些重要的不同之处:(1)他用的是上preDraw监听器与ListView树观察者做实际的删除,(2)他移除行不仅从阵列但也从列表视图。模仿他的code后,它仍然没有奏效。现在的问题是装载机---我使用Loader异步删除行。装载程序似乎强制额外绘制调用到ListView ...的在的该行已在后台删除。这就是闪烁的(其他原因)。还没有想出一个解决办法,但。

解决方案

正如我在评论中,有切特的code中的问题是,它设计用于同步数据访问。一旦你开始异步删除行,他的code失败。

我找到了解决闪烁问题相结合,切特的code这个答案:CursorAdapter支持ListView中删除动画和QUOT;瞬"在删除

C 在access中删除listview上选中的一行数据

要正确地做一个行删除aynchronously解决的办法是:

创建一个在preDraw监听器为ListView树观察员prevent闪烁。所有code在这个监听器的列表视图中重新绘制本身,preventing闪烁之前运行。 找到一种方法来删除列表视图(在数据库中,但还没有)的行。有两种方法(见CursorAdapter支持ListView中删除动画和QUOT;闪烁"在删除): 创建一个AbstractCursor包装,忽略被删除,将其交换为真正的游标行。或 标记的行被删除染色,并重绘该行适当的时候采取行动。 删除行真正的数据库(异步)。

使用AbstractCursor包装(它在技术上被称为代理)一些伪code:

  //当你刷卡行删除名为
    @覆盖
    公共无效onFling(最终诠释positionToRemove,最终的视图中查看)
    {
        最后ViewTreeObserver观察者= listView.getViewTreeObserver();
        observer.addOn preDrawListener(新ViewTreeObserver.On preDrawListener()
        {
            在preDraw公共布尔()
            {
                observer.removeOn preDrawListener(本);

                //从矩阵游标删除行
                CursorProxy newCursor =新CursorProxy(光标,
                                                        positionToRemove);
                swapCursor(newCursor);
                //删除数据库行
             }
        }
  }
 

The problem:

(1) add a touch listener to rows in a listview so that on swipe.

(2) swipe animation plays

(3) rows get deleted in the backend, and

(4) the animation plays without any flicker or jerkiness. By "flicker" I mean that the deleted row briefly shows after the animation finished.

I suspect that something funky was happening with the animation listener, so I ended up doing the following (done in the order given):

Do the animation and make it persist by setting setFillAfter and setFillenabled to true Make the view invisible when the animation ends Delete the row in the database Reset the animation Reload the listview Make the view visible (but wait an additional 300 ms)

The result deletes the row without jerkiness or flicker BUT it now feels sluggish because of extra 300 ms wait. (I'm also not sure if this delay works across all devices.)

Update: I should point out that the 300 ms delay is what makes it work. That's weird because by that point the animation was reset and the listview has the latest data. There should be no reason why making the view visible makes the old row briefly show, right?

I also tried using a ViewPropertyAnimator (as per Using animation on a ViewPager and setFillAfter) but for some reason the onAnimationEnd listener was called at every step of the animation.

I also read that we should implement a custom view and override its onAnimationEnd listener. (However, I haven't tried that approach yet.)

Update: just tried to add an extra dummy animation at the end (as per Android Animation Flicker). However, that doesn't work

My test phone runs Ice Cream Sandwich. My app is targeting Gingerbread and after.

So what's the proper solution? Am I doing this the wrong way?

Here's the code:

 @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;
      //
 }

The "fling listener":

    @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);
    }

Update: After comparing Chet Haase's code, we are doing similar things with some important differences: (1) he uses a onPreDraw listener with the ListView tree observer to do the actual deletion, (2) he removes the row not only from the array but also from the listview. After mimicking his code, it still didn't work. The problem is now the Loader---I use a Loader to delete rows asynchronously. The Loader seems to force an additional draw call to the ListView...before the row has been deleted in the backend. And that's (another) cause of the flicker. Still haven't figured out a workaround though.

解决方案

As I pointed out in the comments, the problem with Chet's code is that its designed for synchronous data access. Once you start asynchronously deleting rows, his code fails.

I found the solution to the flicker problem by combining Chet's code with this answer: CursorAdapter backed ListView delete animation "flickers" on delete

The solution to correctly do a row deletion aynchronously is:

Create a onPreDraw listener for the ListView tree observer to prevent flicker. All code in this listener runs before the list view re-draws itself, preventing flicker. Find a way to delete the row in the listview (but not yet in the database). There are two approaches (see CursorAdapter backed ListView delete animation "flickers" on delete):

Create a AbstractCursor wrapper that ignores the row to be deleted and swap it in for the real cursor. OR Mark the row to be deleted as "stained" and act on it appropriately when redrawing the row.

Remove the row for real in the database (asynchronously).

Some pseudo-code using the AbstractCursor wrapper (it's technically called a "proxy"):

    //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
             }
        }
  }