Асинхронная загрузка рисунков в TextView

в 21:58, , рубрики: android, asynctask, img, textview, Разработка под android, метки: , , ,

Ни для кого не секрет, что TextView в Android поддерживает HTML-теги. Среди них поддерживается и тег , а для его обработки используется класс ImageGetter. И если с отображением локальных графических файлов никаких проблем не возникает, то при попытке программно подгрузить удаленный рисунок в TextView мы получаем NetworkOnMainThreadException на Android версии 3.0 и выше. Как выяснилось, в поисковиках информации для решения этой проблемы находится мало, да и далеко не все из предлагаемых решений работоспособны. Тем не менее, рабочее решение существует.
Решение заключается в повторной переустановке Spanned в TextView после загрузки рисунка. Загруженные рисунки мы будем кэшировать, чтобы не подгружать их всякий раз при отображении нашего поля (ваша реализация может отличаться). Таким образом, мы должны создать две реализации нашего ImageGetter:

static final Map<String, WeakReference<Drawable>> mDrawableCache = Collections.synchronizedMap(new WeakHashMap<String, WeakReference<Drawable>>());

@Override
public void onCreate(Bundle savedInstanceState) {
               //...
               //Создаем первый ImageGetter
	Html.ImageGetter igLoader = new Html.ImageGetter() {
		public Drawable getDrawable(String source) {
                                        //Если рисунок существует в кеше, то просто устанавливаем его и ничего не делаем дальше
			if (mDrawableCache.containsKey(source))
				return mDrawableCache.get(source).get();
                                        //В противном случае, скачиваем его из сети
			new ImageDownloadAsyncTask(source, message, messageView).execute();
                                        //Пока он скачивается устанавливаем пустой рисунок
			return new BitmapDrawable(getResources());
		}
	};
              //И сразу же используем его
	messageView.setText(Html.fromHtml(message, igLoader, null));
}

//Создаем второй ImageGetter.
//В нем возникнет потребность, когда файл загрузится
Html.ImageGetter igCached = new Html.ImageGetter() {
		public Drawable getDrawable(String source) {
                                        //Просто возвращаем наш рисунок из кеша
			if (mDrawableCache.containsKey(source))
				return mDrawableCache.get(source).get();
			return null;
		}
	};

Теперь, определяем наш класс от AsyncTask, который будет заниматься непосредственно скачиванием картинки:

class ImageDownloadAsyncTask extends AsyncTask<Void, Void, Void> {
	private String source;
	private String message;
	private TextView textView;

	public ImageDownloadAsyncTask(String source, String message,
			TextView textView) {
		this.source = source;
		this.message = message;
		this.textView = textView;
	}

	@Override
	protected Void doInBackground(Void... params) {
		if (!mDrawableCache.containsKey(source)) {
			try {
                                                     //Скачиваем картинку в наш кэш
				URL url = new URL(source);
				URLConnection connection = url.openConnection();
				InputStream is = connection.getInputStream();

				Drawable drawable = Drawable.createFromStream(is, "src");

				// Если нужно, чтобы рисунки не масштабировались,
				// закомментируйте строчку выше и расскомментируйте код
				// ниже.

				/*
				Bitmap bmp = BitmapFactory.decodeStream(is);
				DisplayMetrics dm =
				MainActivity.this.getResources().getDisplayMetrics();
				bmp.setDensity(dm.densityDpi); Drawable drawable=new
				BitmapDrawable(MainActivity.this.getResources(),bmp);
				*/

				is.close();

				drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
						drawable.getIntrinsicHeight());
				mDrawableCache.put(source, new WeakReference<Drawable>(
						drawable));
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}
		return null;
	}

	@Override
	protected void onPostExecute(Void result) {
                           // Переустанавливаем содержимое нашего поля
		textView.setText(Html.fromHtml(message, igCached, null));
	}
}

Конечно, нужно не забыть установить в манифесте разрешение android.permission.INTERNET.

Источник / Проект целиком

Автор: bartwell

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js