25

Sto usando android.support.v7.widget.RecyclerView per mostrare elementi semplici contenenti testo e in alcuni casi anche immagini. Fondamentalmente ho seguito il tutorial da qui https://developer.android.com/training/material/lists-cards.html e ho appena cambiato il tipo di mDataset da String [] a JSONArray e l'xml di ViewHolder. Il mio RecyclerView si trova in un semplice frammento:Strano comportamento delle immagini in RecyclerView

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin"> 

<!-- A RecyclerView with some commonly used attributes --> 
<android.support.v7.widget.RecyclerView 
    android:id="@+id/my_hopps_recycler_view" 
    android:scrollbars="vertical" 
    android:layout_centerInParent="true" 
    android:layout_width="200dp" 
    android:layout_height="wrap_content"/> 

</RelativeLayout> 

In questo frammento sto caricando tutte le informazioni da un server web. Dopo aver ricevuto le informazioni, inizializzo gli elementi in RecyclerView. Io uso un LinearLayoutManager che è impostato nel metodo onCreate del mio frammento. Dopo aver aggiunto l'adattatore, chiamo il metodo setHasFixedSize (true) sul mio RecyclerView. La mia classe Adapter si presenta così:

import android.graphics.BitmapFactory; 
import android.support.v7.widget.RecyclerView; 
import android.util.Base64; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ImageButton; 
import android.widget.ImageView; 
import android.widget.LinearLayout; 
import android.widget.TextView; 

import org.apache.commons.lang3.StringEscapeUtils; 
import org.json.JSONArray; 
import org.json.JSONObject; 

import saruul.julian.MyFragment; 
import saruul.julian.R; 

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { 

/** 
* Fragment holding the RecyclerView. 
*/ 
private MyFragment myFragment; 
/** 
* The data to display. 
*/ 
private JSONArray mDataset; 

public static class ViewHolder extends RecyclerView.ViewHolder { 

    public TextView text; 
    ImageView image; 

    public ViewHolder(View v) { 
     super(v); 
     text = (TextView) v.findViewById(R.id.my_view_text); 
     image = (ImageView) v.findViewById(R.id.my_view_image); 
    } 
} 

public MyAdapter(MyFragment callingFragment, JSONArray myDataset) { 
    myFragment = callingFragment; 
    mDataset = myDataset; 
} 

@Override 
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, 
               int viewType) { 
    View v = LayoutInflater.from(parent.getContext()) 
      .inflate(R.layout.my_view, parent, false); 
    ViewHolder vh = new ViewHolder(v); 
    return vh; 
} 

// Replace the contents of a view (invoked by the layout manager) 
@Override 
public void onBindViewHolder(ViewHolder holder, int position) { 
    try { 
     JSONObject object = mDataset.getJSONObject(position); 
     if (object.has("image")){ 
      if (!hopp.getString("image").equals("")){ 
       try { 
        byte[] decodedString = Base64.decode(StringEscapeUtils.unescapeJson(object.getString("image")), Base64.DEFAULT); 
        holder.image.setImageBitmap(BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length)); 
        holder.image.setVisibility(View.VISIBLE); 
       } catch (Exception e){ 
        e.printStackTrace(); 
       } 
      } 
     } 
     if (object.has("text")){ 
      holder.text.setText(object.getString("text")); 
     } 
    } catch (Exception e){ 
     e.printStackTrace(); 
    } 
} 

// Return the size of your dataset (invoked by the layout manager) 
@Override 
public int getItemCount() { 
    return mDataset.length(); 
} 
} 

E il mio xml per una vista all'interno del mio RecyclerView:

<android.support.v7.widget.CardView 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:card_view="http://schemas.android.com/apk/res-auto" 
android:id="@+id/card_view" 
android:layout_gravity="center" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
card_view:cardCornerRadius="4dp"> 

<LinearLayout 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:gravity="center" 
    android:orientation="vertical" 
    android:padding="@dimen/activity_horizontal_margin"> 

    <TextView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:gravity="center" 
     android:id="@+id/my_hopp_people_reached"/> 

    <ImageView 
     android:id="@+id/my_hopp_image" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:visibility="gone" 
     android:adjustViewBounds="true"/> 

    <TextView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:gravity="center" 
     android:id="@+id/my_hopp_text"/> 

    <ImageButton 
     android:id="@+id/my_hopp_delete" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:background="@null" 
     android:src="@drawable/ic_action_discard" 
     /> 

</LinearLayout> 

</android.support.v7.widget.CardView> 

Così ora qui è il mio problema: tutto funziona bene. Testo e immagine sono visualizzati correttamente nel mio RecyclerView. Tuttavia, se raggiungo la fine dell'elenco scorrendo verso il basso e scorrendo di nuovo verso l'alto, le immagini vengono visualizzate in voci che non devono contenere immagini. Al momento ho nove articoli. Gli ultimi due contengono un'immagine. Quindi scorro verso il basso e vedo solo il testo nei primi sette elementi. Raggiungere la fine della lista mi mostra due immagini come previsto. Ma se faccio scorrere verso l'alto, quelle immagini appaiono in altri oggetti.

scorrere su e giù cambia le immagini sempre nello stesso ordine:

  • Il primo scorrendo verso l'alto (dopo essere stato giù una volta) crea un quadro nella seconda voce.
  • Lo scorrimento verso il basso crea un'immagine nel settimo elemento.
  • Lo scorrimento verso l'alto crea un'immagine nel primo elemento.
  • Lo scorrimento verso il basso non crea nulla.
  • Lo scorrimento verso l'alto crea un'immagine nel terzo elemento e l'immagine nel secondo elemento è stata sovrascritta con l'altra immagine.
  • Lo scorrimento verso il basso crea un'immagine nel sesto elemento e fa scomparire l'immagine nel settimo elemento.
  • E così via ...

ho già scoperto che il metodo onBindViewHolder (ViewHolder titolare, int position) è chiamato ogni volta che un oggetto appare di nuovo sullo schermo. Ho controllato la posizione e le informazioni fornite nel mio mDataset. Tutto funziona bene qui: la posizione è giusta e anche le informazioni fornite. Il testo in tutte le voci non cambia e viene sempre visualizzato correttamente. Qualcuno ha qualche tipo di questo problema e conosce qualche soluzione?

risposta

39

penso che è necessario aggiungere una clausola else al vostro if (object.has("image")) dichiarazione nel metodo onBindViewHolder(ViewHolder holder, int position), impostare l'immagine per nulla:

holder.image.setImageDrawable(null); 

penso the documentation for onBindViewHolder lo descrivono meglio il motivo per cui ciò è necessario:

Chiamato da RecyclerView per visualizzare i dati nella posizione specificata. Questo metodo dovrebbe aggiornare il contenuto dell'elementoView per riflettere l'elemento nella posizione specificata.

Si dovrebbe sempre assumere che il ViewHolder passò nel bisogno di essere completamente aggiornato :)

+0

Questo sembra fare il trucco! Ho messo una clausola else alla mia istruzione if (! Hopp.getString ("image"). Equals ("")) perché ci sarà sempre un campo immagine in JSONObjects. Ho provato la stessa cosa ma ho usato holder.image.setImageBitmap (null) che ovviamente non ha funzionato o l'ho inserito anche nell'istruzione if errata. Non importa perché hai salvato la mia giornata. :) –

+1

Grazie. Ho modificato in 'holder.image.setImageResource (R.drawable.image);' – zackygaurav

+0

Non funziona con volley – delive

11

Non so se le persone hanno ancora problemi con questo, ma l'istruzione if non ha funzionato per me. Ciò che ha funzionato per me è questa:

Utilizzando un override su questo metodo l'adattatore

@Override 
    public int getItemViewType(int position) { 
     return position; 
    } 

seguito chiamando il if sulla base di questo per l'immagine stessa all'interno del onBindViewHolder, in questo modo:

int pos = getItemViewType(position); 
      if(theList.get(pos).getPicture() == null) { 

       holder.picture.setVisibility(View.GONE); 
      } else { 

       Picasso.with(context).load(R.drawable.robot).into(holder.picture); 
      } 

Sto usando Picasso ma dovrebbe funzionare con qualsiasi altra situazione. Spero che questo aiuti qualcuno!

+0

grazie amico funziona .... –

+1

Aggiunta getItemViewType risolto il mio problema! – ziniestro

+0

Funziona davvero grazie allo – sukhbir

2

Sulla base della risposta di @ Lion789. Sto utilizzando Google Volley per il caricamento di immagini e json.

Questo ci consente di recuperare la posizione sulla vista che verrà riciclata in seguito.

@Override 
public int getItemViewType(int position) { 
    return position; 
} 

Quando si effettua la richiesta, impostare la posizione di TAG

@Override 
public void onBindViewHolder(final ItemHolder holder, int position) { 
     ImageRequest thumbnailRequest = new ImageRequest(url, 
       new Response.Listener<Bitmap>() { 
        @Override 
        public void onResponse(Bitmap response) {       
         holder.itemThumbnail.setImageBitmap(response); 
        } 
       },[...]); 

     thumbnailRequest.setTag(String.valueOf(position)); //setting the TAG 
     mQueue.addToRequestQueue(thumbnailRequest); 
} 

E quando la vista viene riciclato annulleremo la richiesta, e rimuovere l'immagine corrente

@Override 
public void onViewRecycled(ItemHolder holder) { 
    super.onViewRecycled(holder); 
    mQueue.cancelAll(String.valueOf(holder.getItemViewType())); 
    holder.itemThumbnail.setImageDrawable(null); 
} 

Le garanzie annullate che le richieste annullate non verranno consegnate.

+0

, quindi non chiamare le immagini ogni volta che viene visualizzata la vista? – x10sion

+0

Sì, ma poiché Volley memorizza nella cache tutto, l'immagine sarebbe già in memoria – Marcola