前言
前些天朋友發了一份居於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);
}
}
Ⅲ.個人總結
- 看他人的代碼也是一種學習,那得耐得住
- 養成隨時做筆記的好習慣,尼瑪,去年敲的代碼,今年都陌生了