gpt4 book ai didi

android - 当 AsyncTask 完成时强制 BaseAdapter 更新它的 View

转载 作者:搜寻专家 更新时间:2023-11-01 08:59:10 24 4
gpt4 key购买 nike

好的,所以我的方法是这样的。我在 ActivityAsyncTask 中解码本地存储的加密和序列化对象。 Activity 使用 BaseAdapter (HistoryAdapter) 来显示数据。 AsyncTask 显示一个 ProgressDialog,直到解码完成。首次调用 onProgressUpdate() 时,取消 ProgressDialog。到目前为止,一切都很好。接下来,在 onProgressUpdate() 中,HistoryAdapter 以常用方式通知更改,触发它的 getView() 方法。在 HistoryAdapter 的 getView() 中,运行第二个 AsyncTask 来修改创建的 convertView 并将数据设置到 View.

这就是我失败的地方。我在 onProgressUpdate() 中膨胀最终布局,并在 convertView 上设置属性和数据在这里就好了。即使设置了所有数据,更改也不会显示...

因此,HistoryAdapter 中的 AsyncTask 本身实际上可以完美运行,只是更改不可见。我尝试了 SO 中提到的许多建议,例如使 convertView 无效,传递对 ListView 的引用并使用 invalidateViews() (导致永恒循环但没有可见的变化,这是有道理的)。

我真的很想要这个,因为我真的不想在数据可用之前加载带有图像占位符的布局。我开始工作了,但看起来很讨厌,而且喜欢简单的出路。所以我需要 ListView 仅在进度完成时更新(添加项目)。有什么想法吗?

编辑:澄清一下:数据是在正确的时间在适配器上设置的。问题是,适配器首先创建一个空白的View(占位符)(不知道有什么其他方法,否则你会在getView中得到一个NullPointerException),然后这个ViewonProgressUpdate() 中被另一个 View 膨胀/替换。第二个 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 的引用,但是话又说回来,它是正在进行中的代码。简化示例...

编辑

好吧,看来我的方法一开始就很糟糕,导致需要在我的 HistoryAdapter 中有一个 AsyncTask。我解决了几个问题来解决这个问题。

  1. 根据@Delyan 的建议,我认为最好在实际需要之前加载/解密数据。为此,我正在使用 PropertyChangeListener。这实现了一个 OnSharedPreferenceChangeListener,以便在我需要的数据发生变化时通知它。然后将更改传播到任何感兴趣的监听器。数据在应用程序启动时被解密并存储在一个全局变量中,该变量在整个应用程序中都是可访问的。将其视为他所说的“内存缓存”。
  2. 根据评论和已接受的答案,解密现在在后台完成,因此不再需要 AsyncTasks
  3. 为了进一步优化我的适配器的性能,我将 ListView 所需的图像存储在 SparseArray 中,因此它们只创建和存储一次。不要为此使用 HashMap!此外,如果图像不在 HashMap 中,则仅为当前 View 创建图像(图像不是唯一的)。

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 会为您提供一个 convertview,这个位置之前已经填充了。所以你必须要么用新值重新填充转换 View ,要么关闭性能优化并在每次被要求时提供新 View 。 ListView 无法关闭回收尝试,因为这是 ListView 的本质,它是基于此构建的功能。

其次(源自第一个),绝对避免仅将耗时(或用户输入)数据存储到新创建的 View 中。 View 来来去去,您不想走很长的路来获取昂贵的数据(或者只是丢失用户输入)。唯一的部分排除是大图像延迟加载的简单无效实现。为了减少内存使用,他们只下载用户当前可见的图像。更有效的实现使用 off-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
}
  1. 声明 DecryptedRecord 类,包含填充 View 所需的数据,例如:

  2. 在您的 AsyncTask 中:加载每个 HistoryHolder 后,执行“重解密”并用值填充新的 DecryptedRecord。如果需要加载自定义图片(见编号0*),在这里加载它并将它的索引存储在缓存中(如果0*无关则忽略它)。使用 setTag() 将填充的 DecryptedRecord 附加到 HistoryHolder。调用 publishProgress(HistoryHolder)

  3. 在 onProgressUpdate 中,只需将 HistoryHolder 添加到 Adapter 并调用 historyAdapter.notifyDataSetChanged();

  4. 在 getView() 中,如果 convertView 为空,则同步膨胀 View ,并且在所有情况下用从 HistoryHolder.getTag 获取的 DecryptedRecord 中的数据填充它( ).包括在内,在您的 ImageView 上调用 setImageBitmap(),按索引在相应列表中寻址必要的位图。

这应该做你想做的。请,如果您有错误/问题,请尝试在您的问题中包含完整的代码,否则将很难理解您的情况的细节。希望对您有所帮助。

关于android - 当 AsyncTask 完成时强制 BaseAdapter 更新它的 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16677936/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com