ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

谢谢!

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

B
Blundell
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() 方法?
E
Eric

RecyclerView 负责添加、删除和重新排序动画!

https://i.stack.imgur.com/Q7oYK.gif

simple AndroidStudio project 具有 RecyclerView。看看commits

提交经典的 Hello World Android 应用程序提交,将 RecyclerView 添加到项目(内容不是动态)提交,添加在运行时修改 RecyclerView 内容的功能(但没有动画),最后......提交向 RecyclerView 添加动画


我认为当您需要一个简单的动画时,这是一种简单的方法,但您也可以使用 RecyclerView.setItemAnimator() 方法自定义动画。
最后一步是我缺少的链接。稳定的身份证。谢谢!
这是一个很好的示例项目,很好地演示了 API。
d
dimetil

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

ListViewRemovalAnimation 项目代码和 Video 演示

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


w
whlk

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


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

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


k
karabara

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


C
Community

我拼凑了另一种方法来做到这一点,而不必操纵列表视图。不幸的是,常规的 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));

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


R
Rafael

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

ListView.smoothScrollToPosition(position);

Y
Yevgeny Simkin

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


嘿,谢谢,我会调查的。不幸的是,看起来 animateLayoutChanges 是 API 级别 11 又名 Honeycomb 又名 can't-use-this-on-any-phones-yet 的新内容 :(
S
Sparkup

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

仅供参考,ListView 源代码是 here


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

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

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


R
RevanthKrishnaKumar V.

正如我在我的网站上解释过我的方法一样,我分享了链接。无论如何,这个想法是通过 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

谢谢。


D
Dori

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


M
Mina Wissa

只是分享另一种方法:

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

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

然后我使用处理程序添加项目并延迟更新列表视图:

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