【问题标题】:How to combine an imageview with some text in listview如何将图像视图与列表视图中的一些文本结合起来
【发布时间】:2014-06-26 15:03:25
【问题描述】:

到目前为止,我一直在使用简单的ArrayAdapter 来显示ListView 中的一些项目。现在我还想在ListView 中的文本旁边显示图像。我有一个名为DownloadImageTaskAsyncTask 来下载图像。下载工作正常,但我不知道如何在ListView 中显示图像以及如何使用DownloadImageTask 下载ListView 中的图像。

这是我用来将图像下载到ImageView 中的DownloadImageTask

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    ImageView bmImage;

    public DownloadImageTask(ImageView bmImage) {
        this.bmImage = bmImage;
    }

    protected Bitmap doInBackground(String... urls) {
        String urldisplay = urls[0];
        Bitmap mIcon11 = null;
        try {
            InputStream in = new java.net.URL(urldisplay).openStream();
            mIcon11 = BitmapFactory.decodeStream(in);
        } catch (Exception e) {
            Log.e("Error", e.getMessage());
            e.printStackTrace();
        }
        return mIcon11;
    }

    protected void onPostExecute(Bitmap result) {
        bmImage.setImageBitmap(result);
    }
}

我还在ListView 旁边定义了一个ImageView 以将图像下载到布局中。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="#000000">

    <ImageView 
        android:id="@+id/image1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

我这样称呼DownloadImageTask

new DownloadImageTask((ImageView) findViewById(R.id.image1)).execute(url);

如何使用DownloadImageTask 下载图像并将它们显示在ListView 中的文本旁边?

【问题讨论】:

    标签: android android-layout listview android-listview


    【解决方案1】:

    要实现您想做的事情,您必须创建一个自定义Adapter。要下载图像,我建议您使用 Picasso 之类的库。 Picasso 在下载图片时会处理几乎所有的事情,而且它真的不能更容易使用它,你只需要调用它来将图片下载到ImageView

    Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
    

    它已经缓存了图像,还可以通过多种方式转换图像。 Picasso 是一个非常强大且易于使用的库。


    1) 实现自定义Adapter

    首先我们需要为ListView 中的每一行创建一个布局,在您的情况下,因为您要显示图像和文本,它需要包含TextViewImageView

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
    
        <ImageView
                android:id="@+id/imageView"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_margin="10dp"/>
    
        <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toRightOf="@id/imageView"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:textAlignment="gravity"
                android:gravity="center"/>
    
    </RelativeLayout>
    

    现在我们需要创建一个容器类——称为视图模型——来保存属于ListView 每一行的数据。在您的情况下,此视图模型包含您要显示的文本和图像的 url:

    private class ExampleViewModel {
        private String text;
        private String imageUrl;
    
        private ExampleViewModel(String text, String imageUrl) {
            this.text = text;
            this.imageUrl = imageUrl;
        }
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    
        public String getImageUrl() {
            return imageUrl;
        }
    
        public void setImageUrl(String imageUrl) {
            this.imageUrl = imageUrl;
        }
    }
    

    ListViews 使用视图回收。我们可以通过使用一种称为“视图持有者”的模式来加快ListView 的性能。基本上,我们在每一行内保存对Views 的引用并将其附加到行本身。这样我们只需调用一次昂贵的findViewById()。这个视图持有者类——我喜欢称它们为行——还包含一个名为bind() 的方法,用于将视图模型中的数据绑定到每行中的Views。我们需要对 TextViewImageView 的引用,但我们还需要为 Picasso 提供 Context。我还喜欢将与该行关联的布局定义为该行中的公共常量。

    private class ExampleRow {
    
        // This is a reference to the layout we defined above
        public static final int LAYOUT = R.layout.list_item;
    
        private final Context context;
        private final TextView textView;
        private final ImageView imageView;
    
        private ExampleRow(Context context, View convertView) {
            this.context = context;
            this.imageView = (ImageView) convertView.findViewById(R.id.imageView);
            this.textView = (TextView) convertview.findViewById(R.id.textView);
        }
    
        public void bind(ExampleViewModel exampleViewModel) {
            this.textView.setText(exampleViewModel.getText());
            Picasso.with(this.context).load(exampleViewModel.getImageUrl()).into(this.imageView);
        }
    }
    

    最后我们需要一个自定义的Adapter 来完成这项工作,这真的没什么特别的。唯一有趣的部分是getView()。如有必要,我会评论重要部分:

    public class ExampleAdapter extends BaseAdapter {
    
        private final List<ExampleViewModel> viewModels;
    
        private final Context context;
        private final LayoutInflater inflater;
    
        public ExampleAdapter(Context context) {
            this.context = context;
            this.inflater = LayoutInflater.from(context);
            this.viewModels = new ArrayList<ExampleViewModel>();
        }
    
        public ExampleAdapter(Context context, List<ExampleViewModel> viewModels) {
            this.context = context;
            this.inflater = LayoutInflater.from(context); 
            this.viewModels = viewModels;
        }
    
        public List<ExampleViewModel> viewmodels() {
            return this.viewModels;
        }
    
        @Override
        public int getCount() {
            return this.viewModels.size();
        }
    
        @Override
        public ExampleViewModel getItem(int position) {
            return this.viewModels.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            // We only need to implement this if we have multiple rows with a different layout. All your rows use the same layout so we can just return 0.
            return 0;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            // We get the view model for this position
            final ExampleViewModel viewModel = getItem(position);
    
            ExampleRow row;
            // If the convertView is null we need to create it
            if(convertView == null) {
                convertView = this.inflater.inflate(ExampleRow.LAYOUT, parent, false);
    
                // In that case we also need to create a new row and attach it to the newly created View
                row = new ExampleRow(this.context, convertView);
                convertView.setTag(row);
            }
    
            // After that we get the row associated with this View and bind the view model to it
            row = (ExampleRow) convertView.getTag();
            row.bind(viewModel);
    
            return convertView;
        }
    }
    

    这就是您所需要的一切。这几乎是Adapter 的最佳实践实现。它使用视图持有者模式来获得额外的性能,并与ListView 的视图回收完美配合。它快速、简洁且容易,并且几乎不会为开发人员犯下的错误留下空间,否则会减慢ListView 的速度。您可以完美区分要显示的数据(全部在 ExampleViewModel 中)和显示方式(在 ExampleRow 中)。适配器本身也不知道 - 应该是这样!


    2) 使用方法

    要使用上面的代码,我们首先需要创建视图模型来保存我们想要显示的数据:

    ExampleViewModel firstRow = new ExampleViewModel("First Row". "http://http://upload.wikimedia.org/wikipedia/commons/6/6f/Freiburger_Alpen.JPG");    
    ExampleViewModel secondRow = new ExampleViewModel("Second Row". "http://blog.caranddriver.com/wp-content/uploads/2013/05/lamborghini_egoista_three_quarter_front_view.jpg");    
    ExampleViewModel thirdRow = new ExampleViewModel("Third Row". "http://4.bp.blogspot.com/-vXnf7GjcXmg/UfJZE9rWc2I/AAAAAAAAGRc/x2CIlHM9IAA/s1600/aphoto49721.jpg");
    

    我们需要将所有这些行添加到List

    List<ExampleViewModel> viewModels = new ArrayList<ExampleViewModel>();
    viewModels.add(firstRow);
    viewModels.add(secondRow);
    viewModels.add(thirdRow);
    

    然后我们需要创建ExampleAdapter 的实例并在构造函数中传递视图模型的List。最后我们只需要将Adapter设置为ListView

    ExampleAdapter adapter = new ExampleAdapter(context, viewModels);
    listView.setAdapter(adapter);
    

    您可以稍后使用ExampleAdapterviewmodels() 方法修改ListView 中显示的项目!您只需要记住在修改视图模型后始终在Adapter 上调用notifyDataSetChanged()

    adapter.viewmodels().remove(0); // Remove first view model
    adapter.viewmodels().add(someNewViewModel); // Add some new view model
    
    // Always remember to call this method after modifying the viewmodels.
    // This will apply the changes to the ListView. 
    // If you forget to call this you will get an exception
    adapter.notifyDataSetChanged(); 
    

    希望我能帮到你,如果你有任何其他问题,请随时提出!

    【讨论】:

    • 非常感谢。明天我会详细介绍。
    • 我不会查看您的整个项目并为您修复它。如果你有一个新问题而不是问一个新问题。在此处发布链接,我保证我会看看。
    • 好的。问题出在ExampleAdapter adapter = new ExampleAdapter(context, viewModels); ListView.setAdapter(adapter); 我收到错误消息Cannot make a static reference to the non-static method setAdapter(ListAdapter) from the type ListView
    • 此错误意味着您处于静态方法中并尝试访问非静态的东西 - 在您的情况下是 setAdapter() 方法。提出一个新问题,展示代码的相关部分以及您遇到的任何错误,并尝试解释您迄今为止为解决它所做的工作。我将看看新问题并提出解决方案。
    • 好的,我稍后再问
    【解决方案2】:

    您可以使用像 Picasso 或 Universal Image Loader 这样的外部库,它们会为您提供很多选择。由于他们管理缓存,您将能够从 url 或其他任何时间加载图像并在多个位置显示它们。


    你可以试试:

    我不确定,但也许您可以通过以下方式在列表适配器的 getView() 方法中使用您的 DownloadClass:

      new DownloadImageTask((ImageView) findViewById(R.id.image1)){
          @Override
          protected void onPostExecute(Bitmap bm) {
               //set imageview src with your bitmap }}.execute(url);
            }
        }.execute(archivo,"investigacion");
    

    【讨论】:

    • 我一直在寻找简单的东西,因为我不会遇到任何缓存问题。我的意思是只有少量图像。
    • 你在哪里调用你的新 DownloadImageTask((ImageView) findViewById(R.id.image1)).execute(url);
    • 您无法比Picasso 更简单。它为您处理一切。你可以这样使用它:Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView); 就是这样。
    • @LucianoRodríguez 来自另一个内部类 onPostExecute 方法
    • 我现在会写一个答案来解释它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-29
    • 2022-01-10
    • 1970-01-01
    • 2019-07-01
    • 2015-04-11
    • 1970-01-01
    相关资源
    最近更新 更多