2011-08-28 16 views
13

UPDATE 2011-08-29 Se rimuovo l'immagine nel NodePickup, la scarsità è sparita.ListView molto lento durante lo scorrimento (utilizzando ViewHolder/riciclaggio)

La domanda è: perché?

----

sono tornato a provare alcuni Android Dev nuovo. Ho un "vecchio" telefono HTC Hero in giro, quindi l'ho avviato, ho fatto degli aggiornamenti e ora sono di nuovo in esecuzione con Eclipse e il resto.

Ho Android 2.1 in esecuzione sul dispositivo.

Ho fatto un'app di prova molto semplice che non fa nulla tranne che per mostrare alcune attività e così via. Anche se non esiste una connessione al database, nessun dato recuperato da qualsiasi rete l'app è molto lento. Molto lento.

Ad esempio, ho un ListView con alcuni elementi di layout personalizzati. Quando si aggiungono solo 6-7 elementi (così ottengo lo scorrimento) è incredibilmente lento durante lo scorrimento. Inoltre, ho alcuni pulsanti che cambia l'Activity e anche che è molto molto lento:

mButtonListenerUPP = new OnClickListener() { 
    @Override 
    public void onClick(View v) 
    { 
     Intent myIntent = new Intent(BaseActivity.this, ActivityMain.class); 
     BaseActivity.this.startActivity(myIntent); 
    } 
}; 

io non riesco a capire il motivo per cui, in modo da Im basta inviare il codice qui e spero che qualcuno ha qualche suggerimento per me =)

Thx!

L'adattatore, NodeRowAdapter

import java.util.ArrayList; 
import java.util.List; 

import android.app.Activity; 
import android.content.Context; 
import android.view.*; 
import android.widget.ArrayAdapter; 

import android.widget.TextView; 

public class NodeRowAdapter extends ArrayAdapter 
{ 
    private Activity context; 
    private ArrayList<Node> mList; 
    private LayoutInflater inflater; 

    NodeRowAdapter(Activity context, ArrayList<Node> objects) 
    { 
     super(context, R.layout.nodepickup, objects); 
     this.context=context; 
     mList = objects; 
     inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    } 

    class ViewHolder 
    { 
     TextView name; 
     TextView time; 
     TextView road; 
     Node node; 
    } 

    public Node getNode(int position) 
    { 
     if (mList != null && mList.size() > position) 
      return mList.get(position); 
     else 
      return null; 
    } 
    public View getView(int position, View convertView, ViewGroup parent) 
    { 
     View view = convertView; 
     ViewHolder holder; 
     if (view == null) 
     { 
      view = inflater.inflate(R.layout.nodepickup, parent, false); 
      holder = new ViewHolder(); 
      holder.name =(TextView)view.findViewById(R.id.name); 
      holder.time =(TextView)view.findViewById(R.id.time); 
      holder.road =(TextView)view.findViewById(R.id.road); 
      view.setTag(holder); 
     } 
     else 
     { 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     Node node = mList.get(position); 
     holder.name.setText(node.name); 
     holder.time.setText(node.time); 
     holder.road.setText(node.road); 

     return(view); 
    } 
} 

L'attività principale, ActivityMain

public class ActivityMain extends BaseActivity 
{ 
    private NodeRowAdapter _nodeRowAdapter; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     requestWindowFeature(Window.FEATURE_NO_TITLE); 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     SICApplication._myContext = this; 
     SICApplication._myContext = this; 

     _nodeRowAdapter = new NodeRowAdapter(this, SICApplication.dataHolder_activityMain._nodes); 
     ListView listView1 = (ListView) findViewById(R.id.ListViewNodes); 
     listView1.setOnItemClickListener(new OnItemClickListener() 
     { 
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
      { 
       Node node = _nodeRowAdapter.getNode(position); 
       Log.v("MyApp", "Node=" + node.name); 
      } 
     }); 
     listView1.setAdapter(_nodeRowAdapter); 

    } 

    /* Handles item selections */ 
    public boolean onOptionsItemSelected(MenuItem item) 
    { 
     switch (item.getItemId()) 
     { 
      case R.id.add_item: 
       addNodeItem(); 
       return true; 
     } 
     return false; 
    } 



    private void addNodeItem() 
    { 
     _nodeRowAdapter.add(new Node("Test", "asd asd ", "14:00", 1)); 

    } 
} 

La voce elenco personalizzato, NodePickup

public class NodePickup extends LinearLayout 
{ 
    public NodePickup(Context context, AttributeSet attributeSet) 
    { 
     super(context, attributeSet); 

     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     inflater.inflate(R.layout.nodepickup, this); 

     this.setOnClickListener(new OnClickListener() 
     { 
      @Override 
      public void onClick(View v) 
      { 
       AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 
       builder.setMessage("Ajabaja!") 
       .setCancelable(true) 
       .setPositiveButton("JA!", new DialogInterface.OnClickListener() 
       { 
        public void onClick(DialogInterface dialog, int id) 
        { 
         dialog.cancel(); 
        } 
       }); 
       builder.show(); 
      } 

     }); 
    } 
} 

E, infine, il layout XML NodePickup

<LinearLayout 
    android:id="@+id/LinearLayout01" 
    android:layout_width="fill_parent" 
    android:layout_height="64dip" 
    android:orientation="horizontal" 
    android:background="@drawable/stateful_background" 
    xmlns:android="http://schemas.android.com/apk/res/android"> 

    <ImageView 
     android:id="@+id/ImageView01" 
     android:layout_width="40dip" 
     android:layout_height="40dip" 
     android:src="@drawable/arrow_up_green" 
     android:background="@android:color/transparent"> 
    </ImageView> 

    <LinearLayout 
     android:id="@+id/LinearLayout02" 
     android:background="@android:color/transparent" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:orientation="vertical" 
     xmlns:android="http://schemas.android.com/apk/res/android"> 

     <TextView 
      android:text="14:46 (15 min)" 
      android:id="@+id/time" 
      android:textSize="12dip" 
      android:textColor = "#000000" 
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:background="@android:color/transparent"> 
     </TextView> 

     <TextView 
      android:text="test" 
      android:id="@+id/road" 
      android:textSize="12dip" 
      android:textColor = "#000000" 
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:background="@android:color/transparent"> 
     </TextView> 

     <TextView 
      android:text="test test" 
      android:id="@+id/name" 
      android:textSize="12dip" 
      android:textColor = "#000000" 
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:background="@android:color/transparent"> 
     </TextView> 
    </LinearLayout> 
</LinearLayout> 
+0

Utilizzare Traceview per determinare il punto in cui si sta eseguendo l'ora. – CommonsWare

+0

Thx, ci ho provato. Non mi ha detto niente. Forse mi manca qualcosa. Tuttavia, sembra che sia ImageView a farlo. Se rimuovo quello da NodePickup, è veloce di nuovo – Ted

risposta

16

UPDATE 2011-08-29 Se rimuovo l'immagine nel NodePickup, il lagginess è andato.

La visualizzazione ha difficoltà a capire come deve essere eseguito il rendering del layout. L'xml che hai pubblicato non aiuta molto. Se rimuovi il ImageView, LinearLayout02 prenderà tutta la larghezza del genitore. Ma avendo l'imageView con dimensioni standard e il layout a destra, fill_parent confonde molto la vista. Richiede di nuovo la dimensione di imageView per "spingere i margini a destra" (tipo di).Date un'occhiata a miei suggerimenti sotto

Tip1

utilizzare il peso di proprietà LinearLayout. Crea anche imageView fill_parent e LinearLayout (per la larghezza) e gioca con le proprietà weight. Fallo anche per il layout verticale con TextView. La soluzione migliore dovrebbe essere quella di mettere una dimensione fissa all'altezza del pensiero di TextViews. Considerare anche di cambiare la vista dall'alto in RelativeLayout. Crea l'immagine con dimensioni standard, AlignParentLeft e metti LinearLayout02 su RightView viewView. Solleverai molto l'onMeasure del ViewGroup.

Tip2

Sembra che quando il testo cambia altezza tutta la visione deve essere reinflated.A tecnica comune per evitare che per fare lista Articolo fissato in altezza. Quindi la listview può riutilizzare le viste riciclate senza reinfliggere.

Tip3

Lascia la tua LinearLayout02 un'altezza fissa di 64DP o Fill_Parent in quanto non si dispone di uno spazio a sinistra, ma la disposizione non si sa che e cercare di riorganizzare è di per sé ogni volta poiché il testo è anche Wrap_content.

Inoltre hai detto che se rimuovi il ImageView tutto è veloce di nuovo. Se quanto sopra non ha alcun effetto puoi per favore provare questo? Dato che sai che la dimensione di imageView è fissa.

Estendere l'immagine e sovrascrivere il metodo requestLayout().

public class MyImageView extends ImageView { 

public PImageView(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 

} 

public PImageView(Context context, AttributeSet attrs) { 
    super(context, attrs); 

} 

public PImageView(Context context) { 
    super(context); 

} 

@Override 
    public void requestLayout() { 
     /* 
     * Do nothing here 
     */ 
    } 
} 

Ora includere il widget MyImageView in XML in questo modo.

<com.package_name.MyImageView 
     android:id="@+id/ImageView01" 
     android:layout_width="40dip" 
     android:layout_height="40dip" 
     android:src="@drawable/arrow_up_green" 
     android:background="@android:color/transparent"> 
</com.package_name.MyImageView > 
+0

Thx. Non sono sicuro, la sciatteria è scomparsa anche se non ho fatto nulla per il codice. Un po 'strano, ma ... In ogni caso, ho preso alcuni dei tuoi suggerimenti/suggerimenti =) – Ted

+0

È incredibile come si addolciscono le mie pergamene ListView visto che sto usando il tuo "Suggerimento 3". Come lo hai capito?/Potresti spiegare PERCHE 'è così o se ci sono degli svantaggi? –

+0

@JohannesStaehlin la vista che conosce le sue dimensioni non cerca di capirle. con wrap_content sta provando a farlo in runtime. Il suggerimento – weakwire

0

Caricare l'immagine una volta come bitmap e applicarlo al ImageView di programmazione dopo aver gonfiato esso. Se hai bisogno di immagini diverse per articolo è necessario creare le bitmap in modo asincrono, come descritto nel Android: How to optimize ListView with ImageView + 3 TextViews?

+0

Prenderò in considerazione il tuo suggerimento a breve =) – Ted

+0

Ho delle immagini diverse da mostrare, a seconda dello stato del mio oggetto. Ma per andare a prenderli "asynch" sembra del tutto inutile. Sono imballati con l'app, non è necessario scaricarli ... – Ted

+0

Anche se le immagini sono locali, potrebbe essere necessario un po 'di tempo per caricarle, ad esempio quando si desidera visualizzare le foto dei contatti: possono essere potenzialmente molto grandi e devono essere ridimensionato. In una ListView ciò comporterebbe un ritardo notevole. Le immagini confezionate dovrebbero probabilmente andare bene a meno che tu non abbia un dispositivo molto lento. – mattlaabs

0

Prova utilizzando android:cacheColorHint="#00000000" per il vostro ListView. Per migliorare le prestazioni del disegno durante le operazioni di scorrimento, il framework Android riutilizza il suggerimento sul colore della cache.

Riferimento: developer.android.com articolo.

+0

Vuoi dire # 000000 – strange

+0

@strange il 00 in più significa il canale alfa, quindi sarà trasparente –

+0

per usarlo non ci sono cambiamenti nelle prestazioni. ancora lo scorrimento di listview è più lento. –

15

Dopo aver ottimizzato il metodo getView() per utilizzare un supporto e riutilizzare convertView se non è nullo, il mio listview era ancora piuttosto lento. Poi, ho provato

listView.setScrollingCacheEnabled(false); 

ed è risolto in una sola volta.

Spero che questo aiuti qualcuno.

+0

questo mi ha aiutato. Grazie! –

+0

L'ho visto molte volte nei suggerimenti di battitura. Ma non ha mai saputo lo scopo. +1 –

1

Ci è voluto un po '! Ho provato di tutto. Disabilitare la cache di scorrimento, viewHolder, cacheColorHint ... ma non ha funzionato!

Dopo aver cercato per molte ore ho trovato la radice di tutti i mali!

Nel mio themes.xml ho avuto un'immagine di ridimensionamento di sfondo:

<item name="android:windowBackground">@drawable/window_bg</item> 

Dopo aver rimosso il tutto beackground era il burro morbido.

Spero che questo aiuti qualcuno!

6

Ho appena scoperto questo e voglio condividerlo con voi ragazzi.

Ho provato TUTTE le soluzioni fornite ma non ha funzionato. Ciò che è stato la causa del problema è la lunghezza del testo che sto dando da mangiare uno dei miei punti di vista TextView perché sto utilizzando

mTextView.SetText(theLongString) 

nel mio adattatore nel mio metodo GetView. Ora che ho la mia contrazione String, il ListView è molto liscia :)

+0

ho lo stesso problema anche io !! ma perché!? (grazie mille per la tua bella soluzione !!!: D) – strings95

1

Per migliorare le prestazioni di ListView utilizzare entrambi o uno qualsiasi dei due - (implementazione semplice)

  • ViewHolder
  • AsyncTask (un separato filo)

Se avete moderatamente lunghi elenchi vi consiglio ViewHolder altrimenti per grandi liste (come nel caso di un lettore musicale) mi consiglia di utilizzare ViewHolder con AsyncTask. La chiave per facilitare lo scorrimento di ListView è ridurre il consumo di memoria il più possibile.

Per implementare ViewHolder, è semplice. Basta creare una classe statica nella scheda personalizzata che contiene tutte le viste che trovi tramite findViewById. Quindi questo è come dovrebbe apparire -

static class ViewHolder 
{ 
TextView text; 
TextView timestamp; 
ImageView icon; 
ProgressBar progress; 
int position; 
} 

Quindi, la prossima volta che è necessario findViewById uno qualsiasi di questi punti di vista, basta fare riferimento al ViewHolder come questo-

ViewHolder holder = new ViewHolder(); 
holder.icon = (ImageView) yourView.findViewById(R.id.listitem_image); 
holder.text = (TextView) yourView.findViewById(R.id.listitem_text); 
holder.timestamp = (TextView) yourView.findViewById(R.id.listitem_timestamp); 
holder.progress = (ProgressBar) yourView.findViewById(R.id.progress_spinner); 

Questo è testato codice e preso da uno dei miei progetti. Tuttavia, la fonte originale è qui - http://developer.android.com/training/improving-layouts/smooth-scrolling.html

Gli elenchi diventano più agevoli utilizzando ViewHolder. Usare AsyncTask è un po 'più complesso, ma controlla il link se desideri implementare AsyncTask insieme a ViewHolder per un'esperienza di scorrimento molto più fluida. In bocca al lupo!

0

Questo potrebbe aiutare qualcuno

Se si dispone di un'immagine nella lista il vostro articolo, si deve ricordare di ridurre la qualità di tale immagine. È molto più veloce da caricare in alcuni Kb di alcuni megabyte.

Questo mi ha aiutato

public Bitmap MakeFileSmaller_ToBitmap(File f) { 
     try { 
      // Decode image size 
      BitmapFactory.Options o = new BitmapFactory.Options(); 
      o.inJustDecodeBounds = true; 
      BitmapFactory.decodeStream(new FileInputStream(f), null, o); 

      // The new size we want to scale to 
      final int REQUIRED_SIZE=200; 

      // Find the correct scale value. It should be the power of 2. 
      int scale = 1; 
      while(o.outWidth/scale/2 >= REQUIRED_SIZE && 
        o.outHeight/scale/2 >= REQUIRED_SIZE) { 
       scale *= 2; 
      } 

      // Decode with inSampleSize 
      BitmapFactory.Options o2 = new BitmapFactory.Options(); 
      o2.inSampleSize = scale; 
      return BitmapFactory.decodeStream(new FileInputStream(f), null, o2); 
     } catch (FileNotFoundException e) { 
      Log.d(TAG, "FILE NOT FOUND "); 
     } 
     Log.d(TAG, "OTHER EXCEPTION"); 

     return null; 
    } 
0

Ho avuto lo stesso problema prima, mentre stavo usando layout diverso come LinearLayout, RelativeLayout, CardView come genitore del bambino diverso nella stessa voce di visualizzazione elenco. Ho risolto il problema cambiando tutte le viste all'interno di RelativeLayout.

Utilizzo di RelativeLayout come layout principale e secondario può aumentare la velocità di caricamento di ciascun elemento. Quindi lo scorrimento sarà scorrevole.

<RelativeLayout 
android:id="@+id/LinearLayout01" 
android:layout_width="fill_parent" 
android:layout_height="64dip" 
android:orientation="horizontal" 
android:background="@drawable/stateful_background" 
xmlns:android="http://schemas.android.com/apk/res/android"> 
<RelativeLayout 
    android:layout_width="40dip" 
    android:layout_height="40dip" 
    android:layout_alignParentTop="true" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentStart="true" 
    android:id="@+id/relativeLayout"> 
    <ImageView 
     android:id="@+id/ImageView01" 
     android:layout_width="40dip" 
     android:layout_height="40dip" 
     android:src="@drawable/arrow_up_green" 
     android:background="@android:color/transparent"> 
    </ImageView> 
</RelativeLayout> 
<TextView 
    android:text="14:46 (15 min)" 
    android:id="@+id/time" 
    android:textSize="12dip" 
    android:textColor = "#000000" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:background="@android:color/transparent" 
    android:layout_alignParentTop="true" 
    android:layout_toRightOf="@+id/relativeLayout" 
    android:layout_toEndOf="@+id/relativeLayout" /> 
<TextView 
    android:text="test" 
    android:id="@+id/road" 
    android:textSize="12dip" 
    android:textColor = "#000000" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:background="@android:color/transparent" 
    android:layout_below="@+id/time" 
    android:layout_toRightOf="@+id/relativeLayout" 
    android:layout_toEndOf="@+id/relativeLayout" /> 
<TextView 
    android:text="test test" 
    android:id="@+id/name" 
    android:textSize="12dip" 
    android:textColor = "#000000" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:background="@android:color/transparent" 
    android:layout_below="@+id/road" 
    android:layout_toRightOf="@+id/relativeLayout" 
    android:layout_toEndOf="@+id/relativeLayout"/></RelativeLayout> 
0

È possibile utilizzare listview.setScrollingCacheEnabled (false) .Quando lo scorrimento applicazione zona attesa ListView poi gettando Out Of Memory (OOM) soluzione exception.My è lavorato per me.

Problemi correlati