Mendeklarasikan elemen UI android khusus menggunakan XML

Jawaban:

840

Panduan Pengembang Android memiliki bagian yang disebut Membangun Komponen Kustom . Sayangnya, pembahasan atribut XML hanya mencakup mendeklarasikan kontrol di dalam file layout dan tidak benar-benar menangani nilai-nilai di dalam inisialisasi kelas. Langkah-langkahnya adalah sebagai berikut:

1. Nyatakan atribut dalam 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>

Perhatikan penggunaan nama yang tidak berkualitas dalam declare-styleabletag. Atribut android non-standar seperti extraInformationharus dideklarasikan. Tag yang dideklarasikan dalam superclass akan tersedia dalam subclass tanpa harus dideklarasikan ulang.

2. Buat konstruktor

Karena ada dua konstruktor yang menggunakan AttributeSetinisialisasi untuk, lebih mudah untuk membuat metode inisialisasi terpisah untuk memanggil konstruktor.

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.MyCustomViewadalah int[]sumber daya yang dibuat secara otomatis di mana setiap elemen adalah ID dari suatu atribut. Atribut dihasilkan untuk setiap properti dalam XML dengan menambahkan nama atribut ke nama elemen. Misalnya, R.styleable.MyCustomView_android_textberisi android_textatribut untuk MyCustomView. Atribut kemudian dapat diambil dari TypedArraymenggunakan berbagai getfungsi. Jika atribut tidak didefinisikan dalam XML yang didefinisikan, maka nulldikembalikan. Kecuali, tentu saja, jika jenis kembali adalah primitif, dalam hal ini argumen kedua dikembalikan.

Jika Anda tidak ingin mengambil semua atribut, dimungkinkan untuk membuat larik ini secara manual. ID untuk atribut android standar disertakan android.R.attr, sementara atribut untuk proyek ini ada di R.attr.

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

Harap dicatat bahwa Anda tidak boleh menggunakan apa pun di android.R.styleable, karena utas ini dapat berubah di masa mendatang. Masih dalam dokumentasi sebagai melihat semua konstanta ini di satu tempat berguna.

3. Gunakan dalam file tata letak seperti layout\main.xml

Sertakan deklarasi namespace xmlns:app="http://schemas.android.com/apk/res-auto"di elemen xml tingkat atas. Namespaces menyediakan metode untuk menghindari konflik yang terkadang terjadi ketika skema berbeda menggunakan nama elemen yang sama (lihat artikel ini untuk info lebih lanjut). URL hanyalah cara mengidentifikasi skema unik - tidak ada yang perlu di-host di URL itu . Jika ini tampaknya tidak melakukan apa-apa, itu karena Anda sebenarnya tidak perlu menambahkan awalan namespace kecuali Anda perlu menyelesaikan konflik.

<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"
/> 

Referensi tampilan khusus menggunakan nama yang memenuhi syarat.

Contoh Android LabelView

Jika Anda ingin contoh lengkap, lihat contoh tampilan label 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"/>

Ini terkandung dalam LinearLayoutdengan atribut namespace:xmlns:app="http://schemas.android.com/apk/res-auto"

Tautan

Casebash
sumber
14
Saya ingin menambahkan bahwa jika elemen root Anda membutuhkan namespace kustom Anda, Anda harus menambahkan namespace android standar dan custom Anda sendiri atau Anda mungkin mengalami kesalahan pembuatan.
Chase
11
Jawaban ini adalah sumber yang paling jelas di Internet pada params XML khusus yang dapat saya temukan. Terima kasih, Casebash.
Artem Russakovskii
2
untuk beberapa alasan, editor visual menolak untuk menggunakan nilai teks tertulis untuk android: text, namun perangkat menggunakannya dengan baik. bagaimana bisa ?
pengembang android
2
@androiddeveloper Tampaknya editor Eclipse menolak untuk menggunakan nilai untuk semua atribut android:. Saya ingin tahu apakah ini fitur atau bug
deej
4
Apa tujuan dari xmlns: namespace aplikasi dan res-auto?
IgorGanapolsky
91

Referensi yang bagus. Terima kasih! Tambahan untuk itu:

Jika Anda kebetulan memiliki proyek perpustakaan termasuk yang telah menyatakan atribut khusus untuk tampilan kustom, Anda harus mendeklarasikan namespace proyek Anda, bukan yang perpustakaan. Misalnya:

Mengingat bahwa perpustakaan memiliki paket "com.example.library.customview" dan proyek yang bekerja memiliki paket "com.example.customview", maka:

Tidak akan berfungsi (menunjukkan kesalahan "kesalahan: Tidak ditemukan pengidentifikasi sumber daya untuk atribut 'newAttr' dalam paket 'com.example.library.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" />

Akan bekerja:

<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" />
Andy
sumber
47
Ini telah diperbaiki pada pratinjau ADT 17. Untuk menggunakan namespace aplikasi dari perpustakaan, deklarasikan xmlns:app="http://schemas.android.com/apk/res-auto"Lihat komentar 57 di code.google.com/p/android/issues/detail?id=9656
nmr
2
Termasuk namespace khusus Anda sekarang menghasilkan kesalahanSuspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
Ben Wilkinson
namespace khusus berakhir di res-auto karena kami menggunakan Android Studio dan Gradle. Kalau tidak (misalnya beberapa versi Eclipse) biasanya akan berakhir dengan lib / [nama paket Anda]
Universe
namespace khusus berakhir res-autokarena kami menggunakan Android Studio dan Gradle. Kalau tidak (misalnya beberapa versi Eclipse) biasanya akan berakhir dengan lib/[your package name]. yaituhttp://schemas.android.com/apk/lib/[your package name]
Universe
27

Selain jawaban yang paling banyak dipilih.

memperolehStyledAttributes ()

Saya ingin menambahkan beberapa kata tentang penggunaan diperolehStyledAttributes (), ketika kita membuat tampilan khusus menggunakan atribut android: xxx prdefined. Terutama ketika kita menggunakan TextAppearance.
Seperti yang disebutkan dalam "2. Membuat konstruktor", tampilan kustom mendapat AttributeSet pada pembuatannya. Penggunaan utama dapat kita lihat dalam kode sumber 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();

Apa yang bisa kita lihat di sini?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Set atribut diproses berdasarkan tema sesuai dengan dokumentasi. Nilai atribut dikompilasi langkah demi langkah. Atribut pertama diisi dari tema, lalu nilai-nilai digantikan oleh nilai-nilai dari gaya, dan akhirnya nilai yang tepat dari XML untuk tampilan contoh khusus menggantikan yang lain.
Array atribut yang diminta - com.android.internal.R.styleable.TextView
Ini adalah array konstanta biasa. Jika kami meminta atribut standar, kami dapat membuat array ini secara manual.

Apa yang tidak disebutkan dalam dokumentasi - urutan elemen TypedArray hasil.
Ketika tampilan kustom dideklarasikan di attrs.xml, konstanta khusus untuk indeks atribut dihasilkan. Dan kita dapat mengekstrak nilai-nilai seperti ini: a.getString(R.styleable.MyCustomView_android_text). Tetapi untuk manual int[]tidak ada konstanta. Saya kira, bahwa getXXXValue (arrayIndex) akan berfungsi dengan baik.

Dan pertanyaan lain adalah: "Bagaimana kita bisa mengganti konstanta internal, dan meminta atribut standar?" Kita bisa menggunakan nilai android.R.attr. *.

Jadi jika kita ingin menggunakan atribut TextAppearance standar dalam tampilan kustom dan membaca nilainya dalam konstruktor, kita dapat memodifikasi kode dari TextView dengan cara ini:

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

Di mana CustomLabel didefinisikan:

<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>

Mungkin, saya keliru dalam beberapa hal, tetapi dokumentasi Android tentang memperolehStyledAttributes () sangat buruk.

Memperluas komponen UI standar

Pada saat yang sama kita dapat memperluas komponen UI standar, menggunakan semua atribut yang dideklarasikan. Pendekatan ini tidak begitu baik, karena TextView misalnya menyatakan banyak properti. Dan tidak mungkin untuk mengimplementasikan fungsionalitas penuh di override onMeasure () dan onDraw ().

Tetapi kita dapat mengorbankan penggunaan kembali yang luas secara teoritis dari komponen khusus. Katakan "Saya tahu persis fitur apa yang akan saya gunakan", dan jangan berbagi kode dengan siapa pun.

Kemudian kita dapat mengimplementasikan konstruktor CustomComponent(Context, AttributeSet, defStyle). Setelah memanggil super(...)kita akan memiliki semua atribut diuraikan dan tersedia melalui metode pengambil.

yuriy. kami salah
sumber
apakah atribut android: xxx yang telah ditentukan berfungsi di desainer gerhana?
deej
Atribut tersebut dikenali oleh plugin Eclipse ADT di editor properti. Saya dapat melihat default dari gaya saya, jika beberapa nilai tidak ditentukan. Dan jangan lupa untuk menambahkan anotasi @RemoteView ke kelas Anda.
yuriy.weiss
Tidak bisa membuatnya bekerja Eclipse terus memuat null untuk getText dan melempar android.content.res.Resources $ NotFoundException untuk getResourceId, meskipun aplikasi berjalan dengan baik di perangkat.
deej
Maaf, saya tidak dapat membantu Anda. Saya hanya membuat proyek demo untuk menguji kemungkinan, dan tidak menemukan kesalahan seperti itu.
yuriy.weiss
Ini jauh lebih baik daripada memetakan atribut khusus tampilan kustom ke atribut bawaan tampilan built-in yang terkandung di dalamnya.
samis
13

Tampaknya Google telah memperbarui halaman pengembangnya dan menambahkan berbagai pelatihan di sana.

Salah satunya berkaitan dengan pembuatan tampilan khusus dan dapat ditemukan di sini

mitch000001
sumber
5

Terima kasih banyak atas jawaban pertama.

Sedangkan saya, saya hanya punya satu masalah dengan itu. Ketika menggembungkan pandangan saya, saya memiliki bug: java.lang.NoSuchMethodException: MyView (Konteks, Atribut)

Saya mengatasinya dengan membuat konstruktor baru:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}

Semoga ini bisa membantu!

pengguna2346922
sumber
0

Anda dapat memasukkan file tata letak apa pun di file tata letak lainnya sebagai-

             <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>

di sini file layout di tag include adalah file layout .xml lainnya di folder res yang sama.

Akshay Paliwal
sumber
Saya sudah mencoba ini, masalah yang saya miliki adalah bahwa tata letak yang disertakan tidak dapat 'diadaptasi', tidak dapat membuat obat generik. Sebagai contoh ketika saya memasukkan tombol dengan cara yang sama, jika saya mencoba untuk mengatur teks di xml itu berfungsi.
cfl