我创建了一个ListView
,可以动态添加/删除其集合,并使用自定义ArrayAdapter
和Runnable
动态调整其高度:
以下是我的自定义ArrayAdapter
ItemListAdapter
初始化的方式:
private Activity activity;
public ListView listView;
public ItemListAdapter(Activity activity, ListView listView...) { //other items are irrelevant
super(context, resource, values); //typical initalization
this.activity = activity;
this.listView = listView;
... other codes are irrelevant
}
重要的是,适配器接受一项活动,并在该活动的布局中使用ListView
。
然后,当然,我还有以下重写的notifyDataSetChanged
代码,当在适配器中更改数据集时调用该代码:
@Override
public void notifyDataSetChanged() {
if(listView != null) {
listView.post(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //works well but only once
// activity.runOnUiThread(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //this also not working
}
}
由于在更改数据集时绘制UI之前无法知道新高度,因此上述notifyDataSetChanged
使用listView
方法动态设置ListView.post
的正确高度
Runnable
参数取自static
GetRunnableSetCorrectHeight
中的ListViewHelper
方法class
,如下所示:
public static Runnable GetRunnableSetCorrectHeight(final ListView listView){
return new Runnable() {
@Override
public void run() {
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = DataHolder.LvBaseOffset + getHeight(listView, listView.getAdapter()); //this is the actual set up of the view, the view should be changed because of this, except for the first time...
listView.setLayoutParams(params); //if this is not there, the first show in the main activity will nor be complete (only show the first item)
// listView.requestLayout(); //not needed
}
};
}
并且,可能与该问题没有直接关系,但仅仅是为了完整性'为此,上述方法中调用的getHeight
方法如下:
public static int getHeight(ListView listView, ListAdapter adapter){
int totalHeight = 0;
int listWidth = listView.getMeasuredWidth();
for (int i = 0; i < adapter.getCount(); i++) {
View mView = adapter.getView(i, null, listView);
mView.measure(View.MeasureSpec.makeMeasureSpec(listWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
totalHeight += mView.getMeasuredHeight();
}
return totalHeight + (listView.getDividerHeight() * (adapter.getCount() - 1));
}
在活动的布局中,我有一个Add
和Delete
按钮,可触发调用notifyDataSetChanged
添加/删除项目并重新绘制ListView
正确的身高:
例如,如果按下Add
按钮,结果将如下:
以上代码运行良好,至少在我的Android模拟器,华为平板电脑和小米手机中。当我说得好时,这意味着可以多次点击Add
按钮,它将始终产生新行并调整高度。
但是当我尝试在其他设备上部署时,只能使用一次。我的意思是,如果我第一次按下Add
或Delete
按钮 ,则会在ListView
中添加/删除该项目。但是,当我第二次单击时,应用程序崩溃并显示典型的错误消息框:
Unfortunately, [Application Name] has stopped.
OK
现在,我阅读了一些有关此问题的帖子,其中提到问题可能是由于未在UI线程中运行Runnable
引起的。因此,如上所示,我在ItemListAdapter
的构造中添加了活动,我也尝试更改方法:
@Override
public void notifyDataSetChanged() {
if(listView != null) {
listView.post(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //works well but only once
// activity.runOnUiThread(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //this also not working
}
}
到
@Override
public void notifyDataSetChanged() {
if(listView != null) {
//listView.post(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //works well but only once
activity.runOnUiThread(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //this also not working
}
}
但问题仍然存在。我也试着延迟:
@Override
public void notifyDataSetChanged() {
if(listView != null) {
//listView.post(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //works well but only once
activity.runOnUiThread(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //this also not working
try {
sleep(500); // does not help
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
使用Handler(Looper.getMainLooper()).post
:
@Override
public void notifyDataSetChanged() {
if(listView != null) {
//listView.post(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //works well but only once
new Handler(Looper.getMainLooper()).post(ListViewHelper.GetRunnableSetCorrectHeight(listView)); //this also not working
try {
sleep(500); // does not help
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
但是没有什么有效,而且我的想法用完了......
为什么会这样?为什么post
只能使用一次虽然我试图在UI线程上运行,使用主循环,或者延迟?如何解决?