วิธีเพิ่มสถานะปุ่มแบบกำหนดเอง


132

ตัวอย่างเช่นปุ่มเริ่มต้นมีการอ้างอิงต่อไปนี้ระหว่างสถานะและภาพพื้นหลัง:

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

ฉันจะกำหนดสถานะที่กำหนดเองของฉันเอง (เช่น smth android:state_custom) ได้อย่างไรจากนั้นฉันสามารถใช้เพื่อเปลี่ยนลักษณะที่ปรากฏของปุ่มแบบไดนามิกได้อย่างไร


ฉันต้องการสถานะพิเศษสำหรับมุมมอง EditText เพื่อพิจารณาว่าเมื่อใดที่กล่องรหัสผ่านสองกล่องตรงกันเพื่อแสดงเครื่องหมายถูกเล็กน้อย
Nathan Schwermann

คำตอบ:


275

วิธีแก้ปัญหาที่ระบุโดย @ (Ted Hopp) ใช้งานได้ แต่ต้องการการแก้ไขเล็กน้อย: ในตัวเลือกรายการระบุสถานะต้องใช้คำนำหน้า "app:" มิฉะนั้นตัวจ่ายลมจะไม่รู้จักเนมสเปซที่ถูกต้องและจะล้มเหลวอย่างเงียบ ๆ อย่างน้อยนี่คือสิ่งที่เกิดขึ้นกับฉัน

ให้ฉันรายงานโซลูชั่นทั้งหมดพร้อมที่นี่โดยมีรายละเอียดเพิ่มเติม

ก่อนอื่นให้สร้างไฟล์ "res / values ​​/ 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>

จากนั้นกำหนดคลาสที่คุณกำหนดเอง ตัวอย่างเช่นมันอาจเป็นคลาส "FoodButton" มาจากคลาส "ปุ่ม" คุณจะต้องใช้ตัวสร้าง; ใช้อันนี้ซึ่งดูเหมือนจะเป็นคนที่ใช้โดย inflater:

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

ด้านบนของคลาสที่ได้รับ:

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

นอกจากนี้ตัวแปรสถานะของคุณ:

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

และหมาสองตัว:

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

แทนที่ฟังก์ชั่น "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;
}

ในที่สุดชิ้นส่วนที่บอบบางที่สุดของปริศนานี้ ตัวเลือกที่กำหนด StateListDrawable ที่คุณจะใช้เป็นพื้นหลังสำหรับวิดเจ็ตของคุณ นี่คือไฟล์ "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>

แจ้งให้ทราบล่วงหน้าคำนำหน้า "แอพ:" ในขณะที่สถานะ Android มาตรฐานคุณจะต้องใช้คำนำหน้า "android:" XML เนมสเปซมีความสำคัญสำหรับการตีความที่ถูกต้องโดยผู้จัดทำและขึ้นอยู่กับประเภทของโครงการที่คุณกำลังเพิ่มคุณสมบัติ หากเป็นแอปพลิเคชันให้แทนที่com.mydomain.mypackageด้วยชื่อแพ็คเกจจริงของแอปพลิเคชันของคุณ (ยกเว้นชื่อแอปพลิเคชัน) หากเป็นห้องสมุดคุณต้องใช้ "http://schemas.android.com/apk/res-auto" (และใช้ Tools R17 หรือใหม่กว่า) มิฉะนั้นคุณจะได้รับข้อผิดพลาด runtime

หมายเหตุสองประการ:

  • ดูเหมือนว่าคุณไม่จำเป็นต้องเรียกใช้ฟังก์ชัน "refreshDrawableState" อย่างน้อยโซลูชันจะทำงานได้ดีอย่างที่เป็นอยู่ในกรณีของฉัน

  • ในการใช้คลาสที่กำหนดเองของคุณในไฟล์เลย์เอาต์ xml คุณจะต้องระบุชื่อแบบเต็ม (เช่น com.mydomain.mypackage.FoodButton)

  • คุณสามารถเป็นสถานะมาตรฐาน weel mix-up (เช่น android: กด, android: เปิดใช้งาน, android: เลือก) ด้วยสถานะที่กำหนดเองเพื่อแสดงชุดค่าสถานะที่ซับซ้อนมากขึ้น


3
อัปเดต: หากคลาสที่กำหนดเองนั้นมาจาก TextView แทนที่จะเป็นปุ่มการเรียกไปที่ refreshDrawableState ดูเหมือนจะมีความจำเป็นมิฉะนั้นลักษณะที่ปรากฏของวิดเจ็ตจะไม่ถูกอัพเดต การโทรจะถูกวางใน setters ฉันไม่ได้ลองคลาสอื่น ทำการทดสอบบนอุปกรณ์ froyo
Giorgio Barchiesi

17
refreshDrawableStateเป็นสิ่งสำคัญแน่นอน ฉันไม่แน่ใจจริงๆเมื่อมันจำเป็นจริงๆ แต่ในกรณีของฉันมันจำเป็นเมื่อตั้งค่าสถานะโดยทางโปรแกรม ฉันเดาว่ามันอาจถูกเรียกจากคลาส View โดยอัตโนมัติใน onTouchEvent ฉันควรจะเพิ่มในวิธี setSelected
buergi

1
GiorgioBarchiesi ฉันมีสองปุ่มที่กำหนดเองและเมื่อฉันพยายามเปลี่ยนสถานะของทั้งสองปุ่มจากเหตุการณ์ onClick ของปุ่มเดียวเพียงปุ่มที่คลิกเท่านั้นที่จะได้รับการเปลี่ยนแปลงฉันคิดว่า @buergi ถูกต้องที่วิธี refreshDrawableState เรียกใน onClickEvent ขอบคุณอีกครั้งสำหรับการสอนที่ยอดเยี่ยมของคุณ :)
โบลตัน

2
แต่คุณจะใช้สถานะที่กำหนดเองที่ไม่ได้เป็นbooleanอย่างไร หรือ selector ทำงานเฉพาะใน booleans หรือไม่
Peterdk

2
มันทำงานยังไง? ฉันหมายถึงว่าแอตทริบิวต์ได้รับการปรับปรุงอย่างไรเพื่อระบุว่าเป็นเท็จ / จริง? ใครเป็นคนอัพเดต การผสาน drawablestate เฉพาะเมื่อตัวแปรท้องถิ่นเป็นจริงเท่านั้นอัพเดตสถานะหรือค่าของคุณลักษณะหรือไม่ รหัสใดจะถูกอัปเดต R.attr.state_fried
kAmol

10

กระทู้นี้แสดงวิธีเพิ่มสถานะที่กำหนดเองให้กับปุ่มและสิ่งที่คล้ายกัน (หากคุณไม่เห็นกลุ่ม Google ใหม่ในเบราว์เซอร์ของคุณมีสำเนาของเธรดที่นี่ )


+1 ขอบคุณมากเท็ด! ตอนนี้ที่มาของปัญหาได้หายไปดังนั้นฉันจึงไม่ได้ใช้งานจริง อย่างไรก็ตามลูกค้าของฉันควรกลับมาที่นี่อีกครั้งฉันจะลองวิธีที่คุณแนะนำให้ฉัน
Vit Khudenko

มีลักษณะเหมือนกับสิ่งที่ฉันต้องการ แต่ภาพวาดรัฐรายการสำหรับรัฐที่กำหนดเองของฉันจะไม่เปลี่ยนแปลงฉันจะต้องหายไปบางอย่าง ...
นาธาน Schwermann

คุณกำลังเรียก refreshDrawableState () หรือไม่?
Ted Hopp

ลิงก์ตาย
มิทช์

@ Mitch - นั่นมันแย่มาก ฉันจะดูว่าฉันสามารถหาลิงค์ทดแทนได้ไหม ถ้าไม่ฉันจะลบคำตอบนี้เพราะมันไร้ประโยชน์ตามปกติ ในขณะเดียวกันคำตอบที่ได้รับการยอมรับมีข้อมูลทั้งหมดที่จำเป็น
Ted Hopp

6

โปรดอย่าลืมโทรrefreshDrawableStateภายในเธรด UI:

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

ฉันใช้เวลามากมายในการหาสาเหตุที่ปุ่มของฉันไม่เปลี่ยนสถานะแม้ว่าทุกอย่างจะดูถูกต้อง


ฉันควรโพสต์ตัวจัดการนี้ที่ไหนหรือเมื่อไหร่
Arthur Melo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.