2011-09-15 15 views
6

Sto scrivendo un'app con l'unico scopo di cercare di capire come funziona la gerarchia delle viste in Android. Sto avendo alcuni problemi veramente difficili in questo momento. Cercherò di essere conciso nella mia spiegazione qui.Tentativo di comprendere la gerarchia delle viste

Impostazione:

Attualmente ho tre visualizzazioni. 2 sono ViewGroups e 1 è solo un View. Diciamo che sono in questo ordine:

TestA extends ViewGroup 
    TestB extends ViewGroup 
    TestC extends View 
    TestA->TestB->TestC 
    Where TestC is in TestB and TestB is in TestA. 

Nel mio Activity ho semplicemente visualizzare i punti di vista in questo modo:

TestA myView = new TestA(context); 
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)); 
setContentView(myView); 

Problemi:

  1. Il metodo di Testa onDraw(Canvas canvas) non è mai chiamato. Ho visto un paio di soluzioni a questo dicendo che la mia vista non ha alcuna dimensione (altezza/larghezza = 0), tuttavia, questo non è il caso. Quando eseguo l'override di onLayout(), ottengo le dimensioni del mio layout e sono corrette. Inoltre, getHeight()/Width() sono esattamente come dovrebbero essere. Posso anche eseguire l'override di dispatchDraw() e ottenere le mie visualizzazioni di base da disegnare.

  2. Desidero animare un oggetto in TestB. Tradizionalmente, sostituivo il metodo onDraw() alla chiamata invalidate() su se stesso fino a quando l'oggetto terminava l'animazione che doveva fare. Tuttavia, in TestB, quando chiamo invalidate() la vista non viene mai ridisegnata. Ho l'impressione che sia compito della mia vista genitore chiamare di nuovo il metodo onDraw(), ma la mia vista genitore non chiama di nuovo lo dispatchDraw().

Credo che le mie domande sono, perché sarebbe il mio metodo di mia vista padre onDraw() non vengono mai chiamati per cominciare? Quali metodi nella mia vista genitore dovrebbero essere chiamati quando uno dei suoi figli si annulla? Il genitore è il responsabile per garantire che i bambini vengano prelevati o che Android si occupi di ciò? Se Android risponde a invalidate(), perché il mio TestB non viene mai estratto nuovamente?

risposta

5

Ok, dopo alcune ricerche e un sacco di tentativi, ho scoperto che stavo facendo tre cose sbagliate riguardo al problema n. 2. Molte erano risposte semplici ma non molto ovvie.

  • È necessario eseguire l'override di onMeasure() per ogni vista. Entro onMeasure(), è necessario chiamare measure() per ogni figlio contenuto nello ViewGroup passando in MeasureSpec di cui il bambino ha bisogno.

  • È necessario chiamare addView() per ogni figlio che si desidera includere. Inizialmente, sono stato semplicemente creato un oggetto vista e usandolo direttamente.Questo mi ha permesso di disegnarlo una volta, ma la vista non era inclusa nell'albero della vista, così quando ho chiamato invalidate() non stava invalidando l'albero della vista e non il ridisegno. Per esempio:

a Testa:

TestB childView; 
TestA(Context context){ 
    ****** setup code ******* 
    LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 
    childView = new TestB(context); 
    childView.setLayoutParams(params); 
} 

@Override 
protected void dispatchDraw(Canvas canvas){ 
    childView.draw(canvas); 
} 

Questo attirerà la vista del bambino una volta. Tuttavia, se quella vista ha bisogno di aggiornamenti per le animazioni o qualsiasi altra cosa, è andata così. Inserisco addView(childView) nel costruttore di TestA per aggiungerlo all'albero delle viste. codice finale è come tale:

TestB childView; 
TestA(Context context){ 
    ****** setup code ******* 
    LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 
    childView = new TestB(context); 
    childView.setLayoutParams(params); 
    addView(childView); 
} 

@Override 
protected void dispatchDraw(Canvas canvas){ 
    childView.draw(canvas); 
} 

alternativa, potrei ignorare dispatchDraw(Canvas canvas) in questo modo se ho avuto molti più bambini per disegnare, ma ho bisogno di qualche elemento personalizzato tra ogni disegno come linee della griglia o qualcosa del genere.

@Override 
protectd void dispatchDraw(Canvas canvas){ 
    int childCount = getChildCount(); 
    for(int i = 0; i < size; i++) 
    { 
     drawCustomElement(); 
     getChildAt(i).draw(canvas); 
    } 
} 
  • Devi eseguire l'override onLayout() (è astratto in ViewGroup comunque, quindi è necessario in ogni caso). All'interno di questo metodo, è necessario chiamare layout per ogni bambino. Anche dopo aver fatto le prime due cose, le mie opinioni non sarebbero state invalidate. Non appena ho fatto questo, tutto ha funzionato perfettamente.

UPDATE: Problema # 1 è stato risolto. Un'altra soluzione estremamente semplice ma non così ovvia.

Quando creo un'istanza di TestA, devo chiamare setWillNotDraw(false) oppure Android non la disegna per motivi di ottimizzazione. Quindi la configurazione completa è:

TestA myView = new TestA(context); 
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)); 
myView.setWillNotDraw(false); 
setContentView(myView); 
+0

puoi condividere questo progetto demo del Viewgroup nel tuo blog o da qualche parte sarà molto utile .. Grazie amico .. – user1201239

+0

devo aggiungere il childView anche se lo specifichiamo in XML? – user1201239

+0

Mi dispiace. Non ho un blog o qualcosa su cui caricare. Cercherò di essere il più utile possibile. Per rispondere alla tua prima domanda, no. Quando si gonfia l'xml, il bambino è contenuto nel 'ViewGroup'. Il primo che puoi recuperare è 'onFinishInflate()'. Dopo di che hai una scelta. Prendi i tuoi figli tramite il metodo 'getChildAt (int index)' o usa 'findViewById (int id)'. L'indice sarà l'ordine in cui i bambini si trovano in xml. Non so come funziona l'indicizzazione con i bambini nei bambini. – DeeV

2

Questa non è una risposta diretta, ma here is a fantastic tutorial su come disegnare viste personalizzate e offre un ottimo cours crash in come animare una vista. Anche il codice è molto semplice e pulito.

Spero che questo aiuti!

+0

Grazie. Questo non ha risolto i miei problemi, ma ha aiutato a portare alla soluzione. – DeeV

Problemi correlati