RecyclerView - 活动开始时的SlideIn动画

时间:2015-12-18 17:49:26

标签: android android-animation android-recyclerview

如何在我的回收站视图项目中依次添加动画幻灯片。就像活动开始一样,回收者视图的列表项逐个滑动。我正在使用LinearLayoutManager

不是所有人都应该同时滑入。甚至在滚动时也是如此。就在创建活动时。

我搜索但没有找到任何东西。

我希望实现这样的目标:https://youtu.be/Q8TXgCzxEnw?t=30s

3 个答案:

答案 0 :(得分:1)

我在几个月前整理了一个示例应用程序,它在重新洗牌期间有一个顺序幻灯片插入动画。我们提供了演示视频here。它应该给你一些想法。

指向最相关的类文件的链接是here,我将复制下面的代码。

public class AllNotesFragmentRecyclerView extends RecyclerView {

    private static final int BASE_ANIMATION_TIME = 50;
    private static final int MAX_ANIMATION_TIME_INCREMENT = 100;

    private int screenWidth;
    private int startX, finalX;

    private int[] interpolatedAnimationTimes;

    public AllNotesFragmentRecyclerView(Context context) {
        super(context);
        init(context);
    }

    public AllNotesFragmentRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public AllNotesFragmentRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        calculateScreenWidth(context);

        startX = 0;
        finalX = -(screenWidth);
    }

    private void calculateScreenWidth(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(metrics);
        screenWidth = metrics.widthPixels;
    }

    private int calculateInterpolatedAnimationTime(int currentIndex, int maxIndex) {
        float percentage = ((float)currentIndex/(float)maxIndex);
        float increment = (float) MAX_ANIMATION_TIME_INCREMENT * percentage;
        return (int) (BASE_ANIMATION_TIME + increment);
    }

    public void updateListOrder() {
        createAnimatorSet();
    }

    private void createAnimatorSet() {

        AnimatorSet set = new AnimatorSet();
        ArrayList<Animator> animArrayList = new ArrayList<>();

        for (int i = 0; i < getChildCount(); i++) {
            ObjectAnimator anim = ObjectAnimator
                    .ofFloat(getChildAt(i), "translationX", finalX);

            int duration = calculateInterpolatedAnimationTime(i, getChildCount());

            anim.setDuration(duration);
            anim.addListener(new RowAnimationListener(i, duration, startX));
            animArrayList.add(anim);
        }
        set.setInterpolator(new AccelerateInterpolator());
        set.playSequentially(animArrayList);
        set.start();
    }

    private void animateOn(int childPosition, int duration, int targetValue) {
        ObjectAnimator animator = ObjectAnimator
                .ofFloat(getChildAt(childPosition), "translationX", targetValue);
        animator.setInterpolator(new DecelerateInterpolator());
        animator.setDuration(duration);
        animator.start();
    }
//...

    private class RowAnimationListener implements Animator.AnimatorListener {

        private int position, duration, targetX;

        public RowAnimationListener(int position, int duration, int targetX) {
            this.position = position;
            this.duration = duration;
            this.targetX = targetX;
        }

        @Override
        public void onAnimationStart(Animator animation) {
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            int currentItem = getLinearLayoutManager().findFirstVisibleItemPosition() + position;
            getAdapter().notifyItemChanged(currentItem);
            notifyRowsPeripheralToVisibleItemsDataChanged(position);
            animateOn(position, duration, targetX);
        }

        @Override
        public void onAnimationCancel(Animator animation) { }

        @Override
        public void onAnimationRepeat(Animator animation) { }
    }
}

答案 1 :(得分:1)

最后我找到了解决方案。在下面的代码片段中,我将解释如何实现。它很简单,可以在任何现有工作RecyclerView上完成。我在评论中解释了一切。

这是onCreate / onCreateView方法(我在Fragment中使用了这个,你可以根据需要进行相应更改):

RecyclerView recList = (RecyclerView) rootView.findViewById(R.id.event_list);
recList.setHasFixedSize(true);
LinearLayoutmanager llm = new LinearLayoutManager(getActivity().getApplicationContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);

// This is important. Setting recyclerView's alpha to zero.
// Basically this is just to hide recyclerview at start before binding data
// As setVisibility is not working on recycler view object.
recList.setAlpha(0);

// Create the EventAdapter with the result we got
// EventAdapter is my custom adapter class.
// you should set your adapter class
EventAdapter ea = new EventAdapter(eventResultList);

// Binding the Adapter to RecyclerView list to show the data
recList.setAdapter(ea);

// ********************* Animate at start ********************************

new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {

                // This will give me the initial first and last visible element's position.
                // This is required as only this elements needs to be animated
                // Start will be always zero in this case as we are calling in onCreate
                int start = llm.findFirstVisibleItemPosition();
                int end = llm.findLastVisibleItemPosition();

                Log.i("Start: ", start + "");
                Log.i("End: ", end + "");

                // Multiplication factor
                int DELAY = 50;

                // Loop through all visible element
                for (int i = start; i <= end; i++) {
                    Log.i("Animatining: ", i + "");

                    // Get View
                    View v = recList.findViewHolderForAdapterPosition(i).itemView;

                    // Hide that view initially
                    v.setAlpha(0);

                    // Setting animations: slide and alpha 0 to 1
                    PropertyValuesHolder slide = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 150, 0);
                    PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat(View.ALPHA, 0, 1);
                    ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(v, slide, alpha);
                    a.setDuration(300);

                    // It will set delay. As loop progress it will increment
                    // And it will look like items are appearing one by one.
                    // Not all at a time
                    a.setStartDelay(i * DELAY);

                    a.setInterpolator(new DecelerateInterpolator());

                    a.start();

                }

                // Set Recycler View visible as all visible are now hidden
                // Animation will start, so set it visible
                recList.setAlpha(1);

        }
}, 50);

这是一个没有评论的相当小的代码。

有些事情需要解释:

为什么最初隐藏RecyclerView?

如果最初未隐藏RecyclerView,您会在动画开始前注意到闪烁。原因是当你设置数据适配器时,它会将它定位在默认位置,并在循环后开始动画。因此,在while循环运行之间,您会注意到RecyclerView中的突然闪烁,它们最初都位于其初始位置,而不是突然动画。

首先隐藏它,然后完成循环,并且所有可见位置动画都设置为延迟并开始,我们可以显示RecyclerView。它使滑动看起来很光滑。

使用setAlpha(0)隐藏它的原因是setVisibility()函数无效RecyclerView对象。

只有可见元素才能生成动画?

LayoutManager类中有一些函数可以获取可见元素的位置。在LinearLayoutManager中使用findFirstVisibleItemPosition()从屏幕上可见的回收者视图数据中获取第一个可见视图的位置。可以使用findLastVisibleItemPosition()重试最后一个可见视图的位置。因此,我们可以从第一个视图循环到最后一个视图,并为开始时将在屏幕上显示的初始视图设置动画。

延迟有效吗?

由于循环将从0(开始)进展到end,它将设置延迟从0,50,100,150,...如果DELAY变量设置为50.所以这将首先元素开始动画,第二次延迟50ms后,第三次延迟100ms后依此类推。所以看起来他们会一个接一个地进入。不是全部在一起。

答案 2 :(得分:1)

anim/slide_in.xml文件中创建动画,如下所示

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:shareInterpolator="@android:anim/decelerate_interpolator">
        <translate
            android:fromXDelta="100%" android:toXDelta="0%"
            android:fromYDelta="0%" android:toYDelta="0%"
            android:duration="2000"/>
    </set>

然后在onBindViewHolder方法中的RecyclerView的每个视图上应用此动画。

 @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ViewHolder vh = (ViewHolder) holder;
        vh1.tv_header.setText(mList.get(position));

        Animation animation = AnimationUtils.loadAnimation(mContext,R.anim.rec_anim);
        animation.setStartOffset(30 * position);//Provide delay here 
        holder.itemView.startAnimation(animation);
    }