Android - 通过 getView 函数在自定义 listView 适配器内设置 ImageView 的源无法正常工作

Posted

技术标签:

【中文标题】Android - 通过 getView 函数在自定义 listView 适配器内设置 ImageView 的源无法正常工作【英文标题】:Android - Set the Source of an ImageView inside a custom listView adapter from the getView function does not work properly 【发布时间】:2015-06-11 22:54:56 【问题描述】:

为长标题道歉。

我正在构建一个 android 应用程序,其中一个 Activity 包含一个自定义 listView。在我的自定义列表视图的布局中,我有一个图像视图和三个文本视图。我正在从数据库中获取数据,并且行数据没有问题,因为它们是准确的。但是,我在显示 ImageViews 时遇到问题。

我填充 imageView 的方法是从数据库中获取存储的 imagePath,解析该 URL(仅限本地服务器),然后使用它设置 ImageViews 位图。

在我的 customListView 类的getView 函数中,我创建了一个线程来处理来自数据库的 url,然后我对其进行解析,然后将其设置为 ImageView 的位图。

这是我的getView 函数:

public View getView(int position, View convertView, ViewGroup parent) 

    if (convertView == null) 

        convertView = layoutInflater.inflate(R.layout.nominees_list_layout, null);
        holder = new ViewHolder();

        holder.firstName = (TextView) convertView.findViewById(R.id.nomineesListFirstName);
        holder.middleName = (TextView) convertView.findViewById(R.id.nomineesListMiddleName);
        holder.lastName = (TextView) convertView.findViewById(R.id.nomineesListLastName);
        holder.photo = (ImageView) convertView.findViewById(R.id.nomineesListCandidatePhoto);


        convertView.setTag(holder);
    
    else 
        holder = (ViewHolder) convertView.getTag();
    

    holder.firstName.setText(listData.get(position).getFirst_name());
    holder.middleName.setText(listData.get(position).getMiddle_name());
    holder.lastName.setText(listData.get(position).getLast_name());

    new setDisplayPhoto().execute(listData.get(position).getPicture_path());

    return convertView;

如您所见,我正在初始化视图组件,然后根据从数据库中获取的 listData 设置它们的值。显示的数据都是正确的。然后,我对 imageView 所做的是创建一个 AsyncTask 并在那里提供数据。

这是我的异步任务:

private class setDisplayPhoto extends AsyncTask<String, Void, String> 
    String toastMessage = "Downloading nominees photo finished.";

    Bitmap mIcon_val;

    @Override
    protected void onPreExecute() 
        super.onPreExecute();
    

    @Override
    protected String doInBackground(String... params) 

        Log.d("","doInBackGround Started");

        String photo_url_str  = "http://192.168.0.10:8888/server/path/here/"
                + params[0];

        try 
            Log.d("","photo_url_str = " + photo_url_str);
            newurl = new URL(photo_url_str);
         catch (MalformedURLException e) 
            e.printStackTrace();
        
        try 

            mIcon_val = BitmapFactory.decodeStream(newurl.openConnection().getInputStream());

            holder.photo.setImageBitmap(mIcon_val);

         catch (IOException e) 
            e.printStackTrace();
        


        return "Executed!";
    

    @Override
    protected void onPostExecute(String result) 
        super.onPostExecute(result);
        Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show();
    

但是,照片不会出现,如果出现,有时照片会混乱。

当源是从本地服务器获取时,有人可以帮助我在 customListView 适配器的 getView 功能期间如何正确设置 imageView 的图像源吗?感谢您的任何帮助。

【问题讨论】:

网络操作总是在辅助线程中运行....所以如果你给出一个url路径并尝试下载它会在其他线程中下载它,所以最好有你自己的asynctask尝试下载图像下载后会回电给您的适配器。根据位置的可见性显示图像。 我正试图取消实际下载文件,因为我希望系统能够灵活地从服务器获取文件而无需下载它。 在您的情况下,维护列表项的位置非常重要...因为列表视图基于回收概念。因此,如果您传递 imageview 对象,无论哪个位置可见,您的线程都会尝试将图像设置为该图像视图 您正在下载它,如果不下载它就无法显示服务器图像..只是您没有将其保存在辅助存储中..它将在您的主内存中.. 我不推荐这种图像下载,因为在 android 中保留了这么多图像在主内存中,要么最终导致内存不足异常,要么每次当用户滚动时,你最终都会下载许多相同的副本图片.. 【参考方案1】:

它不起作用的原因是处理图像下载、缓存和视图回收比您的代码处理的要复杂得多。

我可以指出一些我可以从您的代码中看到的错误,但肯定还有更多:

没有 RAM 缓存,这意味着当用户将视图滚动出视图然后返回视图时,图像将再次从服务器下载。 没有磁盘缓存,这意味着如果用户在列表中滚动太远,或者如果他/她离开应用程序几分钟后回来,图像将再次从服务器下载 在这一行 holder.photo.setImageBitmap 上,您正尝试在后台线程中更改 View。 AsyncTask(从某些 API 开始)是一个单线程类,这意味着您的所有下载都将一个接一个地排队,因此用户可能会在看到任何内容之前盯着空白屏幕一段时间。 您没有取消任务,因此在回收过程中,调用 setImageBitmap 可能发生在错误的项目上。

换行:

new setDisplayPhoto().execute(listData.get(position).getPicture_path());

与:

Picasso.with(convertView.getContext())
    .load("http://192.168.0.10:8888/server/path/here/" +
                       listData.get(position).getPicture_path())
    .into(holder.photo);

为此,您需要一个额外的库,您可以通过在清单中添加以下行来添加它:

compile 'com.squareup.picasso:picasso:2.5.2'

有关图书馆的更多信息,请查看:http://square.github.io/picasso/

【讨论】:

我必须在 AsyncTask 中执行此操作吗? 不!直接替换getView里面的代码就可以扔掉AsyncTask的代码了。 Picasso 库处理有关线程的所有问题。 哦,我的天哪,听起来真的非常非常非常好。我会尽快检查并尽快反馈。 您,好心的先生,是救生员。非常感谢! 救生员是那些创建图书馆的人。我很高兴传播这个词【参考方案2】:
package com.example.jojo.gridview;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;

public class GirdViewAdapter extends BaseAdapter 
    private Activity activity;
    private ArrayList<String> ar;
    private LayoutInflater inflater;

    public GirdViewAdapter(ArrayList<String> ar, Activity con)
    
        activity = con;
        this.ar=ar;
        inflater = (LayoutInflater) activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    

    private class ViewHolder 
        public TextView textView;
        public ImageView imageView;
    

    @Override
    public int getCount() 
        // TODO Auto-generated method stub
        return ar.size();
    

    @Override
    public Object getItem(int arg0) 
        // TODO Auto-generated method stub
        return null;
    

    @Override
    public long getItemId(int arg0) 
        // TODO Auto-generated method stub
        return 0;
    

    @Override
    public View getView(int position, View arg1, ViewGroup arg2) 
        // TODO Auto-generated method stub
        View rowView = arg1;
        ViewHolder viewHolder;
        if (arg1 == null) 
            rowView = inflater.inflate(R.layout.style_view, null);
            viewHolder = new ViewHolder();
            viewHolder.textView = (TextView) rowView.findViewById(R.id.textView);
            viewHolder.imageView = (ImageView) rowView.findViewById(R.id.imageView);
            rowView.setTag(viewHolder);
         else
            viewHolder = (ViewHolder) rowView.getTag();
        String rowData[] = this.ar.get(position).split("\\|");
        viewHolder.textView.setText(rowData[0] );
        setImageView(viewHolder.imageView, rowData[1] );
        return rowView;
    

    private void setImageView(ImageView imageView, String url) 
        class ImageDownloadHelper extends AsyncTask<String, String, Bitmap> 
            @Override
            protected Bitmap doInBackground(String... params) 
                // TODO Auto-generated method stub
                URL url;
                String _url = params[0];
                Log.e("url",_url );

                BufferedOutputStream out;
                InputStream in;
                BufferedInputStream buf;

                try 
                    url = new URL(_url);
                    in = url.openStream();
                    buf = new BufferedInputStream(in);

                    Bitmap bMap = BitmapFactory.decodeStream(buf);
                    if (in != null) 
                        in.close();
                    
                    if (buf != null) 
                        buf.close();
                    
                    return bMap;
                 catch (Exception e) 
                    Log.e("Error reading file", e.toString());
                
                return null;
            
        

        ImageDownloadHelper downloadHelper = new ImageDownloadHelper();
        downloadHelper.execute(url);
        try 
            imageView.setImageBitmap(downloadHelper.get());
         catch (Exception e) 
            e.printStackTrace();
        
    

【讨论】:

以上是关于Android - 通过 getView 函数在自定义 listView 适配器内设置 ImageView 的源无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

Android ListView 不调用适配器的类 getView() 函数

适用于Android ListView的适配器类getView()函数

Android OnItemClickListener 和 OnClickListener [重复]

getView() 在我的自定义列表视图中不起作用

getView() 方法是如何使用的,它在哪里被调用?

Android LIstView初次创建getview方法执行多次问题