2015-01-04 20 views
11

Ich habe SO-Threads nach Antworten gesucht, konnte aber mein Problem aus der vorherigen Diskussion nicht herausfinden. Ich habe eine Listenansicht, die ungefähr 50 Bilder lädt (früher waren es ungefähr 100, aber das lud überhaupt keine Bilder ein). Nachdem ich meinen JSON-Inhalt (einschließlich Bild-URL) von einem API-Endpunkt über einen Adapter abgerufen habe, wird er von meinem Code in die Listenansicht eingefügt.Picasso lädt Bilder weiter, während es in der Listenansicht nach oben scrollt, lädt langsam

Zur Zeit, mit 50 Bildern, wird Picasso ein Bild nach dem anderen laden, während ich auf dem Feed nach unten scrolle. Ich habe den Eindruck, dass das Bild schneller geladen wird, wenn die Schriftrolle bei einem Element in der Listenansicht fixiert bleibt. Wenn ich jedoch nach oben scrolle, wird der Platzhalter wieder eingefügt und das Bild erneut geladen. Gibt es eine Möglichkeit, dieses Problem zu lösen?

public class MainActivity extends Activity { 
    private List<Post> myPosts = new ArrayList<Post>(); 
    protected String[] mBlogPostTitles; 
    public static final String TAG = MainActivity.class.getSimpleName();//prints name of class without package name 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     if(isNetworkAvailable()) { 
      GetBlogPostsTask getBlogPostsTask = new GetBlogPostsTask(); // new thread 
      getBlogPostsTask.execute();// don't call do in background directly 
     }else{ 
      Toast.makeText(this, "Network is unavailable", Toast.LENGTH_LONG).show(); 
     } 
    } 
    public boolean isNetworkAvailable() { 
     ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 
     NetworkInfo networkInfo = manager.getActiveNetworkInfo(); 

     boolean isAvailable = false; 

     if(networkInfo != null && networkInfo.isConnected()){ 
      isAvailable = true; 
     } 

     return isAvailable; 
    } 
    private void populateListView() { 
     ArrayAdapter<Post> adapter = new MyListAdapter(); 
     ListView list = (ListView) findViewById(R.id.postsListView); 
     list.setAdapter(adapter); 
    } 

    private class MyListAdapter extends ArrayAdapter<Post>{ 
     public MyListAdapter() { 
      super(MainActivity.this, R.layout.item_view, myPosts); 
     } 

     @Override 
     public View getView(int position, View convertView, ViewGroup parent) { 

      // make sure we have a view to work with 
      View itemView = convertView; 
      if (itemView == null) { 
       itemView = getLayoutInflater().inflate(R.layout.item_view, parent,false); 
      } 
      //find the post to work with 
      Post currentPost = myPosts.get(position); 
      Context context = itemView.getContext(); 

      String imageURL = currentPost.getImage(); 
      if(imageURL == null || imageURL.isEmpty()){ 
       ImageView imageView = (ImageView) itemView.findViewById(R.id.item_image); 
       imageView.setVisibility(View.GONE); 
      }else{ 
       ImageView imageView = (ImageView) itemView.findViewById(R.id.item_image); 
       Picasso.with(context) 
         .load(imageURL) 
         .tag(context) 
         .placeholder(R.drawable.kanye8080s) 
         .error(R.drawable.stadiumarcadium) 
         .into(imageView); 
       imageView.setVisibility(View.VISIBLE); 
      } 

      //Username 
      TextView userText = (TextView) itemView.findViewById(R.id.item_txtUser); 
      userText.setText(currentPost.getUser()); 

      //Time of post 
      TextView timeText = (TextView) itemView.findViewById(R.id.item_txtTime); 
      timeText.setText("" + currentPost.getTime()); 

      //The actual post 
      TextView postText = (TextView) itemView.findViewById(R.id.item_txtPost); 
      postText.setText("" + currentPost.getPost()); 

      //The actual post 
      TextView likesText = (TextView) itemView.findViewById(R.id.item_txtLikes); 
      likesText.setText("" + currentPost.getLikes()); 

      return itemView; 
     } 
    } 

    private class GetBlogPostsTask extends AsyncTask<Object, Void, List> { 

     @Override 
     protected List doInBackground(Object[] params) { 

      int responseCode = -1;//need to have this variable outside scope of try/catch block 
      JSONObject jsonResponse = null; 
      StringBuilder builder = new StringBuilder(); 
      HttpClient client = new DefaultHttpClient(); 
      HttpGet httpget = new HttpGet(""); /// api endpoint redacted 

      try { 

       HttpResponse response = client.execute(httpget); 
       StatusLine statusLine = response.getStatusLine(); 
       responseCode = statusLine.getStatusCode(); 

       if(responseCode == HttpURLConnection.HTTP_OK){ //could have used just 200 value 
        HttpEntity entity = response.getEntity(); 
        InputStream content = entity.getContent(); 
        BufferedReader reader = new BufferedReader(new InputStreamReader(content)); 
        String line; 
        while((line = reader.readLine()) != null){ 
         builder.append(line); 
        } 

        jsonResponse = new JSONObject(builder.toString()); 

        JSONArray jsonPosts = jsonResponse.getJSONArray("posts"); 
        for(int i=0; i < jsonPosts.length(); i++){ 
         JSONObject jsonPost = jsonPosts.getJSONObject(i); 

         int post_id = Integer.parseInt(jsonPost.getString("id")); 
         String post_user = jsonPost.getString("user"); 
         String post_account = jsonPost.getString("account"); 
         int post_time = Integer.parseInt(jsonPost.getString("time")); 
         String post_post = jsonPost.getString("post"); 
         String post_image = jsonPost.getString("image"); 
         int post_likes = Integer.parseInt(jsonPost.getString("likes")); 

         myPosts.add(new Post(post_id, post_user, post_account, post_time, post_post, post_image, "profile picture here", post_likes)); 
        } 
       }else{ 
        Log.i(TAG, "Unsuccessful HTTP Response Code: " + responseCode); 
       } 
      } 
      catch (MalformedURLException e){ 
       Log.e(TAG, "Exception caught"); 
      } 
      catch (IOException e){ 
       Log.e(TAG, "Exception caught"); 
      } 
      catch (Exception e){//must be in this order, this is the last, general catch 
       Log.e(TAG, "Exception caught", e); 
      } 

      return null; 
     } 
     @Override 
     protected void onPostExecute(List result) { 
      // call populateListView method here 
      populateListView(); 
      super.onPostExecute(result); 
     } 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     getMenuInflater().inflate(R.menu.menu_main, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     // Handle action bar item clicks here. The action bar will 
     // automatically handle clicks on the Home/Up button, so long 
     // as you specify a parent activity in AndroidManifest.xml. 
     int id = item.getItemId(); 

     //noinspection SimplifiableIfStatement 
     if (id == R.id.action_settings) { 
      return true; 
     } 

     return super.onOptionsItemSelected(item); 
    } 
} 

jede Hilfe sehr geschätzt würde,

Thank you !!

EDIT:

Hallo allerseits,

ich meinen Code in eine Ansicht Haltermuster aktualisiert haben, erstellt zwei getrennte Ansichten (eine für einen Pfosten mit einem Bild, einer für einen Post mit nur Text) und enthielt auch Picassos neue scroll detection capabilities.

Ich habe eine Verbesserung in einigen der Bilder gesehen, die schneller laden, mindestens wenn die Ansicht beim Scrollen fokussiert wird, wird das Bild jetzt eher geladen. Beim Hochscrollen verschwinden jedoch dieselben Bilder, die einmal geladen wurden. Es fühlt sich an, als ob Picasso nur 4-5 Bilder gleichzeitig lädt und die bereits geladenen ersetzt, um Platz zu schaffen. Mein aktualisierter Code ist unten:

public class MainActivity extends Activity { 
    private List<Post> myPosts = new ArrayList<Post>(); 
    protected String[] mBlogPostTitles; 
    public static final String TAG = MainActivity.class.getSimpleName();//prints name of class without package name 

    ... 

    private void populateListView() { 
     Activity activity = MainActivity.this; 

     ArrayAdapter<Post> adapter = new MyListAdapter(); 
     ListView list = (ListView) findViewById(R.id.postsListView); 
     list.setAdapter(adapter); 
     list.setOnScrollListener(new SampleScrollListener(activity)); 
    } 

    private class MyListAdapter extends ArrayAdapter<Post>{ 
     public MyListAdapter() { 
      super(MainActivity.this, R.layout.item_view, myPosts); 
     } 

     @Override 
     public int getViewTypeCount() { 
      return 2; 
     } 

     @Override 
     public int getItemViewType(int position) { 
      String imageURL = myPosts.get(position).getImage(); 
      if(imageURL == null || imageURL.isEmpty()){ 
       return 1; // text based 
      }else{ 
       return 0; // image based 
      } 
     } 

     @Override 
     public View getView(int position, View convertView, ViewGroup parent) { 

      PostViewHolder holder; 

      int type = getItemViewType(position); 

      View itemView = convertView; 

      // make sure we have a view to work with 
      if (itemView == null) { 
       holder = new PostViewHolder(); 
       if(type == 1) { 
        itemView = getLayoutInflater().inflate(R.layout.item_view, parent, false); 
       }else { 
        itemView = getLayoutInflater().inflate(R.layout.image_post_view, parent, false); 
        holder.image = (ImageView) itemView.findViewById(R.id.item_image); 
       } 
       holder.user = (TextView) itemView.findViewById(R.id.item_txtUser); 
       holder.time = (TextView) itemView.findViewById(R.id.item_txtTime); 
       holder.post = (TextView) itemView.findViewById(R.id.item_txtPost); 
       holder.likes = (TextView) itemView.findViewById(R.id.item_txtLikes); 

       itemView.setTag(holder); 
      } else { 
       holder = (PostViewHolder) itemView.getTag(); 
      } 

      //find the post to work with 
      Post currentPost = myPosts.get(position); 

      if(type != 1) { 
       Context context = itemView.getContext(); 
       String imageURL = currentPost.getImage(); 

       Picasso.with(context).setIndicatorsEnabled(true); 
       //Picasso.with(context).setLoggingEnabled(true); 
       Picasso.with(context) 
         .load(imageURL) 
         .tag(context) 
         .placeholder(R.drawable.kanye8080s) 
         //.skipMemoryCache() 
         .error(R.drawable.stadiumarcadium) 
         .fit() 
         .into(holder.image); 
      } 
      //Username 
      holder.user.setText(currentPost.getUser()); 

      //Time of post 
      holder.time.setText("" + currentPost.getTime()); 

      //The actual post 
      holder.post.setText(currentPost.getPost()); 

      //Likes for the post 
      holder.likes.setText("" + currentPost.getLikes()); 

      return itemView; 
     } 
    } 
    public class SampleScrollListener implements AbsListView.OnScrollListener { 
     private final Context context; 

     public SampleScrollListener(Context context) { 
      this.context = context; 
     } 

     @Override 
     public void onScrollStateChanged(AbsListView view, int scrollState) { 
      final Picasso picasso = Picasso.with(context); 
      if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) { 
       picasso.resumeTag(context); 
      } else { 
       picasso.pauseTag(context); 
      } 
     } 

     @Override 
     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 
          int totalItemCount) { 
      // Do nothing. 
     } 
    } 


    ... 
} 

Woher kommt das Problem? Sollte ich diese Bilder irgendwie im Cache vorladen? Während ich Picassos neues Prioritätsmerkmal bereits untersucht habe, sollte ich Picasso irgendwie sagen, Bilder in der Reihenfolge zu laden, in der sie in meiner Listenansicht erscheinen? Irgendwelche Ideen? Wie kann ich Bilder "aufbewahren", die bereits geladen sind? Vielen Dank!

- 24x7

+0

standardmäßig Picasso hat bereits Caching nach den Empfehlungen der Entwickler-Dokumentation [hier] (http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html). Für eine Diskussion über den verwendeten Cache siehe [diese Antwort] (http://StackOverflow.com/a/20105828/2911458). Wenn Sie überprüfen möchten, ob das Caching funktioniert, aktivieren Sie Picassos Debug-Modus, indem Sie 'setIndicatorsEnabled (true)' auf Ihrer Picasso-Instanz aufrufen. Die Indikatoren sind [hier] beschrieben (http://square.github.io/picasso/). – stkent

+1

Ich habe die Indikatoren eingeschaltet, konnte aber nicht viel feststellen. Selbst bei einer reduzierten Anzahl von Bildern (7) innerhalb einer Listenansicht mit 10 Elementen verhält es sich immer noch abnormal. Bilder werden neu geladen, wenn Sie nach oben scrollen und scheinbar langsam geladen werden, es sei denn, sie befinden sich im Fokus der Listenansicht. – 24x7

+0

Warum rufen Sie setVisibility() auf dem ImageView auf. Probieren Sie es ohne. Verwenden Sie dafür die Platzhalter- und Fehlerbilder in Picasso, dies könnte Teil des Problems sein. Sie können .Load (Null) mit Picasso glaube ich. –

Antwort

4

Die Größe des Cache-Speicher von Picasso beschränkt ist, so dass es nicht aus Speicherfehler erzeugt, wenn er lange Listen scrollen. Sobald die Bilder aus dem Speichercache sind, wird der Platzhalter angezeigt, während das Bild aus dem Datenträgercache oder dem Netzwerk neu geladen wird.

Der Festplatten-Cache ist standardmäßig aktiviert, daher sollte die Nachladezeit sehr schnell sein. Sie können setIndicatorsEnabled(true) verwenden, um zu sehen, woher die Bilder geladen werden.

Wenn Sie feststellen, dass Picasso die Bilder aus dem Netzwerk neu lädt, ist dies wahrscheinlich ein Problem mit den HTTP-Headern, die vom Server gesendet werden. Ich glaube nicht, dass Picasso die Bilder tatsächlich auf der Festplatte zwischenspeichert und sich stattdessen auf die HTTP-Schicht verlässt, die einem No-Cache-Header folgt und nach Ablauf der Ablaufzeit vom Netzwerk neu geladen wird.

+0

Die Quellen der Bild-URLs stammen hauptsächlich von Parse, aber auch von Facebook und imgur. Alle Bilder sind direkte URLs. Ich habe auch die Anzahl der Elemente in der Liste reduziert auf 10 - 7 von denen Bilder haben. Und derselbe Fehler tritt immer noch auf. Die Bilder werden langsam geladen. Wenn Sie dann nach unten und wieder nach oben scrollen, laden Sie neu. – 24x7

+0

Werden sie vom Netzwerk oder vom Festplattencache neu geladen? –

+0

Ich bin mir nicht sicher. Hier ist die Ausgabe von meinem Logcat: http://pastebin.com/usNa77Hq, ich scrollte zweimal nach oben und unten. – 24x7

4

Ich würde zwei Dinge betrachten.

Nummer eins ist die Größe der geladenen Bilder. Ich weiß nicht, wie groß die voreingestellte maximale Cachegröße in Picasso ist, aber es scheint, als ob Sie sie mit nur wenigen Bildern überschreiten, wodurch die anderen aus dem Cache entfernt werden.

Nummer zwei ist wahrscheinlich nicht das Kernproblem, sondern trägt auch zur Leistung bei. Sie tun viel findViewById() Anrufe, die ziemlich teuer sind. Schauen Sie in das "ViewHolder" -Muster nach "Caching" diese Lookups.

Bearbeiten - siehe Jake Wharton's answer to a similar question für weitere Einzelheiten

+0

als Notiz, ändere ich mein Bild und behebe das Problem, das ich als @ 24x7 gesehen habe. – Liangjun

+0

können Sie mir den Code zeigen, wie Sie das Bild auf variabler Höhe und fester Breite skaliert haben? Auch, ist nicht fit() soll das gleiche erreichen? Ich habe einen Anruf dafür bereits in Picasso aufgenommen – 24x7

5

Verwendung mit picasso die Größe

Picasso.with(context) 
.load(imageURL) 
.tag(context) 
.placeholder(R.drawable.kanye8080s) 
.error(R.drawable.stadiumarcadium) 
.into(imageView) 
.resize(x,y); 

// Dies würde

definitiv helfen
-1

Verwendung:

recyclerview.getRecycledViewPool().setMaxRecycledViews(0, 0); 

Das ist mein Problem gelöst

1

Ich würde vorschlagen, dass Sie GLIDE zum Laden von Bildern verwenden. Da GLIDE schnell ist und mit seiner Funktion Cache Laden Sie superschnelle Bild Laden bekommen, mit GLIDE Sie viele Funktionen bekommen ..

Herunterladen https://github.com/bumptech/glide