Android RecyclerView 詳解 RecyclerView的動畫實現(移除、新增、改變、移動)和自定義動畫的實現
一丶新增刪除時候的重新整理問題
先上一下效果圖吧
1.為了方便起見我們還是先新增三個按鈕分別實現新增刪除和改變
2.在Adapter中寫呼叫方法並進行重新整理
-
public void remove(int position){
-
list.remove(position);
-
notifyItemRemoved(position);
-
}
-
public void add(int position,String data) {
-
list.add(position,data);
-
notifyItemInserted(position);
-
}
-
public void change(int position,String data) {
-
list.remove(position);
-
list.add(position,data);
-
notifyItemChanged(position);
-
}
可以發現他和ListView的重新整理不同的是,對應不同的刪除新增或者改變對應的notify都是不同的,如果不這樣的話那麼新增刪除改變的動畫將沒有。
3.在Mainactivity中呼叫方法就好
-
case R.id.btn_add:
-
myAdapter.add(0,"新加資料");
-
myRecyclerView.scrollToPosition(0);
-
break;
-
case R.id.btn_delete:
-
myAdapter.remove(0);
-
break;
-
case R.id.btn_change:
-
myAdapter.change(0,"改變的資料");
-
break;
注意!!!可以發現增加的時候多了一個方法,那麼這個方法是定位顯示的條目位置的,當你新新增方法的時候他不會顯示出來,需要將條目位置進行設定。
二、對原動畫進行更改達到自己想要的動畫
1.如何設定原動畫
-
//設定預設動畫
-
DefaultItemAnimator animator = new DefaultItemAnimator();
-
//設定動畫時間
-
animator.setAddDuration(2000);
-
animator.setRemoveDuration(2000);
-
myRecyclerView.setItemAnimator(animator);
2.設定自己自定義的動畫
(1)首先這些動畫的方法都是私有的所以我們沒有辦法去重寫他,那麼我們可以新建一個屬於自己的動畫類來實現自定義動畫
(2)自定義動畫的實現(依然為了方便起見我們新增四個按鈕來動態的設定自定義動畫方便對比)
a.首先我們需要建一個自己的動畫類將原本的那個程式碼全部複製過來
b.刪除動畫(滑動消失)
先來效果圖吧
原始碼
-
@Override
-
public boolean animateRemove(final ViewHolder holder) {
-
resetAnimation(holder);
-
mPendingRemovals.add(holder);
-
return true;
-
}
這段程式碼是將移除動畫新增到集合當中
真正執行移除的動畫程式碼如下(原始碼)
-
private void animateRemoveImpl(final ViewHolder holder) {
-
final View view = holder.itemView;//首先得到ItemView
-
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);//開啟一個屬性動畫
-
mRemoveAnimations.add(holder);//加入到要刪除的VIew中
-
/* animation.setDuration(getRemoveDuration())//設定延遲時間
-
.alpha(0).setListener(new VpaListenerAdapter() {//listener是開始和結束的監聽*/
-
animation.setDuration(getRemoveDuration())//設定延遲時間
-
.translationX(view.getWidth()).setListener(new VpaListenerAdapter() {//listener是開始和結束的監聽
-
//變到哪裡
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchRemoveStarting(holder);
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
animation.setListener(null);//當動畫結束的時候要將監聽置為空
-
//ViewCompat.setAlpha(view, 1);//因為有複用佈局的問題,所以你將控制元件刪除的時候需要將他還原,要不會出現重複問題
-
ViewCompat.setTranslationX(view, 0);//因為有複用佈局的問題,所以你將控制元件刪除的時候需要將他還原,要不會出現重複問題
-
dispatchRemoveFinished(holder);
-
mRemoveAnimations.remove(holder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
我們可以發現他的動畫是先淡出然後上移,我們想要實現讓他啊滑動出屏幕後其他條目上移的話那麼簡單的將淡入淡出動畫改成移動動畫即可
-
private void animateRemoveImpl(final ViewHolder holder) {
-
final View view = holder.itemView;//首先得到ItemView
-
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);//開啟一個屬性動畫
-
mRemoveAnimations.add(holder);//加入到要刪除的VIew中
-
animation.setDuration(getRemoveDuration())//設定延遲時間
-
.translationX(view.getWidth()).setListener(new VpaListenerAdapter() {//listener是開始和結束的監聽
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchRemoveStarting(holder);
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
animation.setListener(null);//當動畫結束的時候要將監聽置為空
-
//ViewCompat.setAlpha(view, 1);//因為有複用佈局的問題,所以你將控制元件刪除的時候需要將他還原,要不會出現重複問題
-
ViewCompat.setTranslationX(view,0);//因為有複用佈局的問題,所以你將控制元件刪除的時候需要將他還原,要不會出現重複問題
-
dispatchRemoveFinished(holder);
-
mRemoveAnimations.remove(holder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
在點選事件中設定成自己的動畫和動畫延時時間就好
-
myItemAnimator = new MyItemAnimator();
-
myItemAnimator.setRemoveDuration(2000);
-
myRecyclerVIew.setItemAnimator(myItemAnimator);
c.新增時的動畫
先來效果圖
原動畫是先下移然後再從最淡的時候顯示出來那麼我們想讓他從側面進入那麼更改淡入淡出動畫換成移動動畫即可
原始碼
-
@Override
-
public boolean animateAdd(final ViewHolder holder) {
-
resetAnimation(holder);
-
ViewCompat.setAlpha(holder.itemView, 0);
-
mPendingAdditions.add(holder);
-
return true;
-
}
-
void animateAddImpl(final ViewHolder holder) {
-
final View view = holder.itemView;
-
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
-
mAddAnimations.add(holder);
-
animation.alpha(1).setDuration(getAddDuration()).
-
setListener(new VpaListenerAdapter() {
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchAddStarting(holder);
-
}
-
@Override
-
public void onAnimationCancel(View view) {
-
ViewCompat.setAlpha(view, 1);
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
animation.setListener(null);
-
dispatchAddFinished(holder);
-
mAddAnimations.remove(holder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
更改後的程式碼
-
@Override
-
public boolean animateAdd(final ViewHolder holder) {
-
resetAnimation(holder);
-
//ViewCompat.setAlpha(holder.itemView, 0);
-
ViewCompat.setTranslationX(holder.itemView,-holder.itemView.getWidth());
-
mPendingAdditions.add(holder);
-
return true;
-
}
-
void animateAddImpl(final ViewHolder holder) {
-
final View view = holder.itemView;
-
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
-
mAddAnimations.add(holder);
-
//animation.alpha(1).setDuration(getAddDuration()).
-
animation.translationX(0).setDuration(getAddDuration()).
-
setListener(new VpaListenerAdapter() {
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchAddStarting(holder);
-
}
-
@Override
-
public void onAnimationCancel(View view) {
-
ViewCompat.setAlpha(view, 1);
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
animation.setListener(null);
-
dispatchAddFinished(holder);
-
mAddAnimations.remove(holder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
在點選事件中設定動畫和其時間即可
-
myItemAnimator = new MyItemAnimator();
-
myItemAnimator.setAddDuration(2000);
-
myRecyclerVIew.setItemAnimator(myItemAnimator);
d.移動動畫,所謂的移動動畫就是當你刪除或者新增的時候其他條目的動畫,我們可以將其設定成翻轉的效果
效果圖如下
原始碼
-
@Override
-
public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
-
int toX, int toY) {
-
final View view = holder.itemView;
-
fromX += ViewCompat.getTranslationX(holder.itemView);
-
fromY += ViewCompat.getTranslationY(holder.itemView);
-
resetAnimation(holder);
-
int deltaX = toX - fromX;
-
int deltaY = toY - fromY;
-
if (deltaX == 0 && deltaY == 0) {
-
dispatchMoveFinished(holder);
-
return false;
-
}
-
if (deltaX != 0) {
-
ViewCompat.setTranslationX(view, -deltaX);
-
}
-
if (deltaY != 0) {
-
ViewCompat.setTranslationY(view, -deltaY);
-
}
-
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
-
return true;
-
}
-
void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
-
final View view = holder.itemView;
-
final int deltaX = toX - fromX;
-
final int deltaY = toY - fromY;
-
if (deltaX != 0) {
-
ViewCompat.animate(view).translationX(0);
-
}
-
if (deltaY != 0) {
-
ViewCompat.animate(view).translationY(0);
-
}
-
// TODO: make EndActions end listeners instead, since end actions aren't called when
-
// vpas are canceled (and can't end them. why?)
-
// need listener functionality in VPACompat for this. Ick.
-
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
-
mMoveAnimations.add(holder);
-
animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchMoveStarting(holder);
-
}
-
@Override
-
public void onAnimationCancel(View view) {
-
if (deltaX != 0) {
-
ViewCompat.setTranslationX(view, 0);
-
}
-
if (deltaY != 0) {
-
ViewCompat.setTranslationY(view, 0);
-
}
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
animation.setListener(null);
-
dispatchMoveFinished(holder);
-
mMoveAnimations.remove(holder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
在animateMoveImpl中新增我們的翻轉動畫
animation.rotationXBy(180);
因為我們是翻轉180度那麼需要在後面進行重置
在AnimationEnd後重置
ViewCompat.setRotationX(view,0);
更改後的所有程式碼
-
void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
-
final View view = holder.itemView;
-
final int deltaX = toX - fromX;
-
final int deltaY = toY - fromY;
-
if (deltaX != 0) {
-
ViewCompat.animate(view).translationX(0);
-
}
-
if (deltaY != 0) {
-
ViewCompat.animate(view).translationY(0);
-
}
-
// TODO: make EndActions end listeners instead, since end actions aren't called when
-
// vpas are canceled (and can't end them. why?)
-
// need listener functionality in VPACompat for this. Ick.
-
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
-
animation.rotationXBy(180);
-
mMoveAnimations.add(holder);
-
animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchMoveStarting(holder);
-
}
-
@Override
-
public void onAnimationCancel(View view) {
-
if (deltaX != 0) {
-
ViewCompat.setTranslationX(view, 0);
-
}
-
if (deltaY != 0) {
-
ViewCompat.setTranslationY(view, 0);
-
}
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
animation.setListener(null);
-
ViewCompat.setRotationX(view,0);
-
dispatchMoveFinished(holder);
-
mMoveAnimations.remove(holder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
e.改變動畫,就是移除就得得到新的,而原始碼是淡出舊的顯示新的那麼進行適當更改就行了
效果圖如下:
原始碼
-
@Override
-
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
-
int fromX, int fromY, int toX, int toY) {
-
if (oldHolder == newHolder) {
-
// Don't know how to run change animations when the same view holder is re-used.
-
// run a move animation to handle position changes.
-
return animateMove(oldHolder, fromX, fromY, toX, toY);
-
}
-
final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
-
final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
-
final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
-
resetAnimation(oldHolder);
-
int deltaX = (int) (toX - fromX - prevTranslationX);
-
int deltaY = (int) (toY - fromY - prevTranslationY);
-
// recover prev translation state after ending animation
-
ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
-
ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
-
ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
-
if (newHolder != null) {
-
// carry over translation values
-
resetAnimation(newHolder);
-
ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
-
ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
-
ViewCompat.setAlpha(newHolder.itemView, 0);
-
}
-
mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
-
return true;
-
}
-
void animateChangeImpl(final ChangeInfo changeInfo) {
-
final ViewHolder holder = changeInfo.oldHolder;
-
final View view = holder == null ? null : holder.itemView;
-
final ViewHolder newHolder = changeInfo.newHolder;
-
final View newView = newHolder != null ? newHolder.itemView : null;
-
if (view != null) {
-
final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
-
getChangeDuration());
-
mChangeAnimations.add(changeInfo.oldHolder);
-
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
-
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
-
oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchChangeStarting(changeInfo.oldHolder, true);
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
oldViewAnim.setListener(null);
-
ViewCompat.setAlpha(view, 1);
-
ViewCompat.setTranslationX(view, 0);
-
ViewCompat.setTranslationY(view, 0);
-
dispatchChangeFinished(changeInfo.oldHolder, true);
-
mChangeAnimations.remove(changeInfo.oldHolder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
更改後的程式碼
-
@Override
-
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
-
int fromX, int fromY, int toX, int toY) {
-
if (oldHolder == newHolder) {
-
// Don't know how to run change animations when the same view holder is re-used.
-
// run a move animation to handle position changes.
-
return animateMove(oldHolder, fromX, fromY, toX, toY);
-
}
-
final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
-
final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
-
final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
-
resetAnimation(oldHolder);
-
int deltaX = (int) (toX - fromX - prevTranslationX);
-
int deltaY = (int) (toY - fromY - prevTranslationY);
-
// recover prev translation state after ending animation
-
ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
-
ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
-
ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
-
if (newHolder != null) {
-
// carry over translation values
-
resetAnimation(newHolder);
-
ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
-
ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
-
//ViewCompat.setAlpha(newHolder.itemView, 0);
-
//新的移除視線
-
ViewCompat.setTranslationX(newHolder.itemView,-newHolder.itemView.getWidth());
-
}
-
mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
-
return true;
-
}
-
void animateChangeImpl(final ChangeInfo changeInfo) {
-
final ViewHolder holder = changeInfo.oldHolder;
-
final View view = holder == null ? null : holder.itemView;
-
final ViewHolder newHolder = changeInfo.newHolder;
-
final View newView = newHolder != null ? newHolder.itemView : null;
-
if (view != null) {
-
final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
-
getChangeDuration());
-
mChangeAnimations.add(changeInfo.oldHolder);
-
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
-
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
-
//oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
-
//老的移除
-
oldViewAnim.translationX(view.getWidth()).setListener(new VpaListenerAdapter() {
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchChangeStarting(changeInfo.oldHolder, true);
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
oldViewAnim.setListener(null);
-
ViewCompat.setAlpha(view, 1);
-
ViewCompat.setTranslationX(view, 0);
-
ViewCompat.setTranslationY(view, 0);
-
dispatchChangeFinished(changeInfo.oldHolder, true);
-
mChangeAnimations.remove(changeInfo.oldHolder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
-
if (newView != null) {
-
final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
-
mChangeAnimations.add(changeInfo.newHolder);
-
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
-
alpha(1).setListener(new VpaListenerAdapter() {
-
@Override
-
public void onAnimationStart(View view) {
-
dispatchChangeStarting(changeInfo.newHolder, false);
-
}
-
@Override
-
public void onAnimationEnd(View view) {
-
newViewAnimation.setListener(null);
-
ViewCompat.setAlpha(newView, 1);
-
ViewCompat.setTranslationX(newView, 0);
-
ViewCompat.setTranslationY(newView, 0);
-
dispatchChangeFinished(changeInfo.newHolder, false);
-
mChangeAnimations.remove(changeInfo.newHolder);
-
dispatchFinishedWhenDone();
-
}
-
}).start();
-
}
-
}
原始碼:原始碼連結