Cách thực hiện: Xác định mục chủ đề (kiểu) cho tiện ích con tùy chỉnh


86

Tôi đã viết một widget tùy chỉnh cho một điều khiển mà chúng tôi sử dụng rộng rãi trong toàn bộ ứng dụng của mình. Lớp widget bắt nguồn từ ImageButtonvà mở rộng nó theo một vài cách đơn giản. Tôi đã xác định một kiểu mà tôi có thể áp dụng cho tiện ích khi nó được sử dụng, nhưng tôi muốn thiết lập kiểu này thông qua một chủ đề. Trong R.styleabletôi thấy các thuộc tính kiểu widget như imageButtonStyletextViewStyle. Có cách nào để tạo một cái gì đó giống như vậy cho tiện ích con tùy chỉnh mà tôi đã viết không?

Câu trả lời:


209

Có, có một cách:

Giả sử bạn có một khai báo các thuộc tính cho tiện ích con của mình (trong attrs.xml):

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

Khai báo thuộc tính bạn sẽ sử dụng cho tham chiếu kiểu (in attrs.xml):

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

Khai báo một tập hợp các giá trị thuộc tính mặc định cho tiện ích con (trong styles.xml):

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

Khai báo một chủ đề tùy chỉnh (trong themes.xml):

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

Sử dụng thuộc tính này làm đối số thứ ba trong hàm tạo của tiện ích con của bạn (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();
    }
}

Bây giờ bạn phải áp dụng Theme.Customcho tất cả các hoạt động sử dụng CustomImageButton(trong AndroidManifest.xml):

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

Đó là tất cả. Bây giờ CustomImageButtoncố gắng tải các giá trị thuộc tính mặc định từ customImageButtonStylethuộc tính của chủ đề hiện tại. Nếu không tìm thấy thuộc tính như vậy trong chủ đề hoặc giá trị của thuộc tính @nullthì đối số cuối cùng obtainStyledAttributessẽ được sử dụng: Widget.ImageButton.Customtrong trường hợp này.

Bạn có thể thay đổi tên của tất cả các phiên bản và tất cả các tệp (ngoại trừ AndroidManifest.xml) nhưng sẽ tốt hơn nếu sử dụng quy ước đặt tên của Android.


4
Có cách nào để làm điều tương tự mà không cần sử dụng chủ đề không? Tôi có một số widget tùy chỉnh, nhưng tôi muốn người dùng có thể sử dụng các widget này với bất kỳ chủ đề nào họ muốn. Làm theo cách bạn đã đăng sẽ yêu cầu người dùng sử dụng chủ đề của tôi.
theDazzler

3
@theDazzler: Nếu ý của bạn là một thư viện mà các nhà phát triển có thể sử dụng, thì họ sẽ có thể tạo các chủ đề của riêng họ và chỉ định kiểu cho tiện ích bằng cách sử dụng thuộc tính style trong chủ đề ( customImageButtonStyletrong câu trả lời này). Đó là cách thanh tác vụ được tùy chỉnh trên Android. Và nếu bạn muốn thay đổi chủ đề trong thời gian chạy, thì cách tiếp cận này không thể thực hiện được.
Michael

@Michael, Vâng, ý tôi là một thư viện mà các nhà phát triển có thể sử dụng. Bình luận của bạn đã giải quyết được vấn đề của tôi. Cảm ơn!
theDazzler

30
Điều tuyệt vời nhất về tất cả những điều này là ai đó tại Google nghĩ rằng điều này là ổn.
Glenn Maynard

1
Liệu name="CustomTheme"trong declare-styleablethuộc tính wrapper phục vụ mục đích nào? Có phải cái đó chỉ dành cho tổ chức không, vì tôi không thấy nó dùng ở đâu cả. Tôi có thể đặt tất cả các thuộc tính style của mình cho các widget khác nhau trong một trình bao bọc không?
Tenfour04

4

Một khía cạnh khác ngoài câu trả lời xuất sắc của michael là ghi đè các thuộc tính tùy chỉnh trong các chủ đề. Giả sử bạn có một số chế độ xem tùy chỉnh mà tất cả đều tham chiếu đến thuộc tính tùy chỉnh "custom_background".

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

Trong một chủ đề, bạn xác định giá trị là gì

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

hoặc là

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

Bạn có thể tham khảo thuộc tính trong một kiểu

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

Lưu ý dấu chấm hỏi, cũng được sử dụng cho thuộc tính android tham chiếu như trong

?android:attr/colorBackground

Như hầu hết các bạn đã nhận thấy, bạn có thể -và có lẽ nên- sử dụng tham chiếu @color thay vì các màu được mã hóa cứng.

Vậy tại sao không chỉ làm

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

Bạn không thể thay đổi định nghĩa của "my_background_color" trong thời gian chạy, trong khi bạn có thể dễ dàng chuyển đổi chủ đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.