android - AsyncTask 完成时强制 BaseAdapter 更新其 View

标签 android android-listview android-asynctask baseadapter

好吧,我的方法是这样的。我正在解码 AsyncTask 中本地存储的加密和序列化对象在 ActivityActivity使用 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被充气/替换为另一个 ViewonProgressUpdate() 。第二个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 。我解决了一些问题来解决这个问题。

  1. 根据 @Delyan 的建议,我认为最好在实际需要数据之前加载/解密数据。我正在使用 PropertyChangeListener为了这。这实现了OnSharedPreferenceChangeListener以便它收到我需要的数据更改的通知。然后更改将传播给任何感兴趣的听众。数据在应用程序启动时解密并存储在全局变量中,该变量可在整个应用程序中访问。将此视为他提到的“内存缓存”。
  2. 根据评论和已接受的答案,解密现在是在后台完成的,因此不再需要 AsyncTasks .
  3. 为了进一步优化适配器的性能,我正在存储 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
}
  1. 声明 DecryptedRecord 类,包含填充 View 所需的数据,例如:

  2. 在您的 AsyncTask 中:加载每个 HistoryHolder 后,执行“大量解密”并用值填充新的 DecryptedRecord。如果需要加载自定义图像(参见 no.0*),请在此处加载并将其索引存储在缓存中(如果 0* 不相关则忽略此)。使用 setTag() 将填充的 DecryptedRecord 连接到 HistoryHolder。调用publishProgress(HistoryHolder)

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

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

这应该可以满足您的要求。请,如果您有错误/问题,请尝试在您的问题中包含完整的代码,否则将很难理解您的情况的详细信息。希望有帮助。

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

相关文章:

android - Kotlin anko 通用选择器无法像提供的示例那样工作

java - 圆形 PNG 图像使其在 Android 上具有交互性

Android 在应用程序中添加自定义搜索栏

java - Android 布局不一致

android - 单击可扩展 ListView 项/子项时无法启动/打开 Activity 以及如何在此处实现 Toast

android - ListView 选中位置总是重置

android - 从适配器类设置 OnItemClickListener

java - IntelliJ/结构搜索 : Remove useless methods that only call super method

android - 将 url 加载到 WebView 时显示进度栏

java - Android:尝试通过 AsyncTask 加载视频 View 时出现 NullPointerException