最近在我的项目中加入了自定义的TextAreaView,由于项目是在Dialog中只存在一个ListView.发现在此过程中第一条项目(也就是第一个View失去了mAttachInfo).从而导致View处于半瘫痪状态,也就是说View未被执行dispatchAttachedToWindow方法,从而导致View中的Handler为空指针.,也就导致了Post等一切利用View中Hanler进行视图绘制操作无效,除非调用父类进行重绘.
分析开始:
1. 首先我自定义的TextAreaView中有光标重绘的操作,是利用的View中的Post线程进行绘制,但是,我在View中的Post方法中打上Log后,发现确实调用了Post的方法,也就是说我自定义的View没有问题,鉴于第二条第三条View项目都处于正常状态,只有第一条处于瘫痪状态.将问题缩小到Post方法中.
2.使用反射获取Post方法执行时的测试条件if(mAttachInfo == null){...}发现mAttachInfo为空指针.由于我们都知道View在附属的过程中,是通过Parent往下递归附属的,所以我再次反射去查看View中的View.mParent变量是否也为Null,发现此Parent不为Null,由此得出,改View只是未被执行dispatchAttachedToWindow方法.
3.寻找ViewGroup中的执行过程,发现dispatchAttachedToWindow方法实在执行addViewInLayout方法中进行的,也就是说,ListView未对该View执行addViewInLayout,方法.
4.寻找ListView中的addViewInLayout方法.
AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
if (p == null) {
p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
}
p.viewType = mAdapter.getItemViewType(position);
if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&
p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
attachViewToParent(child, flowDown ? -1 : 0, p);
} else {
p.forceAdd = false;
if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
p.recycledHeaderFooter = true;
}
addViewInLayout(child, flowDown ? -1 : 0, p, true);
}
与此可见一定是第一条if语句被执行了,由于我并没有设置Header所有if语句第二个条件不可能通过,只有第一个
由此发现recycled为true,也就是说我的第一条View在返回知道直接被ListView进行了recyle,并且我的LayoutParams中的forceAdd==false.或者LayoutParams == null,
5.我发现ListView在测量过程中如下执行:
mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||
heightMode == MeasureSpec.UNSPECIFIED)) {
final View child = obtainView(0, mIsScrap);
measureScrapChild(child, 0, widthMeasureSpec);
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(
((LayoutParams) child.getLayoutParams()).viewType)) {
mRecycler.addScrapView(child, -1);
}
}
也就是说第一条项目会被特殊处理,直接添加到mRecucler中,从而导致recyle为true,但是在measureScrapChild方法中会进行如下
private void measureScrapChild(View child, int position, int widthMeasureSpec) {
LayoutParams p = (LayoutParams) child.getLayoutParams();
if (p == null) {
p = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
child.setLayoutParams(p);
}
p.viewType = mAdapter.getItemViewType(position);
p.forceAdd = true;
也就是说p.forceAdd被设置为true,由此第4个中的if条件是不可能被执行的,除非p.forceAdd 被赋值为 false.
6.我已经说过没有被执行addViewInLayout,所以p,forceAdd被赋值为false,是不可能在ListView中进行的,那么只有一个原因了,那就是我们重新设置了该项目的LayoutParames,这样LayoutParames中的forceAdd才能为false.
7.我检测我的方法发现,我在getView中由于马虎,竟然将LayoutParames进行了多次设置,也就是说不管是不是View复用,我都new一个新的LayoutParams添加到View中,从而导致了forceAdd为false的情况.将setLayoutParames(new LayoutParames)设置在cOnvertView== null的情况下,问题解决
Node:我又发现,此问题只会发生在Dialog模式下,因为我在Framget中的ListView如此设置并没有出现任何问题,这个问题保留一下,再去详细的查找原因吧.