我希望看到我的RecyclerView项目装饰幻灯片与我的项目在通过TranslateAnimation进行动画处理时的方式相同。
我希望我的装饰品也能滑动
我尝试在 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;
}
}