Skip to content

Instantly share code, notes, and snippets.

@RareScrap
Created May 2, 2017 07:41
Show Gist options
  • Select an option

  • Save RareScrap/6a2d076bb9c87ac22f3bdfb47e8ae529 to your computer and use it in GitHub Desktop.

Select an option

Save RareScrap/6a2d076bb9c87ac22f3bdfb47e8ae529 to your computer and use it in GitHub Desktop.
Хорошие и плохие способы задания айдишников элементам RecyclerView
package com.webtrust.tennosushi.adapters;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.webtrust.tennosushi.R;
import com.webtrust.tennosushi.list_items.FoodItem;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Адаптер для списка меню, основанный на {@link RecyclerView.Adapter<FoodItemRecyclerViewAdapter.ViewHolder>}
* @author RareScrap
*/
public class FoodItemRecyclerViewAdapter extends RecyclerView.Adapter<FoodItemRecyclerViewAdapter.ViewHolder> {
/** Слушатель MainActivity, регистрируемые для каждого элемента списка */
private final View.OnClickListener clickListener;
/** Кэш для уже загруженных картинок (объектов Bitmap) */
private Map<String, Bitmap> bitmaps = new HashMap<>();
/** Список для хранения данных элементов RecyclerView */
private final List<FoodItem> items;
public View latestView; // Нет, сохранять последний показанны View не имеет смысла
/*
И нет, задавать ID через статик переменную тоже не нужно
Для этого в onBindViewHolder() есть position. Использование своей переменной приведет к тому,
что после назажатия Back и возвращения к FoodListFragment, собъется нумерация ID'шников
*/
public static int i = 0;
/**
* Конструктор, инициализирующий свои поля.
* @param items Набор элементов {@link FoodItem}, представляющий
* собой входные данные, которые необходимо отобразить
* @param clickListener Слушатель, который регистрирется для каждого элемента списка
*/
public FoodItemRecyclerViewAdapter(List<FoodItem> items, View.OnClickListener clickListener) {
this.items = items;
this.clickListener = clickListener;
}
/**
* Вложенный субкласс {@link RecyclerView.ViewHolder}. Используется для
* реализации паттерна View-Holder в контексте RecyclerView-логики
* повторного использования представлений.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
/** Ссылка на элемент GUI, представляющий название блюда */
public final TextView nameTextView;
/** Ссылка на элемент GUI, представляющий состав блюда */
public final TextView componentsTextView;
/** Ссылка на элемент GUI, представляющий цену блюда */
public final TextView priceTextView;
/** Ссылка на элемент GUI, представляющий картинку блюда */
public final ImageView foodImageView;
/**
* Конструктор, инициализирующий свои поля.
* @param itemView Представление одного элемента списка
* @param clickListener Слушатель для этого элемента
*/
public ViewHolder(View itemView, View.OnClickListener clickListener) {
super(itemView);
// Получение ссылок на элементы GUI в представлении
nameTextView = (TextView) itemView.findViewById(R.id.menu_text_card);
componentsTextView = (TextView) itemView.findViewById(R.id.components_card);
priceTextView = (TextView) itemView.findViewById(R.id.price_card);
foodImageView = (ImageView) itemView.findViewById(R.id.menu_image_card);
// Связывание слушателя с itemView
itemView.setOnClickListener(clickListener);
}
}
// TODO: Нихера не понял где что "упаковывается". Разобраться
/**
* Создает новый элемент списка и его объект ViewHolder.
*
* <p>
* Компонент RecyclerView вызывает метод onCreateViewHolder
* своего объекта RecyclerView.Adapter для
* заполнения макета каждого элемента RecyclerView
* и упаковки его в объект субкласса RecyclerView.ViewHolder с именем ViewHolder.
* Новый объект ViewHolder возвращается RecyclerView для отображения.
* </p>
*
* @param parent Объект субкласса {@link RecyclerView.ViewHolder} с представлениями View,
* в которых будут отображаться данные.
* @param viewType Значение int, представляющее позицию элемента в списке {@link RecyclerView}.
* @return Объект, отображающий данные в виде GUI-элемента списка.
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//ViewHolder.menuTextView = (TextView) convertView.findViewById(R.id.menu_text);
// Заполнение макета list_item
View view = LayoutInflater.from( parent.getContext() ).inflate(R.layout.food_card_list_item, parent, false);
// Плохая идея назначать вьюшкам ID ЗДЕСЬ и через setID()
//latestView = view;
//view.setId(i++);
// Создание ViewHolder для текущего элемента
return (new ViewHolder(view, clickListener));
}
/**
* Назначает данные элементам GUI.
* @param holder Объект GUI, содеращий поля, которые следует установить
* @param position Порядковый номер элемента {@link FoodItem}, который
* хранится в {@link FoodItemRecyclerViewAdapter#items}
*/
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Получение объекта FoodItem для заданной позиции ListView
FoodItem foodItem = items.get(position);
// Еще плохая идея - ограничиать присваивание ID'шников количеством элементов в списке
// при юзании своей переменной i. Это приведет к тому, что после нажатии на кнопку назад
// и возвратившись к середине списка, нумерация ID'шников начнется с 0 (при условии, что i
// обнуляется при каждом закрытии фрагмента FoodListFragment где-то с серединного элемента списка.
//if (i < 11) {
holder.itemView.setTag(i++);
//}
// Присвоении ID к View на основании его порядкого номера в списке
// Едиственно вернвый вариант
holder.itemView.setTag(position);
// Нецензурый способ проверить верность расставленных айдишников (при юзании
// своей переменной i гарантированно крашит прогу)
if (Integer.parseInt( holder.itemView.getTag().toString() ) != position) {
// все пошло по пизде
throw new NullPointerException("pizda");
}
// Назначения текста элементам GUI
holder.nameTextView.setText(foodItem.name);
holder.componentsTextView.setText(foodItem.components);
holder.priceTextView.setText( String.valueOf(foodItem.price) );
// Если картинка уже загружена, использовать ее; в противном случае загрузить в отдельном потоке
if (bitmaps.containsKey(foodItem.picURL)) {
// Дебажный кусок кода для отладчика
/*String a1 = foodItem.picURL;
Bitmap a2 = bitmaps.get(a1);
holder.foodImageView.setImageBitmap(a2);*/
holder.foodImageView.setImageBitmap(bitmaps.get( foodItem.picURL ));
}else { // Загрузить и вывести значок погодных условий
new LoadImageTask(holder.foodImageView).execute(foodItem.picURL);
}
}
/**
* Возвращение количества элементов, связываемых через адаптер.
* @return Количества элементов, связываемых через адаптер
*/
@Override
public int getItemCount() {
return items.size();
}
// Кажись, изменение imageView так же изменяет и аргумент, переданный в конструкторе LoadImageTask(). Таким образом, создается нечно вроде "ссылки"
// AsyncTask для загрузки изображения в отдельном потоке
/**
* Внутренний класс {@link AsyncTask}, предназначенный
* для загрузки изображения в отдельном потоке
* @author RareScrap
*/
private class LoadImageTask extends AsyncTask<String, Void, Bitmap> {
/** Сохраненная ссылка на {@link ImageView} для вывода изображения */
private ImageView imageView;
/**
* Конструктор, инициализирующий свои поля.
* Сохраняет ссылку на ImageView, куда следует поместить загруженный объект Bitmap.
* @param imageView Ссылка наImageView, куда следует поместить загруженный объект Bitmap.
*/
public LoadImageTask(ImageView imageView) {
this.imageView = imageView;
}
/**
* Загрузить изображение с данного URL адреса.
* @param params URL-адрес изображения
* @return Загруженное изображение
*/
@Override
protected Bitmap doInBackground(String... params) {
Bitmap bitmap = null;
HttpURLConnection connection = null;
try {
URL url = new URL(params[0]); // Создать URL для изображения
// Открыть объект HttpURLConnection, получить InputStream и загрузить изображение
connection = (HttpURLConnection) url.openConnection(); // Преобразование типа необходимо, потому что метод возвращает URLConnection
try (InputStream inputStream = connection.getInputStream()) {
bitmap = BitmapFactory.decodeStream(inputStream);
bitmaps.put(params[0], bitmap); // Кэширование
}
catch (Exception e) {
e.printStackTrace();
}
}
catch (Exception e) {
e.printStackTrace();
}
finally { // Этот участок кода будет выполняться независимо от того, какие исключения были возбуждены и перехвачены
connection.disconnect(); // Закрыть HttpURLConnection
}
return bitmap;
}
// Выполняется в потоке GUI вроде как для вывода изображения
/**
* Связывает изображение с элементом списка.
* @param bitmap Связываемое изображение
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment