Lazy загружает изображения в gridView
В моем приложении мне нужно загрузить много изображений из URL-адресов и отобразить их в gridView. (Это может быть от 1 до 200 изображений). Я не хочу загружать все фотографии сразу. Я читал о ленивой загрузке, и мой вопрос: могу ли я получить только одну часть Json, загрузить изображения в другом streamе, и только если пользователь прокрутит вниз по gridView, я продолжу другие части Json и скоро?
Изменить: Привет. Я хочу реализовать multi select в этом gridView, и мне трудно реализовать код в методе getView () адаптера. Это пример, который я использую: пример . Как я могу объединить этот код в моем методе getView ():
public View getView(int position, View convertView, ViewGroup parent) { CheckableLayout l; ImageView i; if (convertView == null) { i = new ImageView(Grid3.this); i.setScaleType(ImageView.ScaleType.FIT_CENTER); i.setLayoutParams(new ViewGroup.LayoutParams(50, 50)); l = new CheckableLayout(Grid3.this); l.setLayoutParams(new GridView.LayoutParams(GridView.LayoutParams.WRAP_CONTENT, GridView.LayoutParams.WRAP_CONTENT)); l.addView(i); } else { l = (CheckableLayout) convertView; i = (ImageView) l.getChildAt(0); } ResolveInfo info = mApps.get(position); i.setImageDrawable(info.activityInfo.loadIcon(getPackageManager())); return l; } public class CheckableLayout extends FrameLayout implements Checkable { private boolean mChecked; public CheckableLayout(Context context) { super(context); } public void setChecked(boolean checked) { mChecked = checked; setBackgroundDrawable(checked ? getResources().getDrawable(R.drawable.blue) : null); } public boolean isChecked() { return mChecked; } public void toggle() { setChecked(!mChecked); } }
мой код getView ():
- Размер сбора счетчика Hibernate без инициализации
- Hibernate: org.hibernate.LazyInitializationException: не удалось инициализировать прокси - нет сеанса
- Ленивые загрузки изображений в UITableViewCell
- Что такое ленивая загрузка в Hibernate?
- Посмотреть пейджер в списке?
public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolder holder; View vi = convertView; if(convertView == null) { vi = inflater.inflate(com.egedsoft.instaprint.R.layout.item_clickable, null); holder = new ViewHolder(); holder.imgPhoto = (ImageView)vi.findViewById(com.egedsoft.instaprint.R.id.imageClickable); vi.setTag(holder); } else { holder = (ViewHolder) vi.getTag(); } if (!arrayUrls.get(position).getThumbnailUrl().isEmpty()){ imageLoader.DisplayImage(arrayUrls.get(position).getThumbnailUrl(), holder.imgPhoto); } return vi; }
- Entity Framework: как отключить ленивую загрузку для конкретного запроса?
- Как разрешить декларацию с двойной проверкой блокировки в Java?
- Entity Framework linq query Включает () несколько дочерних объектов
- Преобразование прокси-сервера Hibernate в объект реального объекта
- Как запрашивать данные для Primefaces dataTable с помощью ленивой загрузки и разбивки на страницы
Вот как я получаю несколько фотографий в своей деятельности. Вы можете использовать его части для своей логики. Я использую это для получения изображений Facebook из альбома. Поэтому мои потребности (я предполагаю) отличаются от ваших потребностей. Но опять-таки логика может быть вам полезна.
Примечание. Это будет длительным. 😉
Это глобальные декларации для использования через ACtivity:
// HOLD THE URL TO MAKE THE API CALL TO private String URL; // STORE THE PAGING URL private String pagingURL; // FLAG FOR CURRENT PAGE int current_page = 1; // BOOLEAN TO CHECK IF NEW FEEDS ARE LOADING Boolean loadingMore = true; Boolean stopLoadingData = false;
Это блок кода, который извлекает исходный набор изображений:
private class getPhotosData extends AsyncTask { @Override protected Void doInBackground(Void... arg0) { // CHANGE THE LOADING MORE STATUS TO PREVENT DUPLICATE CALLS FOR // MORE DATA WHILE LOADING A BATCH loadingMore = true; // SET THE INITIAL URL TO GET THE FIRST LOT OF ALBUMS URL = "https://graph.facebook.com/" + initialAlbumID + "/photos&access_token=" + Utility.mFacebook.getAccessToken() + "?limit=10"; try { HttpClient hc = new DefaultHttpClient(); HttpGet get = new HttpGet(URL); HttpResponse rp = hc.execute(get); if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { String queryAlbums = EntityUtils.toString(rp.getEntity()); JSONObject JOTemp = new JSONObject(queryAlbums); JSONArray JAPhotos = JOTemp.getJSONArray("data"); // IN MY CODE, I GET THE NEXT PAGE LINK HERE getPhotos photos; for (int i = 0; i < JAPhotos.length(); i++) { JSONObject JOPhotos = JAPhotos.getJSONObject(i); // Log.e("INDIVIDUAL ALBUMS", JOPhotos.toString()); if (JOPhotos.has("link")) { photos = new getPhotos(); // GET THE ALBUM ID if (JOPhotos.has("id")) { photos.setPhotoID(JOPhotos.getString("id")); } else { photos.setPhotoID(null); } // GET THE ALBUM NAME if (JOPhotos.has("name")) { photos.setPhotoName(JOPhotos.getString("name")); } else { photos.setPhotoName(null); } // GET THE ALBUM COVER PHOTO if (JOPhotos.has("picture")) { photos.setPhotoPicture(JOPhotos .getString("picture")); } else { photos.setPhotoPicture(null); } // GET THE PHOTO'S SOURCE if (JOPhotos.has("source")) { photos.setPhotoSource(JOPhotos .getString("source")); } else { photos.setPhotoSource(null); } arrPhotos.add(photos); } } } } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { // SET THE ADAPTER TO THE GRIDVIEW gridOfPhotos.setAdapter(adapter); // CHANGE THE LOADING MORE STATUS loadingMore = false; } }
Это нужно, чтобы определить, когда пользователь прокручивается до конца и извлекает новый набор изображений:
// ONSCROLLLISTENER gridOfPhotos.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int lastInScreen = firstVisibleItem + visibleItemCount; if ((lastInScreen == totalItemCount) && !(loadingMore)) { if (stopLoadingData == false) { // FETCH THE NEXT BATCH OF FEEDS new loadMorePhotos().execute(); } } } });
И, наконец, так я получаю следующий набор изображений:
private class loadMorePhotos extends AsyncTask { @Override protected Void doInBackground(Void... arg0) { // SET LOADING MORE "TRUE" loadingMore = true; // INCREMENT CURRENT PAGE current_page += 1; // Next page request URL = pagingURL; try { HttpClient hc = new DefaultHttpClient(); HttpGet get = new HttpGet(URL); HttpResponse rp = hc.execute(get); if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { String queryAlbums = EntityUtils.toString(rp.getEntity()); // Log.e("PAGED RESULT", queryAlbums); JSONObject JOTemp = new JSONObject(queryAlbums); JSONArray JAPhotos = JOTemp.getJSONArray("data"); // IN MY CODE, I GET THE NEXT PAGE LINK HERE getPhotos photos; for (int i = 0; i < JAPhotos.length(); i++) { JSONObject JOPhotos = JAPhotos.getJSONObject(i); // Log.e("INDIVIDUAL ALBUMS", JOPhotos.toString()); if (JOPhotos.has("link")) { photos = new getPhotos(); // GET THE ALBUM ID if (JOPhotos.has("id")) { photos.setPhotoID(JOPhotos.getString("id")); } else { photos.setPhotoID(null); } // GET THE ALBUM NAME if (JOPhotos.has("name")) { photos.setPhotoName(JOPhotos.getString("name")); } else { photos.setPhotoName(null); } // GET THE ALBUM COVER PHOTO if (JOPhotos.has("picture")) { photos.setPhotoPicture(JOPhotos .getString("picture")); } else { photos.setPhotoPicture(null); } // GET THE ALBUM'S PHOTO COUNT if (JOPhotos.has("source")) { photos.setPhotoSource(JOPhotos .getString("source")); } else { photos.setPhotoSource(null); } arrPhotos.add(photos); } } } } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { // get listview current position - used to maintain scroll position int currentPosition = gridOfPhotos.getFirstVisiblePosition(); // APPEND NEW DATA TO THE ARRAYLIST AND SET THE ADAPTER TO THE // LISTVIEW adapter = new PhotosAdapter(Photos.this, arrPhotos); gridOfPhotos.setAdapter(adapter); // Setting new scroll position gridOfPhotos.setSelection(currentPosition + 1); // SET LOADINGMORE "FALSE" AFTER ADDING NEW FEEDS TO THE EXISTING // LIST loadingMore = false; } }
И это вспомогательный class для SET
и GET
данные, собранные из запросов выше:
public class getPhotos { String PhotoID; String PhotoName; String PhotoPicture; String PhotoSource; // SET THE PHOTO ID public void setPhotoID(String PhotoID) { this.PhotoID = PhotoID; } // GET THE PHOTO ID public String getPhotoID() { return PhotoID; } // SET THE PHOTO NAME public void setPhotoName(String PhotoName) { this.PhotoName = PhotoName; } // GET THE PHOTO NAME public String getPhotoName() { return PhotoName; } // SET THE PHOTO PICTURE public void setPhotoPicture(String PhotoPicture) { this.PhotoPicture = PhotoPicture; } // GET THE PHOTO PICTURE public String getPhotoPicture() { return PhotoPicture; } // SET THE PHOTO SOURCE public void setPhotoSource(String PhotoSource) { this.PhotoSource = PhotoSource; } // GET THE PHOTO SOURCE public String getPhotoSource() { return PhotoSource; } }
Если вам также нужен код adapter
, сообщите мне. Я использую метод Lazy Loading Fedor в адаптере.
Уф. Надеюсь, что это поможет. Если у вас есть дополнительные вопросы, не стесняйтесь спрашивать. 🙂
EDIT: добавлен код адаптера:
public class PhotosAdapter extends BaseAdapter { private Activity activity; ArrayList arrayPhotos; private static LayoutInflater inflater = null; ImageLoader imageLoader; public PhotosAdapter(Activity a, ArrayList arrPhotos) { activity = a; arrayPhotos = arrPhotos; inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); imageLoader = new ImageLoader(activity.getApplicationContext()); } public int getCount() { return arrayPhotos.size(); } public Object getItem(int position) { return arrayPhotos.get(position); } public long getItemId(int position) { return position; } public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; View vi = convertView; if(convertView == null) { vi = inflater.inflate(R.layout.photos_item, null); holder = new ViewHolder(); holder.imgPhoto = (ImageView)vi.findViewById(R.id.grid_item_image); vi.setTag(holder); } else { holder = (ViewHolder) vi.getTag(); } if (arrayPhotos.get(position).getPhotoPicture() != null){ imageLoader.DisplayImage(arrayPhotos.get(position).getPhotoPicture(), holder.imgPhoto); } return vi; } static class ViewHolder { ImageView imgPhoto; } }
EDIT: Добавлены шаги для отображения «Прогресс» при загрузке:
Добавьте ProgressBar в XML, где у вас есть GridView прямо под ним. Играйте с весом, если это вызывает какие-либо проблемы.
В вашей Java объявите Linearlayout linlaProgressBar
как Global и Linearlayout linlaProgressBar
его в onCreate()
и установите его видимость как linlaProgressBar.setVisibility(View.GONE);
И в onPreExecute()
используйте его вот так:
@Override protected void onPreExecute() { // SHOW THE BOTTOM PROGRESS BAR (SPINNER) WHILE LOADING MORE PHOTOS linlaProgressBar.setVisibility(View.VISIBLE); }
И, наконец, добавьте это в onPostExecute()
// HIDE THE BOTTOM PROGRESS BAR (SPINNER) AFTER LOADING MORE ALBUMS linlaProgressBar.setVisibility(View.GONE);
Вы можете взглянуть на « Использование адаптивных представлений» и « GridView» из документации Android. Самое главное, что адаптер вызывает метод getView
передающий только позицию записей, отображаемых на экране, и запрашивает разные позиции при getView
пользователя.
Самый простой способ – загрузить требуемое изображение в методе getView
вашего адаптера с помощью AsyncTask .
Существует пример
Говоря по опыту, сложно добиться плавной прокрутки (и полной отзывчивости) при разумном потреблении памяти.
Было бы неплохо сначала искать существующие решения, например, начать здесь: ленивая загрузка изображений в ListView
Мы закончили с собственными собственными решениями. Это фоновый stream, который ставит запросы на загрузку и загружает и кэширует на внешнем хранилище только изображения, которые все еще видны. Когда приходит новое изображение, уведомление получает уведомление и решает, когда следует уведомить адаптер для обновления.
Он также экономит полосу пропускания, что было важно в некоторых случаях.
Я нашел ответ IceMAN очень полезным, но я также рекомендую избегать использования двух AsyncTasks, и вы можете сделать это легко.
Вам нужно создать универсальный метод для извлечения необходимых данных, где вы можете сделать условие if / else (в качестве примера):
movies = fetchMovie.execute(sort).get(); if (movies == null) { movieList = new ArrayList<>(); } else if (addMovies) { movieList.addAll(movies); } else { movieList = movies; }
addMovies является логическим в методе onScroll. В AsyncTask укажите текущую страницу в URL-адресе запроса и voila – вы уменьшили свой код 🙂