2009-09-25 7 views
129

Die Android-App, die ich gerade entwickle, hat eine Hauptaktivität, die ziemlich groß geworden ist. Dies liegt vor allem daran, dass es eine TabWidget mit 3 Tabs enthält. Jede Registerkarte hat einige Komponenten. Die Aktivität muss alle diese Komponenten gleichzeitig kontrollieren. Ich denke, man kann sich vorstellen, dass diese Aktivität 20 Felder hat (ein Feld für fast jede Komponente). Es enthält auch eine Menge Logik (klicken Sie auf Listener, Logik, um Listen zu füllen, usw.).Android - Eine benutzerdefinierte (zusammengesetzte) Komponente schreiben

Normalerweise verwende ich in komponentenbasierten Frameworks, um alles in benutzerdefinierte Komponenten aufzuteilen. Jede benutzerdefinierte Komponente hätte dann eine eindeutige Verantwortung. Es würde seinen eigenen Satz von Komponenten und alle anderen mit dieser Komponente verbundenen Logik enthalten.

Ich habe versucht, herauszufinden, wie dies getan werden kann, und ich fand etwas in der Android-Dokumentation, was sie gerne eine "Compound Control" nennen. (Siehe http://developer.android.com/guide/topics/ui/custom-components.html#compound und scrollen Sie zum Abschnitt "Zusammengesetzte Steuerelemente".) Ich möchte eine solche Komponente basierend auf einer XML-Datei erstellen, die die Ansichtsstruktur definiert.

In der Dokumentation heißt es:

Beachten Sie, dass nur mit einer Aktivität mögen, Sie entweder die deklarative (XML-basiert) Ansatz zur Schaffung der Komponenten enthalten können, oder Sie können Nest sie programmatisch aus Ihrem Code.

Nun, das sind gute Nachrichten! Der XML-basierte Ansatz ist genau das, was ich will! Aber es sagt nicht, wie es geht, außer dass es "wie mit einer Aktivität" ist ... Aber was ich in einer Aktivität mache, ist Anruf setContentView(...), um die Ansichten von XML aufzublasen. Diese Methode ist nicht verfügbar, wenn Sie beispielsweise die Unterklasse LinearLayout verwenden. So

Ich habe versucht, die XML manuell wie diese aufzublasen:

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); 
    } 
} 

Dies funktioniert, mit Ausnahme der Tatsache, dass die XML Ich bin Laden LinearLayout als Root-Element deklariert hat. Dies führt dazu, dass das aufgeblasene LinearLayout ein Kind von MyCompoundComponent ist, welches selbst bereits ein LinearLayout ist !! Jetzt haben wir ein redundantes LinearLayout zwischen MyCompoundComponent und den Ansichten, die es tatsächlich benötigt.

Kann mir bitte jemand einen besseren Weg geben, um das zu erreichen, indem man vermeidet, dass eine redundante LinearLayout Instanz erstellt wird?

+13

Ich liebe Fragen, von denen ich etwas lerne. Vielen Dank. –

+4

Ich schrieb vor kurzem einen Blog-Eintrag dazu: http://blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3/ –

Antwort

98

Verwenden fusionieren Tag als XML-Stamm

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

Check this article.

+10

Vielen Dank! Genau das habe ich gesucht. Erstaunlich, wie so eine lange Frage so kurz beantwortet werden kann. Ausgezeichnet! –

+0

Wie sieht es mit diesem Zusammenführungslayout in der Landschaft aus? – Kostadin

+0

Aber das bedeutet, dass Sie den visuellen Editor nicht verwenden können ... – Timmmm

0

Ich denke, die Art und Weise Sie soll sind, es zu tun, verwenden Sie Ihre Klassennamen als XML-Wurzelelement:

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

Und dann haben Sie Ihre Klasse von dem Layout abgeleitet, das Sie verwenden möchten. Beachten Sie, dass Sie bei Verwendung dieser Methode nicht das Layout inflater hier verwenden.

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); 
    } 
} 

Und dann können Sie Ihre Ansicht in XML-Layouts als normal verwenden.Wenn Sie die Ansicht machen wollen programmatisch müssen Sie es selbst aufblasen:

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

Leider ist dieses nicht Sie v = new MyView(context) tun nicht lassen, weil es nicht eine Möglichkeit, um die verschachtelten Layouts Problem zu sein scheint, so dass diese isn Es ist wirklich eine vollständige Lösung. Sie könnten eine Methode wie folgt zu MyView hinzufügen, um es schöneres etwas zu machen:

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

Disclaimer: Ich komplette Bollocks sprechen kann.

+0

Danke! Ich denke, diese Antwort ist auch richtig :) Aber vor drei Jahren, als ich diese Frage gestellt habe, hat "merge" auch den Trick gemacht! –

+8

Und dann kommt jemand und versucht, Ihre benutzerdefinierte Ansicht in einem Layout irgendwo mit nur '' '' '' zu verwenden und Ihre 'setData' und' onFinishInflate' Anrufe fangen an, NPEs zu werfen, und Du hast keine Ahnung warum. –

+0

Der Trick hier ist, verwenden Sie Ihre benutzerdefinierte Ansicht, dann im Konstruktor, ein Layout, das ein Merge-Tag als root verwendet. Jetzt können Sie es in XML verwenden oder einfach neu erstellen. Alle Grundlagen sind abgedeckt, was genau die Frage/angenommene Antwort zusammen tut. Was Sie jedoch nicht tun können, bezieht sich direkt auf das Layout. Es gehört nun dem benutzerdefinierten Steuerelement und sollte nur von diesem im Konstruktor verwendet werden. Aber wenn Sie diesen Ansatz verwenden, warum sollten Sie ihn irgendwo anders verwenden? Das würdest du nicht tun. – MarqueIV

Verwandte Themen