使用LayoutAnimationController為RecyclerView添加動畫
使用LayoutAnimationController為RecyclerView添加動畫
@author:莫川
一、前言
為RecyclerView的Item添加動畫有很多中方案,比如通過設置setItemAnimator來實現或者是通過遍曆RecyclerView中的子View,然後分別對子View做動畫。今天介紹一種更加簡單的方式:通過LayoutAnimationController的方式,對RecyclerView的Item做動畫。
二、效果以及源碼
先看效果:首先是對LinearLayoutManager的RecyclerView。
- 1.使用LayoutAnimationController的8種動畫的播放效果
- 2.使用GridLayoutAnimationController的8種動畫的播放效果
Github源碼:https://github.com/nuptboyzhb/RecyclerViewAnimation
三、實現方案
(1)LayoutAnimationController
比如實現從依次從左側進入的動畫效果,我們首先需要實現一個Item的動畫效果,然後創建一個LayoutAnimationController對象,並設置每一個item播放動畫的時間延時和item的播放順序。
以從左側進入為例,每個單獨的Item的動畫如下:
- 1.Item動畫
...
/**
* 從左側進入,並帶有彈性的動畫
*
* @return
*/
public static AnimationSet getAnimationSetFromLeft() {
AnimationSet animationSet = new AnimationSet(true);
TranslateAnimation translateX1 = new TranslateAnimation(RELATIVE_TO_SELF, -1.0f, RELATIVE_TO_SELF, 0.1f,
RELATIVE_TO_SELF, 0, RELATIVE_TO_SELF, 0);
translateX1.setDuration(300);
translateX1.setInterpolator(new DecelerateInterpolator());
translateX1.setStartOffset(0);
TranslateAnimation translateX2 = new TranslateAnimation(RELATIVE_TO_SELF, 0.1f, RELATIVE_TO_SELF, -0.1f,
RELATIVE_TO_SELF, 0, RELATIVE_TO_SELF, 0);
translateX2.setStartOffset(300);
translateX2.setInterpolator(new DecelerateInterpolator());
translateX2.setDuration(50);
TranslateAnimation translateX3 = new TranslateAnimation(RELATIVE_TO_SELF, -0.1f, RELATIVE_TO_SELF, 0f,
RELATIVE_TO_SELF, 0, RELATIVE_TO_SELF, 0);
translateX3.setStartOffset(350);
translateX3.setInterpolator(new DecelerateInterpolator());
translateX3.setDuration(50);
animationSet.addAnimation(translateX1);
animationSet.addAnimation(translateX2);
animationSet.addAnimation(translateX3);
animationSet.setDuration(400);
return animationSet;
}
...
為了讓Item看起來有‘彈性’效果,animationSet添加了三個移動動畫,分別是從左側進入(-100%),移動到右側的10%,然後在從右側(10%)移動到左側(-10%),最後再從(-10%)移動到原本的位置(0%)。這樣就有了移動後的彈性效果。
- 2.設置LayoutAnimationController的屬性
2.1 設置ViewGroup的子View播放動畫之間的offset。
/**
* Sets the delay, as a fraction of the animation duration, by which the children's animations are offset.
*/
void setDelay(float delay)
2.2 設置ViewGroup的子View播放動畫的順序
/**
* Sets the order used to compute the delay of each child's animation.
*/
void setOrder(int order)
setOrder可以取值為LayoutAnimationController.ORDER_NORMAL(正常順序),LayoutAnimationController.ORDER_RANDOM(隨機順序)以及LayoutAnimationController.ORDER_REVERSE(逆序)。這裏的demo設置的是正常順序。
- 3.播放動畫
/**
* 播放RecyclerView動畫
*
* @param animation
* @param isReverse
*/
public void playLayoutAnimation(Animation animation, boolean isReverse) {
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.1f);
controller.setOrder(isReverse ? LayoutAnimationController.ORDER_REVERSE : LayoutAnimationController.ORDER_NORMAL);
mRecyclerView.setLayoutAnimation(controller);
mRecyclerView.getAdapter().notifyDataSetChanged();
mRecyclerView.scheduleLayoutAnimation();
}
通過viewGroup.setLayoutAnimation設置layout動畫。然後通知ViewGroup重新繪製,調用scheduleLayoutAnimation方法播放動畫。
(2)GridLayoutAnimationController
上述方法針對的是線性的RecyclerView,也就是說RecyclerView的LayoutManager設置的是LinearLayoutManager.
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_list);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(new DemoRecyclerViewAdapter());
}
...
而對於使用GridLayoutManager和StaggeredGridLayoutManager的RecyclerView來說,我們需要使用GridLayoutAnimationController,其他步驟與LayoutAnimationController一致。
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_grid);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mRecyclerView = (StaggeredGridRecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
mStaggeredGridAdapter = new StaggeredGridAdapter();
mStaggeredGridAdapter.setDataSet(mockData());
mRecyclerView.setAdapter(mStaggeredGridAdapter);
}
....
同樣的,仍然使用之前的Animation創建GridLayoutAnimationController。
...
/**
* 播放動畫
*
* @param animation
* @param isReverse
*/
public void playLayoutAnimation(Animation animation, boolean isReverse) {
GridLayoutAnimationController controller = new GridLayoutAnimationController(animation);
controller.setColumnDelay(0.2f);
controller.setRowDelay(0.3f);
controller.setOrder(isReverse ? LayoutAnimationController.ORDER_REVERSE : LayoutAnimationController.ORDER_NORMAL);
mRecyclerView.setLayoutAnimation(controller);
mRecyclerView.getAdapter().notifyDataSetChanged();
mRecyclerView.scheduleLayoutAnimation();
}
...
GridLayoutAnimationController的delay方法可以分別按照Column和Row維度進行設置。
本以為到此順利結束。運行後發現,會Crash,log為:
...
E/AndroidRuntime( 7876): java.lang.ClassCastException: android.view.animation.LayoutAnimationController$AnimationParameters cannot be cast to android.view.animation.GridLayoutAnimationController$AnimationParameters
E/AndroidRuntime( 7876): at android.view.animation.GridLayoutAnimationController.getDelayForView(GridLayoutAnimationController.java:299)
E/AndroidRuntime( 7876): at android.view.animation.LayoutAnimationController.getAnimationForView(LayoutAnimationController.java:321)
E/AndroidRuntime( 7876): at android.view.ViewGroup.bindLayoutAnimation(ViewGroup.java:4227)
E/AndroidRuntime( 7876): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3272)
E/AndroidRuntime( 7876): at android.view.View.draw(View.java:15618)
E/AndroidRuntime( 7876): at android.support.v7.widget.RecyclerView.draw(RecyclerView.java:3869)
E/AndroidRuntime( 7876): at android.view.View.updateDisplayListIfDirty(View.java:14495)
E/AndroidRuntime( 7876): at android.view.View.getDisplayList(View.java:14524)
E/AndroidRuntime( 7876): at android.view.View.draw(View.java:15315)
E/AndroidRuntime( 7876): at android.view.ViewGroup.drawChild(ViewGroup.java:3536)
E/AndroidRuntime( 7876): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
E/AndroidRuntime( 7876): at android.view.View.draw(View.java:15618)
E/AndroidRuntime( 7876): at android.view.View.updateDisplayListIfDirty(View.java:14495)
E/AndroidRuntime( 7876): at android.view.View.getDisplayList(View.java:14524)
...
為了解決這個問題,我們需要override RecyclerView的attachLayoutAnimationParameters方法:
package com.github.nuptboyzhb.recyclerviewanimation.grid;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.GridLayoutAnimationController;
/**
* @version Created by haibozheng on 2016/12/9.
* @Author Zheng Haibo
* @Blog github.com/nuptboyzhb
* @Company Alibaba Group
* @Description StaggeredGridRecyclerView
*/
public class StaggeredGridRecyclerView extends RecyclerView {
public StaggeredGridRecyclerView(Context context) {
super(context);
}
public StaggeredGridRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public StaggeredGridRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 支持GridLayoutManager以及StaggeredGridLayoutManager
*
* @param child
* @param params
* @param index
* @param count
*/
@Override
protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params,
int index, int count) {
LayoutManager layoutManager = this.getLayoutManager();
if (getAdapter() != null && (layoutManager instanceof GridLayoutManager
|| layoutManager instanceof StaggeredGridLayoutManager)) {
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {
animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
int columns = 0;
if (layoutManager instanceof GridLayoutManager) {
columns = ((GridLayoutManager) layoutManager).getSpanCount();
} else {
columns = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
}
animationParams.count = count;
animationParams.index = index;
animationParams.columnsCount = columns;
animationParams.rowsCount = count / columns;
final int invertedIndex = count - 1 - index;
animationParams.column = columns - 1 - (invertedIndex % columns);
animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;
} else {
super.attachLayoutAnimationParameters(child, params, index, count);
}
}
}
更多動畫效果,請參見Github源碼:https://github.com/nuptboyzhb/RecyclerViewAnimation
四、總結
通過LayoutAnimationController或者GridLayoutAnimationController來實現RecyclerView的動畫,非常簡單,而且效果很好。該方式不僅可以應用於RecyclerView,而且還適用於ListView、LinearLayout、GridView等ViewGroup。比如,如下是作用在一個LinearLayout的效果。
最後更新:2017-08-13 22:22:46
上一篇:
新零售時代來臨,生鮮行業如何實現電商O2O一體化管理
下一篇:
螞蟻搬新家了,還在辦公室搞了個音樂節
未來智慧城市長啥樣? 大咖解答:萬物互聯成“智能體”
《雲周刊》第121期:圖管夠!灌籃高手、女兒國…阿裏日,這幫程序員太會玩了!
升級Linux(Ubtun/CentOS)的OpenSSL(openssl-1.0.1h.tar.gz)
#飛天·運維# 優雲軟件亮相2017雲棲大會,旗下全線產品首次完整展示
java springmvc mybaits maven shiro 多數據源 後台框架源碼bootstrap
ubuntu 11.10(32位係統)下編譯android源碼 make錯誤解決辦法
ECS-buy 鏡像選擇頁功能更新
電信聯通上海分別啟用181與185號段
《Hadoop與大數據挖掘》一2.6.2 Hadoop TF-IDF編程思路
論網站內容建設策略