Cách làm động các mục RecyclerView khi chúng xuất hiện


237

Làm cách nào tôi có thể làm động các Mục RecyclerView khi xuất hiện?

Trình hoạt hình vật phẩm mặc định chỉ hoạt hình khi dữ liệu được thêm hoặc xóa sau khi dữ liệu tái chế được đặt. Tôi đang phát triển các ứng dụng mới và không có bất kỳ manh mối nào để bắt đầu.

Bất kỳ ý tưởng làm thế nào để đạt được điều này?

Câu trả lời:


42

Đơn giản chỉ với XML

Truy cập liên kết Gist

res / anim / layout_animation.xml

<?xml version="1.0" encoding="utf-8"?>
    <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:animation="@anim/item_animation_fall_down"
        android:animationOrder="normal"
        android:delay="15%" />

res / anim / item_animation_fall_down.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <translate
        android:fromYDelta="-20%"
        android:toYDelta="0"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

    <scale
        android:fromXScale="105%"
        android:fromYScale="105%"
        android:toXScale="100%"
        android:toYScale="100%"
        android:pivotX="50%"
        android:pivotY="50%"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

</set>

Sử dụng trong bố cục và recylcerview như:

<android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layoutAnimation="@anim/layout_animation"
                app:layout_behavior="@string/appbar_scrolling_view_behavior" />

1
@ArnoldBrown Bằng cách thay đổi tệp hoạt hình. Tham khảo: stackoverflow.com/questions/5151591/ khăn
iamnaran

Đây là câu trả lời thiết thực nhất.
Oliver Metz

Làm thế nào để làm cho công việc này mỗi khi danh sách được mở, bởi vì bây giờ nó chỉ làm điều đó lần đầu tiên.
Hiwa Jalal

7
Bạn phải gọi recyclerView.scheduleLayoutAnimation()sau khi tập dữ liệu thay đổi, nếu không, hình động sẽ không hoạt động.
zeleven

Điều này có nên làm việc khi các mặt hàng được tái chế và trở lại vào tầm nhìn? Tôi đã thử giải pháp này và nó hoạt động rất tốt cho hình ảnh động ban đầu khi bạn lần đầu tiên nhìn thấy bố cục. Sau khi cuộn, các mục không có hình động khi quay lại để xem.
Jason p

314

BIÊN TẬP :

Theo tài liệu của ItemAnimator :

Lớp này định nghĩa các hình ảnh động diễn ra trên các vật phẩm khi các thay đổi được thực hiện cho bộ điều hợp.

Vì vậy, trừ khi bạn thêm từng mục một vào mục của mình RecyclerViewvà làm mới chế độ xem ở mỗi lần lặp, tôi không nghĩ ItemAnimatorlà giải pháp cho nhu cầu của bạn.

Đây là cách bạn có thể làm động các RecyclerViewmục khi chúng xuất hiện bằng CustomAd CHƯƠNG:

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder>
{
    private Context context;

    // The items to display in your RecyclerView
    private ArrayList<String> items;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    public static class ViewHolder extends RecyclerView.ViewHolder
    {
        TextView text;
        // You need to retrieve the container (ie the root ViewGroup from your custom_item_layout)
        // It's the view that will be animated
        FrameLayout container;

        public ViewHolder(View itemView)
        {
            super(itemView);
            container = (FrameLayout) itemView.findViewById(R.id.item_layout_container);
            text = (TextView) itemView.findViewById(R.id.item_layout_text);
        }
    }

    public CustomAdapter(ArrayList<String> items, Context context)
    {
        this.items = items;
        this.context = context;
    }

    @Override
    public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_item_layout, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position)
    {
        holder.text.setText(items.get(position));

        // Here you apply the animation when the view is bound
        setAnimation(holder.itemView, position);
    }

    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition)
        {
            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }
}

Và custom_item_layout của bạn sẽ trông như thế này:

<FrameLayout
    android:id="@+id/item_layout_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/item_layout_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"/>

</FrameLayout>

Để biết thêm thông tin về CustomAd chương và RecyclerView, hãy tham khảo khóa đào tạo này trên tài liệu chính thức .

Sự cố khi cuộn nhanh

Sử dụng phương pháp này có thể gây ra vấn đề với cuộn nhanh. Chế độ xem có thể được sử dụng lại trong khi hoạt hình đang diễn ra. Để tránh điều đó được khuyến khích để xóa hình ảnh động khi được tách ra.

    @Override
    public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder)
    {
        ((CustomViewHolder)holder).clearAnimation();
    }

Trên CustomViewHolder:

    public void clearAnimation()
    {
        mRootLayout.clearAnimation();
    }

Câu trả lời cũ:

Hãy nhìn vào repo của Gabriele Mariotti , tôi khá chắc chắn rằng bạn sẽ tìm thấy những gì bạn cần. Anh ta cung cấp ItemAnimator đơn giản cho RecyclerView, chẳng hạn như SlideInItemAnimator hoặc SlideScaleItemAnimator.


1
Tôi đã thấy điều đó, nhưng đó là để thêm và xóa các mục sau khi chúng xuất hiện. Tôi cần bắt đầu hoạt hình ngay trước khi chúng xuất hiện. Dù sao cũng cảm ơn Mathieu.
PaulNunezM

1
Theo như tôi biết, bạn cần phải sử dụng CustomAd CHƯƠNG sau đó.
MathieuMaree

20
Tôi đã tự hỏi nếu bạn đã trải nghiệm và có thể giải quyết hiệu ứng "bị mắc kẹt" với những hình ảnh động này trong RecyclerView? Tôi đã sử dụng mã tương tự cho ListView và các mục của tôi hoạt hình mà không gặp vấn đề gì, dù tôi có cuộn nhanh như thế nào, nhưng với RecyclerView nếu tôi cuộn nhanh một số mục đôi khi bị kẹt trên màn hình trên các mục khác và chúng không ẩn hoàn toàn - nó giống như hoạt hình đã bị dừng trước khi nó kết thúc. Tôi thực sự đã cố gắng nhận xét một phần của mã chứa các trường (cố gắng tăng tốc độ thực thi phương thức onBindViewHolder) vì vậy tôi chỉ để lại mã cho animat
Tomislav

4
@MathieuMaree Cảm ơn vì hoạt hình đáng kinh ngạc này. Nó có vẻ tốt cho việc cuộn chậm, nhưng trên các mục tái chế cuộn nhanh, chồng chéo lên nhau. Bạn có tìm thấy điều này không? Có gợi ý nào để khắc phục vấn đề này không.
Giru Bhai

40
@GiruBhai ghi đè onViewDetachedFromWindowvà gọi clearAnimationvào xem. Vấn đề là có những hình ảnh động đang chạy khi RecyclerView đang cố gắng sử dụng lại chế độ xem.
Xample

62

Tôi hoạt hình mờ dần trong các Recyclerviewmục khi chúng lần đầu tiên xuất hiện như được hiển thị trong mã bên dưới. Có lẽ điều này sẽ được sử dụng cho một ai đó.

private final static int FADE_DURATION = 1000; //FADE_DURATION in milliseconds

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    holder.getTextView().setText("some text");

    // Set the view to fade in
    setFadeAnimation(holder.itemView);            
}

private void setFadeAnimation(View view) {
    AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

Bạn cũng có thể thay thế setFadeAnimation()bằng cách sau đây setScaleAnimation()để tạo sự xuất hiện của các vật phẩm bằng cách nhân rộng chúng từ một điểm:

private void setScaleAnimation(View view) {
    ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

Đoạn mã trên có một số mụn cóc cho đến khi bạn cuộn các RecyclerViewvật phẩm luôn mờ dần hoặc co giãn. Nếu bạn muốn bạn có thể thêm mã để chỉ cho phép hoạt hình xảy ra khi đoạn hoặc hoạt động chứa RecyclerViewlần đầu tiên được tạo (ví dụ: lấy thời gian hệ thống khi tạo và chỉ cho phép hoạt hình trong mili giây FADE_DURATION đầu tiên).


1
Tôi đã sửa đổi nhỏ cho câu trả lời của bạn để làm cho hoạt hình chỉ hoạt động khi cuộn xuống, kiểm tra câu trả lời của tôi
Basheer AL-MOMANI

1
điều này làm hỏng bố cục (các mục danh sách được viết quá mức, một số mục có màu văn bản sai) khi cuộn lên xuống nhanh chóng
mrid

Đây không phải là cách thích hợp hoặc được đề xuất để làm động các mục tái chế. Bạn phải sử dụng lớp
ItemAnimator

25

Tôi đã tạo hoạt hình từ câu trả lời của pbm với một chút modificationđể làm cho việc giải thích chỉ chạy một lần

nói cách khác Animation appear with you scroll down only

private int lastPosition = -1;

private void setAnimation(View viewToAnimate, int position) {
    // If the bound view wasn't previously displayed on screen, it's animated
    if (position > lastPosition) {
        ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
        viewToAnimate.startAnimation(anim);
        lastPosition = position;
    }
}

và trong onBindViewHolderchức năng gọi

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

holder.getTextView().setText("some text");

// call Animation function
setAnimation(holder.itemView, position);            
}

1
LastPocation ở đây là gì? Có phải là ArrayList.size () - 1
Sumit Shukla

1
lastPositionđại diện cho số lượt xem được hiển thị, do đó, bắt đầu giá trị của nó -1, mỗi khi một chế độ xem mới được hiển thị, chúng tôi bắt đầu một hình ảnh động và tăng vị trí
Basheer AL-MOMANI

15

Bạn có thể thêm một android:layoutAnimation="@anim/rv_item_animation"thuộc tính để RecyclerViewthích điều này:

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"                                        
    android:layoutAnimation="@anim/layout_animation_fall_down"
    />

cảm ơn vì bài viết xuất sắc ở đây: https://proandroiddev.com/enter-animation-USE-recyclerview-and-layoutanimation-part-1-list-75a874a5d213


Điều này làm việc cho tôi khi một recyclerview tải lần đầu tiên, nhưng không phải khi thêm các mục mới. (Mặc dù tôi đoán đó là câu hỏi rất hay)
C. Skjerdal

8

Một nơi tốt để bắt đầu là đây: https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/ad chương /AnAdationAd Module.java

Bạn thậm chí không cần thư viện đầy đủ, lớp đó là đủ. Sau đó, nếu bạn chỉ thực hiện lớp Adaptor của mình, hãy tạo một hoạt họa như thế này:

@Override
protected Animator[] getAnimators(View view) {
    return new Animator[]{
            ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0)
    };
}

@Override
public long getItemId(final int position) {
    return getWrappedAdapter().getItemId(position);
}

bạn sẽ thấy các mục xuất hiện từ dưới cùng khi chúng cuộn, cũng tránh sự cố với cuộn nhanh.


3

Các mục hoạt hình trong bộ tái chế khi chúng được liên kết trong bộ chuyển đổi có thể không phải là ý tưởng tốt nhất vì điều đó có thể khiến các mục trong bộ tái chế hoạt động ở các tốc độ khác nhau. Trong trường hợp của tôi, vật phẩm ở cuối recyclerview hoạt động nhanh hơn vị trí của chúng sau đó những vật phẩm ở trên đỉnh như những vật ở trên đỉnh có thể di chuyển xa hơn khiến nó trông không gọn gàng.

Mã ban đầu mà tôi đã sử dụng để tạo hiệu ứng cho từng mục vào recyclerview có thể được tìm thấy ở đây:

http://frogermcs.github.io/Instagram-with-M vật liệu-Design-conception-is-gett-real /

Nhưng tôi sẽ sao chép và dán mã trong trường hợp liên kết bị đứt.

BƯỚC 1: Đặt cái này bên trong phương thức onCreate của bạn để bạn đảm bảo hoạt hình chỉ chạy một lần:

if (savedInstanceState == null) {
    pendingIntroAnimation = true;
}

BƯỚC 2: Bạn sẽ cần đặt mã này vào phương thức mà bạn muốn bắt đầu hoạt hình:

if (pendingIntroAnimation) {
    pendingIntroAnimation = false;
    startIntroAnimation();
}

Trong liên kết, người viết đang tạo hiệu ứng cho các biểu tượng trên thanh công cụ, vì vậy anh ta đặt nó bên trong phương thức này:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    inboxMenuItem = menu.findItem(R.id.action_inbox);
    inboxMenuItem.setActionView(R.layout.menu_item_view);
    if (pendingIntroAnimation) {
        pendingIntroAnimation = false;
        startIntroAnimation();
    }
    return true;
}

BƯỚC 3: Bây giờ hãy viết logic cho startIntroAnimation ():

private static final int ANIM_DURATION_TOOLBAR = 300;

private void startIntroAnimation() {
    btnCreate.setTranslationY(2 * getResources().getDimensionPixelOffset(R.dimen.btn_fab_size));

    int actionbarSize = Utils.dpToPx(56);
    toolbar.setTranslationY(-actionbarSize);
    ivLogo.setTranslationY(-actionbarSize);
    inboxMenuItem.getActionView().setTranslationY(-actionbarSize);

    toolbar.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(300);
    ivLogo.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(400);
    inboxMenuItem.getActionView().animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(500)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    startContentAnimation();
                }
            })
            .start();
}

Lựa chọn thay thế ưa thích của tôi:

Tôi thà làm động toàn bộ recyclerview thay vì các vật phẩm bên trong recyclerview.

BƯỚC 1 và 2 vẫn giữ nguyên.

Trong BƯỚC 3, ngay khi cuộc gọi API của bạn trở lại với dữ liệu của bạn, tôi sẽ bắt đầu hoạt hình.

private void startIntroAnimation() {
    recyclerview.setTranslationY(latestPostRecyclerview.getHeight());
    recyclerview.setAlpha(0f);
    recyclerview.animate()
            .translationY(0)
            .setDuration(400)
            .alpha(1f)
            .setInterpolator(new AccelerateDecelerateInterpolator())
            .start();
}

Điều này sẽ làm sinh động toàn bộ bộ tái chế của bạn để nó bay vào từ phía dưới màn hình.


bạn đang nói về hoạt hình toàn bộ tái chế
Longerian

Vâng chính tôi. Bạn nên đọc đoạn đầu tiên về lý do tại sao tôi nghĩ rằng hoạt hình toàn bộ recyclerview là một ý tưởng tốt hơn so với mỗi mục. Bạn có thể thử hoạt hình từng mục nhưng nó sẽ không đẹp.
Simon

latestPostRecyclerview
Antonio

1
Bạn đã đọc đoạn đầu tiên của câu trả lời của tôi, nơi tôi đã nêu lý do tại sao hoạt hình xem vật phẩm không phải là một ý tưởng tuyệt vời? Ngoài ra tôi sẽ đề nghị thử dùng giải pháp được chấp nhận và sau đó bạn nhận ra vấn đề, quay lại và thử giải pháp này.
Simon

3

Tạo phương thức này vào Bộ điều hợp tái chế của bạn

private void setZoomInAnimation(View view) {
        Animation zoomIn = AnimationUtils.loadAnimation(context, R.anim.zoomin);// animation file 
        view.startAnimation(zoomIn);
    }

Và cuối cùng thêm dòng mã này vào onBindViewHolder

setZoomInAnimation(holder.itemView);


2

Vào năm 2019, tôi sẽ đề nghị đưa tất cả các hình ảnh động vật phẩm vào ItemAnimator.

Hãy bắt đầu với việc khai báo trình hoạt hình trong chế độ xem tái chế:

with(view.recycler_view) {
adapter = Adapter()
itemAnimator = CustomAnimator()
}

Khai báo hoạt hình tùy chỉnh sau đó,

class CustomAnimator() : DefaultItemAnimator() {

     override fun animateAppearance(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo?,
       postInfo: ItemHolderInfo): Boolean{} // declare  what happens when a item appears on the recycler view

     override fun animatePersistence(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo,
       postInfo: ItemHolderInfo): Boolean {} // declare animation for items that persist in a recycler view even when the items change

}

Tương tự như những cái ở trên, có một cái để biến mất animateDisappearance, để thêm animateAdd, để thay đổi animateChangevà di chuyển animateMove.

Một điểm quan trọng là gọi các nhân viên hoạt hình chính xác bên trong họ.


Bạn có thể cung cấp một ví dụ về hoạt hình xuất hiện tùy chỉnh bằng chức năng ghi đè này không? Tôi không thể tìm thấy một ví dụ và tôi không chắc nếu tôi chỉ phải chỉ định hoạt hình trong chức năng.
tối thiểu

1
gist.github.com/tadfisher/120d03f8380bfa8a16bf Tôi tìm thấy điều này trực tuyến trên một tìm kiếm nhanh, điều này cho một ý tưởng về cách quá tải hoạt động
Dinesh

0

Chỉ cần mở rộng Bộ điều hợp của bạn như dưới đây

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

Và thêm siêu phương thức vào onBindViewHolder

@Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        super.onBindViewHolder(holder, position);

Đó là cách tự động để tạo bộ điều hợp hoạt hình như "Basheer AL-MOMANI"

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;

import java.util.Random;

/**
 * Created by eliaszkubala on 24.02.2017.
 */
public class AnimatedRecyclerView<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {


    @Override
    public T onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(T holder, int position) {
        setAnimation(holder.itemView, position);
    }

    @Override
    public int getItemCount() {
        return 0;
    }

    protected int mLastPosition = -1;

    protected void setAnimation(View viewToAnimate, int position) {
        if (position > mLastPosition) {
            ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
            viewToAnimate.startAnimation(anim);
            mLastPosition = position;
        }
    }

}

0

Tôi nghĩ, tốt hơn là sử dụng nó như thế này: (trong bộ điều hợp RecyclerView ghi đè chỉ một phương thức)

override fun onViewAttachedToWindow(holder: ViewHolder) {
    super.onViewAttachedToWindow(holder)

    setBindAnimation(holder)
}

Nếu bạn muốn mọi hình ảnh đính kèm có trong RV.

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.