2012-01-17 6 views
7

Ho riscontrato ciò che posso solo classificare come perdita di memoria per gli elementi ScrollView quando si utilizza il componente Galleria.L'aggiunta di una semplice ScrollView alla Galleria causa una perdita di memoria

Un breve sfondo. Ho un'app esistente che è un'app per slideshow di foto. Utilizza il componente Galleria, ma ogni elemento nell'adattatore viene visualizzato a schermo intero. (la fonte completa è disponibile a this link)

L'elemento Vista scheda è costituito da un ImageView e due TextViews per titolo e descrizione. Poiché le foto hanno una risoluzione piuttosto elevata, l'app utilizza molta memoria, ma in genere la Galleria riesce a riciclarle bene.

Tuttavia, quando ora sto implementando una ScrollView per la descrizione TextView, ho quasi subito problemi di memoria. Questo l'unico cambiamento che ho fatto

<ScrollView 
android:id="@+id/description_scroller" 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:scrollbars="vertical" 
android:fillViewport="true"> 
    <TextView 
    android:id="@+id/slideshow_description" 
    android:textSize="@dimen/description_font_size" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:textColor="@color/white" 
    android:layout_below="@id/slideshow_title" 
    android:singleLine="false" 
    android:maxLines="4"/> 
</ScrollView> 

ho fatto un heap dump e chiaramente potrebbe vedere che era la Scrollview che era la radice dei problemi di memoria.

Ecco due schermate dall'analisi dell'heap dump. Si noti che lo ScrollView mantiene un riferimento a mParent che comprende la foto di grandi dimensioni che uso Heap analysis - leak candidate Heap analysis - drilldown to a single ScrollView

PS stesso problema si verifica se uso lo scorrimento del TextView (Android: barre di scorrimento = "verticale" e .setMovementMethod (nuova ScrollingMovementMethod());

PSS provato lo spegnimento della cache disegno persistente, ma non dreaandroid diverso: persistentDrawingCache = "none"

risposta

3

Basta aggiungere questo -> Android: isScrollContainer = "false"

<ScrollView 
    android:id="@+id/description_scroller" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:scrollbars="vertical" 
    android:fillViewport="true" 
    android:isScrollContainer="false"> 

C'è qualche fonte perché questo è appaia: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/view/View.java

il problema è:

setScrollContainer(boolean isScrollContainer) 

da valore predefinito:

boolean setScrollContainer = false; 

ma in alcuni casi come questo

if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) { 
    setScrollContainer(true); 
} 

può essere vero, e quando happends

/** * Modifica se questo punto di vista è una delle serie di contenitori scorrevoli in * la sua finestra. Questo sarà usato per determinare se la finestra può * ridimensionare o deve scorrere quando un'area di input morbida è aperta - i contenitori scorrevoli * consentono alla finestra di utilizzare la modalità di ridimensionamento poiché il contenitore * si restringerà in modo appropriato. */

public void setScrollContainer(boolean isScrollContainer) { 
    if (isScrollContainer) { 
     if (mAttachInfo != null && (mPrivateFlags&SCROLL_CONTAINER_ADDED) == 0) { 
      mAttachInfo.mScrollContainers.add(this); 
      mPrivateFlags |= SCROLL_CONTAINER_ADDED; 
     } 
     mPrivateFlags |= SCROLL_CONTAINER; 
    } else { 
     if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) { 
      mAttachInfo.mScrollContainers.remove(this); 
     } 
     mPrivateFlags &= ~(SCROLL_CONTAINER|SCROLL_CONTAINER_ADDED); 
    } 
} 

mAttachInfo.mScrollContainers.add (questo) - tutti Vedi messo in ArrayList questo cavo fuoriuscita di memoria talvolta

+0

Impressionante, questo sembra aver risolto la perdita di memoria. Tuttavia, non riesco a scorrere automaticamente TextView. Questo è il codice che sto cercando di usare slideshowDescription.setText (slideshowPhoto.getDescription()); Scroller scroller = new Scroller (context); slideshowDescription.setScroller (scroller); Scroller .computeScrollOffset(); descrizione intBottom = slideshowDescription.getBottom(); scroller.startScroll (slideshowDescription.getScrollX(), slideshowDescription.getScrollY(), 0, descriptionBottom, 400); – dparnas

+0

Sembra che stavo correndo nel famigerato problema con altezza e altre proprietà non inizializzate e quindi aveva il valore 0. Dovrebbe essere in grado di codificare tutto ciò. Grazie – dparnas

+0

Questo approccio non ha funzionato, ma ho trovato una soluzione alternativa che pubblicherò – dparnas

2

Sì ho notato il problema, mi spiace per il mio commento precedente, ho cercato di svuotare i drawable da impostazione precedente Drawable.setCallBack(null); ma non ha funzionato, btw ho quasi lo stesso progetto, io uso ViewFlipper invece di Gallery, così posso controllare ogni cosa, e io uso solo 2 Visualizzazioni in esso, e passare da una all'altra, e nessuna perdita di memoria, e perché non tu ridimensiona l'immagine prima di visualizzarla, quindi ridurrà l'utilizzo della memoria (cerca SO per ridimensionare l'immagine prima di leggerla)

+0

Se avessi cominciato da zero, mi piacerebbe utilizzare ViewFlipper pure. Ma al momento sono abbastanza impegnato ad usare la Gallery e questa perdita di memoria di ScrollView mi sta molestando un bel po '. – dparnas

4

Hai provato a rimuovere la vista di scorrimento ogni volta che la vista del contenitore scorre fuori dallo schermo? Non sono sicuro che funzioni per te ma ne vale la pena uno scatto? In alternativa, prova a chiamare setScrollContainer (false) sulla vista di scorrimento quando lascia lo schermo. Sembra rimuovere la vista dal set mScrollContainers.

Inoltre, this question, risposta di Dianne Hackborn (ingegnere Android), afferma esplicitamente di non utilizzare le viste scorrevoli all'interno di una Galleria. Forse questo problema è perché?

+0

Grazie per l'input. Pensa che questa sia la risposta più vicina che otterrò. Tuttavia, penso che non lo risolverà completamente. Probabilmente potrò rimuovere la scrollview quando sono in onfling o quando partirò dall'app per eseguire una transizione. Ma quando l'adattatore viene modificato con notifyDataSetChanged sarà più difficile – dparnas

0

Provare a spostare "android: layout_below =" @ id/slideshow_title "in TextView a ScrollView.

0

finito con l'attuazione di una soluzione che utilizza un TextSwitcher che viene modificato automaticamente la sottostringa rimanente ogni x secondi.

Ecco la relativa definizione XML dal layout

 <TextSwitcher 
     android:id="@+id/slideshow_description" 
     android:textSize="@dimen/description_font_size" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content"> 
      <TextView 
      android:id="@+id/slideshow_description_anim1" 
      android:textSize="@dimen/description_font_size" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:maxLines="2" 
      android:textColor="@color/white" 
      android:singleLine="false"/> 
         <TextView 
      android:id="@+id/slideshow_description_anim2" 
      android:textSize="@dimen/description_font_size" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:maxLines="2" 
      android:textColor="@color/white" 
      android:singleLine="false"/> 
    </TextSwitcher> 

Qui aggiungo l'animazione passaggio alla TextSwitcher (nel metodo GetView dell'adattatore)

final TextSwitcher slideshowDescription = (TextSwitcher)slideshowView.findViewById(R.id.slideshow_description); 
      Animation outAnim = AnimationUtils.loadAnimation(context, 
        R.anim.slide_out_down); 
      Animation inAnim = AnimationUtils.loadAnimation(context, 
        R.anim.slide_in_up);   

      slideshowDescription.setInAnimation(inAnim); 
      slideshowDescription.setOutAnimation(outAnim); 

Ecco come ho SWAP per la parte della descrizione

 private void updateScrollingDescription(SlideshowPhoto currentSlideshowPhoto, TextSwitcher switcherDescription){ 
     String description = currentSlideshowPhoto.getDescription(); 

     TextView descriptionView = ((TextView)switcherDescription.getCurrentView()); 
     //note currentDescription may contain more text that is shown (but is always a substring 
     String currentDescription = descriptionView.getText().toString(); 

     if(currentDescription == null || description==null){ 
      return; 
     } 

     int indexEndCurrentDescription= descriptionView.getLayout().getLineEnd(1);  

     //if we are not displaying all characters, let swap to the not displayed substring 
     if(indexEndCurrentDescription>0 && indexEndCurrentDescription<currentDescription.length()){ 
      String newDescription = currentDescription.substring(indexEndCurrentDescription); 
      switcherDescription.setText(newDescription);  
     }else if(indexEndCurrentDescription>=currentDescription.length() && indexEndCurrentDescription<description.length()){ 
      //if we are displaying the last of the text, but the text has multiple sections. Display the first one again 
      switcherDescription.setText(description); 
     }else { 
      //do nothing (ie. leave the text) 
     }   

    } 

E infine, qui è dove ho impostato il timer che induce a aggiornare ogni 3,5 secondi

 public void setUpScrollingOfDescription(){ 
     final CustomGallery gallery = (CustomGallery) findViewById(R.id.gallery); 
     //use the same timer. Cancel if running 
     if(timerDescriptionScrolling!=null){ 
      timerDescriptionScrolling.cancel(); 
     } 

     timerDescriptionScrolling = new Timer("TextScrolling"); 
     final Activity activity = this; 
     long msBetweenSwaps=3500; 

     //schedule this to 
     timerDescriptionScrolling.scheduleAtFixedRate(
      new TimerTask() { 
       int i=0; 
       public void run() {      
        activity.runOnUiThread(new Runnable() { 
         public void run() { 
          SlideshowPhoto currentSlideshowPhoto = (SlideshowPhoto)imageAdapter.getItem(gallery.getSelectedItemPosition()); 

          View currentRootView = gallery.getSelectedView(); 
          TextSwitcher switcherDescription = (TextSwitcher)currentRootView.findViewById(R.id.slideshow_description); 

          updateScrollingDescription(currentSlideshowPhoto,switcherDescription); 

          //this is the max times we will swap (to make sure we don't create an infinite timer by mistake 
          if(i>30){ 
           timerDescriptionScrolling.cancel(); 
          } 
          i++; 
         } 
        }); 

       } 
      }, msBetweenSwaps, msBetweenSwaps); 
    } 

Finalmente posso mettere questo problema ad un riposo :)

Problemi correlati