entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

3votos

Problemas al cargar listView (internet) con imagenes

Buenas, me he dado cuenta que en una aplicación que tengo, en la que obtengo los datos a través de una URL y desde Android los muestro en un listview (personalizado), pues al tener como unas 10 o 12 noticias más o menos tengo un problema que pienso que sobrecarga la pila y cuando intento acceder a una noticia en concreto en lugar de entrar me vuelve al inicio de la aplicación. Sé que es problema de las imágenes porque he probado a no mostrarlas y lo hace todo bien.

Pues buscando en internet, me encontré con esto: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap

Necesito implementar estos 2 métodos:

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

Pero no tengo muy claro como hacerlo porque en el ejemplo lo hacen con una imagen que están en drawable y en mi caso la imagen la obtengo de un arrayList y no sé que datos pasar ni qué tengo que modificar del método.
¿Cómo puedo resolver este problema que tengo con las imágenes?
Muchas gracias y un saludo

0voto

white comentado

Como inflas la vista en tu adaptador? cual es el código con el que muestras la imagen? cuando cargas la url de las imagenes lo haces asincronicamente?

0voto

danielreales7 comentado

Buenas, no lo estoy haciendo asíncronamente. Te comento como lo hago todo.

class ListViewItem {
    public String id;
    public String contenido;
    public Bitmap ThumbnailResource;
    public int ThumbnailResourceNull;
    public int MeGusta;
    public String numeroMegusta;
    public String Title;
    public String SubTitle;
    public String URLImagen;
}

public void getData(){
    String result = "";
    InputStream isr = null;
    List<ListViewItem> items = new ArrayList<Noticias.ListViewItem>();
    try{
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("http://www.xxx.com/xxx/noticias.php"); //YOUR PHP SCRIPT ADDRESS 
        HttpResponse response = httpclient.execute(httppost);
        HttpEntity entity = response.getEntity();
        isr = entity.getContent();
    }
    catch(Exception e){
            e.printStackTrace();
    }
    //convert response to string
    try{
        BufferedReader reader = new BufferedReader(new InputStreamReader(isr,"iso-8859-1"),8);
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
        }
        isr.close();

        result=sb.toString();
    }
    catch(Exception e){
        e.printStackTrace();
    }

    //parse json data
    try {
       String s = "";
       JSONArray jArray = new JSONArray(result);
       for(int i=0; i<jArray.length();i++){
           final JSONObject json = jArray.getJSONObject(i);
           final String imagenURL = json.getString("imagen");
           bitmap = getBitmapFromURL(imagenURL);
           if(imagenURL.equals("null")){
               items.add(new ListViewItem(){{
                    id = json.getString("id_noticia");
                    ThumbnailResourceNull = R.drawable.launcher_icon;
                    Title = json.getString("titulo");
                    contenido = json.getString("contenido");
                    URLImagen = json.getString("imagen");
                    SubTitle = json.getString("f_alta");
                    MeGusta = R.drawable.star;
                    numeroMegusta = json.getString("favoritos");
                }});
           }else{
               items.add(new ListViewItem(){{
                    id = json.getString("id_noticia");
                    ThumbnailResource = bitmap;
                    Title = json.getString("titulo");
                    contenido = json.getString("contenido");
                    URLImagen = json.getString("imagen");
                    SubTitle = json.getString("f_alta");
                    MeGusta = R.drawable.star;
                    numeroMegusta = json.getString("favoritos");
                }});
           }
       }

        final CustomListViewAdapterNoticias adapter = new CustomListViewAdapterNoticias(this, items);
        listNoticias.setAdapter(adapter);

        listNoticias.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                // TODO Auto-generated method stub
                ListViewItem chapter = adapter.getCodeLearnChapter(position);

                Intent actividadNoticiaID = new Intent(getApplicationContext(), NoticiaId.class );
                actividadNoticiaID.putExtra("id", chapter.id);
                actividadNoticiaID.putExtra("titulo", chapter.Title);
                actividadNoticiaID.putExtra("contenido", chapter.contenido);
                actividadNoticiaID.putExtra("imagen", chapter.ThumbnailResource);
                actividadNoticiaID.putExtra("imagenNula", chapter.ThumbnailResourceNull);
                actividadNoticiaID.putExtra("urlImagen", chapter.URLImagen);
                actividadNoticiaID.putExtra("fecha", chapter.SubTitle);
                actividadNoticiaID.putExtra("numeroMeGusta", chapter.numeroMegusta);
                startActivity(actividadNoticiaID);        

                //Toast.makeText(getApplicationContext(), chapter.id, Toast.LENGTH_SHORT).show();
            }

        });

       } catch (Exception e) {
        // TODO: handle exception
           e.printStackTrace();
       }

}

Tengo ese método en el que después lo llamo desde el onCreate y el adaptador del listView es donde pongo la imagen, que contiene lo siguiente:

public View getView(final int position, View convertView, ViewGroup parent) {  
    // TODO Auto-generated method stub  
    ListViewItem item = items.get(position);

    View vi=convertView;

    if(convertView==null)
        vi = inflater.inflate(R.layout.item_row_noticias, null);

    ImageView imgThumbnail = (ImageView)vi.findViewById(R.id.imgThumbnail);
    TextView txtTitle = (TextView)vi.findViewById(R.id.txtTitle);
    TextView txtSubtitle = (TextView)vi.findViewById(R.id.txtTitle2);
    ImageView estrella = (ImageView)vi.findViewById(R.id.megusta);
    TextView numeroMeGusta = (TextView)vi.findViewById(R.id.txtMegusta);

    imgThumbnail.setImageBitmap(item.ThumbnailResource);
    imgThumbnail.setImageResource(item.ThumbnailResourceNull);
    txtTitle.setText(item.Title);
    txtSubtitle.setText(item.SubTitle);
    estrella.setImageResource(item.MeGusta);
    numeroMeGusta.setText(item.numeroMegusta);

    return vi;  
}

1 Respuesta

3votos

white Puntos75880

Intenta usar una tarea asincrónica en la carga de imagenes.

antes del metodo:

public View getView(final int position, View convertView, ViewGroup parent)

agrega esta clase:

private class getImageUrl extends AsyncTask<String, Void, Bitmap> {

    ImageView image;

    public getImageUrl(ImageView image) {
        this.image = image;
    }

    protected Bitmap doInBackground(String... urls) {
        String url = urls[0];
        Bitmap img = null;
        try {
            InputStream in = new java.net.URL(url).openStream();
            img = BitmapFactory.decodeStream(in);
        } catch (Exception e) {
            Log.e("Error", e.getMessage());
        }
        return img;
    }

    protected void onPostExecute(Bitmap result) {
        image.setImageBitmap(result);
    }
}

en lugar de estas dos lineas:

imgThumbnail.setImageBitmap(item.ThumbnailResource);
imgThumbnail.setImageResource(item.ThumbnailResourceNull);

agrega esto:

new getImageUrl(imgThumbnail).execute(item.URLImagen);

remueve estas lineas:

bitmap = getBitmapFromURL(imagenURL);

ThumbnailResource = bitmap;

de esta forma cargas las imagenes de mejor forma, asi tu aplicacion tardaría menos en ejecutarse mejorando el performance, sería bueno que tambien sea asincrona la forma en que obtienes la informacion en json.


PD: a modo de sugerencia:

El uso de llaves dobles afecta de cierta forma en cuestion de performance, esto se debe a que por cada dato agregado crea una clase anonima.

new ListViewItem(){{ .. }}

una mejor manera es:

ListViewItem item = new ListViewItem();
item.contenido = ...

0voto

danielreales7 comentado

Buenas te comento:
Al hacer eso me carga bien todas las imágenes, pero al hacer scroll me salen siempre las mismas imágenes de las 6 primeras noticias, es decir, me vuelve a repetir todas las imágenes.

Y si elimino las 2 líneas que me has puesto antes, cuando hace click en alguna noticia no me muestra la imagen, ya que no se la estoy pasando a la actividad.

No se si me he explicado bien, espero que me entiendas, muchas gracias!

Pero hasta ahora me has arreglado el error de antes que no podía entrar en la noticia.

0voto

white comentado

Para prevenir ese problema de recurrencia, intenta con estos cambios

el método getImageUrl sera este:

private class getImageUrl extends AsyncTask<String, Void, Bitmap> {

    private final WeakReference<ImageView> imageViewReference;

    public getImageUrl(ImageView image) {
        imageViewReference = new WeakReference<ImageView>(image);
    }

    protected Bitmap doInBackground(String... urls) {
        String url = urls[0];
        Bitmap bitmap = null;

        if(imagesCache.containsKey(url))
        {
            return imagesCache.get(url);
        }

        try
        {
            InputStream in = new java.net.URL(url).openStream();
            bitmap = BitmapFactory.decodeStream(in);
            imagesCache.put(url, bitmap);
        }
        catch (Exception e) { Log.e("Error", e.getMessage()); }

        return bitmap;
    }

    protected void onPostExecute(Bitmap bitmap)
    {
        imageViewReference.get().setImageBitmap(bitmap);
    }
}

antes de este método agrega esto:

private HashMap<String, Bitmap> imagesCache = new HashMap<>();

en tu metodo getView obten las imagenes de esta forma:

if( !imagesCache.containsKey(item.URLImagen) )
{
    new getImageUrl(imgThumbnail).execute(item.URLImagen);
}
else
{
    imgThumbnail.setImageBitmap(imagesCache.get(item.URLImagen));
}

y nos cuentas despues si te sirvio,

saludos!

0voto

danielreales7 comentado

Perfecto!! Funciona perfectamente, pero ahora como arreglo para que cuando pinche en la noticia me pase la imagen a la actividad?
Porque como me dijiste en el mensaje anterior borré las 2 líneas que me dijiste que eran las que capturaban la imagen.
Es lo único que me falta, muchísimas gracias por todo!

0voto

white comentado

Desconozco que contiene el metodo getCodeLearnChapter(position) ni que contiene la actividad actividadNoticiaID por tu código supongo de ahí obtienes los datos para la noticia, intenta agregar este metodo a tu adpatador:

public Bitmap getThumbnail(int position)
{
    ListViewItem item = items.get(position);
    String url = item.URLImagen;
    Bitmap bitmap = null;

    if( imagesCache.containsKey(item.URLImagen) )
    {
        bitmap = imagesCache.get(url )
    }
    else
    {
        // aqui definir bitmap con alguna imagen por default
        // item.ThumbnailResourceNull quizas?
    }

    return bitmap;
}

y al intentar iniciar la actividad:

actividadNoticiaID.putExtra("imagen", adapter.getThumbnail(position));

eliminar esta linea:

actividadNoticiaID.putExtra("imagenNula", chapter.ThumbnailResourceNull);

no he probado el código pero deberia funcionar, lo hize asi por que desconozco el metodo que te mencione y la actividad.

0voto

danielreales7 comentado

Me vuelve a pasar lo mismo, cuando intento acceder a la noticia me peta la aplicación y vuelve a la pantalla inicial.
El método getCodeLearnChapter(position) contiene esto:

public ListViewItem getCodeLearnChapter(int position) {
    return items.get(position);
}

Pero he utilizado lo que me has puesto y pienso que funciona bien igual.

Pero el problema que tenía antes me sigue pasando, cuando accedo a una noticia en concreto salta a la pantalla de inicio en lugar de la noticia en concreta.

Tiene que estar petando la pila igualmente.

0voto

danielreales7 comentado

Viendo el LogCat me devuelve este error:

¡¡¡ FAILDER BINDER TRANSACTION !!!

0voto

white comentado

que código tienes en el metodo putExtra? podrias agregar el codigo de tu actividad actividadNoticiaID?

el error FAILDER BINDER TRANSACTION se debe a que estas pasando informacion muy pesada, y no es para menos si se esta enviando un bitmap, agrega el codigo de tu actividad a ver si se puede hacer algo asincrono en esa actividad tambien.

0voto

danielreales7 comentado

Si te paso el código de la actividad donde se muestra cada noticia:

tituloView = (TextView) findViewById(R.id.textoTitulo);
    contenidoView = (TextView) findViewById(R.id.textoContenido);
    fechaView = (TextView) findViewById(R.id.textoFecha);
    imagen = (ImageView) findViewById(R.id.imgThumbnail);
    estrella = (ImageView) findViewById(R.id.imgStar);
    meGusta = (TextView) findViewById(R.id.textoFavoritos);

    Bundle bundle = getIntent().getExtras();
    tituloView.setText(bundle.getString("titulo"));
    contenidoView.setText(bundle.getString("contenido"));
    contenidoView.setMovementMethod(new ScrollingMovementMethod());
    fechaView.setText(bundle.getString("fecha"));
    if (bundle.getString("urlImagen").equals("null")) {
        imagen.setImageResource(R.drawable.launcher_icon);
    } else {
        imagen.setImageBitmap((Bitmap) bundle.getParcelable("imagen"));
    }
    estrella.setImageResource(R.drawable.star);
    meGusta.setText(bundle.getString("numeroMeGusta"));

    id = bundle.getString("id");
    favoritos = bundle.getString("numeroMeGusta");
    favoritosEntero = Integer.parseInt(favoritos);
    favoritosEntero = favoritosEntero + 1;

    final Button button = (Button) findViewById(R.id.buttonLike);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Perform action on click
            TextView meGusta2 = (TextView) findViewById(R.id.textoFavoritos);
            String conver = String.valueOf(favoritosEntero);
            meGusta2.setText(conver);

            actualizarFavoritos();
            Toast.makeText(getApplicationContext(), "¡Te gusta!",
                    Toast.LENGTH_SHORT).show();
        }
    });

Este es un método para actualizar los favoritos de la noticia:

public void actualizarFavoritos() {
    String result = "";
    InputStream isr = null;
    try {
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost(
                "http://www.xxxxx.com/xxx/noticiaID.php?id="
                        + id + "&favoritos=" + favoritosEntero); // YOUR PHP
                                                                    // SCRIPT
                                                                    // ADDRESS
        HttpResponse response = httpclient.execute(httppost);
        HttpEntity entity = response.getEntity();
        isr = entity.getContent();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

0voto

white comentado

Hay varias soluciones, como:

  • guardar en el dispositivo las imagenes descargadas y obtenerlas desde este
  • usar una libreria para facilitar la carga de imagenes como picasso
  • usar asynctask en tu actividad para recuperar la imagen desde una url.

asynctask:

En tu actividad actividadNoticiaID agrega esta clase:

private class getImageUrl extends AsyncTask<String, Void, Bitmap> {

    private final WeakReference<ImageView> imageViewReference;

    public getImageUrl(ImageView image) {
        imageViewReference = new WeakReference<ImageView>(image);
    }

    protected Bitmap doInBackground(String... urls) {
        String url = urls[0];
        Bitmap bitmap = null;

        try
        {
            InputStream in = new java.net.URL(url).openStream();
            bitmap = BitmapFactory.decodeStream(in);
        }
        catch (Exception e) { Log.e("Error", e.getMessage()); }

        return bitmap;
    }

    protected void onPostExecute(Bitmap bitmap)
    {
        imageViewReference.get().setImageBitmap(bitmap);
    }
}

en lugar de:

else {
        imagen.setImageBitmap((Bitmap) bundle.getParcelable("imagen"));
    }

usa este código:

else {
        new getImageUrl(imagen).execute(bundle.getString("urlImagen"));
    }

elimina:

actividadNoticiaID.putExtra("imagen", chapter.ThumbnailResource);

actividadNoticiaID.putExtra("imagen", adapter.getThumbnail(position));

tambien olvida y elimina el metodo getThumbnail


picasso:

http://square.github.io/picasso/#download

en lugar de :

imagen.setImageBitmap((Bitmap) bundle.getParcelable("imagen"));

usa:

Picasso.with(context).load(bundle.getString("urlImagen")).into(imagen);

lo mismo podria hacerse para tu adaptador es una sola linea con esta libreria.


0voto

danielreales7 comentado

Todo genial!! Funciona perfectamente. Muchísimas gracias por la ayuda.

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta