2009-09-25 13 views
129

Aplikacja na Androida, którą obecnie rozwijam, ma główną działalność, która stała się dość duża. Dzieje się tak głównie dlatego, że zawiera on TabWidget z 3 zakładkami. Każda karta ma kilka składników. Działanie musi kontrolować wszystkie te komponenty jednocześnie. Myślę więc, że możesz sobie wyobrazić, że to Działanie ma 20 pól (pole dla prawie każdego komponentu). Zawiera również wiele logiki (click słuchaczy, logika do wypełnienia list, itp.).Android - pisanie niestandardowego (złożonego) komponentu

Co zwykle robię w ramach opartych na komponentach to dzielenie wszystkiego na niestandardowe komponenty. Każdy komponent niestandardowy miałby wówczas wyraźną odpowiedzialność. Zawierałby własny zestaw komponentów i wszystkie inne logiki związane z tym komponentem.

Próbowałem dowiedzieć się, jak można to zrobić, i znalazłem coś w dokumentacji systemu Android, co lubią nazywać "Kontrola związku". (Zobacz http://developer.android.com/guide/topics/ui/custom-components.html#compound i przejdź do sekcji "Compound Controls") Chciałbym utworzyć taki komponent w oparciu o plik XML definiujący strukturę widoku.

W dokumentacji jest napisane:

Należy pamiętać, że podobnie jak z działalności, można użyć albo podejście deklaratywne (XML-based) do tworzenia zawierał składniki, lub można zagnieździć programowo z Twojego kodu.

Cóż, to dobra wiadomość! Podejście oparte na XML jest dokładnie tym, czego chcę! Ale nie mówi, jak to zrobić, z wyjątkiem tego, że jest "jak z aktywnością" ... Ale to, co robię w działaniu, to wywołanie setContentView(...) w celu nadmuchania widoków z XML. Ta metoda nie jest dostępna, jeśli na przykład masz podklasę LinearLayout.

Więc starałem się nadmuchać XML ręcznie tak:

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

To działa, z wyjątkiem faktu, że jestem XML ładowanie ma LinearLayout zadeklarowany jako elementu głównego. To powoduje, że napompowane LinearLayout jest dzieckiem o numerze MyCompoundComponent, które już samo w sobie jest LinearLayout !! Tak więc teraz mamy nadmiarowy LinearLayout pomiędzy MyCompoundComponent a widokami, których faktycznie potrzebuje.

Czy ktoś może podać mi lepszy sposób podejścia do tego problemu, unikając utworzenia zbędnego egzemplarza LinearLayout?

+13

Uwielbiam pytania, od których się czegoś uczę. Dzięki. –

+4

Niedawno napisałem wpis na blogu na ten temat: http://blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3/ –

Odpowiedz

98

Zastosowanie scalić tag jako bazowy XML

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

Check this article.

+10

Dziękuję bardzo! Właśnie tego szukałem. Zadziwiające, jak tak długie pytanie może mieć tak krótką odpowiedź. Doskonały! –

+0

A co z włączeniem tego układu scalania w krajobraz? – Kostadin

+0

Ale to oznacza, że ​​nie możesz używać edytora wizualnego ... – Timmmm

0

Myślę, że sposób będziesz miał to zrobić, to użyć nazwy klasy jako elementu głównego XML:

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

A następnie niech twoja klasa pochodzi od dowolnego układu, którego chcesz użyć. Zauważ, że jeśli używasz tej metody, nie używajtutaj.

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

A następnie możesz używać widoku w układach XML, jak zwykle.Jeśli chcesz, aby ten pogląd programowo trzeba go napompować sobie pytanie:

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

Niestety to nie pozwalają zrobić v = new MyView(context) ponieważ nie wydaje się być odwrotnie zagnieżdżonego układy problemu, więc to ISN Naprawdę pełne rozwiązanie. Można dodać metodę tak aby MyView aby uczynić go nieco ładniejszy:

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

Disclaimer: można mówić kompletne bollocks.

+0

Dzięki! Myślę, że ta odpowiedź jest również poprawna :) Ale trzy lata temu, kiedy zadałem to pytanie, "scalenie" również załatwiło sprawę! –

+8

A potem ktoś przychodzi i próbuje użyć twojego niestandardowego widoku w układzie gdzieś po prostu '' 'com.example.views.MyView />' '' i twoje wywołania 'setData' i' onFinishInflate' zaczynają wyrzucać NPE, i nie masz pojęcia dlaczego. –

+0

Chodzi o to, aby użyć niestandardowego widoku, a następnie w konstruktorze nadmuchać układ, który używa znacznika scalania jako root. Teraz możesz użyć go w XML lub po prostu go zaktualizować. Wszystkie zasady są objęte gwarancją, a dokładnie to samo pytanie/zaakceptowana odpowiedź. Czego jednak nie możesz zrobić, to odnieść się do układu bezpośrednio. Jest teraz "własnością" kontrolki niestandardowej i powinna być przez nią używana tylko w konstruktorze. Ale jeśli używasz tego podejścia, dlaczego i tak będziesz musiał go używać nigdzie indziej? Nie zrobiłbyś tego. – MarqueIV

Powiązane problemy