The "Radial reaction" Material Design pattern can be found here.
现在,我正在使用RecyclerView的Subclassed实现,根据设备的大小自动提供正确数量的行和列。在RecyclerView的onBindViewHolder的实现中,我将我的布局的Card元素添加到一个自定义的AnimationHelper类中,而后者应该在该卡的转动时为该卡添加动画。我现在的问题是确定什么时候是每个人......
我的动画效果有点有效,但我需要一些帮助来弄清楚如何更准确地表示模式。现在,我有一个快速的多米诺骨牌"效果,它看起来并不合适。在从材料文档中进一步检查剪辑后,很明显第一个细胞首先着色,然后是下面和右边的细胞,继续使用该模式,直到所有六个细胞都变暗。这就是我需要一些方法的帮助,无论有多少行和列的卡片,都可以动画我的卡片以实现这种难以捉摸的视觉设计模式。
网站上的Here is an imgur album with a few stills from the clip可以更好地了解它们是如何实现的。
以下是Github上代码的链接:github / halfjew22 / AutoFitGridRecyclerView / tree / master / app / src / main / java / com / lustig / autofitgridrecyclerview (对不起,没有至少10个代表,我无法发布链接)
我们非常欢迎任何帮助,协助,对我的代码的建设性批评,以及更好地实现此动画的建议。
以下是相关的代码段:
~~~~~~动画助手~~~~~
package com.lustig.autofitgridrecyclerview.animations;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.os.Handler;
import android.view.View;
import java.util.ArrayList;
/**
* With this class, I am going to achieve the radial reaction effect described on the
* Material Guidelines docs here:
*/
/**
* Questions / Concerns
*
* How many animations can happen at one time?
* How should I deal with that?
* Will a simple boolean flag work for 2, 3 or more animations occurring in parallel?
*/
/**
* I'm not worried about efficiency so much as actually doing what I want to do.
* After I get something working, I can work on finding better ways to do things,
* but if I bog myself down with trying to pre-optimize, ain't shit gon' get did...
*/
public class AnimationHelper {
public static final long DEFAULT_DURATION_MS = 200;
private static final String DEFAULT_PROPERTY_TO_ANIMATE = "alpha";
/* Type of property of View being animated */
private String mViewPropertyToAnimate = DEFAULT_PROPERTY_TO_ANIMATE;
/* Duration of animations, set to default value, but can be changed */
private long mAnimationDuration = DEFAULT_DURATION_MS;
/* Value to determine if an animation is currently occurring */
private boolean isCurrentlyAnimating = false;
/* First, I need an animation queue. I should use a generic view, I'll try that first */
private ArrayList<View> mViewsToAnimate = new ArrayList<View>();
/* Next I'll need a method to add to the animation queue */
public void addViewToQueue(View viewToAnimate) {
/**
* If I've already animated the view, don't do it again
*/
if (viewToAnimate.getVisibility() == View.VISIBLE) {
return;
}
mViewsToAnimate.add(viewToAnimate);
/* This method is the meat and potatoes of this class */
startAnimationChain();
}
/* This method will be in charge of starting the domino effect */
private void startAnimationChain() {
/* If there is currently not an animation happening, start one */
if (mViewsToAnimate.size() >= 2 && !isCurrentlyAnimating) {
animateSingleView(mViewsToAnimate.get(0));
/**
* If we are currently animating, just wait. The next view animation should
* automatically be spawned
*/
} else if (isCurrentlyAnimating) {
// Just wait, animations should continue until the list is empty
}
}
private void animateSingleView(final View v) {
/* Right now, using a single animation, will change in the future */
ObjectAnimator animator =
ObjectAnimator.ofFloat(v, mViewPropertyToAnimate, 0f, 1f);
animator.setDuration(mAnimationDuration);
animator.addListener(
new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
v.setVisibility(View.VISIBLE);
/* Remove the currently animating View from the list */
mViewsToAnimate.remove(v);
/* Notify the class Object that an animation is happening */
isCurrentlyAnimating = true;
/**
* If, after removing the currently animating view, the list is not empty,
* start the next animation after the current animation is halfway done.
*/
if (!mViewsToAnimate.isEmpty()) {
/* Set a timer for (mAnimationDuration / 2) ms to start next animation */
final Handler handler = new Handler();
handler.postDelayed(
new Runnable() {
@Override
public void run() {
// Animate the first item in the list because each time
// an animation starts, it is removed from the list
if (!mViewsToAnimate.isEmpty()) {
animateSingleView(mViewsToAnimate.get(0));
}
}
}, mAnimationDuration / 6);
}
}
@Override
public void onAnimationEnd(Animator animation) {
/**
* Setting this boolean flag could potentially cause issues as I'm going
* to have to use a Runnable to wait some time before starting the next
* animation. If there are any bugs, come back here, debug, and make sure
* that this flag is behaving as expected
*/
/* Notify the class Object that the current animation has finished */
isCurrentlyAnimating = false;
}
@Override
public void onAnimationCancel(Animator animation) {
// Ignored intentionally
}
@Override
public void onAnimationRepeat(Animator animation) {
// Ignored intentionally
}
});
animator.start();
}
}
~~~~~ AutofitRecyclerView ~~~~~
package com.lustig.autofitgridrecyclerview.recyclers;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
public class AutofitRecyclerView extends RecyclerView {
private GridLayoutManager manager;
private int columnWidth = -1;
public AutofitRecyclerView(Context context) {
super(context);
init(context, null);
}
public AutofitRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public AutofitRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
int[] attrsArray = {
android.R.attr.columnWidth
};
TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
columnWidth = array.getDimensionPixelSize(0, -1);
array.recycle();
}
manager = new GridLayoutManager(getContext(), 1);
setLayoutManager(manager);
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
if (columnWidth > 0) {
int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
manager.setSpanCount(spanCount);
}
}
}
~~~~~适配器和ViewHolder实现~~~~~
package com.lustig.autofitgridrecyclerview.adapters;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.lustig.autofitgridrecyclerview.R;
import com.lustig.autofitgridrecyclerview.animations.AnimationHelper;
import java.util.ArrayList;
import java.util.List;
public class NumberedAdapter extends RecyclerView.Adapter<NumberedAdapter.TextViewHolder> {
private List<String> labels;
private AnimationHelper mHelper;
public NumberedAdapter(int count) {
mHelper = new AnimationHelper();
labels = new ArrayList<String>(count);
for (int i = 0; i < count; ++i) {
labels.add(String.valueOf(i));
}
}
@Override
public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new TextViewHolder(view);
}
@Override
public void onBindViewHolder(final TextViewHolder holder, final int position) {
final String label = labels.get(position);
holder.textView.setText(label);
mHelper.addViewToQueue(holder.cardView);
}
@Override
public int getItemCount() {
return labels.size();
}
public class TextViewHolder extends RecyclerView.ViewHolder {
public CardView cardView;
public TextView textView;
public TextViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView.findViewById(R.id.card);
textView = (TextView) itemView.findViewById(R.id.text);
}
}
}
答案 0 :(得分:0)
您不需要这些解决方法。
实施自定义ItemAnimator。按下后退箭头后,从适配器中删除所有项目notifyItemRangeRemoved(0, mAdapter.getItemCount())
;
这会将您的ItemAnimator调用animateRemove
所有视图。在那里你可以记录观点,决定订单等;在此之后,您将收到runPendingAnimations
来电,您可以在其中运行实际的动画。
有关详细信息,请参阅DefaultItemAnimator的实现。