Ленивая загрузка изображений в ListView


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

общее количество изображений не фиксируется.

30 1766

30 ответов:

вот что я создал, чтобы держать изображения, которые мое приложение в настоящее время отображает. Обратите внимание, что используемый здесь объект" Log " - это моя пользовательская оболочка вокруг конечного класса журнала внутри Android.

package com.wilson.android.library;

/*
 Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
    private final Map<String, Drawable> drawableMap;

    public DrawableManager() {
        drawableMap = new HashMap<String, Drawable>();
    }

    public Drawable fetchDrawable(String urlString) {
        if (drawableMap.containsKey(urlString)) {
            return drawableMap.get(urlString);
        }

        Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
        try {
            InputStream is = fetch(urlString);
            Drawable drawable = Drawable.createFromStream(is, "src");


            if (drawable != null) {
                drawableMap.put(urlString, drawable);
                Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                        + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                        + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
            } else {
              Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
            }

            return drawable;
        } catch (MalformedURLException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        } catch (IOException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        }
    }

    public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
        if (drawableMap.containsKey(urlString)) {
            imageView.setImageDrawable(drawableMap.get(urlString));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                //TODO : set imageView to a "pending" image
                Drawable drawable = fetchDrawable(urlString);
                Message message = handler.obtainMessage(1, drawable);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }

    private InputStream fetch(String urlString) throws MalformedURLException, IOException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet request = new HttpGet(urlString);
        HttpResponse response = httpClient.execute(request);
        return response.getEntity().getContent();
    }
}

Я простая демонстрация ленивого списка (находится на GitHub) с изображениями. Это может быть полезно для кого-то. Он загружает изображения в фоновом потоке. Картинки кэшируются на SD карте и в памяти. Реализация кэша очень проста и как раз достаточно для демонстрации. Я декодирую изображения с помощью inSampleSize, чтобы уменьшить потребление памяти. Я также стараюсь правильно обрабатывать переработанные представления.

Alt text

Я рекомендую инструмент с открытым исходным кодом Универсальный Загрузчик Изображений. Он изначально основан на проекте Федора Власова LazyList и был значительно улучшилась.

  • многопоточная загрузка изображений
  • возможность широкой настройки конфигурации ImageLoader (исполнители потоков, downlaoder, декодер, кэш памяти и дисков, параметры отображения изображений и другие)
  • возможность кэширования изображений в памяти и / или на файловой системе устройства (или SD-карте)
  • возможность "слушать" процесс загрузки
  • возможность настроить каждый вызов отображения изображения с разделенными опциями
  • виджет поддержка
  • Android 2.0+ Поддержка

Многопоточность Для Выполнения, учебник Жиля Дебюна.

это из блога разработчиков Android. Предлагаемый код использует:

  • AsyncTasks.
  • жесткий, ограниченный размер,FIFO cache.
  • мягкий, легко garbage collect - ed кэш.
  • A местозаполнительDrawable при загрузке.

enter image description here

обновление: обратите внимание, что этот ответ сейчас довольно неэффективным. Сборщик мусора действует агрессивно на SoftReference и WeakReference, поэтому этот код не подходит для новых приложений. (вместо этого попробуйте библиотеки, такие как Универсальный Загрузчик Изображений предложено в другой ответ.)

спасибо Джеймсу за код, и Bao-Long за предложение использовать SoftReference. Я реализовал изменения SoftReference в коде Джеймса. К сожалению SoftReferences вызвали мой изображения для мусора собираются слишком быстро. В моем случае это было прекрасно без материала SoftReference, потому что мой размер списка ограничен, и мои изображения малы.

есть обсуждение с год назад относительно SoftReferences в группах google:ссылка на поток. В качестве решения слишком ранней сборки мусора они предлагают возможность вручную установить размер кучи виртуальной машины с помощью dalvik.система.VMRuntime.setMinimumHeapSize (), что не очень привлекательно для мне.

public DrawableManager() {
    drawableMap = new HashMap<String, SoftReference<Drawable>>();
}

public Drawable fetchDrawable(String urlString) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null)
            return drawable;
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

    if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
    try {
        InputStream is = fetch(urlString);
        Drawable drawable = Drawable.createFromStream(is, "src");
        drawableRef = new SoftReference<Drawable>(drawable);
        drawableMap.put(urlString, drawableRef);
        if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
        return drawableRef.get();
    } catch (MalformedURLException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    } catch (IOException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    }
}

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null) {
            imageView.setImageDrawable(drawableRef.get());
            return;
        }
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            imageView.setImageDrawable((Drawable) message.obj);
        }
    };

    Thread thread = new Thread() {
        @Override
        public void run() {
            //TODO : set imageView to a "pending" image
            Drawable drawable = fetchDrawable(urlString);
            Message message = handler.obtainMessage(1, drawable);
            handler.sendMessage(message);
        }
    };
    thread.start();
}

Пикассо

используйте библиотеку Пикассо Джейка Уортона. (Идеальная библиотека загрузки изображений от разработчика ActionBarSherlock)

мощная библиотека загрузки и кэширования изображений для Android.

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

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

многие общие подводные камни загрузки изображений на Android обрабатываются автоматически Пикассо:

обработка ImageView рециркуляции и отмены загрузки в адаптере. Сложные преобразования изображений с минимальным использованием памяти. Автоматическое кэширование памяти и дисков.

библиотека Пикассо Джейка Уортона

скольжения

Glide-это быстрая и эффективная платформа управления мультимедиа с открытым исходным кодом для Android, которая обертывает декодирование мультимедиа, кэширование памяти и дисков и объединение ресурсов в простой и легкий в использовании интерфейс.

Glide поддерживает извлечение, декодирование и отображение видеороликов, изображений и анимированных GIF-файлов. Glide включает в себя гибкий api, который позволяет разработчикам подключаться практически к любому сетевому стеку. По умолчанию Glide использует пользовательский стек на основе HttpUrlConnection, но также включает в себя служебные библиотеки, подключаемые к проекту Volley Google или библиотеке OkHttp Square.

Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);

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

Библиотека Загрузки Скользящего Изображения

фреска на Facebook

Fresco-это мощная система для отображения изображений в Android-приложениях.

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

Fresco Github

В Android 4.x и ниже, Fresco помещает изображения в специальную область памяти Android. Это позволяет вашему приложению работать быстрее-и страдать от страшного OutOfMemoryError гораздо реже.

Фреска Документации

высокопроизводительный загрузчик - после изучения предложенных здесь методов, Я использовал Бен С некоторыми изменениями -

  1. Я понял, что работа с чертежами быстрее, чем с растровыми изображениями, поэтому я использую чертежи вместо

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

  3. чтобы открыть InputStream я использовал java. net. URLConnection, который позволяет мне использовать веб-кэш (вам нужно сначала установить кэш ответов, но это уже другая история)

мой код:

import java.util.Map; 
import java.util.HashMap; 
import java.util.LinkedList; 
import java.util.Collections; 
import java.util.WeakHashMap; 
import java.lang.ref.SoftReference; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ExecutorService; 
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.net.MalformedURLException; 
import java.io.IOException; 
import java.net.URL;
import java.net.URLConnection;

public class DrawableBackgroundDownloader {    

private final Map<String, SoftReference<Drawable>> mCache = new HashMap<String, SoftReference<Drawable>>();   
private final LinkedList <Drawable> mChacheController = new LinkedList <Drawable> ();
private ExecutorService mThreadPool;  
private final Map<ImageView, String> mImageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());  

public static int MAX_CACHE_SIZE = 80; 
public int THREAD_POOL_SIZE = 3;

/**
 * Constructor
 */
public DrawableBackgroundDownloader() {  
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);  
}  


/**
 * Clears all instance data and stops running threads
 */
public void Reset() {
    ExecutorService oldThreadPool = mThreadPool;
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
    oldThreadPool.shutdownNow();

    mChacheController.clear();
    mCache.clear();
    mImageViews.clear();
}  

public void loadDrawable(final String url, final ImageView imageView,Drawable placeholder) {  
    mImageViews.put(imageView, url);  
    Drawable drawable = getDrawableFromCache(url);  

    // check in UI thread, so no concurrency issues  
    if (drawable != null) {  
        //Log.d(null, "Item loaded from mCache: " + url);  
        imageView.setImageDrawable(drawable);  
    } else {  
        imageView.setImageDrawable(placeholder);  
        queueJob(url, imageView, placeholder);  
    }  
} 


private Drawable getDrawableFromCache(String url) {  
    if (mCache.containsKey(url)) {  
        return mCache.get(url).get();  
    }  

    return null;  
}

private synchronized void putDrawableInCache(String url,Drawable drawable) {  
    int chacheControllerSize = mChacheController.size();
    if (chacheControllerSize > MAX_CACHE_SIZE) 
        mChacheController.subList(0, MAX_CACHE_SIZE/2).clear();

    mChacheController.addLast(drawable);
    mCache.put(url, new SoftReference<Drawable>(drawable));

}  

private void queueJob(final String url, final ImageView imageView,final Drawable placeholder) {  
    /* Create handler in UI thread. */  
    final Handler handler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
            String tag = mImageViews.get(imageView);  
            if (tag != null && tag.equals(url)) {
                if (imageView.isShown())
                    if (msg.obj != null) {
                        imageView.setImageDrawable((Drawable) msg.obj);  
                    } else {  
                        imageView.setImageDrawable(placeholder);  
                        //Log.d(null, "fail " + url);  
                    } 
            }  
        }  
    };  

    mThreadPool.submit(new Runnable() {  
        @Override  
        public void run() {  
            final Drawable bmp = downloadDrawable(url);
            // if the view is not visible anymore, the image will be ready for next time in cache
            if (imageView.isShown())
            {
                Message message = Message.obtain();  
                message.obj = bmp;
                //Log.d(null, "Item downloaded: " + url);  

                handler.sendMessage(message);
            }
        }  
    });  
}  



private Drawable downloadDrawable(String url) {  
    try {  
        InputStream is = getInputStream(url);

        Drawable drawable = Drawable.createFromStream(is, url);
        putDrawableInCache(url,drawable);  
        return drawable;  

    } catch (MalformedURLException e) {  
        e.printStackTrace();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  

    return null;  
}  


private InputStream getInputStream(String urlString) throws MalformedURLException, IOException {
    URL url = new URL(urlString);
    URLConnection connection;
    connection = url.openConnection();
    connection.setUseCaches(true); 
    connection.connect();
    InputStream response = connection.getInputStream();

    return response;
}
}

Я следил за этим обучением Android, и я думаю, что он отлично справляется с загрузкой изображений, не блокируя основной пользовательский интерфейс. Он также обрабатывает кэширование и работает с прокруткой многих изображений:Загрузка Больших Растровых Изображений Эффективно

1.Пикассо позволяет без проблем загружать изображения в ваше приложение-часто в одной строке кода!

Используете Gradle:

implementation 'com.squareup.picasso:picasso:2.71828'

только одна строка кода!

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

2.скольжения библиотека загрузки и кэширования изображений для Android, ориентированная на плавную прокрутку

использовать Gradle в:

repositories {
  mavenCentral() 
  google()
}

dependencies {
   implementation 'com.github.bumptech.glide:glide:4.7.1'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}

// для простой вид:

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

3.фреска - это мощная система для отображения изображений в Android приложения.Fresco заботится о загрузке и отображении изображений, поэтому у вас нет к.

начало работы с Fresco

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

ленивая загрузка изображений в ListView Tutorial

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

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

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

в принципе это ImageDownloader - Это ваш ответ (так как она охватывает большинство ваших требований). Некоторые вы также можете реализовать в этом.

Я использую NetworkImageView из новой библиотеки Android Volley com.android.volley.toolbox.NetworkImageView, и это, кажется, работает очень хорошо. По-видимому, это тот же вид, который используется в Google Play и другие новые приложения Google. Определенно стоит проверить.

Это общая проблема на Android, которая была решена во многом многие люди. На мой взгляд лучшее решение я видел-это относительно новая библиотека под названием Пикассо. Вот основные моменты:

  • С открытым исходным кодом, но во главе с Jake Wharton на ActionBarSherlock славы.
  • асинхронно загружать изображения из сети или ресурсов приложения с одной строкой кода
  • автоматическая ListView обнаружение
  • автоматическое кэширование диска и памяти
  • можно сделать пользовательские преобразования
  • множество настраиваемых параметров
  • супер простой API
  • часто обновляется!--10-->

ну, время загрузки изображений из Интернета имеет много решений. Вы также можете использовать библиотеку Android-Query. Это даст вам всю необходимую активность. Убедитесь, что вы хотите сделать и прочитать вики-страницу библиотеки. И решить ограничение загрузки изображения.

Это мой код:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if (v == null) {
        LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = vi.inflate(R.layout.row, null);
    }

    ImageView imageview = (ImageView) v.findViewById(R.id.icon);
    AQuery aq = new AQuery(convertView);

    String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";

    aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
        @Override
        public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
            iv.setImageBitmap(bm);
        }
    ));

    return v;
}

Это должно быть решить вашу проблему ленивой загрузки.

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

посмотреть Shutterbug, легкий SDWebImage от Applidium (хорошая библиотека на iOS) порт для Android. Он поддерживает асинхронное кэширование, сохраняет неудачные URL-адреса, хорошо обрабатывает параллелизм и включает полезные подклассы.

запросы (и сообщения об ошибках) тоже добро пожаловать!

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

Универсальный Загрузчик Изображений

особенности

  • многопоточная загрузка изображений (асинхронная или синхронизация)
  • широкая настройка конфигурации ImageLoader (исполнители потоков, загрузчик, декодер, память и дисковый кэш, параметры отображения изображений и т. д.)
  • много вариантов настройки для каждого дисплея вызов изображения (тупиковые изображения, переключатель кэширования, параметры декодирования, обработка и отображение растровых изображений и т. д.)
  • кэширование изображений в памяти и / или на диске (файловая система устройства или SD-карта)
  • прослушивание процесса загрузки (включая прогресс загрузки)

Android 2.0+ Поддержка

enter image description here

DroidParts и ImageFetcher это требует нулевой конфигурации, чтобы начать работу.

  • использует диск и в памяти В Последнее Время Используется (LRU) кэш.
  • эффективно декодирует изображения.
  • поддерживает изменения растровых изображений в фоновом потоке.
  • имеет простой кросс-исчезают.
  • имеет обратный вызов хода загрузки изображения.

клон DroidPartsGram для пример:

Enter image description here

просто Быстрый совет для тех, кто находится в нерешительности относительно того, какую библиотеку использовать для ленивой загрузки изображений:

есть четыре основных способа.

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

  2. ленивая библиотека загрузки Volley = > от парней на android. Это хорошо и все, но плохо документировано и, следовательно, является проблемой для использовать.

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

  4. UIL: лучший способ ленивой загрузки изображений. Вы можете кэшировать изображения (вам нужно разрешение, конечно), инициализировать загрузчик один раз, а затем выполнить свою работу. Самая зрелая асинхронная библиотека загрузки изображений I никогда не видел до сих пор.

Новода также имеет большой отложенная загрузка изображений библиотека и многие приложения, такие как Songkick, Podio, SecretDJ и ImageSearch используют свою библиотеку.

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

Проверьте мою вилку LazyList. В принципе, я улучшаю LazyList, задерживая вызов ImageView и создавая два метода:

  1. когда вам нужно поставить что-то вроде "загрузка изображения..."
  2. когда вам нужно показать загруженное изображение.

Я также улучшил ImageLoader, реализовав синглтон в этот объект.

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

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

пожалуйста, посетите здесь : http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149

Я могу порекомендовать другой способ, который работает как шарм: Android Query.

Вы можете скачать эту JAR С здесь

AQuery androidAQuery = new AQuery(this);

пример:

androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);

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

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

URLImageViewHelper - Это удивительная библиотека, которая поможет вам сделать это.

используйте библиотеку glide. Это сработало для меня и будет работать для вашего кода too.It работает как для изображений, так и для GIF-файлов.

ImageView imageView = (ImageView) findViewById(R.id.test_image); 
    GlideDrawableImageViewTarget imagePreview = new GlideDrawableImageViewTarget(imageView);
    Glide
            .with(this)
            .load(url)
            .listener(new RequestListener<String, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {                       
                    return false;
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    return false;
                }
            })
            .into(imagePreview);
}

Если вы хотите отобразить макет мерцания, как Facebook, для этого есть официальная библиотека facebok. FaceBook Shimmer Android

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

<com.facebook.shimmer.ShimmerFrameLayout
     android:id=“@+id/shimmer_view_container”
     android:layout_width=“wrap_content”
     android:layout_height="wrap_content"
     shimmer:duration="1000">

 <here will be your content to display />

</com.facebook.shimmer.ShimmerFrameLayout>

и вот код java для него.

ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();

добавьте эту зависимость в файл gradle.

implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'

вот как это выглядит как.Shimmer Android screenshot

public class ImageDownloader {

Map<String, Bitmap> imageCache;

public ImageDownloader() {
    imageCache = new HashMap<String, Bitmap>();

}

// download function
public void download(String url, ImageView imageView) {
    if (cancelPotentialDownload(url, imageView)) {

        // Caching code right here
        String filename = String.valueOf(url.hashCode());
        File f = new File(getCacheDirectory(imageView.getContext()),
                filename);

        // Is the bitmap in our memory cache?
        Bitmap bitmap = null;

        bitmap = (Bitmap) imageCache.get(f.getPath());

        if (bitmap == null) {

            bitmap = BitmapFactory.decodeFile(f.getPath());

            if (bitmap != null) {
                imageCache.put(f.getPath(), bitmap);
            }

        }
        // No? download it
        if (bitmap == null) {
            try {
                BitmapDownloaderTask task = new BitmapDownloaderTask(
                        imageView);
                DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
                        task);
                imageView.setImageDrawable(downloadedDrawable);
                task.execute(url);
            } catch (Exception e) {
                Log.e("Error==>", e.toString());
            }

        } else {
            // Yes? set the image
            imageView.setImageBitmap(bitmap);
        }
    }
}

// cancel a download (internal only)
private static boolean cancelPotentialDownload(String url,
        ImageView imageView) {
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

    if (bitmapDownloaderTask != null) {
        String bitmapUrl = bitmapDownloaderTask.url;
        if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
            bitmapDownloaderTask.cancel(true);
        } else {
            // The same URL is already being downloaded.
            return false;
        }
    }
    return true;
}

// gets an existing download if one exists for the imageview
private static BitmapDownloaderTask getBitmapDownloaderTask(
        ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
            return downloadedDrawable.getBitmapDownloaderTask();
        }
    }
    return null;
}

// our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context) {
    String sdState = android.os.Environment.getExternalStorageState();
    File cacheDir;

    if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
        File sdDir = android.os.Environment.getExternalStorageDirectory();

        // TODO : Change your diretcory here
        cacheDir = new File(sdDir, "data/ToDo/images");
    } else
        cacheDir = context.getCacheDir();

    if (!cacheDir.exists())
        cacheDir.mkdirs();
    return cacheDir;
}

private void writeFile(Bitmap bmp, File f) {
    FileOutputStream out = null;

    try {
        out = new FileOutputStream(f);
        bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (out != null)
                out.close();
        } catch (Exception ex) {
        }
    }
}

// download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private String url;
    private final WeakReference<ImageView> imageViewReference;

    public BitmapDownloaderTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    // Actual download method, run in the task thread
    protected Bitmap doInBackground(String... params) {
        // params comes from the execute() call: params[0] is the url.
        url = (String) params[0];
        return downloadBitmap(params[0]);
    }

    @Override
    // Once the image is downloaded, associates it to the imageView
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
            // Change bitmap only if this process is still associated with
            // it
            if (this == bitmapDownloaderTask) {
                imageView.setImageBitmap(bitmap);

                // cache the image

                String filename = String.valueOf(url.hashCode());
                File f = new File(
                        getCacheDirectory(imageView.getContext()), filename);

                imageCache.put(f.getPath(), bitmap);

                writeFile(bitmap, f);
            }
        }
    }

}

static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
        super(Color.WHITE);
        bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
                bitmapDownloaderTask);
    }

    public BitmapDownloaderTask getBitmapDownloaderTask() {
        return bitmapDownloaderTaskReference.get();
    }
}

// the actual download code
static Bitmap downloadBitmap(String url) {
    HttpParams params = new BasicHttpParams();
    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
            HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    final HttpGet getRequest = new HttpGet(url);

    try {
        HttpResponse response = client.execute(getRequest);
        final int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            Log.w("ImageDownloader", "Error " + statusCode
                    + " while retrieving bitmap from " + url);
            return null;
        }

        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                final Bitmap bitmap = BitmapFactory
                        .decodeStream(inputStream);
                return bitmap;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                entity.consumeContent();
            }
        }
    } catch (Exception e) {
        // Could provide a more explicit error message for IOException or
        // IllegalStateException
        getRequest.abort();
        Log.w("ImageDownloader", "Error while retrieving bitmap from "
                + url + e.toString());
    } finally {
        if (client != null) {
            // client.close();
        }
    }
    return null;
 }
}

у меня была эта проблема и реализована lruCache. Я считаю, что вам нужен API 12 и выше или используйте библиотеку совместимости v4. lurCache-это быстрая память, но у нее также есть бюджет, поэтому, если вы беспокоитесь об этом, вы можете использовать diskcache... Все это описано в Кэширование Растровых Изображений.

теперь я предоставлю свою реализацию, которая является синглтон Я звоню из любого места, как это:

//Where the first is a string and the other is a imageview to load.

DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);

вот идеальный код для кэширования и последующего вызова выше в getView адаптера при получении веб-образа:

public class DownloadImageTask {

    private LruCache<String, Bitmap> mMemoryCache;

    /* Create a singleton class to call this from multiple classes */

    private static DownloadImageTask instance = null;

    public static DownloadImageTask getInstance() {
        if (instance == null) {
            instance = new DownloadImageTask();
        }
        return instance;
    }

    //Lock the constructor from public instances
    private DownloadImageTask() {

        // Get max available VM memory, exceeding this amount will throw an
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an
        // int in its constructor.
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        // Use 1/8th of the available memory for this memory cache.
        final int cacheSize = maxMemory / 8;

        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // The cache size will be measured in kilobytes rather than
                // number of items.
                return bitmap.getByteCount() / 1024;
            }
        };
    }

    public void loadBitmap(String avatarURL, ImageView imageView) {
        final String imageKey = String.valueOf(avatarURL);

        final Bitmap bitmap = getBitmapFromMemCache(imageKey);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            imageView.setImageResource(R.drawable.ic_launcher);

            new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
        }
    }

    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    private Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }

    /* A background process that opens a http stream and decodes a web image. */

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

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

        protected Bitmap doInBackground(String... urls) {

            String urldisplay = urls[0];
            Bitmap mIcon = null;
            try {
                InputStream in = new java.net.URL(urldisplay).openStream();
                mIcon = BitmapFactory.decodeStream(in);

            } 
            catch (Exception e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }

            addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);

            return mIcon;
        }

        /* After decoding we update the view on the main UI. */
        protected void onPostExecute(Bitmap result) {
            bmImage.setImageBitmap(result);
        }
    }
}