今日研究研究ListView的onTouch事件,无意发现了一个问题:
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { MyAdapterHolder holder = null; if (convertView == null) { holder = new MyAdapterHolder(); convertView = inflater.inflate(R.layout.item, null); holder.textView = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (MyAdapterHolder) convertView.getTag(); } holder.textView.setText("position = " + position); return convertView;}
运行发现结果截图如下:
同志们看到没有?我设置的明明是android:layout_height="100dp",可是运行出来的结果高度很明显不够。
因此我在convertView = inflater.inflate(R.layout.item, null);行处打了断点,调试发现
mLayoutParams = null.
我们姑且先不管为什么convertView.mLayoutParams = null,我们继续从源码中寻找答案。
我想应该从调用getView( )的地方入手。
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Sets up mListPadding ... mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0, mIsScrap); ... } ...}
此处调用absListView的中obtainView( )获取View,调至此方法我们继续分析:
View obtainView(int position, boolean[] isScrap) { ... final View transientView = mRecycler.getTransientStateView(position); {} ... setItemViewLayoutParams(child, position); ... return child;}
private void setItemViewLayoutParams(View child, int position) { final ViewGroup.LayoutParams vlp = child.getLayoutParams(); LayoutParams lp; if (vlp == null) { lp = (LayoutParams) generateDefaultLayoutParams(); } else if (!checkLayoutParams(vlp)) { lp = (LayoutParams) generateLayoutParams(vlp); } else { lp = (LayoutParams) vlp; } if (mAdapterHasStableIds) { lp.itemId = mAdapter.getItemId(position); } lp.viewType = mAdapter.getItemViewType(position); if (lp != vlp) { child.setLayoutParams(lp); }}
有前面可知convertView.mLayoutParams = null,因此我们查看 generateDefaultLayoutParams():
@Overrideprotected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0);}
看到这里我们应该都明白为什么上面为什么会出现酱紫情况了。
可是,问题又来了,convertView.mLayoutParams = null?为什么?
老办法,看源码:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { ... final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); }}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { ... final AttributeSet attrs = Xml.asAttributeSet(parser); ... View result = root; try { // Look for the root node. .... final String name = parser.getName(); ... if (TAG_MERGE.equals(name)) { ... } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } } ... return result; }}
由此代码我们发现,只有root != null 的情况下才会有root.addView(temp, params);设置layoutParams,可是我们传的root = null,所以有convertView.mLayoutParams = null。
后面我又测试了root !=null,即:
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { MyAdapterHolder holder = null; if (convertView == null) { holder = new MyAdapterHolder(); convertView = inflater.inflate(R.layout.item, parent); holder.textView = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (MyAdapterHolder) convertView.getTag(); } holder.textView.setText("position = " + position); return convertView;}
发现还是报错了:
@Overridepublic void addView(View child, LayoutParams params) { throw new UnsupportedOperationException("addView(View, LayoutParams) " + "is not supported in AdapterView");}
嗯,就是这个异常。不支持啊!