【问题标题】:How to Animate Addition or Removal of Android ListView Rows如何动画添加或删除 Android ListView 行
【发布时间】:2011-04-25 02:23:12
【问题描述】:

在 iOS 中,有一个非常简单而强大的工具来动画添加和删除 UITableView 行,here's a clip from a youtube video 显示默认动画。请注意周围的行如何折叠到已删除的行上。此动画可帮助用户跟踪列表中的更改以及数据更改时他们查看的列表中的位置。

自从我一直在 Android 上进行开发以来,我发现没有类似的工具可以为 TableView 中的各个行设置动画。在我的 Adapter 上调用 notifyDataSetChanged() 会导致 ListView 立即使用新信息更新其内容。我想在数据更改时显示一个新行推入或滑出的简单动画,但我找不到任何记录在案的方法来做到这一点。看起来LayoutAnimationController 可能是让它工作的关键,但是当我在我的 ListView 上设置一个 LayoutAnimationController(类似于ApiDemo's LayoutAnimation2)并在列表显示后从我的适配器中删除元素时,元素会立即消失而不是变得生动起来。

我还尝试了以下方法来在单个项目被删除时对其进行动画处理:

@Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
    Animation animation = new ScaleAnimation(1, 1, 1, 0);
    animation.setDuration(100);
    getListView().getChildAt(position).startAnimation(animation);
    l.postDelayed(new Runnable() {
        public void run() {
            mStringList.remove(position);
            mAdapter.notifyDataSetChanged();
        }
    }, 100);
}

但是,动画行周围的行不会移动位置,直到调用notifyDataSetChanged() 时它们跳转到新位置。似乎 ListView 在放置元素后不会更新其布局。

虽然我想到了编写自己的 ListView 实现/分支,但这似乎不应该那么困难。

谢谢!

【问题讨论】:

  • 有人找到答案了吗?请分享
  • @Alex:你有没有像 youtube 视频(如 iphone)那样制作动画,如果你有任何 android 的演示或链接,请给我,我尝试在 xml 文件中使用 animateLayoutChanges,但它不完全像 iphone
  • @Jayesh,很遗憾我不再从事 Android 开发工作。我还没有测试过 2012 年左右之后写的这个问题的任何答案。

标签: android listview animation


【解决方案1】:
Animation anim = AnimationUtils.loadAnimation(
                     GoTransitApp.this, android.R.anim.slide_out_right
                 );
anim.setDuration(500);
listView.getChildAt(index).startAnimation(anim );

new Handler().postDelayed(new Runnable() {

    public void run() {

        FavouritesManager.getInstance().remove(
            FavouritesManager.getInstance().getTripManagerAtIndex(index)
        );
        populateList();
        adapter.notifyDataSetChanged();

    }

}, anim.getDuration());

用于自上而下的动画:

<set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate android:fromYDelta="20%p" android:toYDelta="-20"
            android:duration="@android:integer/config_mediumAnimTime"/>
        <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
            android:duration="@android:integer/config_mediumAnimTime" />
</set>

【讨论】:

  • 最好使用anim.setAnimationListener(new AnimationListener() { ... })而不是使用Handler
  • populateList() 是干什么用的?
  • @MrAppleBR 有几个原因。首先,您没有创建不需要的Handler。其次,它在动画结束时已经使用了内置回调,从而降低了代码的复杂性。第三,你不知道HandlerAnimation的实现在各个平台上会如何工作;您的延迟运行可能会在动画结束之前发生。
  • "但是,动画行周围的行不会移动位置,直到调用 notifyDataSetChanged() 时它们跳转到新位置。"这个问题仍然存在于您的解决方案中。
  • 什么是 FavouritesManager 类和 populateList() 方法??
【解决方案2】:

【讨论】:

  • 当你需要一个简单的动画时,我认为这是最简单的方法,但你也可以使用 RecyclerView.setItemAnimator() 方法自定义动画。
  • 最后一步是我缺少的链接。稳定的身份证。谢谢!
  • 这是一个很好的示例项目,很好地展示了 API。
【解决方案3】:

查看 Google 解决方案。这里只是一种删除方法。

ListViewRemovalAnimation项目代码和Video演示

它需要 Android 4.1+ (API 16)。但我们还有 2014 年。

【讨论】:

    【解决方案4】:

    由于ListViews 高度优化,我认为这是不可能的。您是否尝试过通过代码创建您的“ListView”(即通过从 xml 扩充您的行并将它们附加到 LinearLayout)并为它们设置动画?

    【讨论】:

    • 仅仅为了添加动画而重新实现listview是没有意义的。
    • 我当然可以只使用LinearLayout,但是对于非常大的数据集LinearLayout 的性能将变得很糟糕。 ListView 围绕视图回收进行了许多智能优化,使其能够处理大型数据集(iOS 的 UITableView 具有相同的优化)。编写我自己的视图回收器属于重新实现 ListView 的类别:(
    • 我知道这没有意义 - 但如果您只使用几行,那么拥有漂亮的输入/输出动画的好处可能值得一试。
    【解决方案5】:

    您是否考虑过制作向右扫掠的动画?您可以执行一些操作,例如在列表项的顶部绘制一个逐渐变大的白条,然后将其从列表中删除。其他单元仍会猛拉到位,但总比没有好。

    【讨论】:

      【解决方案6】:

      调用 listView.scheduleLayoutAnimation(); 在更改列表之前

      【讨论】:

        【解决方案7】:

        我拼凑了另一种方法来做到这一点,而不必操纵列表视图。不幸的是,常规的 Android 动画似乎可以操纵行的内容,但实际上无法缩小视图。所以,首先考虑这个处理程序:

        private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            Bundle bundle = message.getData();
        
            View view = listView.getChildAt(bundle.getInt("viewPosition") - 
                listView.getFirstVisiblePosition());
        
            int heightToSet;
            if(!bundle.containsKey("viewHeight")) {
                Rect rect = new Rect();
                view.getDrawingRect(rect);
                heightToSet = rect.height() - 1;
            } else {
                heightToSet = bundle.getInt("viewHeight");
            }
        
            setViewHeight(view, heightToSet);
        
            if(heightToSet == 1)
                return;
        
            Message nextMessage = obtainMessage();
            bundle.putInt("viewHeight", (heightToSet - 5 > 0) ? heightToSet - 5 : 1);
            nextMessage.setData(bundle);
            sendMessage(nextMessage);
        }
        

        将此集合添加到您的列表适配器:

        private Collection<Integer> disabledViews = new ArrayList<Integer>();
        

        并添加

        public boolean isEnabled(int position) {
           return !disabledViews.contains(position);
        }
        

        接下来,无论您要隐藏哪一行,添加以下内容:

        Message message = handler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putInt("viewPosition", listView.getPositionForView(view));
        message.setData(bundle);
        handler.sendMessage(message);    
        disabledViews.add(listView.getPositionForView(view));
        

        就是这样!您可以通过更改一次缩小高度的像素数来更改动画的速度。不是很复杂,但很有效!

        【讨论】:

          【解决方案8】:

          向 ListView 插入新行后,我只需将 ListView 滚动到新位置。

          ListView.smoothScrollToPosition(position);
          

          【讨论】:

            【解决方案9】:

            我还没有尝试过,但看起来 animateLayoutChanges 应该可以满足您的需求。我在 ImageSwitcher 类中看到它,我假设它也在 ViewSwitcher 类中?

            【讨论】:

            • 嘿,谢谢,我会调查的。不幸的是,animateLayoutChanges 似乎是 API 级别 11 又名 Honeycomb 又名 can't-use-this-on-any-phones-yet 的新内容 :(
            【解决方案10】:

            由于 Android 是开源的,因此您实际上不需要重新实现 ListView 的优化。您可以获取 ListView 的代码并尝试找到一种方法来破解动画,您也可以在 android bug tracker 中open a feature request(如果您决定实现它,请不要忘记contribute a patch)。

            仅供参考,ListView 源代码是here

            【讨论】:

            • 这不是实施此类更改的方式。首先,您应该构建 AOSP 项目——这是一种将动画添加到列表视图的非常繁琐的方法。请查看各种开源库中的实现。
            【解决方案11】:

            这是source code,可让您删除行并重新排序。

            还提供了一个演示 APK 文件。删除行更多地沿着谷歌的 Gmail 应用程序完成,该应用程序在滑动顶部视图后显示底部视图。底部视图可以有一个撤消按钮或任何你想要的。

            【讨论】:

              【解决方案12】:

              正如我在我的网站中解释我的方法一样,我分享了链接。无论如何,这个想法是创建位图 通过 getdrawingcache . 有两个位图并为较低的位图设置动画以创建移动效果

              请看以下代码:

              listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
                  {
                      public void onItemClick(AdapterView<?> parent, View rowView, int positon, long id)
                      {
                          listView.setDrawingCacheEnabled(true);
                          //listView.buildDrawingCache(true);
                          bitmap = listView.getDrawingCache();
                          myBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), rowView.getBottom());
                          myBitmap2 = Bitmap.createBitmap(bitmap, 0, rowView.getBottom(), bitmap.getWidth(), bitmap.getHeight() - myBitmap1.getHeight());
                          listView.setDrawingCacheEnabled(false);
                          imgView1.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap1));
                          imgView2.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap2));
                          imgView1.setVisibility(View.VISIBLE);
                          imgView2.setVisibility(View.VISIBLE);
                          RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
                          lp.setMargins(0, rowView.getBottom(), 0, 0);
                          imgView2.setLayoutParams(lp);
                          TranslateAnimation transanim = new TranslateAnimation(0, 0, 0, -rowView.getHeight());
                          transanim.setDuration(400);
                          transanim.setAnimationListener(new Animation.AnimationListener()
                          {
                              public void onAnimationStart(Animation animation)
                              {
                              }
              
                              public void onAnimationRepeat(Animation animation)
                              {
                              }
              
                              public void onAnimationEnd(Animation animation)
                              {
                                  imgView1.setVisibility(View.GONE);
                                  imgView2.setVisibility(View.GONE);
                              }
                          });
                          array.remove(positon);
                          adapter.notifyDataSetChanged();
                          imgView2.startAnimation(transanim);
                      }
                  });
              

              图片理解see this

              谢谢。

              【讨论】:

                【解决方案13】:

                我做过类似的事情。一种方法是在动画时间内插入视图的高度随时间在行onMeasure 内,同时为listView 发出requestLayout()。是的,直接在 listView 代码中执行可能会更好,但这是一个快速的解决方案(看起来不错!)

                【讨论】:

                  【解决方案14】:

                  只是分享另一种方法:

                  首先将列表视图的android:animateLayoutChanges设置为true

                  <ListView
                          android:id="@+id/items_list"
                          android:layout_width="match_parent"
                          android:layout_height="match_parent"
                          android:animateLayoutChanges="true"/>
                  

                  然后我使用 handler 添加项目并延迟更新列表视图:

                  Handler mHandler = new Handler();
                      //delay in milliseconds
                      private int mInitialDelay = 1000;
                      private final int DELAY_OFFSET = 1000;
                  
                  
                  public void addItem(final Integer item) {
                      mHandler.postDelayed(new Runnable() {
                          @Override
                          public void run() {
                              new Thread(new Runnable() {
                                  @Override
                                  public void run() {
                                      mDataSet.add(item);
                                      runOnUiThread(new Runnable() {
                                          @Override
                                          public void run() {
                                              mAdapter.notifyDataSetChanged();
                                          }
                                      });
                                  }
                              }).start();
                  
                          }
                      }, mInitialDelay);
                      mInitialDelay += DELAY_OFFSET;
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2017-06-13
                    • 1970-01-01
                    • 2022-01-24
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-09-17
                    • 1970-01-01
                    • 2019-04-12
                    相关资源
                    最近更新 更多