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

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

Поскольку я разрабатывал Android, я не нашел эквивалентного средства для анимации отдельных строк в TableView. Вызов notifyDataSetChanged() моего адаптера заставляет ListView немедленно обновлять его содержимое с помощью новой информации. Я хочу показать простую анимацию новой строки, которая будет нажата или сдвинется, когда данные изменятся, но я не могу найти документально подтвержденный способ сделать это. Похоже, LayoutAnimationController может содержать ключ, чтобы заставить это работать, но когда я устанавливаю LayoutAnimationController в свой ListView (аналогично ApiDemo 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 не обновляет свой макет, когда его элементы были размещены.

При написании моей собственной реализации /fork ListView это перешло мне в голову, это похоже на то, что не должно быть так сложно.

Спасибо!

Ответ 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>

Ответ 2

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

RecyclerView in action

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

Ответ 3

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

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

Ему нужен Android 4.1+ (API 16). Но у нас 2014 снаружи.

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

ListView.smoothScrollToPosition(position);

Ответ 9

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

FYI, исходный код ListView здесь.

Ответ 10

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

Ответ 11

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

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

Спасибо.

Ответ 12

Здесь исходный код, чтобы вы могли удалять строки и изменять их порядок.

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

Ответ 13

Я сделал что-то подобное этому. Один из подходов состоит в том, чтобы интерполировать по времени анимации высоту представления во времени внутри строк onMeasure при выпуске requestLayout() для listView. Да, может быть, лучше сделать внутри кода 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 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;
}