Как анимировать добавление или удаление строк Android ListView


212

В iOS есть очень простое и мощное средство для анимации добавления и удаления строк UITableView, вот клип из видео на YouTube, показывающий анимацию по умолчанию. Обратите внимание, как окружающие строки сворачиваются в удаленную строку. Эта анимация помогает пользователям отслеживать, что изменилось в списке и где они находились в списке при изменении данных.

Так как я разрабатывал для Android, я не нашел эквивалентного средства для анимации отдельных строк в TableView . При вызове notifyDataSetChanged()моего адаптера ListView немедленно обновляет свое содержимое новой информацией. Я хотел бы показать простую анимацию новой строки, которая сдвигается или сдвигается при изменении данных, но я не могу найти никакого документированного способа сделать это. Похоже, что LayoutAnimationController может содержать ключ к тому, чтобы заставить это работать, но когда я устанавливаю LayoutAnimationController в моем ListView (аналогично LayoutAnimation2 в ApiDemo ) и удаляю элементы из моего адаптера после отображения списка, элементы сразу исчезают, а не выводятся из анимации. ,

Я также пробовал что-то вроде следующего, чтобы оживить отдельный элемент при его удалении:

@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 приходило мне в голову, это кажется чем-то, что не должно быть таким сложным.

Спасибо!


кто-то нашел ответ на это? Просьба Shareee
AndroidGecko

@Alex: вы делали эту анимацию, как это видео на YouTube (например, iphone), если у вас есть демоверсия или ссылка для Android, пожалуйста, дайте мне, я пробовал использовать animateLayoutChanges в XML-файле, но это не совсем как iphone
Jayesh

@Jayesh, к сожалению, я больше не работаю над разработкой Android. Я не проверял ни одного ответа на этот вопрос, который был написан после 2012 года.
Алекс Прецлав

Ответы:


124
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>

70
Лучше использовать, чем anim.setAnimationListener(new AnimationListener() { ... })использовать обработчик
Олег Васкевич

1
для чего populateList()?
Василий Сочинский

5
@MrAppleBR по нескольким причинам. Во-первых, вы не создаете ненужного Handler. Во-вторых, он делает код менее сложным благодаря использованию встроенного обратного вызова для завершения анимации. В-третьих, вы не знаете, как работает реализация Handlerи Animationбудет работать на каждой платформе; Вполне возможно, что ваш отложенный запуск произойдет до того, как закончится анимация.
Олег Васкевич

7
«Однако строки, окружающие анимированную строку, не перемещаются, пока не перейдут на свои новые позиции при вызове notifyDataSetChanged ()». эта проблема все еще сохраняется в вашем решении.
Борис Русев

5
Что такое класс FavouritesManager и метод populateList () ??
Джаеш

14

1
Я думаю, что это простой способ, когда вам нужна простая анимация, но вы также можете настроить анимацию с помощью метода RecyclerView.setItemAnimator ().
Кат

2
Последний шаг - недостающее звено, которое у меня было. стабильный идентификатор. Спасибо!
Амир Увал

Это отличный пример проекта, который хорошо демонстрирует API.
Mr-IDE


2

Так как ListViewsони высоко оптимизированы, я думаю, что это невозможно. Вы пытались создать свой «ListView» с помощью кода (то есть, надувая строки из xml и добавляя их в a LinearLayout) и анимируя их?


6
Нет смысла переопределять просмотр списка, просто чтобы добавить анимацию.
Macarse

Я, конечно, мог бы просто использовать a LinearLayout, однако при работе с очень большими наборами данных LinearLayoutпроизводительность станет ужасной. ListViewимеет много интеллектуальных оптимизаций по переработке представления, которые позволяют ему обрабатывать большие наборы данных (UITableView iOS имеет те же оптимизации). Написание собственного представления переработчика подпадает под категорию переиздания ListView :(
Алекс Прецлав

Я знаю, что это не имеет смысла - но если вы работаете всего с несколькими строками, то полезная анимация входа / выхода может стоить попробовать.
2010 г.

2

Рассматривали ли вы анимацию развертки вправо? Вы можете сделать что-то вроде рисования постепенно увеличивающейся белой полосы в верхней части элемента списка, а затем удалить ее из списка. Другие камеры все равно встали бы на свои места, но лучше, чем ничего.


2

вызовите listView.scheduleLayoutAnimation (); перед изменением списка


2

Я взломал вместе другой способ сделать это без необходимости манипулировать списком. К сожалению, обычные 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);
}

Добавьте эту коллекцию в свой адаптер List:

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

Это оно! Вы можете изменить скорость анимации, изменив количество пикселей, на которое она сжимается по высоте сразу. Не очень сложно, но это работает!


2

После вставки новой строки в ListView, я просто прокручиваю ListView в новую позицию.

ListView.smoothScrollToPosition(position);

1

Я не пробовал, но похоже, что animateLayoutChanges должен делать то, что вы ищете. Я вижу это в классе ImageSwitcher, я полагаю, что это также и в классе ViewSwitcher?


Эй, спасибо, я посмотрю на это. К сожалению, похоже, что animateLayoutChanges является новым с API уровня 11, то есть Honeycomb, или еще не может использовать это на любых телефонах :(
Алекс Прецлав

1

Поскольку Android является открытым исходным кодом, вам не нужно переопределять оптимизацию ListView. Вы можете взять код ListView и попытаться найти способ взломать анимацию, вы также можете открыть запрос функции в трекере ошибок Android (и если вы решили его реализовать, не забудьте внести патч ).

К вашему сведению, исходный код ListView здесь .


1
Это не способ, которым такие изменения должны быть реализованы. Прежде всего, у вас должна быть сборка проекта AOSP - довольно сложный подход к добавлению анимации в представления списка. Пожалуйста, посмотрите на реализации в различных библиотеках с открытым исходным кодом.
OrhanC1

1

Вот исходный код, позволяющий вам удалять строки и изменять их порядок.

Также доступен демонстрационный APK-файл. Удаление строк выполняется больше по аналогии с приложением Gmail от Google, которое показывает вид снизу после прокрутки вида сверху. Вид снизу может иметь кнопку «Отменить» или что угодно.


1

Как я объяснил мой подход на своем сайте, я поделился ссылкой. В любом случае, идея заключается в создании растровых изображений с помощью 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);
        }
    });

Для понимания с изображениями смотрите это

Спасибо.


0

Я сделал что-то похожее на это. Один из подходов заключается в интерполяции во времени анимации высоты представления во времени внутри строк onMeasureпри выдаче requestLayout()для listView. Да, это может быть лучше сделать внутри кода listView напрямую, но это было быстрое решение (это выглядело хорошо!)


0

Просто делюсь другим подходом:

Сначала установите для 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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.