问题描述
其实这是很久之前准备写的博客...
在学习 Spinner 时仅总结了 静态加载 的方法,动态加载 方法可参照 Android UI - Spinner。
而在实际使用中,当存在较多 Spinner 时,使用静态数据显然是不科学的(一个Spinner一个xml文件心好累的 )。
那么我们需要用适配器动态的添加数据,可参考Android 基础知识学习 - spinner 使用
动态添加 Spinner 适配器数据 与 ListView 结合时,由于我是在 ListView 的 getView( ) 方法中实现数据动态加载,实验结果发现Spinner列表项 的数据被多次动态添加(且不规则),导致错乱。
原因
当 ListView 的高度为 xxdp (固定) 或者 match_parent 的时候,ListView 很容易就能计算出容器内可以显示多少行。但如果我们使用了 wrap_content,这是只有在屏幕内控件完全加载后才知道到底能显示多少行数据,ListView 自身便会做一些尝试性计算,就会导致重复调用多次 getView()。
而我在 getView()中调用了一个
spinner_data.getOption();
在这个方法中,我将 Spinner 列表项数据 add( ) 后,再返回该 ArrayList。多次调用时,会造成多次添加的问题。
好了,找到问题所在,对症下药。
解决方法
先说说我的问题
修改 spinner_data.getOption() 方法,让它只返回 ArryList实例;而添加数据的工作,放到构造函数中,这样可以避免多次调用 add() 方法。
当然这只是针对本人 demo 的一些修改,如果你有 Spinner 动态添加数据 + ListView 更好的做法,欢迎留言拍我。
通用解决方法-简单粗暴版
根据上文原因描述,有一个特别简单的解决方法:指定 ListView 的高度为固定值,这样就不会触发多次调用的问题。
且不说高度的计算方法,单单是每次增删 ListView Item项 的时候,都需要手动计算再修改高度属性...我就无法接受。
如果你也和我一样,那么请接往下看。
通用解决方法-动态设置listView高度
任何一个视图都不可能凭空突然出现在屏幕上,它们都是要经过非常科学的绘制流程后才能显示出来的;ListView 中的 Item控件 也一样。每一个视图的绘制过程都必须经历三个最主要的阶段,即 onMeasure ( )、onLayout ( ) 和 onDraw ( )。
在 measure 阶段时主要就是为了计算两个参数:height 和 width。而且要注意的是,这是个递归的过程,从顶向下,DecorView 开始依次调用自己子元素的 Measure。计算完成这两个参数后就开始 Layout,最后再是 Draw的调用。
虽然我在源码中并没有找出具体的原因,但如果你自己做一下实验的话就会发现,即使是一个再简单的 View,在展示到界面上之前都会经历至少两次 onMeasure () 和两次 onLayout () 的过程。 ---- 郭霖
对于 ListView,每显示一个 ItemVIew,它都去测量 View 的高度,执行 onMeasure ( ) 方法,导致 getView( ) 执行多次。
那么我们可以通过以下函数 动态设置ListView高度
private void fixListViewHeight(ListView listView)
{
int totalHeight = 0;
// 如果没有设置数据适配器,则 ListView 没有子项,返回
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null)
return;
for (int index = 0,len = listAdapter.getCount(); index < len; index++ )
{
View listViewItem = listAdapter.getView(index,null,listView);
// 计算子项 View 的宽高
listViewItem.measure(0, 0);
// 计算所有子项的高度和
totalHeight += listViewItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
//listView.getDividerHeight () 获取子项间分隔符的高度
//params.height 设置 ListView 完全显示需要的高度
params.height = totalHeight + (listView.getDividerHeight()*(listAdapter.getCount()-1));
listView.setLayoutParams(params);
}
使用也很简单
fixListViewHeight(listView);
ps:注意该方法需在ListView设置完设配器后调用。
参考鸣谢
ListView,GridView 的 getView 多次调用问题
解决 listview,gridview 的 getview 多次调用问题
关于 ListView 的 getView 方法被多次重复调用的问题
Android 视图绘制流程完全解析,带你一步步深入了解 View (二)
Android ListView 工作原理完全解析,带你从源码的角度彻底理解