Deklarowanie niestandardowego elementu UI Androida za pomocą XML

Jak zadeklarować element interfejsu użytkownika Androida za pomocą XML?

Author: Casebash, 2010-04-23

6 answers

Android Developer Guide zawiera sekcję o nazwie budowanie niestandardowych komponentów . Niestety, dyskusja atrybutów XML obejmuje tylko deklarowanie sterowania wewnątrz pliku układu i nie obsługę wartości wewnątrz inicjalizacji klasy. Kroki są następujące:

1. Zadeklaruj atrybuty w values\attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="android:text"/>
        <attr name="android:textColor"/>            
        <attr name="extraInformation" format="string" />
    </declare-styleable>
</resources>

Zwróć uwagę na użycie niewykwalifikowanej nazwy w znaczniku declare-styleable. Niestandardowe atrybuty Androida, takie jak extraInformation, muszą mieć swój typ zadeklarowane. Tagi zadeklarowane w superklasie będą dostępne w podklasach bez konieczności ponownego zgłaszania.

2. Tworzenie konstruktorów

Ponieważ istnieją dwa konstruktory, które używają AttributeSet do inicjalizacji, wygodne jest utworzenie oddzielnej metody inicjalizacji, którą konstruktory mogą wywołać.

private void init(AttributeSet attrs) { 
    TypedArray a=getContext().obtainStyledAttributes(
         attrs,
         R.styleable.MyCustomView);

    //Use a
    Log.i("test",a.getString(
         R.styleable.MyCustomView_android_text));
    Log.i("test",""+a.getColor(
         R.styleable.MyCustomView_android_textColor, Color.BLACK));
    Log.i("test",a.getString(
         R.styleable.MyCustomView_extraInformation));

    //Don't forget this
    a.recycle();
}

R.styleable.MyCustomView jest autogenerowanym zasobem int[], gdzie każdy element jest identyfikatorem atrybutu. Atrybuty są generowane dla każdej właściwości w XML przez dodanie nazwa atrybutu do nazwy elementu. Na przykład R.styleable.MyCustomView_android_text zawiera atrybut android_text dla MyCustomView. Atrybuty można następnie pobrać z TypedArray za pomocą różnych funkcji get. Jeżeli atrybut nie jest zdefiniowany w XML, wtedy jest zwracane null. Z wyjątkiem, oczywiście, jeśli Typ return jest prymitywny, w którym to przypadku zwracany jest drugi argument.

Jeśli nie chcesz pobierać wszystkich atrybutów, możesz utworzyć tę tablicę ręcznie.ID dla standard atrybuty Androida są zawarte w android.R.attr, podczas gdy atrybuty dla tego projektu są w R.attr.

int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};

Pamiętaj, że powinieneś nie używać czegokolwiek w android.R.styleable, ponieważ w ten wątek może się zmienić w przyszłości. Nadal jest w dokumentacji, ponieważ wyświetlanie wszystkich tych stałych w jednym miejscu jest przydatne.

3. Użyj go w plikach układu, takich jak layout\main.xml

Zawiera deklarację przestrzeni nazw xmlns:app="http://schemas.android.com/apk/res-auto" w elemencie xml najwyższego poziomu. Przestrzenie nazw zapewniają metoda unikania konfliktów, które czasami występują, gdy różne schematy używają tych samych nazw elementów (zobacz ten artykuł aby uzyskać więcej informacji). Adres URL jest po prostu sposobem jednoznacznej identyfikacji schematów- nic tak naprawdę nie musi być hostowane pod TYM adresem URL . Jeśli wydaje się, że to nic nie robi, dzieje się tak dlatego, że nie musisz dodawać prefiksu przestrzeni nazw, chyba że musisz rozwiązać konflikt.

<com.mycompany.projectname.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:text="Test text"
    android:textColor="#FFFFFF"
    app:extraInformation="My extra information"
/> 

Odwołaj się do widoku niestandardowego, używając w pełni kwalifikowanego nazwisko.

Android LabelView Sample

Jeśli chcesz uzyskać pełny przykład, spójrz na przykładowy widok etykiet systemu android.

LabelView.java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);

Attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>

Custom_view_1.xml

<com.example.android.apis.view.LabelView
    android:background="@drawable/blue"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    app:text="Blue" app:textSize="20dp"/>
To jest zawarte w LinearLayout z atrybutem przestrzeni nazw: xmlns:app="http://schemas.android.com/apk/res-auto"

Linki

 812
Author: Casebash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:03

Świetne odniesienie. Dzięki! Dodatek do niego:

Jeśli zdarzy ci się, że masz dołączony projekt biblioteki, który zadeklarował własne atrybuty dla niestandardowego widoku, musisz zadeklarować przestrzeń nazw projektu, a nie bibliotekę. np:

Biorąc pod uwagę, że biblioteka posiada pakiet " com.przykład.biblioteka.customview", a projekt roboczy ma pakiet " com.przykład.customview", następnie:

Nie będzie działać (wyświetla błąd "error: No resource identifier found for atrybut 'newAttr' w pakiecie "com.przykład.biblioteka.customview'"):

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.library.customview"
        android:id="@+id/myView"
        app:newAttr="value" />

Zadziała:

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.customview"
        android:id="@+id/myView"
        app:newAttr="value" />
 89
Author: Andy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-28 06:24:38

Dodanie do najczęściej głosowanej odpowiedzi.

ObtainStyledAttributes ()

Chcę dodać kilka słów o użyciu obtainStyledAttributes (), kiedy tworzymy własny widok używając atrybutów android: xxx prdefiniowane. Zwłaszcza, gdy używamy TextAppearance.
Jak wspomniano w "2. Tworzenie konstruktorów", widok niestandardowy otrzymuje Atrybuteset przy jego tworzeniu. Główne zastosowanie widzimy w kodzie źródłowym TextView (API 16).

final Resources.Theme theme = context.getTheme();

// TextAppearance is inspected first, but let observe it later

TypedArray a = theme.obtainStyledAttributes(
            attrs, com.android.internal.R.styleable.TextView, defStyle, 0);

int n = a.getIndexCount();
for (int i = 0; i < n; i++) 
{
    int attr = a.getIndex(i);
    // huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();
Co tu widzimy?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Zestaw atrybutów jest przetwarzany według tematu zgodnie z dokumentacją. Wartości atrybutów są kompilowane krok po kroku. Najpierw atrybuty są wypełnione z motywu, następnie wartości są zastępowane przez wartości ze stylu, a na koniec dokładne wartości z XML dla specjalnej instancji widoku zastępują inne.
Tablica żądanych atrybutów - com.android.internal.R.styleable.TextView
Jest to zwykła tablica stałych. Jeśli żądamy standardowych atrybutów, możemy zbudować tę tablicę ręcznie.

Czego nie ma w dokumentacji - kolejność wyników typowanych żywioły.
Gdy widok niestandardowy jest zadeklarowany w attrs.XML, generowane są specjalne stałe dla indeksów atrybutów. I możemy wyodrębnić wartości w ten sposób: a.getString(R.styleable.MyCustomView_android_text). Ale dla manuala int[] nie ma stałych. Przypuszczam, że getXXXValue (arrayIndex) będzie działać dobrze.

I inne pytanie brzmi: "jak możemy zastąpić wewnętrzne stałe i zażądać standardowych atrybutów?"Możemy używać Androida.R. attr.* wartości.

Więc jeśli chcemy użyć standardowego atrybutu TextAppearance w widoku niestandardowym i odczytać jego wartości w konstruktorze, możemy zmodyfikować kod z TextView w ten sposób:

ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;

Resources.Theme theme = context.getTheme();

TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
    appearance = 
        theme.obtainStyledAttributes(apResourceId, new int[] { android.R.attr.textColor, android.R.attr.textSize, 
            android.R.attr.typeface, android.R.attr.textStyle });
}
if (appearance != null)
{
    textColorApp = appearance.getColorStateList(0);
    textSize = appearance.getDimensionPixelSize(1, textSize);
    typefaceIndex = appearance.getInt(2, -1);
    styleIndex = appearance.getInt(3, -1);

    appearance.recycle();
}

Gdzie zdefiniowano CustomLabel:

<declare-styleable name="CustomLabel">
    <!-- Label text. -->
    <attr name="android:text" />
    <!-- Label text color. -->
    <attr name="android:textColor" />
    <!-- Combined text appearance properties. -->
    <attr name="android:textAppearance" />
</declare-styleable>

Być może, w jakiś sposób się mylę, ale dokumentacja Androida na obtainStyledAttributes() jest bardzo uboga.

Rozszerzenie standardowego elementu UI

W tym samym czasie możemy po prostu rozszerzyć standardowy komponent UI, używając wszystkich jego zadeklarowanych atrybutów. Takie podejście nie jest zbyt dobre, ponieważ na przykład TextView deklaruje wiele właściwości. I będzie impossible aby zaimplementować pełną funkcjonalność w nadrzędnych OnMeasure () i onDraw ().

Ale możemy poświęcić teoretyczne szerokie ponowne użycie niestandardowego komponentu. Powiedzieć "Wiem dokładnie, jakich funkcji będę używał", a nie udostępniaj nikomu kodu.

Następnie możemy zaimplementować konstruktor CustomComponent(Context, AttributeSet, defStyle). Po wywołaniu super(...) będziemy mieli wszystkie atrybuty przetworzone I dostępne za pomocą metod getter.

 26
Author: yuriy.weiss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-01 07:58:39

Wygląda na to, że Google zaktualizowało swoją stronę dewelopera i dodało tam różne szkolenia.

Jeden z nich zajmuje się tworzeniem niestandardowych widoków i można go znaleźć TUTAJ

 11
Author: mitch000001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-21 19:13:57

Wielkie dzięki za pierwszą odpowiedź.

Jeśli chodzi o mnie, miałem z tym tylko jeden problem. Gdy nadmuchiwałem widok, miałem Buga : java.lang.NoSuchMethodException: MyView (Kontekst, Atrybuty)

Rozwiązałem to tworząc nowy konstruktor:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}
Mam nadzieję, że to pomoże !
 5
Author: user2346922,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-07 17:31:02

Możesz umieścić dowolny plik układu w innym pliku układu jako -

             <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="30dp" >

                <include
                    android:id="@+id/frnd_img_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_imagefile"/>

                <include
                    android:id="@+id/frnd_video_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_video_lay" />

                <ImageView
                    android:id="@+id/downloadbtn"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_centerInParent="true"
                    android:src="@drawable/plus"/>
            </RelativeLayout>

Tutaj Pliki układów w znaczniku include są inne .Pliki Układów XML w tym samym folderze res.

 0
Author: Akshay Paliwal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-04 08:16:50