PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接
ps:源码是基于 android api 27 来剖析的
这篇文章咱们来继 Android 中 LayoutAnimation 的剖析(一)写,在 Android 中 LayoutAnimation 的剖析(一)这篇文章中,咱们次要列举了 demo 进行演示,当初咱们往下剖析;在上一篇文章中,都用了 LayoutAnimationController 和 GridLayoutAnimationController 的 xml 形式进行动画;其实咱们上一篇文章用到的 xml 形式进行动画最终是通过 AnimationUtils 实现动画文件加载工作,咱们用到的是 RecyclerView 和 GridView 作为演示动画,RecyclerView 和 GridView 都继承于 ViewGroup,咱们看一下 ViewGroup 解析无关属性的办法 initFromAttributes;
private void initFromAttributes(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
......
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {int attr = a.getIndex(i);
switch (attr) {
......
case R.styleable.ViewGroup_layoutAnimation:
int id = a.getResourceId(attr, -1);
if (id > 0) {
//1、setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
}
break;
......
}
}
......
}
看正文 1,只有放在布局文件的 ViewGroup 增加了 layoutAnimation 属性,那么 xml 形式进行动画最终是通过 AnimationUtils 实现动画文件加载工作的,如果咱们以上一篇文章中 LayoutAnimationController 的 xml 形式进行动画为例的话,正文 1 中的 id 就是 R.anim.layout_animation_fall_down;咱们跟踪正文 1 的代码,看看如何解析 xml 形式进行动画的,看 AnimationUtils 的 loadLayoutAnimation(Context context, @AnimRes int id) 办法;
public static LayoutAnimationController loadLayoutAnimation(Context context, @AnimRes int id)
throws Resources.NotFoundException {
......
try {
//2、parser = context.getResources().getAnimation(id);
//3、return createLayoutAnimationFromXml(context, parser);
} catch (XmlPullParserException ex) {......} catch (IOException ex) {......} finally {......}
}
正文 2 中依据 id 创立一个 XmlResourceParser 类型的对象;正文 3 中将 XmlResourceParser 类型的对象作为参数,而后调用 AnimationUtils 的 createLayoutAnimationFromXml(Context c,XmlPullParser parser) 办法;
private static LayoutAnimationController createLayoutAnimationFromXml(Context c,
XmlPullParser parser) throws XmlPullParserException, IOException {return createLayoutAnimationFromXml(c, parser, Xml.asAttributeSet(parser));
}
AnimationUtils 的 createLayoutAnimationFromXml(Context c,XmlPullParser parser) 办法又调用了 AnimationUtils 的 createLayoutAnimationFromXml(Context c,XmlPullParser parser, AttributeSet attrs) 办法;
private static LayoutAnimationController createLayoutAnimationFromXml(Context c,
XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {
LayoutAnimationController controller = null;
int type;
int depth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {if (type != XmlPullParser.START_TAG) {continue;}
String name = parser.getName();
//4、if ("layoutAnimation".equals(name)) {controller = new LayoutAnimationController(c, attrs);
//5、} else if ("gridLayoutAnimation".equals(name)) {controller = new GridLayoutAnimationController(c, attrs);
} else {throw new RuntimeException("Unknown layout animation name:" + name);
}
}
return controller;
}
name 只能是 ViewGroup 的 layoutAnimation 属性援用的 xml 文件里的 layoutAnimation 标签或者 gridLayoutAnimation 标签(就好比 Android 中 LayoutAnimation 的剖析(一)这篇文章中 LayoutAnimationController xml 进行动画的 layout_animation_fall_down.xml 文件里的 layoutAnimation 标签和 GridLayoutAnimationController xml 进行动画的 layout_animation_grid.xml 文件里的 gridLayoutAnimation 标签),否则用其余标签就会报错;正文 4 中,如果是 layoutAnimation 标签就创立一个 LayoutAnimationController 对象;正文 5 中,如果是 gridLayoutAnimation 标签就创立一个 GridLayoutAnimationController 对象;咱们往下看正文 4 中的 LayoutAnimationController(Context context, AttributeSet attrs) 构造方法;
public LayoutAnimationController(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation);
Animation.Description d = Animation.Description.parseValue(a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay));
//6、mDelay = d.value;
//7、mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL);
......
}
看正文 6 和正文 7,该构造方法实现了从 ViewGroup 的 layoutAnimation 属性援用的 xml 文件(比方 Android 中 LayoutAnimation 的剖析(一)这篇文章中的 LayoutAnimationController xml 形式进行动画的 layout_animation_fall_down.xml 文件)中的 layoutAnimation 标签解析出 delay 和 animationOrder 属性。
咱们回下面正文 5 的代码,也就是 GridLayoutAnimationController 的实例化,也顺便调用 GridLayoutAnimationController(Context context, AttributeSet attrs) 办法;
public GridLayoutAnimationController(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.GridLayoutAnimation);
Animation.Description d = Animation.Description.parseValue(a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay));
//8、mColumnDelay = d.value;
d = Animation.Description.parseValue(a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay));
//9、mRowDelay = d.value;
//noinspection PointlessBitwiseExpression
//10、mDirection = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_direction,
DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM);
//11、mDirectionPriority = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_directionPriority,
PRIORITY_NONE);
a.recycle();
}
看正文 8、正文 9、正文 10 和 正文 11,该构造方法实现了从 ViewGroup 的 layoutAnimation 属性援用的 xml 文件(比方 Android 中 LayoutAnimation 的剖析(一)这篇文章中的 GridLayoutAnimationController xml 形式进行动画的 layout_animation_grid.xml 文件)中的 gridLayoutAnimation 标签解析出 columnDelay、rowDelay、direction(这个属性在 demo 演示中咱们没有增加)和 directionPriority 属性。
好了,这里咱们就有一个疑难了,RecyclerView 也能够应用网格布局啊,是否能够应用 gridLayoutAnimation 动画,好,咱们先举个例子演示一下;
(1)在 app 目录下的 build.gradle 里增加 RecyclerView 的依赖:
implementation “com.android.support:recyclerview-v7:26+”
(2)新建一个 Activity,名叫 GridLayoutAnimationActivity:
public class GridLayoutAnimationActivity extends AppCompatActivity {
RecyclerView mRv;
GridAdapter mGrideAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid_layout_animation);
mRv = findViewById(R.id.rv);
List<String> list = new ArrayList<String>();
for (int i = 0; i < 100;i++) {list.add("item--"+i);
}
mGrideAdapter = new GridAdapter(this,list);
GridLayoutManager gm = new GridLayoutManager(this,3);
mRv.setLayoutManager(gm);
mRv.setAdapter(mGrideAdapter);
}
}
(3)GridLayoutAnimationActivity 对应的布局文件 activity_grid_layout_animation.xml:
<?xml version=”1.0″ encoding=”utf-8″?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layoutAnimation="@anim/layout_animation_grid"
tools:context=".GridLayoutAnimationActivity">
</android.support.v7.widget.RecyclerView>
(4)在 res 目录下新建一个 anim 文件夹,在 anim 目录下新建一个 layoutAnimation 属性援用的 xml 文件 layout_animation_grid.xml:
<gridLayoutAnimation xmlns:android=”http://schemas.android.com/apk/res/android”
android:rowDelay="0%"
android:columnDelay="60%"
android:directionPriority="column"
android:animation="@anim/slide_in_left"/>
(5)在 anim 文件夹上面新建一个 animation 属性援用的 xml 文件 slide_in_left.xml:
<?xml version=”1.0″ encoding=”utf-8″?>
<set xmlns:android=”http://schemas.android.com/apk/res/android” android:duration=”1000″>
<translate android:fromXDelta="-50%p" android:toXDelta="0"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0" />
</set>
(6)RecyclerView 对应的 Adapter 实现类 GridAdapter:
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
private Context mContext;
private List<String> mList;
public GridAdapter(Context context, List<String> list) {
mContext = context;
mList = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View item = LayoutInflater.from(mContext).inflate(R.layout.item_grid, parent, false);
ViewHolder viewHolder = new ViewHolder(item);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
}
@Override
public int getItemCount() {return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {public ViewHolder(View itemView) {super(itemView);
}
}
}
(7)GridAdapter 对应的 item 布局文件 item_grid.xml:
<?xml version=”1.0″ encoding=”utf-8″?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:text="hellow"
android:layout_height="wrap_content">
</TextView>
程序运行后,发现呈现如下报错:
java.lang.ClassCastException: android.view.animation.LayoutAnimationController$AnimationParameters cannot be cast to android.view.animation.GridLayoutAnimationController$AnimationParameters
at android.view.animation.GridLayoutAnimationController.getDelayForView(GridLayoutAnimationController.java:299)
at android.view.animation.LayoutAnimationController.getAnimationForView(LayoutAnimationController.java:323)
at android.view.ViewGroup.bindLayoutAnimation(ViewGroup.java:5295)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4109)
at android.view.View.draw(View.java:20486)
at android.support.v7.widget.RecyclerView.draw(RecyclerView.java:3987)
at android.view.View.updateDisplayListIfDirty(View.java:19294)
at android.view.View.draw(View.java:20211)
at android.view.ViewGroup.drawChild(ViewGroup.java:4394)
从报错信息来看,它说 LayoutAnimationController.AnimationParameters 不能转换成 GridLayoutAnimationController.AnimationParameters,还提醒了是 GridLayoutAnimationController 的 getDelayForView 办法的第 299 行代码,咱们来看这个办法;
@Override
protected long getDelayForView(View view) {ViewGroup.LayoutParams lp = view.getLayoutParams();
//12、AnimationParameters params = (AnimationParameters) lp.layoutAnimationParameters;
......
return (long) (normalizedDelay * totalDelay);
}
正文 12 就是 getDelayForView 办法的第 299 行代码,params 就是 GridLayoutAnimationController.AnimationParameters 类型的对象,而 lp.layoutAnimationParameters 是 LayoutAnimationController.Ani-mationParameters 类型的对象,所以转换异样;咱们看一下 lp.layoutAnimationParameters 初始化是在什么中央,咱们看一下 ViewGroup 的 dispatchDraw(Canvas canvas) 办法;
@Override
protected void dispatchDraw(Canvas canvas) {
......
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < childrenCount; i++) {final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
//13、final ViewGroup.LayoutParams params = child.getLayoutParams();
//14、attachLayoutAnimationParameters(child, params, i, childrenCount);
bindLayoutAnimation(child);
}
}
......
}
......
}
正文 13 示意 ViewGroup 的子元素获取到相应的布局参数;正文 14 是把子元素的布局参数传入 ViewGroup 的 attachLayoutAnimationParameters(View child,LayoutParams params, int index, int count) 办法中,咱们看一下 attachLayoutAnimationParameters(View child,LayoutParams params, int index, int count) 办法;
protected void attachLayoutAnimationParameters(View child,
ViewGroup.LayoutParams params, int index, int count) {
LayoutAnimationController.AnimationParameters animationParams =
params.layoutAnimationParameters;
if (animationParams == null) {animationParams = new LayoutAnimationController.AnimationParameters();
//15、params.layoutAnimationParameters = animationParams;
}
animationParams.count = count;
animationParams.index = index;
}
看正文 15,ViewGroup 子元素的 params 中的 layoutAnimationParameters 是一个 LayoutAnimationController.Ani-mationParameters 类型的对象,也就是说 ViewGroup 的子类如果没有重写 attachLayoutAnimationParameters(View child,LayoutParams params, int index, int count) 办法,那么 ViewGroup 子元素的 params 中的 layoutAnimationParameters 就会默认被赋值一个 Layout-AnimationController.AnimationParameters 类型的对象。
咱们查看 RecyclerView 的源码发现并没有重写 attachLayoutAnimationParameters(View child,LayoutParams params, int index, int count) 办法;在上一篇文章中,咱们用 GridView 来进行 GridLayoutAnimation 的动画,咱们查看 GridView 的源码,发现 Grid-View 重写了 attachLayoutAnimationParameters(View child,Layout-Params params, int index, int count) 办法;
@Override
protected void attachLayoutAnimationParameters(View child,
ViewGroup.LayoutParams params, int index, int count) {
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {animationParams = new GridLayoutAnimationController.AnimationParameters();
//16、params.layoutAnimationParameters = animationParams;
}
......
}
看正文 16,animationParams 是一个 GridLayoutAnimationControl-ler.AnimationParameters 类型的对象,GridView 之所以能进行 GridLayoutAnimation 的动画,是因为重写了 attachLayoutAnimation-Parameters(View child,Layout-Params params, int index, int count) 办法并创立了 GridLayoutAnimationControl-ler.AnimationParameters 类型的对象;那如果咱们要 RecyclerView 进行 GridLayoutAnimation 的动画,那么就要重写 attachLayoutAnimation-Parameters(View child,Layout-Params params, int index, int count) 办法;
(1)我自定义一个 RecyclerView(包名 com.example.layoutanimationdemo.layoutanimationdemo):
public class MyRecyclerView extends RecyclerView {
public MyRecyclerView(Context context) {super(context);
}
public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);
}
public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);
}
@Override
protected void attachLayoutAnimationParameters(View child,
ViewGroup.LayoutParams params, int index, int count) {
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
animationParams.count = count;
animationParams.index = index;
}
}
(2)拿这篇文章的下面报错的 demo 略微改一下,只须要改 activity_grid_layout_animation.xml 文件就好了,其余文件不必改:
<?xml version=”1.0″ encoding=”utf-8″?>
<com.example.layoutanimationdemo.layoutanimationdemo.MyRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layoutAnimation="@anim/layout_animation_grid"
tools:context=".GridLayoutAnimationActivity">
</com.example.layoutanimationdemo.layoutanimationdemo.MyRecyclerView>
程序运行没有报错,成果如下所示:
这篇文章写到这里先告一段落了,然而还没完,下一篇会持续剖析它。