Lazy descarga imágenes en gridView

En mi aplicación, necesito descargar muchas imágenes de las URL y mostrarlas en una vista de cuadrícula. (Puede tener entre 1 y 200 imágenes). No quiero descargar todas las imágenes a la vez. Leí acerca de la descarga perezosa y mi pregunta es: ¿Puedo obtener solo una parte de Json, descargar las imágenes en un hilo diferente, y solo si el usuario se desplaza hacia abajo en la vista de cuadrícula, continuaré hacia las otras partes de Json, y ¿pronto?

Editar: Hola de nuevo. Quiero implementar selección múltiple en este gridView y tengo dificultades para implementar el código en el método getView () del adaptador. Este es el ejemplo que estoy usando: ejemplo . ¿Cómo puedo combinar este código en mi método 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); } } 

mi código getView ():

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

Así es como busco varias fotos en mi actividad. Puedes usar partes para que se ajusten a tu lógica. Lo uso para buscar imágenes de Facebook de un álbum. Entonces mis necesidades son (supongo) diferentes de tus necesidades. Pero de nuevo, la lógica puede ser útil para usted.

Nota: Esto será largo. 😉

Estas son las declaraciones globales para su uso a través de la actividad:

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

Este es el bloque de código que recupera el conjunto inicial de Imágenes:

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

Esto es para detectar cuándo el usuario se desplazó hasta el final y buscar un nuevo conjunto de imágenes:

 // 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(); } } } }); 

Y finalmente, así es como busco el siguiente conjunto de imágenes:

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

Y esta es la clase de ayuda para establecer y GET los datos recostackdos de las consultas anteriores:

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

Si también quieres el código del adapter , házmelo saber. Utilizo el método de carga diferida de Fedor en el adaptador.

Uf. Espero que alguno de estos te ayude. Si tiene más preguntas, siéntase libre de preguntar. 🙂

EDITAR: se agregó el código del adaptador:

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

EDITAR: Se agregaron los pasos para mostrar el progreso durante la carga:

Agregue un ProgressBar a usted XML donde tiene el GridView justo debajo de él. Juega con el peso si causa algún problema.

    

En su Java, declare Linearlayout linlaProgressBar como Global y onCreate() en onCreate() y configure su visibilidad como linlaProgressBar.setVisibility(View.GONE);

Y en onPreExecute() así:

 @Override protected void onPreExecute() { // SHOW THE BOTTOM PROGRESS BAR (SPINNER) WHILE LOADING MORE PHOTOS linlaProgressBar.setVisibility(View.VISIBLE); } 

Y finalmente agregue, esto en el onPostExecute()

 // HIDE THE BOTTOM PROGRESS BAR (SPINNER) AFTER LOADING MORE ALBUMS linlaProgressBar.setVisibility(View.GONE); 

Puede, eche un vistazo a Usar vistas de adaptador y GridView de la documentación de Android. Lo más importante es que el adaptador llame al método getView pasando solo la posición de las entradas que se muestran en pantalla, y solicitando diferentes posiciones cuando el usuario se desplaza.

La manera más fácil de hacerlo es descargar la imagen requerida en el método getView de su adaptador con AsyncTask .

Hay un ejemplo

Hablando por experiencia, es complicado lograr un desplazamiento suave (y capacidad de respuesta general) mientras se consume memoria razonablemente.

Sería una buena idea buscar primero las soluciones existentes, por ejemplo, comenzar aquí: carga lenta de imágenes en ListView

Terminamos con una solución patentada personalizada. Es un hilo de fondo que pone en cola las solicitudes de descarga y las descargas y almacena en caché en el almacenamiento externo solo las imágenes que todavía están visibles. Cuando llega una nueva imagen, la vista recibe una notificación y decide cuándo notificar al adaptador para su actualización.

También ahorra ancho de banda, lo cual era importante en algunos casos.

Encontré que la respuesta de IceMAN es muy útil, pero también recomiendo evitar el uso de dos AsyncTasks y puedes hacerlo fácilmente.

Debe crear un método universal para obtener los datos necesarios, donde puede hacer una condición if / else (como ejemplo):

  movies = fetchMovie.execute(sort).get(); if (movies == null) { movieList = new ArrayList<>(); } else if (addMovies) { movieList.addAll(movies); } else { movieList = movies; } 

addMovies es un booleano en tu método onScroll. En AsyncTask, proporciona la página actual en la URL de la consulta y listo: has reducido tu código 🙂