Как анимировать добавление или удаление строк 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>
OAK
источник
70
Лучше использовать, чем anim.setAnimationListener(new AnimationListener() { ... })использовать обработчик
Олег Васкевич
1
для чего populateList()?
Василий Сочинский
5
@MrAppleBR по нескольким причинам. Во-первых, вы не создаете ненужного Handler. Во-вторых, он делает код менее сложным благодаря использованию встроенного обратного вызова для завершения анимации. В-третьих, вы не знаете, как работает реализация Handlerи Animationбудет работать на каждой платформе; Вполне возможно, что ваш отложенный запуск произойдет до того, как закончится анимация.
Олег Васкевич
7
«Однако строки, окружающие анимированную строку, не перемещаются, пока не перейдут на свои новые позиции при вызове notifyDataSetChanged ()». эта проблема все еще сохраняется в вашем решении.
Борис Русев
5
Что такое класс FavouritesManager и метод populateList () ??
Джаеш
14

Он RecyclerViewзаботится о добавлении, удалении и изменении порядка анимации!

RecyclerView в действии

Этот простой проект AndroidStudio имеет RecyclerView. взгляните на коммиты :

  1. коммит классического приложения Hello World для Android
  2. коммит, добавление RecyclerView в проект (контент не динамический)
  3. коммит, добавление функциональности для изменения содержимого RecyclerView во время выполнения (но без анимации)
  4. и наконец ... добавьте анимацию в RecyclerView
Эрик
источник
1
Я думаю, что это простой способ, когда вам нужна простая анимация, но вы также можете настроить анимацию с помощью метода RecyclerView.setItemAnimator ().
Кат
2
Последний шаг - недостающее звено, которое у меня было. стабильный идентификатор. Спасибо!
Амир Увал
Это отличный пример проекта, который хорошо демонстрирует API.
Mr-IDE
9

Посмотрите на решение Google. Вот только метод удаления.

ListViewRemovalAnimation код проекта и демонстрация видео

Требуется Android 4.1+ (API 16). Но у нас 2014 снаружи.

диметил
источник
2

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

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

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

Сэм Дюфель
источник
2

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

karabara
источник
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));

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

jkschneider
источник
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, которое показывает вид снизу после прокрутки вида сверху. Вид снизу может иметь кнопку «Отменить» или что угодно.

AndroidDev
источник
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;
}
Мина Сэми
источник