Cách tạo hiệu ứng bổ sung hoặc xóa hàng ListView của Android


212

Trong iOS, có một phương tiện rất dễ dàng và mạnh mẽ để tạo hiệu ứng cho việc thêm và xóa các hàng UITableView, đây là clip từ video youtube hiển thị hình động mặc định. Lưu ý cách các hàng xung quanh thu gọn vào hàng đã xóa. Hoạt hình này giúp người dùng theo dõi những gì đã thay đổi trong danh sách và nơi trong danh sách họ đang xem khi dữ liệu thay đổi.

Vì tôi đã phát triển trên Android nên tôi không tìm thấy cơ sở tương đương nào để tạo hiệu ứng cho các hàng riêng lẻ trong TableView . Gọi notifyDataSetChanged()Bộ điều hợp của tôi khiến ListView cập nhật ngay nội dung của nó với thông tin mới. Tôi muốn hiển thị một hình ảnh động đơn giản của một hàng mới đẩy vào hoặc trượt ra khi dữ liệu thay đổi, nhưng tôi không thể tìm thấy bất kỳ cách nào được làm tài liệu để làm điều này. Có vẻ như LayoutAnimationContoder có thể giữ một khóa để làm việc này, nhưng khi tôi đặt LayoutAnimationContoder trên ListView của tôi (tương tự như LayoutAnimation2 của ApiDemo ) và xóa các phần tử khỏi bộ điều hợp của tôi sau khi danh sách được hiển thị, các phần tử sẽ biến mất ngay lập tức .

Tôi cũng đã thử những thứ như sau để làm động một vật phẩm riêng lẻ khi nó bị xóa:

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

Tuy nhiên, các hàng xung quanh hàng hoạt hình không di chuyển vị trí cho đến khi chúng nhảy đến vị trí mới khi notifyDataSetChanged()được gọi. Có vẻ như ListView không cập nhật bố cục của nó một khi các yếu tố của nó đã được đặt.

Trong khi viết triển khai / ngã ba ListView của riêng tôi đã vượt qua tâm trí tôi, điều này có vẻ như là một điều không nên quá khó khăn.

Cảm ơn!


ai đó đã tìm thấy câu trả lời cho điều này? vui lòng chia sẻ
AndroidGecko

@Alex: bạn đã thực hiện hoạt hình đó như video youtube đó (như iphone) chưa, nếu bạn có bất kỳ bản demo hoặc liên kết nào cho android thì hãy cho tôi, tôi đã thử sử dụng animateLayoutChanges trong tệp xml, nhưng nó không chính xác như iphone
Jayesh

@Jayesh, thật không may, tôi không còn làm việc về phát triển Android. Tôi chưa kiểm tra bất kỳ câu trả lời nào cho câu hỏi này được viết sau khoảng năm 2012.
Alex Pretzlav

Câu trả lời:


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

để sử dụng hoạt hình từ trên xuống dưới:

<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
Tốt hơn là sử dụng anim.setAnimationListener(new AnimationListener() { ... })thay vì sử dụng Trình xử lý
Oleg Vaskevich

1
những gì là populateList()gì?
Vasily Sochinsky

5
@MrAppleBR vì nhiều lý do. Đầu tiên, bạn không tạo ra một thứ không cần thiết Handler. Thứ hai, nó làm cho mã ít phức tạp hơn bằng cách sử dụng một cuộc gọi lại tích hợp sẵn khi hoạt ảnh kết thúc. Thứ ba, bạn không biết cách triển khai HandlerAnimationsẽ hoạt động trên mỗi nền tảng; có thể là sự chậm trễ của bạn có thể xảy ra trước khi hoạt hình kết thúc.
Oleg Vaskevich

7
"Tuy nhiên, các hàng xung quanh hàng hoạt hình không di chuyển vị trí cho đến khi chúng nhảy sang vị trí mới khi thông báoDataSetChanged () được gọi." vấn đề này vẫn còn tồn tại trong giải pháp của bạn.
Boris Rusev

5
Lớp FavouritesManager và phương thức popatedList () là gì ??
Jayesh

14

1
Tôi nghĩ rằng đây là cách đơn giản khi bạn cần một hình ảnh động đơn giản, nhưng bạn cũng có thể tùy chỉnh hình ảnh động bằng phương thức RecyclerView.setItemAnimator ().
qatz

2
Bước cuối cùng là liên kết còn thiếu tôi đã có. Id ổn định. Cảm ơn!
Amir Uval

Đây là một dự án mẫu tuyệt vời thể hiện API tốt.
Mr-IDE

9

Hãy xem giải pháp của Google. Đây chỉ là một phương pháp xóa.

ListViewRemovalAnimationMã dự án và trình diễn video

Nó cần Android 4.1+ (API 16). Nhưng chúng ta có 2014 bên ngoài.


2

ListViewsđược tối ưu hóa cao, tôi nghĩ rằng điều này là không thể. Bạn đã thử tạo "ListView" của mình bằng mã (tức là bằng cách bơm các hàng của bạn từ xml và nối chúng vào a LinearLayout) và tạo hiệu ứng cho chúng?


6
Sẽ không có ý nghĩa khi thực hiện lại listview chỉ để thêm hình ảnh động.
Macude

Tôi chắc chắn chỉ có thể sử dụng một LinearLayout, tuy nhiên với LinearLayouthiệu suất của bộ dữ liệu rất lớn sẽ trở nên tồi tệ. ListViewcó rất nhiều tối ưu hóa thông minh xung quanh việc tái chế chế độ xem cho phép nó xử lý các tập dữ liệu lớn (UITableView của iOS có cùng các tối ưu hóa). Viết trình tái chế quan điểm của riêng tôi thuộc danh mục thực hiện lại ListView :(
Alex Pretzlav

Tôi biết nó không có ý nghĩa gì - nhưng nếu bạn làm việc chỉ với một vài hàng thì lợi ích của việc có hoạt hình vào / ra đẹp có thể đáng để thử.
whlk

2

Bạn đã xem xét hoạt hình quét sang phải chưa? Bạn có thể làm một cái gì đó như vẽ một thanh trắng lớn hơn dần dần trên đầu của danh sách, sau đó xóa nó khỏi danh sách. Các tế bào khác vẫn sẽ nhảy vào vị trí, nhưng tốt hơn là không có gì.


2

gọi listView.scheduleLayoutAnimation (); trước khi thay đổi danh sách


2

Tôi đã hack cùng nhau một cách khác để làm điều đó mà không phải thao tác xem danh sách. Thật không may, Ảnh động Android thông thường dường như thao túng nội dung của hàng, nhưng không hiệu quả trong việc thu nhỏ tầm nhìn. Vì vậy, trước tiên hãy xem xét xử lý này:

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

Thêm bộ sưu tập này vào bộ điều hợp Danh sách của bạn:

private Collection<Integer> disabledViews = new ArrayList<Integer>();

và thêm

public boolean isEnabled(int position) {
   return !disabledViews.contains(position);
}

Tiếp theo, bất cứ nơi nào bạn muốn ẩn một hàng, hãy thêm vào đây:

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

Đó là nó! Bạn có thể thay đổi tốc độ của hình ảnh động bằng cách thay đổi số pixel mà nó thu nhỏ chiều cao cùng một lúc. Không thực sự tinh vi, nhưng nó hoạt động!


2

Sau khi chèn hàng mới vào ListView, tôi chỉ cần cuộn ListView sang vị trí mới.

ListView.smoothScrollToPosition(position);

1

Tôi đã không thử nó nhưng có vẻ như animateLayoutChanges sẽ làm những gì bạn đang tìm kiếm. Tôi thấy nó trong lớp ImageSwitcher, tôi giả sử nó cũng thuộc lớp ViewSwitcher?


Xin cảm ơn, tôi sẽ xem xét điều đó. Thật không may, có vẻ như animateLayoutChanges là tính năng mới của API Cấp 11 hay còn gọi là Honeycomb aka không thể sử dụng cái này trên bất kỳ điện thoại nào :(
Alex Pretzlav

1

Vì Android là nguồn mở, nên bạn thực sự không cần phải thực hiện lại các tối ưu hóa của ListView. Bạn có thể lấy mã của ListView và cố gắng tìm cách hack trong hoạt hình, bạn cũng có thể mở một yêu cầu tính năng trong trình theo dõi lỗi android (và nếu bạn quyết định thực hiện nó, đừng quên đóng góp một bản vá ).

FYI, mã nguồn ListView ở đây .


1
Đây không phải là cách mà những thay đổi như vậy nên được thực hiện. Trước bất cứ điều gì, bạn nên xây dựng dự án AOSP - một cách tiếp cận khá nặng tay để thêm hình ảnh động vào danh sách lượt xem. Vui lòng xem các triển khai trong các thư viện nguồn mở khác nhau.
OrhanC1

1

Đây là mã nguồn để cho phép bạn xóa các hàng và sắp xếp lại chúng.

Một tập tin APK demo cũng có sẵn. Việc xóa các hàng được thực hiện nhiều hơn dọc theo các dòng của ứng dụng Gmail của Google cho thấy chế độ xem dưới cùng sau khi vuốt chế độ xem trên cùng. Chế độ xem dưới cùng có thể có nút Hoàn tác hoặc bất cứ điều gì bạn muốn.


1

Như tôi đã giải thích cách tiếp cận của mình trong trang web của mình, tôi đã chia sẻ liên kết. Dù sao, ý tưởng là tạo ra bitmap bằng cách rút tiền. Có hai bitmap và tạo hiệu ứng bitmap thấp hơn để tạo hiệu ứng di chuyển

Vui lòng xem đoạn mã sau:

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

Để hiểu với hình ảnh xem điều này

Cảm ơn.


0

Tôi đã làm một cái gì đó tương tự như thế này. Một cách tiếp cận là nội suy theo thời gian hoạt ảnh chiều cao của chế độ xem theo thời gian bên trong các hàng onMeasuretrong khi phát hành requestLayout()cho listView. Có, có thể tốt hơn để thực hiện trực tiếp bên trong mã listView nhưng đó là một giải pháp nhanh chóng (có vẻ tốt!)


0

Chỉ chia sẻ một cách tiếp cận khác:

Đầu tiên, thiết lập chế độ xem danh sách android: animateLayoutChanges thành true :

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

Sau đó, tôi sử dụng một trình xử lý để thêm các mục và cập nhật listview với độ trễ:

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.