如何以与项目相同的方式为我的RecyclerView项目装饰设置动画

时间:2017-06-30 17:02:25

标签: android android-recyclerview android-animation

我希望看到我的RecyclerView项目装饰幻灯片与我的项目在通过TranslateAnimation进行动画处理时的方式相同。

我希望我的装饰品也能滑动

enter image description here

我尝试在 SeparatorDecoration 中的onDraw方法中使用getTranslationX(),但没有成功。

有人对实现这一目标的好方法有所了解吗?谢谢!

我在

中播放动画
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

mAnimator.onCreateViewHolder(v);

VideoAdapter.java

package com.example.username.todoapp.model;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.VisibleForTesting;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.example.username.todoapp.R;
import com.example.username.todoapp.utils.Utils;

import java.util.List;


/**
 * An {@link VideoAdapter} knows how to create a list item layout for each Youtube video
 * in the data source (a list of {@link YoutubeVideo} objects).
 * <p>
 * These list item layouts will be provided to an adapter view like ListView
 * to be displayed to the user.
 */
public class VideoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    private static final String TAG = VideoAdapter.class.getSimpleName();

    private static final String YOUTUBE_VIDEO_BASE_URL = "https://www.youtube.com/watch?v=";
    public static int CRUSH_TYPE = 1;
    public static int INTERVIEW_TYPE = 2;
    public static int FOOTER_TYPE = 3;

    private Callback mListener;
    private List<YoutubeVideo> mVideos;
    private static int mListItemType;
    private boolean needFooter;

    private RecyclerViewAnimator mAnimator;

    public interface Callback {
        void onFooterItemClick();
    }

    private static class VideoViewHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener {
        private TextView mTitleView;
        private ImageView mThumbnailView;
        private TextView mSongView;
        private TextView mDescriptionView;
        private ViewHolderClicks mListener;

        interface ViewHolderClicks {
            void onItemClick(View v, int position);
        }

        private VideoViewHolder(View v, ViewHolderClicks listener) {
            super(v);
            mListener = listener;
            if (mListItemType == CRUSH_TYPE) {
                mTitleView = (TextView) v.findViewById(R.id.crush_youtube_video_title);
                mThumbnailView = (ImageView) v.findViewById(R.id.crush_youtube_video_thumbnail);
                mSongView = (TextView) v.findViewById(R.id.crush_youtube_video_song);
                mDescriptionView = (TextView) v.findViewById(R.id.crush_youtube_video_description);
            } else if (mListItemType == INTERVIEW_TYPE) {
                mTitleView = (TextView) v.findViewById(R.id.interview_youtube_video_title);
                mThumbnailView = (ImageView) v.findViewById(R.id.interview_youtube_video_thumbnail);
            }
            v.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            mListener.onItemClick(v, getLayoutPosition());
        }
    }

    @VisibleForTesting
    public static class ProgressViewHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener {

        interface ViewHolderClicks {
            void onFooterClick();
        }

        private ProgressViewHolder.ViewHolderClicks mListener;
        private TextView footerEmptyStateTextView;
        private View footerLoadingView;

        private ProgressViewHolder(View v, ProgressViewHolder.ViewHolderClicks listener) {
            super(v);
            mListener = listener;
            footerEmptyStateTextView = (TextView) v.findViewById(R.id.textview_video_footer);
            footerLoadingView = v.findViewById(R.id.progressbar_video_footer);
            v.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            Log.d(TAG, "Method: onFooterItemClick");
            mListener.onFooterClick();
        }
    }

    public VideoAdapter(Callback listener, List<YoutubeVideo> videos, int listItemType) {
        mListener = listener;
        mVideos = videos;
        mListItemType = listItemType;
        mAnimator = new RecyclerViewAnimator();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        if (viewType == FOOTER_TYPE) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.recyclerview_footer, parent, false);
            return new ProgressViewHolder(v, new ProgressViewHolder.ViewHolderClicks() {
                @Override
                public void onFooterClick() {
                    mListener.onFooterItemClick();
                }
            });
        } else if (mListItemType == CRUSH_TYPE) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.crush_item, parent, false);
        } else if (mListItemType == INTERVIEW_TYPE) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.interview_item, parent, false);
        } else {
            throw new RuntimeException("there is no type that matches the type "
                    + viewType + " + make sure your using types correctly");
        }
        VideoViewHolder vh = new VideoViewHolder(v, new VideoViewHolder.ViewHolderClicks() {
            @Override
            public void onItemClick(View v, int position) {
                Context context = v.getContext();
                Uri videoUri = Uri.parse(YOUTUBE_VIDEO_BASE_URL + mVideos.get(position).getId());
                Intent intent = new Intent(Intent.ACTION_VIEW, videoUri);
                if (intent.resolveActivity(context.getPackageManager()) != null) {
                    context.startActivity(intent);
                }
            }
        });
        // Run the animation when the view is created
        mAnimator.onCreateViewHolder(v);

/*
        Animation animation = AnimationUtils.loadAnimation((Context) mListener,
                R.anim.slide_item_in_end);
        animation.setDuration(4000);
        animation.setStartOffset(vh.getAdapterPosition() * 50);
        animation.setInterpolator(new FastOutSlowInInterpolator());
        v.startAnimation(animation);
        */
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof VideoViewHolder) {
            YoutubeVideo currentVideo = mVideos.get(position);
            if (mListItemType == CRUSH_TYPE) {
                ((VideoViewHolder) holder).mTitleView.setText(currentVideo.getTitle());
                Glide.with(((VideoViewHolder) holder).mThumbnailView.getContext())
                        .load(currentVideo.getThumbnail())
                        .into(((VideoViewHolder) holder).mThumbnailView);
                ((VideoViewHolder) holder).mSongView.setText(currentVideo.getSongTitle());
                ((VideoViewHolder) holder).mDescriptionView.setText(currentVideo.getDescription());
            } else if (mListItemType == INTERVIEW_TYPE) {
                ((VideoViewHolder) holder).mTitleView.setText(currentVideo.getTitle());
                Glide.with(((VideoViewHolder) holder).mThumbnailView.getContext())
                        .load(currentVideo.getThumbnail())
                        .into(((VideoViewHolder) holder).mThumbnailView);
            }
        } else {
            Log.d(TAG, "position=" + position);
            if (Utils.hasNetworkConnection((Activity) mListener)) {
                ((ProgressViewHolder) holder).footerLoadingView.setVisibility(View.VISIBLE);
                ((ProgressViewHolder) holder).footerEmptyStateTextView.setVisibility(View.GONE);
            } else {
                ((ProgressViewHolder) holder).footerLoadingView.setVisibility(View.GONE);
                ((ProgressViewHolder) holder).footerEmptyStateTextView.setVisibility(View.VISIBLE);
            }
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (needFooter && isLastItem(position)) {
            return FOOTER_TYPE;
        } else {
            return mListItemType;
        }
    }

    private boolean isLastItem(int position) {
        return position == getItemCount() - 1;
    }

    @Override
    public int getItemCount() {
        return needFooter ? mVideos.size() + 1 : mVideos.size();
    }

    public boolean getNeedFooter() {
        return this.needFooter;
    }

    public void setNeedFooter(boolean value) {
        Log.d(TAG, "Method: setNeedFooter");
        Log.d(TAG, "value=" + value);
        this.needFooter = value;
    }

    public boolean animationEnded() {
        return mAnimator.hasEnded();
    }
}

SeparatorDecoration.java

package com.example.username.todoapp.model;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;


/**
 * SeparatorDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
 * between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
 * {@link #VERTICAL} orientations.
 * <p>
 * <pre>
 *     mDividerItemDecoration = new SeparatorDecoration(recyclerView.getContext(),
 *             mLayoutManager.getOrientation());
 *     recyclerView.addItemDecoration(mDividerItemDecoration);
 * </pre>
 */
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    public static final int VERTICAL = LinearLayout.VERTICAL;

    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    private Drawable mDivider;

    /**
     * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
     */
    private int mOrientation;

    private final Rect mBounds = new Rect();

    /**
     * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
     * {@link LinearLayoutManager}.
     *
     * @param context     Current context, it will be used to access resources.
     * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
     */
    public SeparatorDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    /**
     * Sets the orientation for this divider. This should be called if
     * {@link RecyclerView.LayoutManager} changes orientation.
     *
     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
     */
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException(
                    "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
        }
        mOrientation = orientation;
    }

    /**
     * Sets the {@link Drawable} for this divider.
     *
     * @param drawable Drawable that should be used as a divider.
     */
    public void setDrawable(@NonNull Drawable drawable) {
        if (drawable == null) {
            throw new IllegalArgumentException("Drawable cannot be null.");
        }
        mDivider = drawable;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getLayoutManager() == null) {
            return;
        }
        if (mOrientation == VERTICAL) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    private void drawVertical(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int left;
        final int right;
        if (parent.getClipToPadding()) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            canvas.clipRect(left, parent.getPaddingTop(), right,
                    parent.getHeight() - parent.getPaddingBottom());
        } else {
            left = 0;
            right = parent.getWidth();
        }

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            final View child = parent.getChildAt(i);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int top;
        final int bottom;
        if (parent.getClipToPadding()) {
            top = parent.getPaddingTop();
            bottom = parent.getHeight() - parent.getPaddingBottom();
            canvas.clipRect(parent.getPaddingLeft(), top,
                    parent.getWidth() - parent.getPaddingRight(), bottom);
        } else {
            top = 0;
            bottom = parent.getHeight();
        }

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            final View child = parent.getChildAt(i);
            parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
            final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));
            final int left = right - mDivider.getIntrinsicWidth();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                               RecyclerView.State state) {
        if (mOrientation == VERTICAL) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

RecyclerViewAnimator.java

package com.example.username.todoapp.model;


import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;

import com.example.username.todoapp.utils.AnimationListenerAdapter;

public class RecyclerViewAnimator {
    /**
     * Initial delay before to show items - in ms
     */
    private static final String TAG = RecyclerViewAnimator.class.getSimpleName();

    private static final int INIT_DELAY = 0;

    private boolean mFirstViewInit;
    private boolean mHasEnded;
    private int mStartDelay;

    public RecyclerViewAnimator() {
        mFirstViewInit = true;
        mHasEnded = false;
        mStartDelay = INIT_DELAY;
    }

    public void onCreateViewHolder(View item) {
        Log.d(TAG, "Method: onCreateViewHolder");
        /**
         * mFirstViewInit is used because we only want to show animation once at initialization.
         * (onCreateViewHolder can be called after if you use multiple view types).
         */
        if (mFirstViewInit) {
            slideInEnd(item);
            mStartDelay += 50;
        }
    }

    /**
     * Performs an animation on given view.
     * @param item
     */
    private void slideInEnd(final View item) {
        TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);
        animation.setDuration(4000);
        animation.setStartOffset(mStartDelay);
        animation.setInterpolator(new FastOutSlowInInterpolator());
        animation.setAnimationListener(new AnimationListenerAdapter() {
            @Override
            public void onAnimationEnd(Animation animation) {
                mFirstViewInit = false;
                mHasEnded = true;
                animation.setAnimationListener(null);
            }
        });
        item.startAnimation(animation);
    }

    /**
     * Helper to know if it is safe to scroll in the RecyclerView.
     * @return true if the first item animation has ended, else false.
     */
    public boolean hasEnded() {
        return mHasEnded;
    }
}

0 个答案:

没有答案