前言
前些天朋友发了一份居于Android5.0以下实现转场动画的代码,对动画挺感兴趣,所以就看了看效果的展示,果真跟5.0的转场动画一样,那么怎么实现的呢?有代码当然看代码了,那就看吧!
Ⅰ.简述
说明:效果就是点击当前列表页的某条目的图片,然后跳转到另个页面A,被点击的图片随着页面的跳转,缩放并移动到另一页面A的中心位置.
下面看下整体的代码实现吧!
MainActivity.java
public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.MyRecyclerViewAdapterListener {
private static final String TAG = MainActivity.class.getSimpleName();
RecyclerView recyclerView;
MyRecyclerViewAdapter myRecyclerViewAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myRecyclerViewAdapter=new MyRecyclerViewAdapter();
myRecyclerViewAdapter.setListener(this);
recyclerView= (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(myRecyclerViewAdapter);
}
@Override
public void onItemClick(Rect srcRect, int imageId, String title) { //rv条目点击事件的回调
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("SRC_RECT", srcRect);
intent.putExtra("IMAGE_ID",imageId);
intent.putExtra("TITLE",title);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); //取消Activity跳转滑动的动画----> Intent.FLAG_ACTIVITY_NO_ANIMATION
startActivity(intent);
}
}
MyRecyclerViewAdapter.java
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyRecyclerViewHolder> {
private static final int[] LOGOS = {R.drawable.logo0, R.drawable.logo1, R.drawable.logo2, R.drawable.logo3, R.drawable.logo4, R.drawable.logo5, R.drawable.logo6, R.drawable.logo7, R.drawable.logo8, R.drawable.logo9, R.drawable.logo10, R.drawable.logo11, R.drawable.logo12, R.drawable.logo13};
MyRecyclerViewAdapterListener listener = null;
@Override
public MyRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
return new MyRecyclerViewHolder(view, listener);
}
@Override
public void onBindViewHolder(MyRecyclerViewHolder holder, int position) {
holder.updateItem(LOGOS[position], "ITEM " + position);
}
@Override
public int getItemCount() {
return LOGOS.length;
}
public class MyRecyclerViewHolder extends RecyclerView.ViewHolder {
MyRecyclerViewAdapterListener listener = null;
int imageId;
String title;
ImageView itemIv;
TextView itemTv;
public MyRecyclerViewHolder(View itemView, MyRecyclerViewAdapterListener listener) {
super(itemView);
itemIv = (ImageView) itemView.findViewById(R.id.itemIv);
itemTv = (TextView) itemView.findViewById(R.id.itemTv);
this.listener = listener;
this.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (MyRecyclerViewHolder.this.listener != null) {
int[] coords = new int[2];
itemIv.getLocationOnScreen(coords); //View在屏幕的左上角的x、y坐标
Rect srcRect = new Rect(coords[0], coords[1], coords[0] + itemIv.getMeasuredWidth(), coords[1] + itemIv.getMeasuredWidth());
//上面将形状的大小用 rect标识出来,然后点击时带到下个页面
MyRecyclerViewHolder.this.listener.onItemClick(srcRect, imageId, title);
}
}
});
}
public void updateItem(int imageId, String title) {
this.imageId = imageId;
this.title = title;
itemIv.setImageResource(imageId);
itemTv.setText(title);
}
}
public void setListener(MyRecyclerViewAdapterListener listener) {
this.listener = listener;
}
interface MyRecyclerViewAdapterListener {
void onItemClick(Rect srcRect, int imageId, String title);
}
}
SecondActivity.java
public class SecondActivity extends AppCompatActivity {
private static final String TAG = SecondActivity.class.getSimpleName();
ImageView detailIv;
View detailView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
detailIv = (ImageView) findViewById(R.id.detailIv);
detailView=findViewById(R.id.detailView);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Rect srcRect = getIntent().getParcelableExtra("SRC_RECT");
int[] coords = new int[2];
detailIv.getLocationOnScreen(coords); //View在屏幕的左上角的x、y坐标
Rect targetRect = new Rect(coords[0], coords[1], coords[0] + detailIv.getMeasuredWidth(), coords[1] +detailIv.getMeasuredHeight());
detailIv.setPivotX(0); //旋转或缩放的中心点X
detailIv.setPivotY(0); //旋转或缩放的中心点Y
//下面的设置移动和大小的补间动画是为了让detailIv还跟点击时候的位置和大小一样
detailIv.setTranslationY(srcRect.top - targetRect.top); //用于设置detailIv左上角的Y值,设置的值是相对于原本左上角Y值的
detailIv.setTranslationX(srcRect.left - targetRect.left);
detailIv.setScaleX((float) srcRect.width() / targetRect.width());
detailIv.setScaleY((float) srcRect.height() / targetRect.height());
detailIv.setImageResource(getIntent().getIntExtra("IMAGE_ID",R.mipmap.ic_launcher));
detailIv.setVisibility(View.VISIBLE);
//移动到原有detailIv的位置,缩小到原有detailIv的大小,也就是还在中间位置,大小不变
PropertyValuesHolder transY = PropertyValuesHolder.ofFloat("translationY", 0f);
PropertyValuesHolder transX = PropertyValuesHolder.ofFloat("translationX", 0f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1f);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(detailIv, transY, transX, scaleX, scaleY).setDuration(900);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
//让屏幕的背景渐变为原设置的背景色
TransitionDrawable transitionDrawable= (TransitionDrawable) detailView.getBackground();
transitionDrawable.startTransition(900);
}
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(0,0);
}
}
Ⅱ.学习记录点
卡片布局
属性
<!--cardCornerRadius 卡片布局的边角半径-->
app:cardCornerRadius="10dp"
<!--cardElevation 卡片布局的Z轴阴影-->
app:cardElevation="15dp">
单一职责原则
下面的recyclerView三个需要重写的方法,是不是看着挺郁闷的,三个方法的方法体都是一两行代码而已。
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyRecyclerViewHolder> {
@Override
public MyRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
return new MyRecyclerViewHolder(view, listener);
}
@Override
public void onBindViewHolder(MyRecyclerViewHolder holder, int position) {
holder.updateItem(LOGOS[position], "ITEM " + position);
}
@Override
public int getItemCount(){
return LOGOS.length;
}
}
那么看看逻辑都跑到holder类里去处理了
public class MyRecyclerViewHolder extends RecyclerView.ViewHolder {
MyRecyclerViewAdapterListener listener = null;
int imageId;
String title;
ImageView itemIv;
TextView itemTv;
public MyRecyclerViewHolder(View itemView, MyRecyclerViewAdapterListener listener) {
super(itemView);
itemIv = (ImageView) itemView.findViewById(R.id.itemIv);
itemTv = (TextView) itemView.findViewById(R.id.itemTv);
this.listener = listener;
this.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (MyRecyclerViewHolder.this.listener != null) {
int[] coords = new int[2];
itemIv.getLocationOnScreen(coords);
Rect srcRect = new Rect(coords[0], coords[1], coords[0] + itemIv.getMeasuredWidth(), coords[1] + itemIv.getMeasuredWidth());
MyRecyclerViewHolder.this.listener.onItemClick(srcRect, imageId, title);
}
}
});
}
public void updateItem(int imageId, String title) {
this.imageId = imageId;
this.title = title;
itemIv.setImageResource(imageId);
itemTv.setText(title);
}
}
接口回调
由于想对RecyclerView里的条目进行点击事件的监听处理,发现RecyclerView控件并没有提供setOnItemClickListener方法?? 凉拌了去…
//setOnClickListener明显没什么用,那么谷歌开发者为啥还提供了这个api,
//点击进去源码发现setOnClickListener是从View类继承而来的,
recyclerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
一不留神就跑去View类去了,还郁闷了半天setOnClickListener的用处,还不是继承惹的祸。那么既然要实现条目点击事件的监听,就来实现实现吧!
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyRecyclerViewHolder> {
//将接口实现类传进来
public void setListener(MyRecyclerViewAdapterListener listener) {
this.listener = listener;
}
监听接口
interface MyRecyclerViewAdapterListener {
void onItemClick(Rect srcRect, int imageId, String title);
}
public class MyRecyclerViewHolder extends RecyclerView.ViewHolder {
MyRecyclerViewAdapterListener listener = null;
int imageId;
String title;
ImageView itemIv;
TextView itemTv;
public MyRecyclerViewHolder(View itemView, MyRecyclerViewAdapterListener listener) {
super(itemView);
itemIv = (ImageView) itemView.findViewById(R.id.itemIv);
itemTv = (TextView) itemView.findViewById(R.id.itemTv);
this.listener = listener;
this.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (MyRecyclerViewHolder.this.listener != null) {//记得判断是否为null
int[] coords = new int[2];
itemIv.getLocationOnScreen(coords);
Rect srcRect = new Rect(coords[0], coords[1], coords[0] + itemIv.getMeasuredWidth(), coords[1] + itemIv.getMeasuredWidth());
//将接口实现类进行注册
MyRecyclerViewHolder.this.listener.onItemClick(srcRect, imageId, title);
}
}
});
}
}
好了,条目事件的监听通过接口回调的方式就解决了。
实现转场动画
关于Android5.0的转场动画,官方都给提供现成的api,现在问题是如何在5.0以下的系统实现转场动画,那么怎么实现呢?那就需要用到补间动画和属性动画咯,看实现的代码吧!多余的代码就不贴了。
public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.MyRecyclerViewAdapterListener {
@Override
public void onItemClick(Rect srcRect, int imageId, String title) { //rv条目点击事件的回调
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("SRC_RECT", srcRect); //当前被点击的图片,用于记录rect的大小位置
intent.putExtra("IMAGE_ID",imageId); //图片资源id
intent.putExtra("TITLE",title);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); //取消Activity跳转滑动的动画----> Intent.FLAG_ACTIVITY_NO_ANIMATION
startActivity(intent);
}
}
点击触发回调的原点在上面MyRecyclerViewHolder内部类的OnClickListener里,上面已经对Rect进行了创建,可以看看。接着看点击之后跳转的页面的实现代码
public class SecondActivity extends AppCompatActivity {
private static final String TAG = SecondActivity.class.getSimpleName();
ImageView detailIv;
View detailView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
detailIv = (ImageView) findViewById(R.id.detailIv);
detailView=findViewById(R.id.detailView);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Rect srcRect = getIntent().getParcelableExtra("SRC_RECT");
int[] coords = new int[2];
detailIv.getLocationOnScreen(coords); //coords存储View在屏幕的左上角的x、y坐标
Rect targetRect = new Rect(coords[0], coords[1], coords[0] + detailIv.getMeasuredWidth(), coords[1] +detailIv.getMeasuredHeight());
detailIv.setPivotX(0); //旋转或缩放的中心点X
detailIv.setPivotY(0); //旋转或缩放的中心点Y
//下面的设置移动和大小的补间动画是为了让detailIv还跟点击时候的位置和大小一样
detailIv.setTranslationY(srcRect.top - targetRect.top); //用于设置detailIv左上角的Y值,设置的值是相对于原本左上角Y值的
detailIv.setTranslationX(srcRect.left - targetRect.left);
detailIv.setScaleX((float) srcRect.width() / targetRect.width());
detailIv.setScaleY((float) srcRect.height() / targetRect.height());
detailIv.setImageResource(getIntent().getIntExtra("IMAGE_ID",R.mipmap.ic_launcher));
detailIv.setVisibility(View.VISIBLE);
//移动到原有detailIv的位置,缩小到原有detailIv的大小,也就是还在中间位置,大小不变
PropertyValuesHolder transY = PropertyValuesHolder.ofFloat("translationY", 0f);
PropertyValuesHolder transX = PropertyValuesHolder.ofFloat("translationX", 0f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1f);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(detailIv, transY, transX, scaleX, scaleY).setDuration(900);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
//让屏幕的背景渐变为原设置的背景色
TransitionDrawable transitionDrawable= (TransitionDrawable) detailView.getBackground();
transitionDrawable.startTransition(900);
}
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(0,0);
}
}
Ⅲ.个人总结
- 看他人的代码也是一种学习,那得耐得住
- 养成随时做笔记的好习惯,尼玛,去年敲的代码,今年都陌生了