Як ліниво завантажувати зображення в ListView в Android


1930

Я використовую a ListViewдля відображення деяких зображень та підписів, пов’язаних із цими зображеннями. Я отримую зображення з Інтернету. Чи є спосіб ледачого завантаження зображень, тому під час відображення тексту інтерфейс користувача не блокується, а зображення відображаються під час завантаження?

Загальна кількість зображень не фіксована.


10
Ви можете використовувати AsyncImageView GreenDroid . Просто зателефонуйте setUrl.
Паскаль Дімасімо

7
Я ним користувався. Це чудова реалізація. Погана новина про те, що AsyncImageView є частиною великого проекту GreenDroid, завдяки якому ваша програма збільшується навіть у тому випадку, якщо все, що вам потрібно, - AsyncImageView. Крім того, здається, проект GreenDroid не оновлюється з 2011 року
borisstr

5
Ви навіть можете спробувати цю бібліотеку: Android-http-image-manager, який, на мою думку, є найкращим для асинхронного завантаження зображень.
Ritesh Kumar Dubey

34
Просто використовуйте Пікассо, він все зробить сам. 'Picasso.with (yourContext) .load (img src / path / dravable here) .into (imageView, тобто ваша мета);' Це воно!
Ануй Шарма

6
спробуйте скористатися: github.com/nostra13/Android-Universal-Image-Loader , ця бібліотека дуже швидка та ефективна для ледачого завантаження та кешування зображень
krunal patel

Відповіді:


1086

Ось що я створив для зберігання зображень, які наразі відображає моя програма. Зверніть увагу, що використовуваний тут об'єкт "Журнал" - це моя спеціальна обгортка навколо остаточного класу Журналу в 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();
    }
}

104
Я думаю, ви повинні використовувати SoftReferences, щоб ваша програма ніколи не викликала OutOfMemoryException. Оскільки GC може очистити софтреференції, коли розмір купи збільшується ... ви можете керувати власним поколінням, як через кілька секунд, ви можете помістити свої зображення до цього списку, а перед завантаженням слід перевірити, чи існує зображення, то не завантажуйте його знову, а не збирайте це з цього списку, а також повернення його до вашого списку програмного забезпечення, і коли-небудь ви можете очистити ваш жорсткий список :)
AZ_

36
Проект "Полиці Google" - прекрасний приклад, подивіться, як вони робили code.google.com/p/shelves
AZ_

12
Ви не пропустите повернення, коли dravableMap містить зображення ... без запуску нитки для отримання?
Каруссел

5
Цей код має кілька проблем. По-перше, ви повинні кешувати Drawables, що спричинить витік пам'яті: stackoverflow.com/questions/7648740/… . По-друге, сам кеш ніколи не очищається, тому він зростатиме назавжди, це ще одна витік пам'яті.
Satur9nine


1024

Я склав просту демонстрацію лінивого списку (розміщеного на GitHub) із зображеннями.

Основне використання

ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView); 

Не забудьте додати наступні дозволи до свого AndroidManifest.xml:

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please

створити лише один екземпляр ImageLoader і повторно використовувати його навколо своєї програми. Таким чином кешування зображень буде набагато ефективнішим.

Можливо, комусь це стане в нагоді. Він завантажує зображення у фонову нитку. Зображення кешуються на SD-картці та в пам'яті. Реалізація кешу дуже проста і достатньо для демонстрації. Я декодую зображення за допомогою inSampleSize, щоб зменшити споживання пам'яті. Я також намагаюся правильно обробляти перероблені подання.

Alt текст


7
Дякую, я використовую трохи модифіковану версію вашого коду майже скрізь у своєму проекті: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/…
shaman.sir

3
Хтось розглядав код Гілла Дебунна як альтернативу. Я бачу дві великі відмінності: 1) в кешування пам’яті проти SD-карти та 2) використання AsyncTasks замість пулу потоків. android-developers.blogspot.com/2010/07/…
Річард

4
Існує помилка, іноді її запускають: 10-13 09: 58: 46.738: ERROR / AndroidRuntime (24250): Uncaught обробник: основний потік, що виходить із-за невдалого винятку 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250) : java.lang.ArrayIndexOutOfBoundsException: Індекс масиву поза діапазоном: 0 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): на java.util.Vector.elementAt (Vector.javaната31) 10-13 09: 58: 46.768: ПОМИЛКА / AndroidRuntime (24250): на java.util.Vector.get (Vector.java:245) 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): на com.my.app.image .ImageLoader $ PhotosQueue.Clean (ImageLoader.java:91)
Mikooos

64
Я хотів би додати для новачків, як я, що якщо ви хочете додати цей код до власного проекту, ви повинні додати дозволи в зовнішньому кеші до маніфесту. Дякую
Адам

2
@BruceHill В останньому вихідному коді немає жодної фотографії. Ви, здається, використовуєте якусь дуже стару версію.
Федір

551

Я рекомендую пристрій з відкритим кодом Universal Loader Image . Спочатку він базується на проекті Федора Власова LazyList і з того часу значно покращився.

  • Завантаження багатопотокових зображень
  • Можливість широкої настройки конфігурації ImageLoader (виконавці потоків, завантажувач, декодер, кеш пам'яті та диска, параметри зображення на дисплеї та інші)
  • Можливість кешування зображень у пам'яті та / або у файловій системі файлу пристрою (або SD-картки)
  • Можливість "прослуховувати" процес завантаження
  • Можливість налаштувати кожен графічний дзвінок із окремими параметрами
  • Підтримка віджетів
  • Підтримка Android 2.0+


16
якщо ви шукаєте чудовий код "завантаження зображення", який його творець вважає і підтримує як свою дорогу дитину (не лише одноразове рішення), ви просто знайшли це; великі злети Nostra
AndroidGecko

Дійсно чудова робота, але це трохи відстає від мого списку. Якщо ви можете сказати найкращу збірку "ImageLoader" для виконання, або, можливо, це тому, що "DisplayImageOptions".
диніго

Мені дуже подобається цей gridview, але я не можу використовувати його для свого підручника. Я початківець андроїд. Я роблю заявку про gridview, але моя програма була targetSdkVersion 15, тому мені потрібно використовувати Asynctask для процесу у фоновому потоці. Раніше я використовував цю сітку, але вона не працює. як я можу використовувати його в targetSdkVersion 15?
Конгкеа

Можна створити окреме запитання з тегом [Universal-image-loader]
nostra13,

3
Прочитавши офіційні документи Android і спробувавши зробити цей матеріал самостійно, я впевнений, що ця бібліотека закінчується всім завантаженням зображень. Неможливо внести достатню пропозицію.
Основна

158

Multithreading for Performance , навчальний посібник Жиля Дебунна.

Це з блогу розробників Android. Запропонований код використовує:

  • AsyncTasks.
  • Тверда, обмежений розмір, FIFO cache.
  • М’який garbage collectкеш , що легко лежить.
  • Заповнювач Drawable в той час як ви завантажуєте.

введіть тут опис зображення


10
Він прекрасно працює і в 2.1. Просто не використовуйте AndroidHttpClient.
Thomas Ahle

2
@ thomas-ahle Дякую, я бачив, що AndroidHttpClient помилявся в 2.1, оскільки він реалізований з 2.2, але насправді не намагався знайти щось інше, щоб замінити його.
Адінія

4
@Adina Ви маєте рацію, я забув. Однак у рецепті немає нічого, що не може бути так добре, як це можна зробити з звичайним HttpClient.
Thomas Ahle

Я чув у кількох місцях, що Google не рекомендує м'яких посилань, тому що ядро ​​андроїда дуже хоче збирати ці посилання порівняно з попередніми версіями системи.
Мухаммед Ахмед Абуталіб

108

Оновлення: Зауважте, що ця відповідь зараз досить неефективна. Garbage Collector діє агресивно на SoftReference та WeakReference, тому цей код НЕ підходить для нових програм. (Натомість спробуйте бібліотеки, такі як Universal Image Loader, запропоновані в інших відповідях.)

Дякую Джеймсу за код та Бао-Лонгу за пропозицію використовувати SoftReference. Я реалізував зміни SoftReference в коді Джеймса. На жаль, SoftReferences спричинив занадто швидке збирання моїх зображень. У моєму випадку все було добре без програм SoftReference, оскільки розмір мого списку обмежений, а мої зображення невеликі.

З року тому обговорювались програми SoftReferences щодо гугл-груп: посилання на нитку . Як рішення занадто раннього збирання сміття, вони пропонують можливість вручну встановити розмір купівлі VM за допомогою Dalvik.system.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();
}

3
ви можете створити такі покоління, як жорстке покоління та м'яке покоління. Ви можете виправити кеш-час, щоб очистити всі зображення, до яких не було доступно за 3 секунди .. Ви можете переглянути проект полках Google
AZ_

developer.android.com/reference/java/lang/ref/… SoftReference doc має примітку про кешування, див. розділ "Уникати м'яких посилань для кешування". Більшість додатків повинні використовувати android.util.LruCache замість м'яких посилань.
вокілам

Я захоплююсь вашим кодом, але зараз у новій Android O / S відбувається "агресивне" збирання сміття. Тримати слабку орієнтир для мене немає сенсу.
j2emanue

@ j2emanue Ви маєте рацію, як я намагався вказати у верхній частині своєї відповіді: SoftReferences сміття збирається занадто швидко. Я спробую відредагувати цю відповідь, щоб зробити це ще зрозумілішим.
TalkLittle

96

Пікассо

Використовуйте бібліотеку Пікассо Джейка Уартона. (Ідеальна бібліотека ImageLoading формує розробника 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 полягає в тому, щоб прокручувати будь-який вид списку зображень максимально гладко та швидко, але Glide також ефективний майже для будь-якого випадку, коли потрібно отримати, змінити розмір та відобразити віддалене зображення.

Бібліотека завантаження зображення ковзання

Фреска у Facebook

Fresco - потужна система відображення зображень в додатках для Android.

Fresco піклується про завантаження та показ зображень, тому вам не доведеться цього робити. Він завантажує зображення з мережі, локального сховища чи локальних ресурсів та відображатиме заповнювач, поки зображення не надійде. Він має два рівні кешу; один у пам’яті, а інший - у внутрішній пам’яті.

Фреска Гітуб

В Android 4.x і новіших версіях Fresco розміщує зображення в спеціальній області пам'яті Android. Це дозволяє вашій програмі працювати швидше - і страждати від страшного OutOfMemoryError набагато рідше.

Документація фрески


Пікассо - бібліотека, розроблена компанією Square
LordRaydenMK

82

Високопродуктивний навантажувач - вивчивши запропоновані тут методи, я використав рішення Бена з деякими змінами -

  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;
}
}

Чудово працює! До речі, в назві класу є помилка друку.
Маллінз

5
У випадку, якщо це економить комусь інший час: 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;
Майкл Рід

Велике спасибі, це приємна реалізація. Я також розміщую інший заповнювач, коли завантажується файл, щоб користувач міг отримати зворотній зв'язок.
Хуан Ернандес

Також я думаю, що краще використовувати чергу LIFO у executorService (mThreadPool) замість FIFO за замовчуванням, тому останні запитувані зображення (які, мабуть, є видимими) завантажуються першими. Дивіться stackoverflow.com/questions/4620061/how-to-create-lifo-executor
Хуан Ернандес

9
@MichaelReed, якщо ви користувач Eclipse, рекомендую використовувати Ctrl-Shift-O (це літера O, а не число 0). Він автоматизує процес додавання імпорту та організовує їх для вас. Якщо ви на Mac, використовуйте Command-Shift-O замість цього.
SilithCrowe

78

Я дотримувався цієї програми Android Training, і думаю, що це прекрасна робота в завантаженні зображень, не блокуючи основний інтерфейс користувача. Він також обробляє кешування і вирішує прокручування багатьох зображень: Ефективне завантаження великих растрових зображень


Вибачте, я вказав лише на один клас для додатка Google IO (і я занадто пізно редагував). Ви дійсно повинні вивчити всі їхні програми завантаження та кешування зображень, які ви можете знайти в тому ж пакеті, що і клас кешу .
mkuech

Хто-небудь порекомендує захопити файли DiskLruCache, Image * .java з папки утиліт програми iosched, щоб допомогти з завантаженням та кешуванням зображень для перегляду списку? Я маю на увазі, що, безумовно, варто дотримуватися онлайн-посібників для розробників на цю тему, але ці класи (з iosched) йдуть трохи далі за схемою.
Гаутам

65

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

Використовуйте Gradle:

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

Всього один рядок коду!

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

2. Glide Бібліотека завантаження та кешування зображень для 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. Фреско піклується про завантаження та показ зображень, тому вам не доведеться цього робити.

Початок роботи з фрескою


Цей підручник може вам більше допомогти для PICASOO: - androidtutorialshub.com/… та GLIDE: - androidtutorialshub.com/…
lalit vasan

52

Я написав підручник, в якому пояснюється, як зробити ледачу завантаження зображень у перегляді списку. Я детально розглядаю питання переробки та одночасності. Я також використовую пул фіксованих ниток, щоб запобігти нерестування багатьох ниток.

Ледаче завантаження зображень у підручнику Listview


41

Я це роблю, запускаючи потік для завантаження зображень у фоновому режимі та передаючи йому зворотний виклик для кожного елемента списку. Після закінчення завантаження зображення він викликає зворотний дзвінок, який оновлює подання для елемента списку.

Цей метод працює не дуже добре, якщо ви переробляєте подання.


використання нитки для кожного зображення - це підхід, який я також використовую. Якщо ви відокремили модель від перегляду, ви можете зберегти модель поза діяльністю (як, наприклад, у вашому класі програми), щоб зберегти кеш. Будьте обачні, якщо у вас багато зображень.
Джеймс А Вілсон

Чи можете ви, будь ласка, докладно. Я новачок у розробці андроїдів. Дякую за підказки, проте
lostInTransit

14
Запуск нової нитки для кожного зображення не є ефективним рішенням. Ви можете отримати багато потоків у пам'яті та заморозити інтерфейс користувача.
Федір

Федір, погодився, я зазвичай використовую чергу та пул потоків, це найкращий спосіб перейти до imo.
jasonhudgins

32

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

В основному цей ImageDownloader - це ваша відповідь (оскільки він охоплює більшість ваших вимог). Деякі ви також можете реалізувати в цьому.


2
Клас ImageDownloader не отримують виконав: см рішення нижче code.google.com/p/parleys-android-nextgen/issues/detail?id=1
Sam

29

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

  • Відкритий вихідний код, але очолив Jake Whartonз ActionBarSherlock слави.
  • Асинхронно завантажуйте зображення з мережевих чи програмних ресурсів одним рядком коду
  • Автоматичне ListViewвиявлення
  • Автоматичне кешування диска та пам'яті
  • Може робити власні перетворення
  • Багато налаштованих варіантів
  • Супер простий API
  • Часто оновлюється

29

Я використовую NetworkImageView з нової бібліотеки Android Volley com.android.volley.toolbox.NetworkImageView, і це, здається, працює досить добре. Мабуть, це той самий погляд, який використовується в Google Play та інших нових програмах Google. Однозначно варто перевірити.


1
Я думаю, що це найкраще рішення - інші відповіді дуже давні - залп дійсно швидкий і поєднується з Джейком Warthons disklrucache - це ідеальне рішення - я спробував багато інших, але не один стабільний і швидкий, як залп
Олександр Сидіков Pfeif

26

Ну, час завантаження зображення з Інтернету має багато рішень. Ви також можете використовувати бібліотеку 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;
}

Це має вирішити вашу проблему з ледачим завантаженням.


Мені приємно працювати, але потрібен файл Jar, щоб включити його у свій проект. Ви можете завантажити цей файл JAR звідси AQuery androidAQuery = новий AQuery (це); посилання: code.google.com/archive/p/android-query/downloads
Селім Раза

25

Я думаю, що це питання дуже популярне серед розробників Android, і є багато таких бібліотек, які заявляють, що вирішити цю проблему, але лише деякі з них, здається, знаходяться на шляху відбиття. AQuery - одна з таких бібліотек, але вона краща за більшість із них у всіх аспектах і її варто спробувати.


22

Ви повинні спробувати цей універсальний навантажувач найкраще. Я використовую це після багатьох RnD на ледачому завантаженні.

Універсальний завантажувач зображень

Особливості

  • Завантаження багатопотокових зображень (асинхронізація чи синхронізація)
  • Широке налаштування конфігурації ImageLoader (виконавці потоків, завантажувач, декодер, кеш пам’яті та диска, параметри відображення зображення тощо)
  • Безліч параметрів налаштування для кожного дзвінка з відображуваним зображенням (зображення заглушки, перемикання кешування, параметри декодування, обробка та відображення растрових зображень тощо)
  • Кешування зображень у пам'яті та / або на диску (файлова система пристрою або SD-карта)
  • Процес завантаження прослуховування (включаючи хід завантаження)

Підтримка Android 2.0+

введіть тут опис зображення


20

Погляньте на Shutterbug , легкий порт WebWebImage Applidium (приємна бібліотека на iOS) для Android. Він підтримує асинхронний кешування, зберігає невдалі URL-адреси, добре обробляє паралельність, а також є корисні підкласи.

Запрошення на виклик (і звіти про помилки) також вітаються!


16

У DroidParts є ImageFetcher, який вимагає нульової конфігурації для початку роботи.

  • Використання диска і в пам'яті Найменш недавно використовувалися (LRU) кеша.
  • Ефективно розшифровує зображення.
  • Підтримує зміну растрових зображень у фоновому потоці.
  • Має просте перехресне вицвітання.
  • Має зворотний зворотний зворот завантаження зображення.

Клон DroidPartsGram для прикладу:

Введіть тут опис зображення


Привіт, я переглянув приклади коду, але у мене виникають проблеми із використанням ImageFetcher з ArrayAdapter, чи не заперечуєте ви, дивлячись на моє питання? stackoverflow.com/questions/21089147/… Дякую =]
masha

16

Лише короткий підказ для того, хто нерішучий щодо того, яку бібліотеку використовувати для ледачих завантажень зображень:

Існує чотири основні способи.

  1. Зроби сам => Не найкраще рішення, але для кількох зображень, і якщо ти хочеш пройти без зайвих проблем з використанням інших бібліотек

  2. Бібліотека ленівого завантаження Воллі => Від хлопців на android. Це добре, і все, але погано задокументовано, а отже, це проблема у використанні.

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

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


15

У Novoda також є чудова бібліотека для завантаження зображень і багато програм, таких як Songkick, Podio, SecretDJ і ImageSearch, використовують свою бібліотеку.

Їх бібліотека розміщується тут, на Github, і у них є досить активний трекер проблем . Їх проект, здається, також досить активний, тому що на момент написання цієї відповіді було зроблено понад 300 комісій.


1
Насправді Novoda - це чудова бібліотека, але ... іноді вам не потрібна величезна бібліотека і лише простий підхід до рішення. Ось чому LazyList в Github настільки хороший, якщо у ваших програмах відображається лише зображення у listView і не є головною особливістю вашого додатка, просто інша діяльність, яку я вважаю за краще використовувати щось легше. В іншому випадку, якщо ви знаєте, що вам доведеться використовувати часто і є частиною основної, спробуйте Novoda.
Nicolas Jafelle

13

Перевір мою вилку LazyList . В основному, я вдосконалюю LazyList, затримуючи виклик ImageView і створюючи два методи:

  1. Коли потрібно поставити щось на кшталт "Завантаження зображення ..."
  2. Коли потрібно показати завантажене зображення.

Я також покращив ImageLoader, застосувавши синглтон у цьому об’єкті.


12

Якщо ви хочете показати макет Shimmer, як Facebook, для цього є офіційна facebook-бібліотека. 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'

Ось як це виглядає.Світловий скріншот Android


11

Усі вищезгадані кодекси мають свою цінність, але з мого особистого досвіду просто спробуйте з Пікассо.

Пікассо - це бібліотека спеціально для цієї мети, фактично вона керуватиме кешем та всіма іншими мережевими операціями автоматично. Вам потрібно буде додати бібліотеку у свій проект і просто написати єдиний рядок коду, щоб завантажити зображення з віддаленої URL-адреси.

Завітайте сюди: http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149


9

Використовуйте бібліотеку ковзання. Він працював на мене і буде працювати і для вашого коду. Він працює як для зображень, так і для 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);
}

7

Я можу порекомендувати інший спосіб, який працює як шарм: 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);

Це дуже швидко і точно, і за допомогою цього ви можете знайти ще багато функцій, таких як анімація під час завантаження, отримання растрової карти (за потреби) тощо.


7

Дайте Aquery спробувати. Він має надзвичайно прості методи для асинхронного завантаження та кешування зображень.



4
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;
 }
}

4

У мене була ця проблема і реалізовано lruCache. Я вважаю, що вам потрібен API 12 і вище або використовувати бібліотеку v4 сумісності. lurCache швидка пам'ять, але вона також має бюджет, так що якщо ви турбуєтеся про те, що ви можете використовувати diskcache ... Це все описано в Caching Bitmaps .

Тепер я надаю свою реалізацію, яка є сингл, яку я дзвоню з будь-якого місця:

//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);
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.