2015-04-10 7 views
7

Was ich versuche zu erstellen ist eine horizontale Scrolling-Bildergalerie. Ich habe einen RecyclerView (Unterstützung 22.0.0). Das Problem, das ich habe, ist, dass, wenn ich bis zum Ende blättern und dann zurück scrollen, in der Regel ein Bild fehlt, manchmal zwei. Merkwürdigerweise könnte ein anderes Bild fehlen, wenn ich ständig hin und her wische. Hier ist das Layout für das Element:RecyclerView verschwinden Bilder

<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="fill_parent" 
android:layout_height="160dp"> 

<ImageView 
    android:id="@+id/product_variation_image" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:scaleType="centerCrop" 
    android:layout_gravity="center"/> 

Hier ist der Adaper:

public class TestAdapter extends RecyclerView.Adapter<TestAdapter.ViewHolder> { 
private String[] mDataset; 

public static class ViewHolder extends RecyclerView.ViewHolder { 

    public ImageView mImageView; 
    public ViewHolder(View v) { 
     super(v); 
     mImageView = (ImageView) v.findViewById(R.id.product_variation_image); 
    } 
} 

public TestAdapter(String[] myDataset) { 
    mDataset = myDataset; 
} 

@Override 
public TestAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, 
               int viewType) { 
    // create a new view 
    View v = LayoutInflater.from(parent.getContext()) 
      .inflate(R.layout.variaton_list_item, parent, false); 
    ViewHolder vh = new ViewHolder(v); 
    return vh; 
} 

@Override 
public void onBindViewHolder(ViewHolder holder, int position) { 

    holder.mImageView.setImageDrawable(null); 
    String url = mDataset[position]; 
    Log.i("TEST", "position = " + position); 
    ((MainActivity)MainActivity.getInstance()).imageDownloader.download(url, holder.mImageView); 
} 

@Override 
public int getItemCount() { 
    return mDataset.length; 
} 

Die Download-Methode holt das Bild, von einer URL oder wird es aus dem Speicher, wenn es hat wurde zwischengespeichert. Dies funktioniert in allen anderen Layouts, z. ListView oder GridView. Hier ist der Code, den ich es einrichten aufbrauchen im Fragmente:

final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 
    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); 
    mRecyclerView.setLayoutManager(layoutManager); 

Dies ist in der onCreateView Methode. Wenn ich die Urls bekommen füllen ich sie und setzen Sie den Adapter verwenden:

myDataset[i] = imageURL; // for each image  
    mAdapter = new TestAdapter(myDataset); 
    mRecyclerView.setAdapter(mAdapter); 

Das Interessante an der Linie in der onBindViewHolder Verfahren in dem Adapter ist, wo ich die Position loggt sein. Was ich gefunden habe ist, dass Zellen, in denen das Bild nicht gezeigt wird, dass diese Methode nicht aufgerufen wird. Es ist so, als würde es die Zelle aus irgendeinem Grund überspringen. Noch seltsamer, wenn ich eine Zelle halte und von links nach rechts wische, wenn eine Zelle aus dem Bildschirm verschwindet und dann wieder reinkommt, ist das Bild verschwunden, da die Methode onBindViewHolder nicht mehr aufgerufen wird.

+0

bitte teilen die ImageDownloader Code von Ihrem MainActivity zu ersetzen –

+0

Hallo, Ich kann es nicht wirklich teilen, da es groß ist. Im Grunde holt es das Bild und setzt dann das Zeichen des Zeichens, sobald es aus dem Speicher oder aus der Ferne gekommen ist. Es basiert auf dem BitmapFun-Beispiel. Es funktioniert überall wo ich Bilder in der Anwendung verwende. Ich denke, das Problem ist der Adapter selbst, und nicht das Bild holen. –

+0

Keine große Sache, ich denke, Ihr Problem ist, wie unten vorgeschlagen, Ihr Aufruf, ein neues Bild herunterzuladen, aber Ihr Bitmap auf Null für Ihre Bildansicht. Sie sollten dies jedes Mal herunterladen, wenn Sie onBindViewHolder aufrufen, wenn es bereits heruntergeladen wurde –

Antwort

2

Die eine Klasse, die ich nicht derjenige war, würde keine Rolle spielte denken, dass das Problem verursacht wurde. Ich bin mir nicht sicher, was der Grund ist, aber es befindet sich in einer benutzerdefinierten ImageView-Klasse, die ich für das Recycling verwende, das ich aus dem BitmapFun-Beispiel erhalten habe.

public class RecyclingImageView extends ImageView { 

    public RecyclingImageView(Context context) { 
     super(context); 
    } 

    public RecyclingImageView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    /** 
    * @see android.widget.ImageView#onAttachedToWindow() 
    */ 
    @Override 
    protected void onAttachedToWindow() {} 

    /** 
    * @see android.widget.ImageView#onDetachedFromWindow() 
    */ 
    @Override 
    protected void onDetachedFromWindow() { 
     // This has been detached from Window, so clear the drawable 

     setImageDrawable(null); 

     super.onDetachedFromWindow(); 
    } 

    /** 
    * @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable) 
    */ 
    @Override 
    public void setImageDrawable(Drawable drawable) { 
     // Keep hold of previous Drawable 
     final Drawable previousDrawable = getDrawable(); 

     // Call super to set new Drawable 
     super.setImageDrawable(drawable); 

     // Notify new Drawable that it is being displayed 
     notifyDrawable(drawable, true); 

     // Notify old Drawable so it is no longer being displayed 
     notifyDrawable(previousDrawable, false); 
    } 

    /** 
    * Notifies the drawable that it's displayed state has changed. 
    * 
    * @param drawable 
    * @param isDisplayed 
    */ 
    private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) { 
     if (drawable instanceof RecyclingBitmapDrawable) { 
      // The drawable is a CountingBitmapDrawable, so notify it 
      ((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed); 
     } else if (drawable instanceof LayerDrawable) { 
      // The drawable is a LayerDrawable, so recurse on each layer 
      LayerDrawable layerDrawable = (LayerDrawable) drawable; 
      for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) { 
       notifyDrawable(layerDrawable.getDrawable(i), isDisplayed); 
      } 
     } 
    } 

} 

Wenn ich ersetzen diese mit einem normalen Image, bekomme ich nicht mehr das Problem.

+1

RecyclingBitmapDrawable - was ist das? Wo ist die Implementierung? –

1

Wäre es möglich, etwas zu testen? Können Sie diese Bibliothek verwenden, um die Bilder von den URLs zu laden? http://square.github.io/picasso/ Es speichert alles und es behandelt alles asynchron.

Verwenden es so etwas wie ...

@Override 
public void onBindViewHolder(ViewHolder holder, int position) { 

    Picasso.with(mImageView.getContext()).cancelRequest(holder.mImageView); 

    String url = mDataset[position]; 
    Picasso.with(mImageView.getContext()).load(url).placeholder(R.drawable.placeholder).into(holder.mImageView); 

} 

... und sehen, ob es immer noch nicht ein paar Bilder. Wenn dies der Fall ist, werden Sie zumindest 100% sicher sein, dass das Problem nicht in Ihrem Download-Mechanismus liegt (was ich denke, dass es sein könnte).

Wenn Sie Android Studio verwenden, fügen Sie einfach die Abhängigkeit compile 'com.squareup.picasso:picasso:2.5.2' hinzu, wenn nicht, können Sie die Bibliothek hinzufügen, die Sie unter dem obigen Link finden.

Es ist einen Versuch wert ...

+0

Danke, aber das hat keinen Unterschied gemacht. –

0

Wir können das Problem beheben, indem wir LinearLayoutManager und ImageView erweitern.

1. Creats ein PrecachingLinearLayoutManager

public class PrecachingLinearLayoutManager extends LinearLayoutManager { 

    private static final int DEFAULT_EXTRA_LAYOUT_SPACE = 600; 

    private int extraLayoutSpace = -1; 

    @SuppressWarnings("unused") 
    private Context mContext; 

    public PrecachingLinearLayoutManager(Context context) { 
     super(context); 
     this.mContext = context; 
    } 

    public PrecachingLinearLayoutManager(Context context, int extraLayoutSpace) { 
     super(context); 
     this.mContext = context; 
     this.extraLayoutSpace = extraLayoutSpace; 
    } 

    public PrecachingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 
     super(context, orientation, reverseLayout); 
     this.mContext = context; 
    } 

    public void setExtraLayoutSpace(int extraLayoutSpace) { 
     this.extraLayoutSpace = extraLayoutSpace; 
    } 

    @Override 
    protected int getExtraLayoutSpace(RecyclerView.State state) { 
     if (extraLayoutSpace > 0) { 
      return (extraLayoutSpace); 
     } 
     return (DEFAULT_EXTRA_LAYOUT_SPACE); 
    } 
} 

2.PrecachingLinearLayoutManager verwenden LinearLayoutManager

DisplayMetrics displayMetrics = new DisplayMetrics(); 
    getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); 
    PrecachingLinearLayoutManager layout = new PrecachingLinearLayoutManager(getActivity()); 
    layout.setExtraLayoutSpace(displayMetrics.heightPixels); 
    recyclerview.setLayoutManager(layout); 

3. creats ein RecycleImageView

private Object tag = null; 

@Override 
protected void onAttachedToWindow() { 
    Object tag = getTag(); 
    if (tag == null || !tag.equals(this.tag)) { 
     // Will cause displayed bitmap wrapper to 
     // be 'free-able' 
     setImageDrawable(null); 
     this.tag = null; 
     super.onDetachedFromWindow(); 
    } 
    super.onAttachedToWindow(); 
} 

@Override 
protected void onDetachedFromWindow() { 
    Object tag = getTag(); 
    if (tag != null) { 
     this.tag = tag; 
    } else { 
     // Will cause displayed bitmap wrapper to 
     // be 'free-able' 
     setImageDrawable(null); 
     this.tag = null; 
     super.onDetachedFromWindow(); 
    } 
} 

4. Verwenden RecycleImageView zu ersetzen Image

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:extends="http://schemas.android.com/apk/res/com.yourdomain.yourpackage" 
xmlns:tools="http://schemas.android.com/tools" 
android:id="@+id/viewgroup" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:orientation="vertical" > 
<com.yourdomain.yourpackage.RecycleImageView 
    android:id="@+id/photo" 
    android:layout_width="40dp" 
    android:layout_height="40dp" 
    extends:delayable="true" 
    android:contentDescription="@string/nothing" 
    android:src="@drawable/photo_placeholder" > 
</com.yourdomain.yourpackage.RecycleImageView> 
</LinearLayout>