Подтвердить что ты не робот

Recyclerview мучительно медленно загружает кешированные изображения из Picasso

Я реализовал RecyclerView, который содержит в основном изображения, которые загружаются через Picasso. Моя проблема заключается в том, что как только я прокручу вниз или вверх по виду, изображение заполнителя появится примерно на ок. секунду, прежде чем он будет заменен фактическим изображением. Он прокручивается очень плавно, но практически непригодным для использования. Это происходит каждый раз, когда что-то прокручивается с экрана.

Picasso явно кэширует изображения на диск, так как они загружаются быстрее, чем начальное время загрузки, но они, безусловно, мгновенно перезагружаются из кеша. Что я неправильно выполнил?

Мой код адаптера:

public class ExploreAdapter extends RecyclerView.Adapter<ExploreAdapter.ExploreViewHolder> {

    private List<ExploreItem> exploreItemList;
    private Context mContext;
    private int deviceWidth = PrefUtils.getDeviceWidth();

    public ExploreAdapter(Context context, List<ExploreItem> exploreItemList){
        this.mContext = context;
        this.exploreItemList = exploreItemList;
    }

    @Override
    public int getItemCount(){
        return exploreItemList.size();
    }

    @Override
    public void onBindViewHolder(ExploreViewHolder exploreViewHolder, int i){
        ExploreItem item = exploreItemList.get(i);
        exploreViewHolder.getvTitle().setText(item.getTitle());
        exploreViewHolder.getvUsername().setText(item.getUsername());
        exploreViewHolder.getvNumLikes().setText(item.getNbOfLikes());

        Picasso.with(mContext).load(item.getMediaLocation().trim())
                .resize(deviceWidth, deviceWidth)
                .centerCrop()
                .placeholder(R.drawable.profile_background)
                .into(exploreViewHolder.getvImage());

        Picasso.with(mContext).load(item.getUserProfilePicUrl().trim())
                .placeholder(R.drawable.profile_picture)
                .into(exploreViewHolder.getvProfilePic());
    }

    @Override
    public ExploreViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){
        View itemView = LayoutInflater.
                from(viewGroup.getContext()).
                inflate(R.layout.explore_item, viewGroup, false);

        return new ExploreViewHolder(itemView);
    }

    public static class ExploreViewHolder extends RecyclerView.ViewHolder{
        private TextView  vTitle,
                          vUsername,
                          vNumLikes;

        private SquareImage vImage;
        private ImageView vProfilePic;

        public ExploreViewHolder(View v){
            super(v);
            this.vTitle = (TextView) v.findViewById(R.id.explore_item_title);
            this.vUsername = (TextView) v.findViewById(R.id.explore_item_username);
            this.vNumLikes = (TextView) v.findViewById(R.id.explore_item_number_likes);
            this.vImage = (SquareImage) v.findViewById(R.id.explore_item_image);
            this.vProfilePic = (ImageView) v.findViewById(R.id.explore_item_profile_picture);
        }

        public TextView getvTitle() {
            return vTitle;
        }

        public TextView getvUsername() {
            return vUsername;
        }

        public ImageView getvProfilePic() {
            return vProfilePic;
        }

        public SquareImage getvImage() {
            return vImage;
        }

        public TextView getvNumLikes() {
            return vNumLikes;
        }

        public void setvImage(SquareImage vImage) {
            this.vImage = vImage;
        }

        public void setvNumLikes(TextView vNumLikes) {
            this.vNumLikes = vNumLikes;
        }

        public void setvProfilePic(ImageView vProfilePic) {
            this.vProfilePic = vProfilePic;
        }

        public void setvTitle(TextView vTitle) {
            this.vTitle = vTitle;
        }

        public void setvUsername(TextView vUsername) {
            this.vUsername = vUsername;
        }
    }
}

Любая помощь приветствуется.

4b9b3361

Ответ 1

Настройка параметров recyclerView, как показано ниже, решена моя проблема заикания:

recyclerView.setHasFixedSize(true);
recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);

Надеюсь, это поможет и кому-то другому.

Ответ 2

Используйте fit().

Picasso.with(context)
        .load(file)
        .fit()
        .centerCrop()
        .into(imageView);

fit() фактически изменяет размер изображения, чтобы он соответствовал границам imageView. Это не загружает полное изображение и не сглаживает прокрутку.

Ответ 3

Я смешал ответ от @ergunkocak и @Mangesh Ghotage, используя это прекрасно работает:

recyclerView.setHasFixedSize(true);
recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);

И это на каждом изображении:

Picasso.with(context)
        .load(file)
        .fit()
        .into(imageView);

кроме того, вы можете настроить Пикассо на использование одного экземпляра, подобного следующему:

Picasso.setSingletonInstance(picasso);

И, наконец, включение индикаторов кеша даст вам понять, что происходит не так:

Picasso  
    .with(context)
    .setIndicatorsEnabled(true);

Подробнее о Пикассо

Ответ 4

Недавно я столкнулся с этой проблемой, и да, вы правы. Picasso - хорошая библиотека для показа изображений с SD-карты, но у нее есть некоторые проблемы с кешем, если вы загружаете изображения в Интернете, поэтому вы видите изображение владельца места при прокрутке. Я пробовал разные вещи, и проблема не была решена.

Существует альтернативная библиотека, то есть "AQuery", которая очень хороша для извлечения и кэширования изображений из сети.

http://code.google.com/p/android-query/

Основными преимуществами AQuery/Android Query являются то, что вы можете очистить кеш, без запаздывания во время прокрутки и достаточно эффективно для кэширования изображений и не показывать изображения владельца места во время прокрутки.

Я решил проблему, удалив picaaso и используя Aquery. Это всего лишь замена одной строки, и вы закончили свою проблему.

(new AQuery(mContext)).id(exploreViewHolder.getvProfilePic()).image(item.getUserProfilePicUrl().trim(), true, true, device_width, R.drawable.profile_background, aquery.getCachedImage(R.drawable.profile_background),0);

Кроме того, вы можете легко перейти от picasso к AQuery, поскольку нет такой вещи, которая доступна в picasso, но не в AQuery, и синтаксис почти такой же. Поэтому я бы рекомендовал вам использовать AQuery, пока это не будет зафиксировано в picasso.

Ответ 5

Я не могу проверить правильность этого решения, но я тоже столкнулся с этой проблемой и подошел к ней, следуя этой идее:

@Override
public void onBindViewHolder(ViewHolder viewHolder, final int i) {

...

    if(mImageView.getDrawable() == null)
         Picasso.with(context)
            .load(path)
            .error(errorResId)
            .tag(tag)
            .into(mImageView);
...  
}

Он предназначен для того, чтобы запретить Пикассо загружать что-либо, кроме случаев, когда ImageView нечего отображать.

Ответ 6

Picasso автоматически кэширует изображения на двух уровнях:

  • дисковый кеш (на самом деле это НЕ на пикассо, а на сетевом уровне, который используется в picasso)
  • кэш памяти

Они инициализируются ботом по умолчанию, которые содержат большинство приложений.

Когда кеш памяти становится слишком большим, самая старая используемая растровая карта удаляется из кеша памяти (который по умолчанию равен 1/7 от доступного пространства кучи).

Я думаю, что здесь происходит то, что вы близки к пределу памяти кеша и, следовательно, изображения снова декодируются каждый раз, когда вам это нужно.

Смотрите здесь: Android Picasso Настройка размера LruCache

Но я советюсь ПРОДОЛЖИТЬ увеличение размера кеша памяти, если вы действительно не измените размер изображений до фактического размера, который вы используете.

Я думаю, что проблема с вашим кодом такова:

.resize(deviceWidth, deviceWidth)

Вы уверены, что вам действительно нужен образ такого размера? Что вы храните в PrefUtils? он обновляется, когда устройство вращается? (пейзаж против портрета)

Если вам нужны меньшие изображения, попробуйте передать размер фактического размера изображения, которое вы собираетесь использовать. Если вы не знаете (вычисленный во время выполнения с макетом), выполните приближение или напишите свой код, чтобы получить фактический размер (обычно я пишу пользовательские классы, расширяющие ImageView для этих вещей).

Если вам действительно нужны изображения такого размера, просто увеличьте размер кеша (см. ссылку выше), и вам должно быть хорошо идти.

Ответ 7

Лучший ответ на этот вопрос - не использовать круглое изображение, просто используйте преобразование с простым изображением, потому что из-за круглого изображения приложение занимает много памяти, просто используйте простое представление изображения и установите преобразование с ним. Используйте это преобразование

Ответ 8

просто используйте атрибут noPlaceholder.