好吧,我的方法是这样的。我正在解码 AsyncTask
中本地存储的加密和序列化对象在 Activity
。 Activity
使用 BaseAdapter
(HistoryAdapter) 用于显示数据。 AsyncTask
显示 ProgressDialog
直到解码完成。当onProgressUpdate()
首先调用 ProgressDialog
取消。到目前为止,一切都很好。接下来,在 onProgressUpdate()
,HistoryAdapter 以常见方式收到更改通知,触发它的 getView()
方法。在历史适配器的 getView()
,第二个AsyncTask
运行以修改创建的 convertView
并将数据设置到 View
.
这就是我一切失败的地方。我在 onProgressUpdate()
中膨胀了最终布局,并设置 convertView
的属性和数据这里很好。即使设置了所有数据,更改也不会显示...
所以,AsyncTask
事实上,HistoryAdapter 本身工作得很好,只是变化不可见。我尝试了上面提到的许多建议,例如使 convertView
无效,传递对 ListView
的引用并使用 invalidateViews()
(导致永恒循环,但没有可见的变化,这是有道理的)。
我真的很想要这个,因为我真的不想在数据可用之前加载带有图像占位符的布局。我开始工作,但看起来很糟糕,而且喜欢简单的出路。所以我需要ListView
仅当进度完成时才更新(添加项目)。有什么想法吗?
编辑:澄清一下:数据是在正确的时间设置在适配器上的。问题是,适配器创建了一个空白View
(占位符)首先(不知道有什么其他方法,否则你会在getView中得到一个NullPointerException),然后是这个View
被充气/替换为另一个 View
在onProgressUpdate()
。第二个View
是应该可见的人。这有点有效,因为我可以在新膨胀的 View 上获取和设置属性。更改是不可见的,我仍然看到空白的、最初创建的 View 。我想在每个添加的项目上更新 ListView,而不是在所有项目加载完成后...
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//convertView = mInflater.inflate(R.layout.history_list_item_holo_dark, null);
convertView = mInflater.inflate(R.layout.blank, parent, false); // CHEAT: LOAD BLANK/ EMPTY LAYOUT
HistoryHolder item = history.get(position);
new AsyncRequest(convertView, position).execute(item);
}
this.parent = parent;
return convertView;
}//end method
static class ViewHolder {
TextView TITLE;
TextView SUMMARY;
TextView DATE;
ImageView CONTACT_ICON;
ImageView OPTIONS_ICON;
ImageView TYPE_ICON;
}//end class
private class AsyncRequest extends AsyncTask<HistoryHolder, View, View> {
ViewHolder holder = null;
String title = "";
String summary = "";
String date = "";
long id = 0;
private View convertView = null;
private String type = "";
private int position = -1;
public AsyncRequest(View superView, int position){
this.convertView = superView;
this.position = position;
}//end constructor
@Override
protected View doInBackground(HistoryHolder... item) {
Thread.currentThread().setName(getClass().getSimpleName());
if (item[0].TYPE.equals("log")){
//massive decrypting going on here 50-100 ms
//values like title and summray set here
}
if (item[0].TYPE.equals("sms")){
//massive decrypting going on here 50-100 ms
//values like title and summray set here
}
publishProgress(convertView);
return convertView;
}// end method
@Override
protected void onProgressUpdate(View... view) {
super.onProgressUpdate(view);
}// end method
@Override
protected void onPostExecute(View result) {
super.onPostExecute(result);
if (!MathUtils.isEven(position)){
result .setBackgroundColor(context.getResources().getColor(R.color.darker)); //this works as expected, list items vary in color
} else {
result .setBackgroundColor(context.getResources().getColor(R.color.medium_dark));
} //this works as expected, list items vary in color
result = mInflater.inflate(R.layout.history_list_item_holo_dark, parent, false);
result.setTag(id);
holder = new ViewHolder();
holder.TITLE = (TextView) result .findViewById(R.id.title);
holder.SUMMARY = (TextView) result .findViewById(R.id.summary);
holder.DATE = (TextView) result .findViewById(R.id.date);
holder.CONTACT_ICON = (ImageView) result .findViewById(R.id.icon);
holder.TYPE_ICON = (ImageView) result .findViewById(R.id.type);
holder.OPTIONS_ICON = (ImageView) result .findViewById(R.id.options);
holder.OPTIONS_ICON.setFocusable(false);
holder.OPTIONS_ICON.setTag(id);
holder.TITLE.setText(title); //this change doesnt show
holder.SUMMARY.setText(summary); //and so on
result .setTag(holder);
}//end method
}//end inner class
我知道我可以修改我的 AsynTask 并且不需要在很多地方传递对 View 的引用,但话又说回来,它是正在进行中的代码。简化示例...
编辑
好吧,看来我的方法一开始就很糟糕,导致需要 AsyncTask
在我的HistoryAdapter
。我解决了一些问题来解决这个问题。
- 根据 @Delyan 的建议,我认为最好在实际需要数据之前加载/解密数据。我正在使用
PropertyChangeListener
为了这。这实现了OnSharedPreferenceChangeListener
以便它收到我需要的数据更改的通知。然后更改将传播给任何感兴趣的听众。数据在应用程序启动时解密并存储在全局变量中,该变量可在整个应用程序中访问。将此视为他提到的“内存缓存”。 - 根据评论和已接受的答案,解密现在是在后台完成的,因此不再需要
AsyncTasks
. - 为了进一步优化适配器的性能,我正在存储
ListView
所需的图像。在SparseArray
,因此它们仅创建和存储一次。不要使用HashMap
为了这!此外,图像仅为当前View
创建。如果他们还没有在HashMap
中(图像并不唯一)。
public class HistoryAdapter extends BaseAdapter{
private static Context context = ApplicationSubclass.getApplicationContext_();
private Contacts contacts = Contacts.init(context);
private SparseArray<Drawable> c_images = new SparseArray<Drawable>();
private HashMap<Long, Drawable> contact_imgs = new HashMap<Long, Drawable>();
private ArrayList<HistoryHolder> history;
private LayoutInflater mInflater;
public HistoryAdapter(Context context) {
HistoryAdapter.context = context;
mInflater = LayoutInflater.from(context);
}//end constructor
...
public View getView(int position, View convertView, ViewGroup parent) {
final HistoryHolder item = history.get(position);
Drawable d = null;
if (c_images.get(position) == null){
if (!contact_imgs.containsKey(item.CONTACT_ID)){
if (item.IN_CONTACTS && item.CONTACT_ID != 0 && item.CONTACT_ID != -1){
Bitmap photo = contacts.getContactPhotoThumbnailByContactId(item.CONTACT_ID);
if (photo != null){
d = Convert.bitmapToDrawable(context, photo, 128, 128);
} else {
d = context.getResources().getDrawable(R.drawable.ic_contact_picture);
}
} else {
d = context.getResources().getDrawable(R.drawable.ic_contact_picture);
}
contact_imgs.put(item.CONTACT_ID, d);
}
}
c_images.put(position, contact_imgs.get(item.CONTACT_ID));
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.history_list_item_holo_dark, null);
holder = new ViewHolder();
holder.POSITION = position;
holder.TITLE = (TextView) convertView.findViewById(R.id.title);
holder.SUMMARY = (TextView) convertView.findViewById(R.id.summary);
holder.DATE = (TextView) convertView.findViewById(R.id.date);
holder.CONTACT_ICON = (ImageView) convertView.findViewById(R.id.icon);
holder.CONTACT_ICON.setTag(position);
holder.OPTIONS_ICON = (ImageView) convertView.findViewById(R.id.options);
holder.OPTIONS_ICON.setFocusable(false);
holder.OPTIONS_ICON.setTag(position);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.CONTACT_ICON.setBackgroundDrawable(c_images.get(position));
holder.TITLE.setText(item.TITLE);
holder.SUMMARY.setText(item.SUMMARY);
holder.SUMMARY.setMaxLines(2);
holder.DATE.setText(item.DATE);
if (!MathUtils.isEven(position)){
convertView.setBackgroundColor(context.getResources().getColor(R.color.darker));
} else {
convertView.setBackgroundColor(context.getResources().getColor(R.color.medium_dark));
}
return convertView;
}//end method
static class ViewHolder {
TextView TITLE;
TextView SUMMARY;
TextView DATE;
ImageView CONTACT_ICON;
ImageView OPTIONS_ICON;
int POSITION;
}//end inner class
}//end class
最佳答案
Dmmh,你说的,你想做的(所以我需要 ListView 仅在进度完成时更新(添加项目))和你正在做的(getView 中的 AsyncTask)是完全相反的事情。
getView 末尾的 AsyncTask 用于延迟图像加载(但以不同的方式),即显示大图像并显示文本+图像占位符,直到下载完成。
您试图在第一个 AsyncTask 中逐渐填充适配器的数据源,但每次,您通知观察者数据集中的更改,您都会对数据集中的每个项目进行另一个 getView 调用周期。不好。
首先,永远不要,永远不要!!! 假设 getView 将为您提供一个转换 View ,该 View 先前已填充此位置。因此,您必须使用新值重新填充转换 View ,或者关闭性能优化并在每次要求时提供新 View 。 ListView 无法关闭回收尝试,因为这是 ListView 的本质,也是它所构建的功能。
第二(从第一开始),完全避免仅将耗时(或用户输入)的数据存储到新创建的 View 中。 View 来来去去,您不想走很长的路来获取昂贵的数据(或者只是丢失用户输入)。唯一的部分排除是大图像延迟加载的简单无效实现。为了减少内存使用量,他们只下载用户当前可见的图像。更有效的实现使用 ListView 之外的缓存。
因此,如果您确实希望一次添加一个 ListView 中的项目,但又完全满意,您应该:
0*。如果您必须加载用户提供的图标,并且这需要大量时间来加载(我不明白您最初的帖子),请创建一个空的 ArrayList 来缓存加载的图标并通过索引访问它们。如果所有图像都已可通过某个索引获取,请忽略此问题。
class DecryptedRecord {
String title = "";
String summary = "";
String date = "";
int contactViewIndex = -1;
int contactOptionsIndex = -1;
int contactImageIndex = -1;
int typeImageIndex = -1;
int optionsImageIndex = -1; //if option icon varies. ignore otherwise
}
声明 DecryptedRecord 类,包含填充 View 所需的数据,例如:
在您的 AsyncTask 中:加载每个 HistoryHolder 后,执行“大量解密”并用值填充新的 DecryptedRecord。如果需要加载自定义图像(参见 no.0*),请在此处加载并将其索引存储在缓存中(如果 0* 不相关则忽略此)。使用 setTag() 将填充的 DecryptedRecord 连接到 HistoryHolder。调用publishProgress(HistoryHolder)
在onProgressUpdate中只需将HistoryHolder添加到Adapter并调用historyAdapter.notifyDataSetChanged();
在 getView() 中,如果 ConvertView 为 null,则同步膨胀 View ,并且在所有情况下使用从 HistoryHolder.getTag 获取的 DecryptedRecord 数据填充 View ( )。包括在内,在您的 ImageView 上调用 setImageBitmap(),按索引在相应列表中处理必要的 Bitmap。
这应该可以满足您的要求。请,如果您有错误/问题,请尝试在您的问题中包含完整的代码,否则将很难理解您的情况的详细信息。希望有帮助。
关于android - AsyncTask 完成时强制 BaseAdapter 更新其 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16677936/