ปุ่มเหลืออยู่ในสถานะถูกเน้นด้วย touchListener และ clickListener


10

ฉันมีปัญหากับปุ่มของฉันอยู่ในสถานะไฮไลต์หลังจากทำสิ่งต่อไปนี้:

public class MainActivity extends AppCompatActivity {

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Test", "calling onClick");
            }
        });
        button.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN: {
                        v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                        v.invalidate();
                        break;
                    }
                    case MotionEvent.ACTION_UP: {
                        v.getBackground().clearColorFilter();
                        v.invalidate();
                        v.performClick();
                        Log.d("Test", "Performing click");
                        return true;
                    }
                }
                return false;
            }
        });

    }
}

เกี่ยวกับรหัสด้านบนเมื่อใช้งานฉันคาดหวังว่าการคลิกปุ่มจะได้รับการจัดการโดยการสัมผัสและการคืนค่า "จริง" การจัดการควรหยุดที่ touchListener

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

สิ่งที่ฉันได้รับคือ:

Test - calling onClick
Test - Performing click

ในทางกลับกันหากฉันใช้รหัสต่อไปนี้ปุ่มจะถูกคลิกพิมพ์เหมือนกัน แต่ปุ่มไม่ได้ติดอยู่ในสถานะไฮไลต์:

public class MainActivity extends AppCompatActivity {

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Test", "calling onClick");
            }
        });
        button.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN: {
                        v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                        v.invalidate();
                        break;
                    }
                    case MotionEvent.ACTION_UP: {
                        v.getBackground().clearColorFilter();
                        v.invalidate();
                        // v.performClick();
                        Log.d("Test", "Performing click");
                        return false;
                    }
                }
                return false;
            }
        });

    }
}

ฉันสับสนเล็กน้อยเกี่ยวกับสิ่งที่ห่วงโซ่การตอบกลับเหตุการณ์การสัมผัส ฉันเดาว่ามัน:

1) TouchListener

2) ClickListener

3) ParentViews

บางคนสามารถยืนยันสิ่งนี้ได้เช่นกัน?


สิ่งที่คุณต้องการทำจริง ๆ มันคือการจัดการโดยการสัมผัสหรือเปลี่ยนสีเพียงแค่กด?
Haider Saleem

ฉันต้องการเรียกใช้ตรรกะบางอย่างในการติดต่อแล้วโทร performClick และมันจะไม่เปลี่ยนสีของปุ่ม
Whitebear

@ Whitebear โปรดตรวจสอบคำตอบด้านล่าง บางทีฉันสามารถเพิ่มข้อมูลเพิ่มเติมได้
GensaGames

นี่อาจช่วยให้คุณเข้าใจการไหลของกิจกรรมการสัมผัส ยังไม่ชัดเจนว่าคุณต้องการเกิดอะไรขึ้น คุณต้องการตัวจัดการการคลิกและคลิกเพื่อดำเนินการหรือไม่ คุณต้องการให้ปุ่มเปลี่ยนจากสีเริ่มต้นไปเป็นสถานะที่กำหนดโดยตัวกรองสีจากนั้นกลับสู่สีเริ่มต้นหรือไม่
Cheticamp

ให้ฉันอธิบายสิ่งที่ฉันหมายถึง ฉันมี touchListener และ clickListener บนปุ่ม การแตะมาก่อนการคลิกตามลำดับความสำคัญและคืนค่าจริงถ้าจัดการเหตุการณ์ซึ่งหมายความว่าไม่มีใครควรจัดการ นี่คือสิ่งที่ฉันทำผ่านการสัมผัสการจัดการการคลิกและการคืนค่าจริง แต่ปุ่มยังคงเน้นอยู่แม้ว่าจะมีการเรียกใช้ตัวฟัง onclick และการไหลทำได้อย่างถูกต้อง
Whitebear

คำตอบ:


10

การปรับแต่งดังกล่าวไม่จำเป็นต้องมีการปรับเปลี่ยนทางโปรแกรม คุณสามารถทำได้ง่ายๆในxmlไฟล์ ก่อนอื่นให้ลบsetOnTouchListenerวิธีที่คุณระบุไว้ในonCreateทั้งหมด ถัดไปกำหนดสีของตัวเลือกในres/colorไดเรกทอรีดังต่อไปนี้ (หากไม่มีไดเรกทอรีให้สร้างขึ้น)

ความละเอียด / สี / button_tint_color.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#e0f47521" android:state_pressed="true" />
    <item android:color="?attr/colorButtonNormal" android:state_pressed="false" />
</selector>

ตอนนี้ตั้งค่าapp:backgroundTintคุณลักษณะของปุ่ม:

<androidx.appcompat.widget.AppCompatButton
    android:id="@+id/mybutton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    app:backgroundTint="@color/button_tint_color" />


ผลภาพ:

ป้อนคำอธิบายรูปภาพที่นี่



แก้ไข: (เพื่อแก้ไขปัญหาเหตุการณ์การสัมผัส)

จากมุมมองโดยรวมโฟลว์ของเหตุการณ์การสัมผัสเริ่มต้นจากจากActivityนั้นไหลลงสู่เค้าโครง (จากแม่ไปยังเค้าโครงลูก) และมุมมอง (การไหล LTR ในภาพต่อไปนี้)

ป้อนคำอธิบายรูปภาพที่นี่

เมื่อเหตุการณ์แตะถึงมุมมองเป้าหมายมุมมองสามารถจัดการเหตุการณ์จากนั้นตัดสินใจส่งผ่านไปยังเค้าโครง / กิจกรรมก่อนหน้าหรือไม่ (กลับมาfalseจากtrueในonTouchวิธีการ) (การไหลของ RTL ในภาพด้านบน)

ตอนนี้ลองมาดูซอร์สโค้ดของViewเพื่อให้ได้ข้อมูลเชิงลึกที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับเหตุการณ์การสัมผัส โดยการดูที่การนำไปปฏิบัติdispatchTouchEventเราจะเห็นว่าหากคุณตั้งค่าเป็นOnTouchListenerมุมมองจากนั้นกลับมาtrueในonTouchวิธีonTouchEventการมุมมองจะไม่ถูกเรียก

public boolean dispatchTouchEvent(MotionEvent event) {
    // removed lines for conciseness...
    boolean result = false;    
    // removed lines for conciseness...
    if (onFilterTouchEventForSecurity(event)) {
        // removed lines for conciseness...
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) { // <== right here!
            result = true;
        }
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    // removed lines for conciseness...
    return result;
}

ตอนนี้ให้ดูที่วิธีการที่การกระทำเหตุการณ์onTouchEvent MotionEvent.ACTION_UPเราเห็นว่าการกระทำคลิกกระทำเกิดขึ้นที่นั่น ดังนั้นการกลับมาtrueในOnTouchListener's onTouchและจึงไม่เรียกonTouchEventสาเหตุไม่เรียกOnClickListener' sonClick s

มีปัญหาอื่นที่ไม่ได้โทรหาonTouchEventซึ่งเกี่ยวข้องกับสถานะที่กดและคุณพูดถึงในคำถาม ดังที่เราเห็นในบล็อกโค้ดด้านล่างมีอินสแตนซ์ของการUnsetPressedStateโทรนั้นเมื่อมันทำงาน ผลลัพธ์ของการไม่เรียกใช้คือมุมมองค้างอยู่ในสถานะที่กดและสถานะที่ดึงได้จะไม่เปลี่ยนแปลง setPressed(false)setPressed(false)

public boolean onTouchEvent(MotionEvent event) {
    // removed lines for conciseness...
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                // removed lines for conciseness...
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    // removed lines for conciseness...
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // removed lines for conciseness...
                        if (!focusTaken) {
                            // Use a Runnable and post this rather than calling
                            // performClick directly. This lets other visual state
                            // of the view update before click actions start.
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClickInternal();
                            }
                        }
                    }
                    if (mUnsetPressedState == null) {
                        mUnsetPressedState = new UnsetPressedState();
                    }
                    if (prepressed) {
                        postDelayed(mUnsetPressedState,
                                ViewConfiguration.getPressedStateDuration());
                    } else if (!post(mUnsetPressedState)) {
                        // If the post failed, unpress right now
                        mUnsetPressedState.run();
                    }
                    // removed lines for conciseness...
                }
                // removed lines for conciseness...
                break;
            // removed lines for conciseness...
        }
        return true;
    }
    return false;
}

UnsetPressedState :

private final class UnsetPressedState implements Runnable {
    @Override
    public void run() {
        setPressed(false);
    }
}


เกี่ยวกับคำอธิบายข้างต้นคุณสามารถเปลี่ยนรหัสได้โดยเรียกsetPressed(false)ตัวคุณเองเพื่อเปลี่ยนสถานะ drawable ซึ่งการกระทำของกิจกรรมคือMotionEvent.ACTION_UP:

button.setOnTouchListener(new View.OnTouchListener() {

    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                v.invalidate();
                break;
            }
            case MotionEvent.ACTION_UP: {
                v.getBackground().clearColorFilter();
                // v.invalidate();
                v.setPressed(false);
                v.performClick();
                Log.d("Test", "Performing click");
                return true;
            }
        }
        return false;
    }
});

นี่ไม่ใช่สิ่งที่ฉันกำลังมองหาเพื่อนของฉัน ฉันกำลังมองหาที่จะเข้าใจการเปลี่ยนแปลงพฤติกรรมในทั้งสองสถานการณ์ที่ฉันอธิบายไว้ข้างต้น หากมีสิ่งที่ฉันสามารถอธิบายรายละเอียดได้โปรดแจ้งให้เราทราบ ฉันไม่ต้องการลบตัวจัดการแบบสัมผัสหรือตัวจัดการการคลิก กรุณาตรวจสอบความคิดเห็นของฉันเพื่อคำตอบข้างต้น
Whitebear

@Whitebear: ฉันได้อัพเดตคำตอบแล้ว กรุณาตรวจสอบมันเพื่อน
อะมิโนกราฟ

นั่นเป็นคำตอบที่ดีและฉันก็ยอมรับมัน ตัวชี้สองสามตัวที่จะเปลี่ยนแปลง: Now, look at the onTouchEvent method where the event action is MotionEvent.ACTION_UP. We see that perform-click action happens there. So, returning true in the OnTouchListener's onTouch and consequently not calling the onTouchEvent, causes not calling the OnClickListener's onClick.ในกรณีของฉัน onClick ถูกเรียก mUnsetPressedState ตรวจสอบว่ามันเป็นโมฆะก่อนที่จะตั้งค่าเป็นเท็จและยังเรียกใช้ไม่ได้ว่าจะทำงานถ้าเราถูกบีบอัด ฉันไม่เข้าใจว่าคุณจะ
Whitebear

เรียกว่าเพราะคุณกำลังเรียกร้องonClick v.performClick();โปรดตรวจสอบรหัสข้างต้นในMotionEvent.ACTION_UPส่วนอีกครั้งsetPressed(false)เรียกว่าไม่ว่าจะmUnsetPressedStateเป็นโมฆะหรือไม่ไม่ว่าจะprepressedเป็นจริงหรือไม่ ความแตกต่างคือในวิธีการโทรsetPressed(false)ที่สามารถผ่านpost/ postDelayedหรือโดยตรง
อะมิโนกราฟ

2

คุณกำลังยุ่งไปtouchกับfocusกิจกรรมและ เริ่มต้นด้วยพฤติกรรมการทำความเข้าใจที่มีสีเดียวกัน ตามค่าเริ่มต้นจะมีการSelectorกำหนดเป็นพื้นหลังให้กับButtonใน Android ดังนั้นเพียงแค่เปลี่ยนสีพื้นหลังทำให้เป็นแบบคงที่ (สีจะไม่เปลี่ยนแปลง) แต่มันไม่ใช่พฤติกรรมดั้งเดิม

Selector อาจมีลักษณะเช่นนี้

<?xml version="1.0" encoding="utf-8"?> 
  <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_focused="true"
        android:state_pressed="true"
        android:drawable="@drawable/bgalt" />

    <item
        android:state_focused="false"
        android:state_pressed="true"
        android:drawable="@drawable/bgalt" />

    <item android:drawable="@drawable/bgnorm" />
</selector>

ขณะที่คุณสามารถดูด้านบนมีรัฐและรัฐfocused pressedโดยการตั้งค่าที่คุณจะจัดการกับเหตุการณ์สัมผัสซึ่งไม่มีอะไรจะทำอย่างไรกับonTouchListenerfocus

Selectorของปุ่มควรแทนที่focusเหตุการณ์ด้วยtouchในระหว่างการคลิกเหตุการณ์บนปุ่ม แต่ในส่วนแรกของรหัสของคุณคุณดักกิจกรรมสำหรับtouch(กลับจริงจากการโทรกลับ) การเปลี่ยนสีไม่สามารถดำเนินการต่อไปและแช่แข็งด้วยสีเดียวกัน และนั่นคือสาเหตุที่ตัวแปรที่สอง (ไม่มีการสกัดกั้น) ทำงานได้ดีและนั่นเป็นความสับสนของคุณ

UPDATE

Selectorทั้งหมดที่คุณต้องทำก็จะเปลี่ยนพฤติกรรมและสีสำหรับ สำหรับอดีต Buttonโดยใช้พื้นหลังต่อไปสำหรับ และลบออกonTouchListenerจากการใช้งานของคุณเลย

<?xml version="1.0" encoding="utf-8"?> 
  <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@color/color_pressed" />

    <item android:drawable="@color/color_normal" />
</selector>

คุณจะเปลี่ยนตัวอย่างแรกให้ทำงานได้ดีอย่างไร
Whitebear

ฉันไม่ต้องการลบผู้ฟังแบบสัมผัสออกจากการใช้งานของฉัน เนื่องจากฉันต้องการจับการคลิกในมุมมองที่ระบุโดยใช้ตัวจัดการการสัมผัสแล้วจัดการด้วยตนเอง (ใช้ performClick) และคืนค่าจริงจากตัวฟังการสัมผัสเพื่อบอกตัวจัดการอื่น ๆ ว่าไม่มีการจัดการใด ๆ เพิ่มเติม บันทึกของฉันพิมพ์ออกมาได้ดีในตัวอย่างของฉัน แต่ปุ่มยังคงเน้นอยู่
Whitebear

@ Whitebear คุณไม่ได้พูดถึงเรื่องนั้นในคำถามของคุณ ไม่ว่าในทางใดคุณสามารถใช้งานได้มากonTouchListenerเท่าที่คุณต้องการ return trueคุณก็ไม่จำเป็นต้องใช้เหตุการณ์โดย
GensaGames

@Whitebear หรือbackgroundColorเลือกถอดและสีดิบชุดปุ่มผ่านทาง
GensaGames

พวกคุณยังไม่ได้อยู่ในสิ่งที่ผมเขียนไว้ในโพสต์ต้นฉบับ ..
Whitebear

0

หากคุณกำหนดพื้นหลังให้กับปุ่มมันจะไม่เปลี่ยนสีเมื่อคลิก

 <color name="myColor">#000000</color>

และตั้งเป็น backgrount ไปที่ปุ่มของคุณ

android:background="@color/myColor"

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