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;@Overrideprotected 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;}@Overridepublic 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;}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {}@Overridepublic 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行代码,咱们来看这个办法;

@Overrideprotected 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) 办法;

@Overrideprotected 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) 办法;

@Overrideprotected 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);}@Overrideprotected 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>

程序运行没有报错,成果如下所示:

这篇文章写到这里先告一段落了,然而还没完,下一篇会持续剖析它。