Cara: Menentukan item tema (gaya) untuk widget khusus

88

Saya telah menulis widget khusus untuk kontrol yang kami gunakan secara luas di seluruh aplikasi kami. Kelas widget berasal dari ImageButtondan mengembangkannya dalam beberapa cara sederhana. Saya telah menentukan gaya yang dapat saya terapkan ke widget seperti yang digunakan, tetapi saya lebih suka mengaturnya melalui tema. Dalam R.styleablesaya melihat atribut gaya widget seperti imageButtonStyledan textViewStyle. Apakah ada cara untuk membuat sesuatu seperti itu untuk widget khusus yang saya tulis?

jsmith.dll
sumber

Jawaban:

211

Ya, ada satu cara:

Misalkan Anda memiliki deklarasi atribut untuk widget Anda (dalam attrs.xml):

<declare-styleable name="CustomImageButton">
    <attr name="customAttr" format="string"/>
</declare-styleable>

Deklarasikan atribut yang akan Anda gunakan untuk referensi gaya (dalam attrs.xml):

<declare-styleable name="CustomTheme">
    <attr name="customImageButtonStyle" format="reference"/>
</declare-styleable>

Deklarasikan sekumpulan nilai atribut default untuk widget (dalam styles.xml):

<style name="Widget.ImageButton.Custom" parent="android:style/Widget.ImageButton">
    <item name="customAttr">some value</item>
</style>

Deklarasikan tema khusus (dalam themes.xml):

<style name="Theme.Custom" parent="@android:style/Theme">
    <item name="customImageButtonStyle">@style/Widget.ImageButton.Custom</item>
</style>

Gunakan atribut ini sebagai argumen ketiga dalam konstruktor widget Anda (dalam CustomImageButton.java):

public class CustomImageButton extends ImageButton {
    private String customAttr;

    public CustomImageButton( Context context ) {
        this( context, null );
    }

    public CustomImageButton( Context context, AttributeSet attrs ) {
        this( context, attrs, R.attr.customImageButtonStyle );
    }

    public CustomImageButton( Context context, AttributeSet attrs,
            int defStyle ) {
        super( context, attrs, defStyle );

        final TypedArray array = context.obtainStyledAttributes( attrs,
            R.styleable.CustomImageButton, defStyle,
            R.style.Widget_ImageButton_Custom ); // see below
        this.customAttr =
            array.getString( R.styleable.CustomImageButton_customAttr, "" );
        array.recycle();
    }
}

Sekarang Anda harus menerapkan Theme.Customke semua aktivitas yang menggunakan CustomImageButton(di AndroidManifest.xml):

<activity android:name=".MyActivity" android:theme="@style/Theme.Custom"/>

Itu saja. Sekarang CustomImageButtonmencoba untuk memuat nilai atribut default dari customImageButtonStyleatribut tema saat ini. Jika atribut seperti itu tidak ditemukan dalam tema atau nilai atribut @nullmaka argumen terakhir untuk obtainStyledAttributesakan digunakan: Widget.ImageButton.Customdalam kasus ini.

Anda dapat mengubah nama semua instance dan semua file (kecuali AndroidManifest.xml) tetapi akan lebih baik menggunakan konvensi penamaan Android.

Michael
sumber
4
Adakah cara untuk melakukan hal yang sama persis tanpa menggunakan tema? Saya memiliki beberapa widget khusus, tetapi saya ingin pengguna dapat menggunakan widget ini dengan tema apa pun yang mereka inginkan. Melakukannya seperti yang Anda posting akan mengharuskan pengguna menggunakan tema saya.
theDazzler
3
@theDazzler: Jika yang Anda maksud adalah pustaka yang dapat digunakan pengembang, mereka akan dapat membuat tema mereka sendiri dan menentukan gaya widget menggunakan atribut gaya dalam tema ( customImageButtonStyledalam jawaban ini). Begitulah cara bilah tindakan disesuaikan di Android. Dan jika Anda bermaksud mengubah tema pada waktu proses, pendekatan ini tidak mungkin dilakukan.
Michael
@ Michael, Ya, maksud saya perpustakaan yang dapat digunakan oleh pengembang. Komentar Anda menyelesaikan masalah saya. Terima kasih!
theDazzler
32
Hal yang paling menakjubkan tentang semua ini adalah seseorang di Google menganggap ini baik-baik saja.
Glenn Maynard
1
Apakah name="CustomTheme"di declare-styleableatribut wrapper memiliki tujuan apa pun? Apakah itu hanya untuk organisasi, karena saya tidak melihatnya digunakan di mana pun. Bisakah saya meletakkan semua atribut gaya saya untuk berbagai widget dalam satu pembungkus?
Tenfour04
4

Aspek lain selain jawaban michael yang luar biasa adalah mengganti atribut khusus dalam tema. Misalkan Anda memiliki sejumlah tampilan kustom yang semuanya merujuk ke atribut kustom "custom_background".

<declare-styleable name="MyCustomStylables">
    <attr name="custom_background" format="color"/>
</declare-styleable>

Dalam sebuah tema Anda menentukan apa nilainya

<style name="MyColorfulTheme" parent="AppTheme">
    <item name="custom_background">#ff0000</item>
</style>

atau

<style name="MyBoringTheme" parent="AppTheme">
    <item name="custom_background">#ffffff</item>
</style>

Anda dapat merujuk ke atribut dengan gaya

<style name="MyDefaultLabelStyle" parent="AppTheme">
    <item name="android:background">?background_label</item>
</style>

Perhatikan tanda tanya, seperti yang juga digunakan untuk referensi atribut android seperti pada

?android:attr/colorBackground

Seperti yang diketahui oleh sebagian besar dari Anda, Anda dapat -dan mungkin harus- menggunakan referensi @color alih-alih warna yang di-hardcode.

Jadi kenapa tidak lakukan saja

<item name="android:background">@color/my_background_color</item>

Anda tidak dapat mengubah definisi "my_background_color" saat runtime, sedangkan Anda dapat dengan mudah mengganti tema.

mir
sumber