参考:实现Instagram的Material Design概念设计
Android动画可以分为三类:帧动画(Frame Animation)、补间动画(Tween Animation)、属性动画(Property Animation)。我们主要这里用到了补间动画的Alpha(透明度)和Translate(位移)属性。
由于我们是为RecyclerView的Item视图绘制动画效果,所以我们在自定义的MyAdapter(继承了RecyclerView.Adapter)的onBindViewHolder(…)方法里为Item项开启动画。
仔细观察动画效果,我们可以看到动画效果由两部分组成:
private void runEnterAnimation(View view, int position) {
if (animationsLocked) return; //animationsLocked是布尔类型变量,一开始为false
//确保仅屏幕一开始能够容纳显示的item项才开启动画
if (position > lastAnimatedPosition) {//lastAnimatedPosition是int类型变量,默认-1,
//这两行代码确保了recyclerview滚动式回收利用视图时不会出现不连续效果
lastAnimatedPosition = position;
view.setTranslationY(500); //Item项一开始相对于原始位置下方500距离
view.setAlpha(0.f); //item项一开始完全透明
//每个item项两个动画,从透明到不透明,从下方移动到原始位置
view.animate()
.translationY(0).alpha(1.f) //设置最终效果为完全不透明
//并且在原来的位置
.setStartDelay(delayEnterAnimation ? 20 * (position) : 0)//根据item的位置设置延迟时间
//达到依次动画一个接一个进行的效果
.setInterpolator(new DecelerateInterpolator(0.5f)) //设置动画位移先快后慢的效果
.setDuration(700)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animationsLocked = true;
//确保仅屏幕一开始能够显示的item项才开启动画
//也就是说屏幕下方还没有显示的item项滑动时是没有动画效果
}
})
.start();
}
}
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contentRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="16dp"
android:orientation="vertical"
tools:context=".MainActivity">
"@+id/rcv"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp" />
"2dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
"@+id/edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
"match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingTop="8dp">
"@+id/ivUser"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:src="@drawable/icon3" />
"@+id/tvComment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="16dp"
android:layout_weight="1"
android:text="Lorem ipsum dolor sit amet。" />
"match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginBottom="2dp"
android:layout_marginLeft="88dp"
android:background="#cccccc" />
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.EditText;
import java.util.ArrayList;
import butterknife.ButterKnife;
import butterknife.BindView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.rcv)
RecyclerView rcy;
@BindView(R.id.edit)
EditText editText;
@BindView(R.id.contentRoot)
LinearLayout contentRoot;
private ArrayList mDataSet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initDate();
initRcy();
}
private void initRcy() {
rcy.setLayoutManager(new LinearLayoutManager(this));
rcy.setAdapter(new MyAdapter(this,mDataSet));
}
private void initDate() {
mDataSet=new ArrayList();
mDataSet.add("要想实现这个动画效果,必须考虑到取消原本的重绘回调机制。");
mDataSet.add("item项首先是不透明,并且在原来位置的下方100处,然后慢慢变回透明,并且同时回到原来的位置");
mDataSet.add("底部的输入评论视图首先在原来的位置下方100处,item动画完成后重新回到原来的位置");
mDataSet.add("要想实现这个动画效果,必须考虑到取消原本的重绘回调机制。");
mDataSet.add("item项首先是不透明,并且在原来位置的下方100处,然后慢慢变回透明,并且同时回到原来的位置");
mDataSet.add("底部的输入评论视图首先在原来的位置下方100处,item动画完成后重新回到原来的位置");
mDataSet.add("要想实现这个动画效果,必须考虑到取消原本的重绘回调机制。");
mDataSet.add("item项首先是不透明,并且在原来位置的下方100处,然后慢慢变回透明,并且同时回到原来的位置");
mDataSet.add("底部的输入评论视图首先在原来的位置下方100处,item动画完成后重新回到原来的位置");
mDataSet.add("要想实现这个动画效果,必须考虑到取消原本的重绘回调机制。");
mDataSet.add("item项首先是不透明,并且在原来位置的下方100处,然后慢慢变回透明,并且同时回到原来的位置");
mDataSet.add("底部的输入评论视图首先在原来的位置下方100处,item动画完成后重新回到原来的位置");
}
}
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import butterknife.ButterKnife;
import butterknife.BindView;
/**
* Created by yang on 16-4-18.
*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context mContext;
private ArrayList mDataSet;
private int lastAnimatedPosition=-1;
private boolean animationsLocked = false;
private boolean delayEnterAnimation = true;
private int itemCount=0;
public void setmDataSet(ArrayList mDataSet) {
this.mDataSet = mDataSet;
}
public MyAdapter(Context mContext, ArrayList mDataSet) {
this.mContext = mContext;
this.mDataSet = mDataSet;
}
public MyAdapter(Context mContext) {
this.mContext = mContext;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder viewHolder = new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_view, parent, false));
return viewHolder;
}
private void runEnterAnimation(View view, int position) {
if (animationsLocked) return; //animationsLocked是布尔类型变量,一开始为false
//确保仅屏幕一开始能够容纳显示的item项才开启动画
if (position > lastAnimatedPosition) {//lastAnimatedPosition是int类型变量,默认-1,
//这两行代码确保了recyclerview滚动式回收利用视图时不会出现不连续效果
lastAnimatedPosition = position;
view.setTranslationY(500); //Item项一开始相对于原始位置下方500距离
view.setAlpha(0.f); //item项一开始完全透明
//每个item项两个动画,从透明到不透明,从下方移动到原始位置
view.animate()
.translationY(0).alpha(1.f) //设置最终效果为完全不透明
//并且在原来的位置
.setStartDelay(delayEnterAnimation ? 20 * (position) : 0)//根据item的位置设置延迟时间
//达到依次动画一个接一个进行的效果
.setInterpolator(new DecelerateInterpolator(0.5f)) //设置动画位移先快后慢的效果
.setDuration(700)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animationsLocked = true;
//确保仅屏幕一开始能够显示的item项才开启动画
//也就是说屏幕下方还没有显示的item项滑动时是没有动画效果
}
})
.start();
}
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
runEnterAnimation(holder.itemView,position);
if(position%2==0){
holder.ivUser.setImageResource(R.drawable.icon2);
}else if(position%3==0){
holder.ivUser.setImageResource(R.drawable.icon3);
}else{
holder.ivUser.setImageResource(R.drawable.icon1);
}
holder.tvComment.setText(mDataSet.get(position));
}
@Override
public int getItemCount() {
return mDataSet.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.tvComment)
TextView tvComment;
@BindView(R.id.ivUser)
ImageView ivUser;
public MyViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this,itemView);
}
}
}
这样子最终效果就出来了,如果有错误的地方请指出。
PS:
该动画效果仅在首次显示RecyclerView时显示,如果有刷新等操作需要重新显示动画效果时,可以考虑重新恢复animationsLoacked和LastAnimatedPosition的值为false
和-1
。