2009-09-25 2 views
129

L'app per Android attualmente in sviluppo ha un'attività principale che è diventata abbastanza grande. Questo è principalmente perché contiene uno TabWidget con 3 schede. Ogni scheda ha alcuni componenti. L'attività deve controllare tutti quei componenti contemporaneamente. Quindi penso che tu possa immaginare che questa attività abbia come 20 campi (un campo per quasi ogni componente). Inoltre contiene molta logica (click listener, logica per compilare liste, ecc.).Android - Scrittura di un componente (composto) personalizzato

Quello che faccio normalmente in framework basati su componenti è dividere tutto in componenti personalizzati. Ogni componente personalizzato avrebbe quindi una chiara responsabilità. Conterrebbe il proprio insieme di componenti e tutte le altre logiche relative a quel componente.

Ho cercato di capire come farlo e ho trovato nella documentazione di Android qualcosa che a loro piace chiamare un "controllo composto". (Vedere http://developer.android.com/guide/topics/ui/custom-components.html#compound e scorrere fino alla sezione "Controlli composti") Vorrei creare un componente del genere sulla base di un file XML che definisce la struttura della vista.

Nella documentazione si dice:

Nota che, proprio come con un'attività, è possibile utilizzare sia l'approccio dichiarativo (basato su XML) per creare la componenti contenuti, oppure si può nido programmaticamente dal tuo codice.

Bene, questa è una buona notizia! L'approccio basato su XML è esattamente quello che voglio! Ma non dice come farlo, tranne che è "come con un'attività" ... Ma quello che faccio in un'attività è chiamare setContentView(...) per gonfiare le viste da XML. Questo metodo non è disponibile se si ad esempio sottoclasse LinearLayout.

Così ho provato a gonfiare manualmente il codice XML in questo modo:

public class MyCompoundComponent extends LinearLayout { 

    public MyCompoundComponent(Context context, AttributeSet attributeSet) { 
     super(context, attributeSet); 
     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     inflater.inflate(R.layout.my_layout, this); 
    } 
} 

Questo funziona, tranne per il fatto che l'XML io sono carico ha LinearLayout dichiarato come l'elemento radice. Ciò si traduce nel gonfiato LinearLayout di essere un figlio di MyCompoundComponent che già è un LinearLayout !! Quindi ora abbiamo un LinearLayout ridondante tra MyCompoundComponent e le viste di cui ha effettivamente bisogno.

Qualcuno può fornirmi un modo migliore per avvicinarsi a questo, evitando di avere un LinearLayout ridondante istanziato?

+13

Amo le domande dalle quali apprendo qualcosa. Grazie. –

+4

Recentemente ho scritto un post su questo argomento: http://blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3/ –

risposta

98

Usa unire tag come radice XML

<merge xmlns:android="http://schemas.android.com/apk/res/android"> 
<!-- Your Layout --> 
</merge> 

Check this article.

+10

Grazie mille! Questo è esattamente quello che stavo cercando. Incredibile come una domanda così lunga possa avere una risposta così breve. Eccellente! –

+0

Che dire di questo layout di unione in orizzontale? – Kostadin

+0

Ma questo significa che non puoi usare l'editor visuale ... – Timmmm

0

Penso che il modo si suppone di farlo, è utilizzare il nome della classe come l'elemento principale XML:

<com.example.views.MyView xmlns:.... 
     android:orientation="vertical" etc.> 
    <TextView android:id="@+id/text" ... /> 
</com.example.views.MyView> 

E quindi avere la classe derivata da qualsiasi layout che si desidera utilizzare. Tieni presente che se stai utilizzando questo metodo, non utilizzare qui.

public class MyView extends LinearLayout 
{ 
    public ConversationListItem(Context context, AttributeSet attrs) 
    { 
     super(context, attrs); 
    } 
    public ConversationListItem(Context context, AttributeSet attrs, int defStyle) 
    { 
     super(context, attrs, defStyle); 
    } 


    public void setData(String text) 
    { 
     mTextView.setText(text); 
    } 

    private TextView mTextView; 

    @Override 
    protected void onFinishInflate() 
    { 
     super.onFinishInflate(); 

     mTextView = (TextView)findViewById(R.id.text); 
    } 
} 

E quindi è possibile utilizzare la visualizzazione nei layout XML normalmente.Se si vuole fare la visualizzazione a livello di codice è necessario gonfiare te stesso:

MyView v = (MyView)inflater.inflate(R.layout.my_view, parent, false); 

Purtroppo questo non ti permette di fare v = new MyView(context) perché non sembra essere un modo per aggirare il problema layout nidificato, quindi questo isn è davvero una soluzione completa. Si potrebbe aggiungere un metodo come questo per MyView per renderlo un po 'più bello:

public static MyView create(Context context) 
{ 
    return (MyView)LayoutInflater.fromContext(context).inflate(R.layout.my_view, null, false); 
} 

Disclaimer: io può parlare bollocks completi.

+0

Grazie! Penso che anche questa risposta sia corretta :) Ma tre anni fa, quando ho fatto questa domanda, "unire" ha fatto anche il trucco! –

+8

E poi qualcuno arriva e prova a usare la tua vista personalizzata in un layout da qualche parte con "=" "' "e le tue chiamate' setData' e 'onFinishInflate' iniziano a lanciare gli NPE, e non hai idea del perché. –

+0

Il trucco qui è utilizzare la vista personalizzata, quindi nel costruttore, gonfiare un layout che utilizza un tag di unione come radice. Ora puoi usarlo in XML, o semplicemente aggiornandolo. Tutte le basi sono coperte, che è esattamente ciò che la domanda/risposta accettata fanno insieme. Ciò che non puoi fare è comunque fare riferimento direttamente al layout. Ora è "posseduto" dal controllo personalizzato e dovrebbe essere utilizzato solo da quello nel costruttore. Ma se stai usando questo approccio, perché dovresti comunque usarlo altrove? Non lo faresti. – MarqueIV