代码之家  ›  专栏  ›  技术社区  ›  Alex Pretzlav

如何动画添加或删除Android ListView行

  •  207
  • Alex Pretzlav  · 技术社区  · 15 年前

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

    自从我在android上开发以来,我就没有找到类似的工具来动画 TableView . 打电话 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);
    }
    

    但是,围绕动画行的行不会移动位置,除非它们在 通知数据集更改() 被称为。似乎ListView在放置元素后不会更新其布局。

    在编写我自己的ListView实现/分支时,我的脑海中闪过一个念头,这似乎不应该那么困难。

    谢谢!

    14 回复  |  直到 9 年前
        1
  •  122
  •   Blundell    14 年前
    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>
    
        3
  •  9
  •   dimetil    12 年前

    看看谷歌的解决方案。这里只有一个删除方法。

    ListViewRemovalAnimation 项目代码和 Video 示范

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

        4
  •  2
  •   whlk    15 年前

    自从 ListViews 高度优化,我认为这是不可能实现的。您是否试图通过代码创建“listview”(即从XML中展开行并将其附加到 LinearLayout )然后制作动画?

        5
  •  2
  •   Sam Dufel    15 年前

    你有没有考虑过在右边设置扫描动画?您可以做一些事情,比如在列表项的顶部绘制一个逐渐变大的白色条,然后将其从列表中删除。其他的细胞仍然会猛拉到位,但总比什么都没有要好。

        6
  •  2
  •   karabara    13 年前

    呼叫 listView.schedulelayoutanimation(); 在更改列表之前

        7
  •  2
  •   Community Mohan Dere    11 年前

    我用另一种方法破解了它,而不必操纵列表视图。不幸的是,常规的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
  •  2
  •   Rafael    11 年前

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

    ListView.smoothScrollToPosition(position);
    
        9
  •  1
  •   Yevgeny Simkin    15 年前

    我还没有尝试过,但看起来动画布局更改应该做你想做的。我在Imageswitcher类中看到它,我假设它也在Viewswitcher类中?

        10
  •  1
  •   Sparkup Andrey Solera    15 年前

    open a feature request 在Android Bug Tracker中(如果您决定实现它,请不要忘记 contribute a patch )

    仅供参考,ListView源代码是 here .

        11
  •  1
  •   RevanthKrishnaKumar V. Glen Best    11 年前

    这里是 source code 删除行并重新排序。

    还提供了一个演示APK文件。删除行更多的是沿着谷歌的Gmail应用程序的行进行的,该应用程序在浏览完顶视图后会显示一个底视图。底部视图可以有一个“撤消”按钮或您想要的任何内容。

        12
  •  1
  •   RevanthKrishnaKumar V. Glen Best    11 年前

    正如我在我的网站上解释过我的方法一样,我共享了这个链接。 通过getdrawingcache.have two bitmap and animated the lower bitmap to create the moving effect

    请参见以下代码:

    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
  •  0
  •   Dori    14 年前

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

        14
  •  0
  •   Mina Wissa    10 年前

    只是分享另一种方法:

    首先设置列表视图的 Android:动画布局更改 :

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

    然后我用 处理程序 要添加项并延迟更新ListView,请执行以下操作:

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