как отобразить только видимые элементы представления списка

#android #listview

Вопрос:

В моем понимании ListView загружает и отображает только видимые элементы, но в моем случае кажется, что он всегда отображает все элементы. Из-за этого требуется до 5 секунд (для ~150 записей), пока представление списка не станет видимым.

Logcat показывает:

 I/OpenGLRenderer: Davey! duration=4740ms; 
 

Если я не загружу изображения, это займет ~1 секунду. С моей точки зрения, тоже сильно притормозить.

Ограничив его 5 записями, список быстро расширяется…

Вот упрощенный код, который я использую.

 listView = (ListView) view.findViewById(R.id.contact_list);
listView.setAdapter(new ContactAdapter(context, contacts));
 

 ContactAdapter(Context context, List<Contact> contacts) {
    this.contactList = contacts;
    this.mInflater = LayoutInflater.from(context);
    this.context = context;
}

public View getView(final int position, View convertView, final ViewGroup parent) {
    final ViewHolder holder;
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.contact_with_pic_ex, null);
        holder = new ViewHolder();
        holder.imgPicture = (ImageView) convertView.findViewById(R.id.picture);
        holder.txtName = (TextView) convertView.findViewById(R.id.name);
        holder.txtDate = (TextView) convertView.findViewById(R.id.date);

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

    final Contact contact = contactList.get(position);

    holder.imgPicture.setImageBitmap(getPhotoAsBitmap(contact.getID()));
    holder.txtName.setText(contact.getName());
    holder.txtDate.setText(contact.getDate());
    
    return convertView;
}

private Bitmap getPhotoAsBitmap(final int ID) {
    Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, ID);
    InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri, true);

    Bitmap bitmap;
    if (is == null)
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.contour_dark);
    else {
        bitmap = BitmapFactory.decodeStream(input);
    }
    return bitmap;
}
 

Это занимает примерно одно и то же время, независимо от того, использую ли я кадры или миниатюры.

Не могли бы вы, пожалуйста, помочь мне сделать код быстрее.

Комментарии:

1. Я бы посоветовал вам использовать более новый RecyclerView вместо ListView .

2. Самая большая проблема здесь в том, что вы не кэшировали ни одну из переменных. Ограничение до 5 и размещение журнала внутри getView() , чтобы сообщить вам, сколько раз он вызывается, — это один из способов. Включение и отключение представлений, используемых position в качестве основы, может помочь. Но в конце концов, почему бы просто не использовать RecyclerView ?

3. Если RecyclerView ускорит его работу, я попробую это сделать.

Ответ №1:

Рендеринг выполняется медленно из BitmapFactory.decodeStream -за того, что эта инструкция выполняется медленно, вам нужно выполнить обработку в фоновом потоке перед рендерингом.

это может быть что-то вроде этого:

 private static class ContactLoader extends AsyncTask<Void, Void, List<Contact>> {
    
    private final Context context;
    
    public ContactLoader(Context context) {
        this.context = context;
    }

    @Override
    protected List<Contact> doInBackground(Void... voids) {
        List<Contact> contacts = getContactListSomeWhere();
        for(Contact contact : contacts) {
            contact.setImageBitmap(getPhotoAsBitmap(contact.getID()))
        }
        return contacts;
    }

    @Override
    protected void onPostExecute(List<Contact> contacts) {
        super.onPostExecute(contacts);
        // Render data to ListView
    }

    private Bitmap getPhotoAsBitmap(final int ID) {
        Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, ID);
        InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri, true);

        Bitmap bitmap;
        if (is == null)
            bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.contour_dark);
        else {
            bitmap = BitmapFactory.decodeStream(input);
        }
        return bitmap;
    }
}
 

Комментарии:

1. Я не понимаю, как это должно помочь. Метод doInBackground также загружает последовательные изображения. onPostExecution вызывается после завершения doInBackground. Он также потребляет много памяти, потому что все изображения хранятся в списке контактов. Кстати, AsycTask не подходит для API 30 . Не могли бы вы объяснить, пожалуйста, как ваше решение должно помочь.