在ListItem中使用多个Imageview时,Android自定义Listview太慢

时间:2016-07-02 01:11:03

标签: android listview imageview

我认为标题中的问题很明确。我不知道如何解释这一点但是对于ListView性能,我尝试了很多东西,例如使用ViewHolder,将Bitmaps保留在列表中并在我的适配器的getView()方法中引用它们,删除每个控件和getView()等中的onClicklistener方法但是根据我的经验,在一个ListItem中使用2个ImageView是可以的,但是当使用3个ImageView时,初始化和滚动都非常慢。

ArrayList<User> users;
Context context;
DatabaseHelper db;
LayoutInflater inflater;
Bitmap defaultImage;
ArrayList<Bitmap> userPhotos;
Bitmap messageImage;
Bitmap callImage;

public FriendsAdapter(Context context){
    this.context = context;
    inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    db = new DatabaseHelper(context);
    users = db.getFriends();
    defaultImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.no_image_small);
    userPhotos = new ArrayList<Bitmap>();
    for (int i = 0; i < users.size(); i++){
        if(users.get(i).getPhoto() != null && !users.get(i).getPhoto().isEmpty()){
            userPhotos.add(getCircularCroppedBitmap(stringToBitMap(users.get(i).getPhoto())));
        }
        else{
            userPhotos.add(null);
        }
    }
    messageImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.message);
    callImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.call);

}

在这里你可以看到两个来自drawable的图像和一个来自string的图像,但它对于siliding并不重要,因为它们都存储在变量中。

@Override
public View getView(final int position, View convertView, ViewGroup parent) {


    ViewHolder holder;

    if(convertView == null){
        convertView = inflater.inflate(R.layout.friend_item, parent, false);

        holder = new ViewHolder();
        holder.imageViewPhoto = (ImageView) convertView.findViewById(R.id.friend_item_photo);
        holder.textViewName = (TextView) convertView.findViewById(R.id.friend_item_name);
        holder.imageViewMessage = (ImageView) convertView.findViewById(R.id.friend_item_message_photo);
        holder.imageViewCall = (ImageView) convertView.findViewById(R.id.friend_item_call_photo);

        convertView.setTag(holder);
    }
    else{
        holder = (ViewHolder) convertView.getTag();
    }
    holder.imageViewMessage.setImageBitmap(messageImage);
    //holder.imageViewCall.setImageBitmap(callImage);

    User user = users.get(position);

    Bitmap photo = userPhotos.get(position);
    if(photo == null){
        holder.imageViewPhoto.setImageBitmap(defaultImage);
    }
    else{
        holder.imageViewPhoto.setImageBitmap(photo);
    }

    holder.textViewName.setText(user.getName() + " " + user.getSurname());


    return convertView;
}

private static class ViewHolder{
    ImageView imageViewPhoto;
    TextView textViewName;
    ImageView imageViewMessage;
    ImageView imageViewCall;

}

在此代码中一切正常。但是当我执行holder.imageViewCall.setImageBitmap(callImage);一切都太慢了。

<ImageView
    android:id="@+id/friend_item_photo"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"/>

<TextView
    android:id="@+id/friend_item_name"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="3"
    android:maxLines="1"
    android:gravity="center_vertical"/>

<ImageView
    android:id="@+id/friend_item_call_photo"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"/>

<ImageView
    android:id="@+id/friend_item_message_photo"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"/>

这是我的listItem xml。 linearLayout包含了但我无法在此添加。这是我的第一篇文章。

编辑:它已经是一个基础适配器。

4 个答案:

答案 0 :(得分:2)

适配器的构造函数正在主线程上执行 lot 工作。

  1. 首先,您正在进行数据库查询,这可能很慢,或者如果另一个线程同时写入数据库,则可能会阻止。

  2. 您的数据库代码未发布,但看起来您正在迭代Cursor并从结果中构建ArrayList。这很好,但需要时间。如果您直接使用Cursor,则可以在查询完成后立即返回。

  3. 您再次迭代所有结果并为每个项目创建一个图像,包括从某处(I / O)加载位图并将其裁剪为圆形。位图操作通常不便宜。此外,您正在为每个项目创建这些图像并将其存储在内存中,但用户可能永远不会看到超过少量项目。如果您总共有一千个项目,并且屏幕上只有10行,那么您现在可以制作990多个图像。用户可能永远不会滚动浏览所有项目,这意味着您浪费了大量工作来查看无法看到的图像。

  4. 同样,所有都发生在主线程上。我猜#3是花费大部分时间的地方。

    对于问题1和2,您应该学习如何使用Loader框架或其他异步机制在后台线程上加载数据。通常,Activity或Fragment负责此,而不是适配器本身。它可能仍然需要时间,但它将在后台,因此主线程未被阻止,您可以在等待时在屏幕上显示加载微调器。

    对于问题3,您应该使用像Glide或Fresco这样的图像缓存框架。这些库根据需要在后台加载图像并对其进行缓存(这样,如果您需要再次使用相同的图像,如果图像仍然被缓存,它可以跳过加载)。调用getView()时,开始加载该项目的图像。加载完成后,设置该行的图像 - 通常这些框架会自动执行此操作,您甚至可以指定在加载时显示的默认图像。

答案 1 :(得分:1)

使用图像缓存和加载库,如Glide,Picasso等

使用异步任务加载图像也可以提高性能。

答案 2 :(得分:1)

感谢Guilherme P. &#34;图片大吗?也许,你应该在列表视图中只使用图片的小版本(缩略图)...也许,你可以用非常小的图片进行测试,以检查性能是否得到改善&#34;

这是真正的答案。我忘了调整图片大小。来自字符串的图像很小(125x125)但其他两个图像太大。其中一个是512x512,另一个是1024x1024。我将它们调整为100x100,现在一切都很顺利。这是非常基础的,应该是找到问题的第一种方法,但项目中有很多东西,很多时候忽略了基本的想法。

编辑:感谢您的其他答案,但我在很多活动和片段中完成了这些事情。我知道我在构造函数中做了很多事情,但我必须这样做。项目的其他部分有更复杂的代码,但没有问题。

编辑2:Karakuri也谢谢你。你的答案很好但我的问题并不复杂。当只有6-7项时,我的代码工作缓慢。您的答案适用于数百种商品。我稍后会做的。

答案 3 :(得分:0)

Recycler Views出于这个原因构建的视图。由于您可能对列表视图做了太多,可能会尝试做太多,这可能是导致问题的原因。 https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html