5

Próbuję utworzyć aplikację podobną do jednego: An app with some images and description (cardview) in a recyclerviewAsyncTask loading image RecyclerView

Najpierw w ogóle załadować wszystkie informacje o moim CardView z bazy tytuł obrazu, opis i adres URL z obrazu. Po umieszczeniu wszystkich tych informacji w widoku RecyclerView (tytuł i opis) jest wyświetlany poprawnie, ale dla obrazu tworzę klasę AsyncTask w celu załadowania obrazów z adresu URL i pozwól użytkownikowi nie czekać zbyt długo na oczekiwanie aplikacji do odpowiedzi.

Jeśli użytkownik przewija powoli obrazy są ładowane poprawnie i wszystko jest w porządku, ale jeśli przewijam szybko, mam pewne problemy, na przykład obraz pokazany na przykład w punkcie 3 jest pokazany jako ostatni element aż obraz ostatniego elementu jest ładowany i odświeżyć ....

tu kodu do mojego adaptera gdzie załadować obraz:

@Override 
public void onBindViewHolder(ViewHolder holder, final int position) { 
    if (holder instanceof EventosViewHolder) { 
     ...... 
     //Load the image (getFoto() get drawable) 
     if (eventoInfoAux.getFoto()==null){ 
      CargarImagen cargarImagen = new CargarImagen(((EventosViewHolder) holder).vRelativeLayout,eventoInfo,position); 
      cargarImagen.execute(); 
     }else{ 
      ((EventosViewHolder) holder).vRelativeLayout.setBackground(eventoInfoAux.getFoto()); 
     } 
    } 
} 

Oto kod dla CargarImagen klasy:

//Clase para cargar imagenes con una tarea asíncrona desde un url de la imagen 
public class CargarImagen extends AsyncTask<String, String, Boolean>{ 

//RelativeLayout donde se introduce la imagen de fondo 
RelativeLayout relativeLayout=null; 
//EventoInfo para obtener la url de la imagen 
List<EventoInfo> eventoInfo=null; 
//Posición de la imagen clicada 
int i; 

//Se cargan todos los valores de las variables necesarias desde los datos introducidos 
public CargarImagen(RelativeLayout relativeLayout,List<EventoInfo> eventoInfo,int i) { 
    this.relativeLayout = relativeLayout; 
    this.eventoInfo = eventoInfo; 
    this.i = i; 
} 

//Pintamos el fondo de gris mientras se está cargando la imagen 
@Override 
protected void onPreExecute() { 
    super.onPreExecute(); 
    relativeLayout.setBackgroundResource(R.color.fondo_card_view); 
} 

//Se realiza la carga de la imagen en segundo plano 
@SuppressWarnings("deprecation") 
@Override 
protected Boolean doInBackground(String... params) { 
    //Necesario para hacer la llamada a la red 
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();  
    StrictMode.setThreadPolicy(policy); 

    //obtenemos la imagen con el metodo getBitmapFromURL 
    Bitmap myImage = getBitmapFromURL(eventoInfo.get(i).url); 

    //Si se tiene imagen se pinta, si no no se hace nada 
    if (myImage !=null){ 
     Drawable dr = new BitmapDrawable(myImage); 
     eventoInfo.get(i).setFoto(dr); 
     return true; 
    } 
    return false; 
} 

//Al finalizar la carga de la imagen se pinta el fondo del relative layout 
protected void onPostExecute(Boolean result) { 
    if(result){ 
     relativeLayout.setBackground(eventoInfo.get(i).foto); 
    } 
} 

//Metodo para obtener un bitmap desde una url 
public Bitmap getBitmapFromURL(String imageUrl) { 
    try { 

     URL url = new URL(imageUrl); 
     HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 
     connection.setDoInput(true); 
     connection.connect(); 
     InputStream input = connection.getInputStream(); 
     Bitmap myBitmap = BitmapFactory.decodeStream(input); 
     return myBitmap; 

    } catch (IOException e) { 
     e.printStackTrace(); 
     return null; 
    } 
} 

} 

Mogę dać ci plik .apk i możesz zobaczyć problem.

Z góry dziękuję. :)

Oto pełny kod mojego adaptera

import java.util.List; 

import com.abdevelopers.navarraongoing.R; 
import com.abdevelopers.navarraongoing.detalle.DetalleActivity; 

import android.content.Intent; 
import android.support.v7.widget.CardView; 
import android.support.v7.widget.RecyclerView; 
import android.support.v7.widget.RecyclerView.ViewHolder; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.view.ViewGroup.LayoutParams; 
import android.widget.Button; 
import android.widget.RelativeLayout; 
import android.widget.TextView; 

public class EventosAdapter extends RecyclerView.Adapter<ViewHolder>{ 

    private static final int TYPE_EVENTO = 0; 
    private static final int TYPE_FOOTER = 1; 

//lista de eventos 
private List<EventoInfo> eventoInfo; 


private String usuario; 
private EventoInfo eventoInfoAux; 
private PaginaInicioActivity paginaInicio; 

//Se pasan los valores necesarios para obtener información de los eventos 
public EventosAdapter(List<EventoInfo> eventoInfo, String usuario, PaginaInicioActivity paginaInicio) { 
    this.eventoInfo = eventoInfo; 
    this.usuario = usuario; 
    this.paginaInicio = paginaInicio; 
} 


//Metodo para obtener el numero de items en la lista que introducimos 
@Override 
public int getItemCount() { 
    return eventoInfo.size(); 
} 

@Override 
public int getItemViewType(int position) { 
    if (position + 1 == getItemCount()) { 
     return TYPE_FOOTER; 
    } else { 
     return TYPE_EVENTO; 
    } 
} 

//Se asignan los datos a cada uno de los elementos de la cardview 
@Override 
public void onBindViewHolder(ViewHolder holder, final int position) { 
    if (holder instanceof EventosViewHolder) { 
     //Obtenemos cada uno de los eventos 
     eventoInfoAux = eventoInfo.get(position); 
     //eventosViewHolder = holder; 

     //Introducimos el título del evento 
     ((EventosViewHolder) holder).vTitle.setText(eventoInfoAux.titulo); 
     //Introdicumos la fecha y el lugar del evento 
     ((EventosViewHolder) holder).vFechaLugar.setText(eventoInfoAux.fecha+", "+eventoInfoAux.lugar); 
     //obtenemos el numero de asistentes al evento 
     String asistentes = Integer.toString(eventoInfoAux.asistentes); 
     ((EventosViewHolder) holder).vLikeButton.setText(asistentes); 

     //Se pinta el boton de like dependiendo de si está o no like 
     if(eventoInfoAux.like){ 
      ((EventosViewHolder) holder).vLikeButton.setBackgroundResource(R.drawable.button_like_liked); 
     }else{ 
      ((EventosViewHolder) holder).vLikeButton.setBackgroundResource(R.drawable.button_like); 
     } 

     //Se pinta la imagen del evento dependiendo de si está en la base de datos o no 
     if (eventoInfoAux.foto==null){ 
      CargarImagen cargarImagen = new CargarImagen(((EventosViewHolder) holder).vRelativeLayout,eventoInfo,position); 
      cargarImagen.execute(); 
     }else{ 
      ((EventosViewHolder) holder).vRelativeLayout.setBackground(eventoInfoAux.foto); 
     } 
    } 
} 



@Override 
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    if (viewType == TYPE_EVENTO) { 
     View view = LayoutInflater. 
       from(parent.getContext()). 
       inflate(R.layout.evento_card_view, parent, false); 
     return new EventosViewHolder(view,eventoInfo,usuario,paginaInicio); 
    } else if (viewType == TYPE_FOOTER) { 
     View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.pie_carga_pagina_inicio, parent, false); 
     view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); 
     return new FooterViewHolder(view); 
    } 
    return null; 
} 

//Clase para crear el footerview donde se cargan más eventos 
class FooterViewHolder extends ViewHolder { 

    public FooterViewHolder(View view) { 
     super(view); 
    } 

} 

//Clase para crear los eventos con su cardview 
class EventosViewHolder extends ViewHolder implements View.OnClickListener{ 

    private TextView vTitle; 
    private TextView vFechaLugar; 
    private Button vLikeButton; 
    private RelativeLayout vRelativeLayout; 
    private List<EventoInfo> eventoInfo; 
    private String usuario; 
    private LikesDDBB likesManager; 
    private PaginaInicioActivity paginaInicio; 
    private CardView vCardView; 

    public EventosViewHolder(View itemView,List<EventoInfo> eventoInfo,String usuario, 
      PaginaInicioActivity paginaInicio) { 
     super(itemView); 

     this.paginaInicio = paginaInicio; 
     this.usuario = usuario; 
     vTitle = (TextView)itemView.findViewById(R.id.titulo); 
     vFechaLugar = (TextView) itemView.findViewById(R.id.fecha_lugar); 
     vLikeButton = (Button) itemView.findViewById(R.id.like_button); 
     vRelativeLayout = (RelativeLayout) itemView.findViewById(R.id.layout_cardview); 
     vCardView = (CardView)itemView.findViewById(R.id.card_view); 

     //Valores por defecto de los botones y del fondo en caso de no haber like ni foto 
     vLikeButton.setBackgroundResource(R.drawable.button_like); 
     vRelativeLayout.setBackgroundResource(R.color.fondo_card_view); 
     vLikeButton.setOnClickListener(this); 
     vCardView.setOnClickListener(this); 
     vLikeButton.setTag("boton"); 
     vCardView.setTag("evento"); 

     this.eventoInfo = eventoInfo; 
    } 


    @SuppressWarnings("deprecation") 
    @Override 
    public void onClick(View v) { 
     if(v.getTag().equals("boton")){ 
      likesManager = new LikesDDBB(this.eventoInfo.get(getPosition()).like, usuario, 
        vLikeButton, getPosition(), eventoInfo, paginaInicio); 
      likesManager.execute(); 
     }else if(v.getTag().equals("evento")){ 
      Intent inicion = new Intent(paginaInicio,DetalleActivity.class); 

      paginaInicio.startActivity(inicion); 
     } 

    } 

} 

//Para obtener la lista de eventos desde la clase PaginaInicioActivity 
public List<EventoInfo> getEventoInfo() { 
    return eventoInfo; 
} 

public void setEventoInfo(List<EventoInfo> eventoInfo) { 
    this.eventoInfo = eventoInfo; 
} 

} 
+0

Pozwoliłem ci tutaj plik .apk https://mega.co.nz/#!Wc1hlagS!XMOMauYpbdk_UZBIswBgcltbRpwAc7VqaJmlS4JZ9FE w aplikacji Użytkownik: Bruno i Hasło: 1234 – Bruno

+0

Musisz anulować AsyncTask w odpowiednim miejscu (gdy nie ma już potrzeby, aby wynik). Na przykład użyj metody anulowania AsyncTask w metodzie 'onDetachedFromWindow()' niestandardowego widoku, jeśli masz własny widok niestandardowy. –

+0

[tutaj znajduje się rozwiązanie problemu] (https://www.youtube.com/watch?v=8MIfSxgsHIs&feature=youtu.be&list=PLWz5rJ2EKKc86y1CjAlexivfvOms6_0NC) –

Odpowiedz

4

Jak wskazuje nazwa "RecyclerView", przetwarza/ponownie wykorzystuje widoki utworzone w celu wyświetlenia twoich przedmiotów/kart.

Pozwala więc założyć, że Twój RecyclerView ma 3 CardView, które przetwarza podczas przewijania, a my mamy 4 przedmioty do wyświetlenia zawartości.

Początkowo treść punktu 1 zostanie wyświetlona w CardView 1 pkt 2 zostanie wyświetlona w CardView 2 i pkt 3 zostanie wyświetlona w CardView 3.

Teraz, jak przewijanie, CardView 1 znika, zostaje poddane recyklingowi i zawartość pozycja 4 jest pokazana w CardView 1.

Dopóki nie zresetujesz przed włożoną zawartością, CardView 1 wyświetli je - w twoim przypadku - tak długo, jak AsyncTask potrzebuje do uzupełnienia zawartości przed ustawionym elementem zostanie wyświetlone.

celu rozwiązania problemu możesz użyć imageloading (i buforowanie) biblioteki takich jak:

Twoje rozwiązanie jest również podatne na wyścig warunków (gdy później zadania ukończone przed wcześniejszymi)

+0

Witam i dziękuję za odpowiedź. Po wyszukaniu rozwiązań pobrałem bibliotekę picasso i zamiast tego używając AsicTask użyłem picassa, ale problem nadal istnieje. – Bruno

+0

Witam patrząc z komentarzem myślę, że problem powinien być coś takiego http://stackoverflow.com/questions/26591390/recycler-view-with-volley-image-request-cancel-request-request – Bruno

+0

Tak, podobny problem - picasso i uil radzą sobie z tym, jeśli chcesz rozwiązać samodzielnie, musisz śledzić pobieranie obrazów, kierując reklamy na ten sam widok (z recyklingu). –

0

myślę, że należy użyć biblioteki strony trzeciej jak Picasso do asynchronicznie pobrać obrazy z URL. Biblioteka zapewnia znacznie więcej, a możesz bardziej skupić się na funkcjonalności swojej aplikacji, niż zaplątać się w konfigurowanie własnej AsynkTask.

+0

Witaj, niż dla odpowiedzi, ale próbowałem to zrobić zamiast asynktaksu i problem nadal istnieje. – Bruno

+0

Być może powinieneś opublikować kod, aby zobaczyć problem. – SebastianGreen

+0

dodane i plik .apk dziękuję Sebastian – Bruno