前言
我在做收到礼物 显示 动画,并在指定时间内自动划走的需求中,莫名其妙的遇到了如下异常,并没有定位到具体哪行出了问题,于是排除业务逻辑,查源码定位到问题所在。
问题代码:
在动画结束回调onAnimationEnd()中remove view触发invalidate(),然后再dispatchDraw方法中的child.mViewFlag获取中抛出NullPointerException,继续寻找,函数getAndVerifyPreorderedView 来获取child,具体的参数情况,是 children 里的个数是1,但是childIndex是1,得到的结果肯定null。
@Override
protected void dispatchDraw(Canvas canvas) {
...省略若干代码.....
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
...省略若干代码.....
// Only use the preordered list if not HW accelerated, since the HW pipeline will do the
// draw reordering internally
final ArrayList
preorderedList = usingRenderNodeProperties ? null : buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
//这里发生了空指针异常,child为null
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
...省略若干代码.....
}
解决办法:
知道了原因,再来解决就很简单了。以最开始的核心问题代码,来演示如何解决。问题出现remove view的时候,在dispatchDraw 中改变了viewGroup已有的子view的数量,导致只有N个view,最大索引是N-1,想要获取第N个view,出现了异常。那么我们可以考虑不在本次执行中,remove view。在下一次的loop消息中执行remove 操作,那么就通过post 或 handler 发送消息来操作view。