Cách thêm trạng thái nút tùy chỉnh


132

Chẳng hạn, nút mặc định có các phụ thuộc sau giữa trạng thái và hình nền của nó:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:state_enabled="true"
        android:drawable="@drawable/btn_default_normal" />
    <item android:state_window_focused="false" android:state_enabled="false"
        android:drawable="@drawable/btn_default_normal_disable" />
    <item android:state_pressed="true" 
        android:drawable="@drawable/btn_default_pressed" />
    <item android:state_focused="true" android:state_enabled="true"
        android:drawable="@drawable/btn_default_selected" />
    <item android:state_enabled="true"
        android:drawable="@drawable/btn_default_normal" />
    <item android:state_focused="true"
        android:drawable="@drawable/btn_default_normal_disable_focused" />
    <item
        android:drawable="@drawable/btn_default_normal_disable" />
</selector>

Làm cách nào tôi có thể xác định trạng thái tùy chỉnh của riêng mình (như smth android:state_custom), vì vậy sau đó tôi có thể sử dụng nó để tự động thay đổi giao diện nút của mình?


Tôi muốn có thêm trạng thái cho chế độ xem Chỉnh sửa để xác định khi nào hai hộp mật khẩu khớp với nhau để hiển thị một dấu kiểm nhỏ.
Nathan Schwermann

Câu trả lời:


275

Giải pháp được chỉ định bởi @ (Ted Hopp) hoạt động, nhưng cần một chút hiệu chỉnh: trong bộ chọn, các trạng thái mục cần một tiền tố "ứng dụng:", nếu không, bộ lọc sẽ không nhận ra không gian tên chính xác và sẽ thất bại trong âm thầm; ít nhất đây là những gì xảy ra với tôi.

Cho phép tôi báo cáo ở đây toàn bộ giải pháp, với một số chi tiết khác:

Đầu tiên, tạo tệp "res / value / attrs.xml":

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="food">
        <attr name="state_fried" format="boolean" />
        <attr name="state_baked" format="boolean" />
    </declare-styleable>
</resources>

Sau đó xác định lớp tùy chỉnh của bạn. Ví dụ, nó có thể là một lớp "FoodButton", xuất phát từ lớp "Nút". Bạn sẽ phải thực hiện một hàm tạo; thực hiện cái này, cái mà dường như là cái được sử dụng bởi Inflater:

public FoodButton(Context context, AttributeSet attrs) {
    super(context, attrs);
}

Trên đầu lớp dẫn xuất:

private static final int[] STATE_FRIED = {R.attr.state_fried};
private static final int[] STATE_BAKED = {R.attr.state_baked};

Ngoài ra, các biến trạng thái của bạn:

private boolean mIsFried = false;
private boolean mIsBaked = false;

Và một vài setters:

public void setFried(boolean isFried) {mIsFried = isFried;}
public void setBaked(boolean isBaked) {mIsBaked = isBaked;}

Sau đó, ghi đè chức năng "onCreateDrawableState":

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
    if (mIsFried) {
        mergeDrawableStates(drawableState, STATE_FRIED);
    }
    if (mIsBaked) {
        mergeDrawableStates(drawableState, STATE_BAKED);
    }
    return drawableState;
}

Cuối cùng, mảnh tinh tế nhất của câu đố này; bộ chọn xác định StateListDrawable mà bạn sẽ sử dụng làm nền cho widget của mình. Đây là tệp "res / drawable / food_button.xml":

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.mydomain.mypackage">
<item
    app:state_baked="true"
    app:state_fried="false"
    android:drawable="@drawable/item_baked" />
<item
    app:state_baked="false"
    app:state_fried="true"
    android:drawable="@drawable/item_fried" />
<item
    app:state_baked="true"
    app:state_fried="true"
    android:drawable="@drawable/item_overcooked" />
<item
    app:state_baked="false"
    app:state_fried="false"
    android:drawable="@drawable/item_raw" />
</selector>

Lưu ý tiền tố "ứng dụng:", trong khi với các trạng thái Android tiêu chuẩn, bạn sẽ sử dụng tiền tố "android:". Không gian tên XML là rất quan trọng để giải thích chính xác bởi người thổi phồng và phụ thuộc vào loại dự án mà bạn đang thêm thuộc tính. Nếu đó là một ứng dụng, hãy thay thế com.mydomain.mypackage bằng tên gói thực tế của ứng dụng của bạn (loại trừ tên ứng dụng). Nếu đó là thư viện, bạn phải sử dụng "http://schemas.android.com/apk/res-auto" (và đang sử dụng Công cụ R17 trở lên) nếu không bạn sẽ gặp lỗi thời gian chạy.

Một vài lưu ý:

  • Có vẻ như bạn không cần phải gọi hàm "refreshDrawableState", ít nhất là giải pháp hoạt động tốt như trong trường hợp của tôi

  • Để sử dụng lớp tùy chỉnh của bạn trong tệp xml bố cục, bạn sẽ phải chỉ định tên đủ điều kiện (ví dụ: com.mydomain.mypackage.FoodButton)

  • Bạn có thể như weel trộn các trạng thái tiêu chuẩn (ví dụ: android: nhấn, android: enable, android: đã chọn) với các trạng thái tùy chỉnh, để thể hiện các kết hợp trạng thái phức tạp hơn


3
Cập nhật: nếu lớp tùy chỉnh xuất phát từ TextView, thay vì Nút, thì lệnh gọi refreshDrawableState dường như là cần thiết, nếu không thì giao diện tiện ích không được cập nhật. Cuộc gọi sẽ được đặt trong setters. Tôi chưa thử các lớp khác. Các thử nghiệm được thực hiện trên một thiết bị froyo.
Giorgio Barchiesi

17
Điều refreshDrawableStatechắc chắn là quan trọng. Tôi không chắc chắn khi nào nó thực sự cần thiết. Nhưng trong trường hợp của tôi, nó là cần thiết khi thiết lập trạng thái theo chương trình. Tôi đoán nó có thể được gọi tự động từ lớp View trong onTouchEvent. Tôi tốt hơn nên thêm nó trong phương thức setSelected.
buergi

1
GiorgioBarchiesi, tôi có hai Nút tùy chỉnh và khi tôi cố gắng thay đổi trạng thái của cả hai nút từ sự kiện onClick của một nút, chỉ có nút được nhấp sẽ được thay đổi, tôi nghĩ rằng @buergi đúng là phương thức refreshDrawableState được gọi trong onClickEvent. Cảm ơn một lần nữa cho hướng dẫn tuyệt vời của bạn :)
Bolton

2
Nhưng làm thế nào bạn có thể sử dụng trạng thái tùy chỉnh mà không phải là boolean? Hoặc là các bộ chọn chỉ hoạt động trên booleans?
Peterdk

2
Làm thế nào nó hoạt động? Ý tôi là, làm thế nào thuộc tính được cập nhật thành trạng thái đúng / sai? Ai cập nhật nó? Việc hợp nhất drawableestate, chỉ khi biến cục bộ là đúng, cập nhật trạng thái hoặc giá trị của thuộc tính? Mã nào chính xác sẽ được cập nhật R.attr.state_fried?
kAmol

10

Chủ đề này cho thấy làm thế nào để thêm trạng thái tùy chỉnh vào các nút và tương tự. (Nếu bạn không thể thấy các nhóm Google mới trong trình duyệt của mình, thì có một bản sao của chuỗi ở đây .)


+1 cảm ơn rất nhiều, Ted! Ngay bây giờ nguồn gốc của sự cố đã biến mất nên tôi không thể thực hiện được. Tuy nhiên, nếu khách hàng của tôi quay lại vấn đề này một lần nữa, tôi sẽ thử cách bạn chỉ cho tôi.
Vit Khudenko

Trông giống hệt những gì tôi cần, tuy nhiên, các danh sách rút gọn trong danh sách trạng thái tùy chỉnh của tôi không thay đổi Tôi phải thiếu thứ gì đó ...
Nathan Schwermann

Bạn đang gọi refreshDrawableState ()?
Ted Hopp

Các liên kết đã chết.
Mitch

@Mitch - Chà, tệ quá. Tôi sẽ xem nếu tôi có thể tìm thấy một số liên kết thay thế. Nếu không, tôi sẽ xóa câu trả lời này, vì về cơ bản nó vô dụng. Trong khi đó, câu trả lời được chấp nhận có tất cả các thông tin cần thiết.
Ted Hopp

6

Xin đừng quên gọi refreshDrawableStatetrong chủ đề UI:

mHandler.post(new Runnable() {
    @Override
    public void run() {
        refreshDrawableState();
    }
});

Tôi đã mất rất nhiều thời gian để tìm hiểu tại sao nút của tôi không thay đổi trạng thái mặc dù mọi thứ có vẻ đúng.


Tôi nên đăng trình xử lý này ở đâu hoặc khi nào?
Arthur Melo
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.