Instrukcje: definiowanie motywu (stylu) dla niestandardowego widżetu

86

Napisałem niestandardowy widget dla kontrolki, której używamy szeroko w naszej aplikacji. Klasa widżetu wywodzi się z niej ImageButtoni rozszerza ją na kilka prostych sposobów. Zdefiniowałem styl, który mogę zastosować do widżetu podczas jego używania, ale wolałbym ustawić to za pomocą motywu. W R.styleablewidzę atrybuty stylu widżetów, takie jak imageButtonStylei textViewStyle. Czy istnieje sposób na stworzenie czegoś takiego dla niestandardowego widżetu, który napisałem?

jsmith
źródło

Odpowiedzi:

209

Tak, jest jeden sposób:

Załóżmy, że masz deklarację atrybutów swojego widżetu (in attrs.xml):

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

Zadeklaruj atrybut, którego będziesz używać jako odniesienia do stylu (w attrs.xml):

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

Zadeklaruj zestaw domyślnych wartości atrybutów dla widgetu (in styles.xml):

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

Zadeklaruj motyw niestandardowy (in themes.xml):

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

Użyj tego atrybutu jako trzeciego argumentu w konstruktorze widgetu (in 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();
    }
}

Teraz musisz zastosować się Theme.Customdo wszystkich działań, które używają CustomImageButton(w AndroidManifest.xml):

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

To wszystko. Teraz CustomImageButtonpróbuje załadować domyślne wartości customImageButtonStyleatrybutów z atrybutu bieżącego motywu. Jeśli w motywie nie zostanie znaleziony taki atrybut lub wartość atrybutu to, zostanie użyty @nullostatni argument do obtainStyledAttributes: Widget.ImageButton.Customw tym przypadku.

Możesz zmienić nazwy wszystkich instancji i wszystkich plików (z wyjątkiem AndroidManifest.xml), ale lepiej byłoby użyć konwencji nazewnictwa Androida.

Michał
źródło
4
Czy istnieje sposób na zrobienie dokładnie tego samego bez użycia motywu? Mam kilka niestandardowych widżetów, ale chcę, aby użytkownicy mogli używać tych widżetów z dowolnym motywem. Zrobienie tego w sposób, w jaki opublikowałeś, wymagałoby od użytkowników użycia mojego motywu.
theDazzler
3
@theDazzler: Jeśli masz na myśli bibliotekę, z której mogą korzystać programiści, będą mogli tworzyć własne motywy i określać styl widżetu za pomocą atrybutu stylu w motywie ( customImageButtonStylew tej odpowiedzi). W ten sposób pasek akcji jest dostosowywany na Androida. A jeśli masz na myśli zmianę motywu w czasie wykonywania, to nie jest to możliwe przy takim podejściu.
Michael
@Michael, tak, miałem na myśli bibliotekę, z której mogą korzystać programiści. Twój komentarz rozwiązał mój problem. Dzięki!
theDazzler
30
Najbardziej niesamowite w tym wszystkim jest to, że ktoś w Google uważa, że ​​to jest w porządku.
Glenn Maynard
1
Czy name="CustomTheme"w declare-styleableatrybucie wrapper bezcelowe? Czy to tylko dla organizacji, ponieważ nie widzę, aby było nigdzie używane. Czy mogę po prostu umieścić wszystkie atrybuty stylu dla różnych widżetów w jednym opakowaniu?
Tenfour04
4

Innym aspektem, oprócz doskonałej odpowiedzi Michaela, jest nadpisywanie niestandardowych atrybutów w motywach. Załóżmy, że masz kilka widoków niestandardowych, z których wszystkie odwołują się do atrybutu niestandardowego „custom_background”.

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

W motywie definiujesz wartość

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

lub

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

Możesz odwołać się do atrybutu w stylu

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

Zwróć uwagę na znak zapytania, który jest również używany do odniesienia atrybutu android, jak w

?android:attr/colorBackground

Jak większość z was zauważyła, możecie - i prawdopodobnie powinniście - używać odwołań @color zamiast kolorów zakodowanych na stałe.

Więc dlaczego po prostu nie zrobić

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

Nie możesz zmienić definicji „my_background_color” w czasie wykonywania, ale możesz łatwo zmieniać motywy.

mir
źródło