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>
程序运行没有报错,成果如下所示:
这篇文章写到这里先告一段落了,然而还没完,下一篇会持续剖析它。