36

Ich habe eine RecyclerView implementiert, die hauptsächlich Bilder enthält, die durch Picasso geladen wird. Mein Problem ist, dass, sobald ich die Ansicht nach unten oder oben scrolle, das Platzhalterbild für ca. erscheint. eine Sekunde bevor es durch das eigentliche Bild ersetzt wird. Es scrollt sehr flüssig, ist aber praktisch unbrauchbar. Dies passiert jedes Mal, wenn etwas vom Bildschirm rollt.Recyclerview schmerzhaft langsam geladen zwischengespeicherte Bilder von Picasso

Picasso speichert die Bilder eindeutig auf der Festplatte, da sie schneller als die anfängliche Ladezeit geladen werden, aber sie werden definitiv sofort wieder aus dem Cache geladen. Was habe ich falsch implementiert?

Mein Adaptercode:

public class ExploreAdapter extends RecyclerView.Adapter<ExploreAdapter.ExploreViewHolder> { 

    private List<ExploreItem> exploreItemList; 
    private Context mContext; 
    private int deviceWidth = PrefUtils.getDeviceWidth(); 

    public ExploreAdapter(Context context, List<ExploreItem> exploreItemList){ 
     this.mContext = context; 
     this.exploreItemList = exploreItemList; 
    } 

    @Override 
    public int getItemCount(){ 
     return exploreItemList.size(); 
    } 

    @Override 
    public void onBindViewHolder(ExploreViewHolder exploreViewHolder, int i){ 
     ExploreItem item = exploreItemList.get(i); 
     exploreViewHolder.getvTitle().setText(item.getTitle()); 
     exploreViewHolder.getvUsername().setText(item.getUsername()); 
     exploreViewHolder.getvNumLikes().setText(item.getNbOfLikes()); 

     Picasso.with(mContext).load(item.getMediaLocation().trim()) 
       .resize(deviceWidth, deviceWidth) 
       .centerCrop() 
       .placeholder(R.drawable.profile_background) 
       .into(exploreViewHolder.getvImage()); 

     Picasso.with(mContext).load(item.getUserProfilePicUrl().trim()) 
       .placeholder(R.drawable.profile_picture) 
       .into(exploreViewHolder.getvProfilePic()); 
    } 

    @Override 
    public ExploreViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){ 
     View itemView = LayoutInflater. 
       from(viewGroup.getContext()). 
       inflate(R.layout.explore_item, viewGroup, false); 

     return new ExploreViewHolder(itemView); 
    } 

    public static class ExploreViewHolder extends RecyclerView.ViewHolder{ 
     private TextView vTitle, 
          vUsername, 
          vNumLikes; 

     private SquareImage vImage; 
     private ImageView vProfilePic; 

     public ExploreViewHolder(View v){ 
      super(v); 
      this.vTitle = (TextView) v.findViewById(R.id.explore_item_title); 
      this.vUsername = (TextView) v.findViewById(R.id.explore_item_username); 
      this.vNumLikes = (TextView) v.findViewById(R.id.explore_item_number_likes); 
      this.vImage = (SquareImage) v.findViewById(R.id.explore_item_image); 
      this.vProfilePic = (ImageView) v.findViewById(R.id.explore_item_profile_picture); 
     } 

     public TextView getvTitle() { 
      return vTitle; 
     } 

     public TextView getvUsername() { 
      return vUsername; 
     } 

     public ImageView getvProfilePic() { 
      return vProfilePic; 
     } 

     public SquareImage getvImage() { 
      return vImage; 
     } 

     public TextView getvNumLikes() { 
      return vNumLikes; 
     } 

     public void setvImage(SquareImage vImage) { 
      this.vImage = vImage; 
     } 

     public void setvNumLikes(TextView vNumLikes) { 
      this.vNumLikes = vNumLikes; 
     } 

     public void setvProfilePic(ImageView vProfilePic) { 
      this.vProfilePic = vProfilePic; 
     } 

     public void setvTitle(TextView vTitle) { 
      this.vTitle = vTitle; 
     } 

     public void setvUsername(TextView vUsername) { 
      this.vUsername = vUsername; 
     } 
    } 
} 

Jede Hilfe sehr geschätzt wird.

+0

btw was ist SquareImage? – Atul

Antwort

1

ich die Richtigkeit dieser Lösung nicht überprüfen können, aber ich war auch mit diesem Problem konfrontiert und näherte sich ihm dieser Idee folgt:

@Override 
public void onBindViewHolder(ViewHolder viewHolder, final int i) { 

... 

if(mImageView.getDrawable == null()){ 
    Picasso.with(context) 
      .load(path) 
      .error(errorResId) 
      .tag(tag) 
      .into(mImageView); 
} 

... 

}

Es ist beabsichtigt, Picasso von Laden zu verbieten alles, es sei denn, die ImageView hat nichts anzuzeigen.

+2

Ich glaube nicht, dass das funktionieren wird. Sicher, wird für das erste Element in der RecyclerView arbeiten, aber für alle anderen wird das gleiche Bild angezeigt. – 4gus71n

+0

Ich kann bestätigen, dass es für alle Elemente funktioniert. –

+0

Nun, funktioniert nicht für mich. Mein ViewHolder macht dies in der onBindViewHolder-Methode. 'image.setOnClickListener (neue View.OnClickListener() { @Override öffentliche void onClick (View v) { GoTo.imageReelGalery (itemView.getContext(), v, 0, Image.toArray (publication.getImages())); } }); if (image.getDrawable() == null) PicassoHelper.downloadImage (itemView.getContext(), "http: //" + publication.getImages() [0], Bild); ' Vielleicht weil ich von einem laden URL? – 4gus71n

1

Picasso automatisch Cache Bilder in zwei Ebenen:

  • Disk-Cache (die eigentlich nicht in picasso ist aber in der Netzwerkschicht, die Verwendung picasso)
  • Speicher-Cache

Sie sind Bot Initialisiert mit Standardeinstellungen, die den meisten Anwendungen entsprechen.

Wenn der Speichercache zu groß wird, wird die älteste verwendete Bitmap aus dem Speichercache entfernt (standardmäßig 1/7 des verfügbaren Heapspeichers).

Ich denke, was passiert hier ist, dass Sie in der Nähe der Speichergrenze des Caches sind und Bilder werden so wieder jedes Mal entschlüsselt, wenn Sie sie wieder benötigen.

Siehe hier: Android Picasso Configure LruCache Size

Aber ich empfehlen, auf die Speicher-Cache-Größe zu erhöhen, wenn Sie wirklich sind Neudimensionierung die Bilder auf die tatsächliche Größe, die Sie verwenden ..

ich das Problem mit Ihrem Code denken ist das:

.resize(deviceWidth, deviceWidth) 

Sind Sie sicher, dass Sie wirklich ein Bild von dieser Größe brauchen? Was speichern Sie in PrefUtils? Wird es aktualisiert, wenn sich das Gerät dreht? (Landscape vs Portrait)

Wenn Sie kleinere Bilder benötigen, versuchen Sie, die Größe des Bildes, das Sie verwenden, zu verändern. Wenn Sie nicht wissen (berechnet zur Laufzeit mit dem Layout) tun Sie mit einer Annäherung oder schreiben Sie Ihren Code, um die tatsächliche Größe zu erhalten (Ich schreibe normalerweise benutzerdefinierte Klassen, die ImageView für diese Sachen erweitern).

Wenn Sie wirklich die Bilder in dieser Größe brauchen, erhöhen Sie einfach die Cache-Größe (siehe Link oben) und Sie sollten gut gehen.

3

Ich habe dieses Problem vor kurzem aufgetreten, und ja, Sie haben Recht. Picasso ist eine gute Bibliothek zum Anzeigen von Bildern von SD-Karte, aber es hat einige Probleme bezüglich des Caches, wenn Sie Bilder online holen, deshalb sehen Sie ein Platzhalterbild beim Scrollen. Ich habe verschiedene Dinge ausprobiert und das Problem wurde nicht gelöst.

Es gibt eine alternative Bibliothek, d. H. "AQuery", die sehr gut zum Abrufen und Zwischenspeichern von Bildern aus dem Netzwerk ist.

http://code.google.com/p/android-query/

Die wichtigsten Vorteile der AQuery/Android-Abfrage ist, dass Sie den Cache löschen können, keine Verzögerung beim Scrollen und sehr effektiv für Bilder Caching und nicht Platzhalter Bilder zeigen beim Scrollen.

Ich löste mein Problem, indem ich Picaaso entfernte und Aquery verwendete. Es ist nur eine Ein-Zeilen-Ersatz und Sie sind fertig mit Ihrem Problem.

(new AQuery(mContext)).id(exploreViewHolder.getvProfilePic()).image(item.getUserProfilePicUrl().trim(), true, true, device_width, R.drawable.profile_background, aquery.getCachedImage(R.drawable.profile_background),0); 

Neben Sie leicht von picasso zu AQuery verschieben können, da es nicht so etwas ist, das in picasso vorhanden ist, aber nicht in AQuery und die Syntax ist nahezu gleich. Also würde ich dir empfehlen, AQuery zu benutzen, bis dies von Picasso behoben wird.

21

Verwenden Sie fit().

Picasso.with(context) 
     .load(file) 
     .fit() 
     .centerCrop() 
     .into(imageView); 

The fit() ändert die Größe tatsächlich das Bild in den imageView ‚s Grenzen passen. Dies lädt nicht das gesamte Bild und glättet das Scrollen.

+0

es wird noch langsamer, wenn ich .fit(). CenterCrop() verwende? – umerk44

+0

Stellen Sie eine neue Frage und schreiben Sie Ihren Code. – Mangesh

+1

Verwenden Sie nur .fit(), das funktioniert –

52

Einstellung recyclerView Parameter gelöst, wie sie unter meinem Stottern Problem:

recyclerView.setHasFixedSize(true); 
recyclerView.setItemViewCacheSize(20); 
recyclerView.setDrawingCacheEnabled(true); 
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH); 

ich hoffe, dass es jemand anderes zu helfen.

+2

Das funktionierte für mich auch. Aber ich benutze nur 2 und 3 Zeilen von Ihrem Code) – mohax

+3

Dies ist eine großartige Lösung. – Relm

+2

Was ist die magische Zahl "20"? – Aleksandr

-2

Verwenden Sie einfach noPlaceholder Attribut.

5

Ich mischte die Antwort von @ergunkocak und @Mangesh Ghotage verwenden funktioniert dies sehr gut:

recyclerView.setHasFixedSize(true); 
recyclerView.setItemViewCacheSize(20); 
recyclerView.setDrawingCacheEnabled(true); 

Und das für jedes Bild:

Picasso.with(context) 
     .load(file) 
     .fit() 
     .into(imageView); 

neben Ihnen Picasso einstellen könnte einen einzigen verwenden Instanz wie folgt:

Picasso.setSingletonInstance(picasso); 

Und schließlich die Anzeige der Cache-Indikatoren gibt Ihnen Hinweise o n, was los ist falsch:

Picasso 
    .with(context) 
    .setIndicatorsEnabled(true); 

More about Picasso