Як анімувати додавання чи видалення 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 перейшло мені на думку, це здається чимось, що не повинно бути таким складним.

Дякую!


хтось знайшов відповідь на це? pls shareee
AndroidGecko

@ Алекс: ти робив таку анімацію, як це відео на 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 () метод ??
Jayesh

14

1
Я думаю, що це простий спосіб, коли вам потрібна проста анімація, але ви також можете налаштувати анімацію, використовуючи метод RecyclerView.setItemAnimator ().
qatz

2
Останнім кроком є ​​відсутність у мене зв’язку. стабільні ІД. Дякую!
Амір Уваль

Це чудовий зразок проекту, який добре демонструє API.
Містер-ІДЕ

9

Погляньте на рішення Google. Ось лише метод видалення.

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

Для цього потрібен Android 4.1+ (API 16). Але у нас 2014 рік поза межами.


2

Оскільки ListViewsдуже оптимізовані, я думаю, що цього неможливо досягти. Ви намагалися створити свій "ListView" за кодом (тобто, надувши рядки з xml та додавши їх до a LinearLayout) та анімувати їх?


6
Немає сенсу повторно виконувати перегляд списку просто для додавання анімації.
Макарс

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

Я знаю, що це не має сенсу - але якщо ви працюєте лише з декількома рядами, користь від того, щоб мати гарні анімації для введення / виводу, варто спробувати.
хлюп

2

Чи розглядали ви анімацію підмітання праворуч? Можна зробити щось на кшталт того, як намалювати прогресивно більшу білу смужку вгорі списку, видаливши її зі списку. Інші клітини все-таки смикаються на місці, але краще, ніж нічого.



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

Додайте цю колекцію до адаптера списку:

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 aka не може використовувати-це-на-будь-які телефони-поки :(
Alex Pretzlav

1

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

FYI, вихідний код ListView знаходиться тут .


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

1

Ось вихідний код дозволяє видалити рядки та упорядкувати їх.

Також доступний демо-файл APK. Видалення рядків робиться більше в порядку програми Google Gmail, яка виявляє вигляд знизу після того, як проведіть вигляд зверху. Вид знизу може мати кнопку Скасувати або все, що завгодно.


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

Просто поділився іншим підходом:

Спочатку встановіть андроїд перегляду списку : animateLayoutChanges на істинний :

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