टचलाइस्टनर और क्लिकलिस्टनर के साथ हाइलाइट की गई स्थिति में बटन बचे


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;
            }
        });

    }
}

ऊपर दिए गए कोड के बारे में, इसका उपयोग करते समय, मैं उम्मीद कर रहा हूं कि बटन स्पर्श द्वारा नियंत्रित होने के लिए क्लिक करें, और "सही" लौटने पर हैंडलिंग को टचलिस्ट पर रोकना चाहिए।

पर ये स्थिति नहीं है। बटन एक हाइलाइट स्थिति में रहता है, भले ही क्लिक कहा जा रहा हो।

मुझे क्या मिलता है:

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) टचलिस्ट

2) ClickListener

3) जनक दृश्य

क्या कोई इसकी पुष्टि कर सकता है?


आप वास्तव में क्या करना चाहते हैं, क्या यह सिर्फ प्रेस पर स्पर्श या रंग बदलने से है?
हैदर सलीम

मैं संपर्क में कुछ तर्क चलाना चाहता हूं और फिर प्रदर्शन क्लिक करें और इसके लिए बटन का रंग नहीं बदलना चाहिए।
व्हाइटबियर

@Whitebear कृपया नीचे दिए गए उत्तर की जाँच करें। शायद मैं अधिक जानकारी जोड़ सकता हूं।
गेन्सेम्स

यह आपको स्पर्श घटनाओं के प्रवाह को समझने में मदद कर सकता है। यह स्पष्ट नहीं है कि आप क्या करना चाहते हैं। क्या आप एक क्लिक हैंडलर चाहते हैं और प्रदर्शन क्लिक करना चाहते हैं? क्या आप चाहते हैं कि बटन अपने प्रारंभिक रंग से रंगीन फ़िल्टर द्वारा सेट की गई अवस्था में जाए और फिर अपने प्रारंभिक रंग में वापस आ जाए?
Cheticamp

मुझे मेरा आशय समझाने दीजिए। मेरे पास एक टचलिस्ट और बटन पर क्लिक करने वाला है। स्पर्श प्राथमिकता से क्लिक को रोकता है और अगर यह घटना को संभाला है तो यह सही है, जिसका अर्थ है कि किसी और को इसे संभालना नहीं चाहिए। यह वही है जो मैं स्पर्श के माध्यम से कर रहा हूं, क्लिक को संभालना और सही लौटना, फिर भी बटन अभी भी हाइलाइट रहता है भले ही यह ऑनक्लिक श्रोता कहलाता है और प्रवाह सही ढंग से किया जाता है।
व्हाइटबियर

जवाबों:


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विधि)। (उपरोक्त चित्र में आरटीएल प्रवाह)

अब स्पर्श दृश्य प्रवाह में गहरी अंतर्दृष्टि प्राप्त करने के लिए दृश्य स्रोत कोड पर एक नज़र डालते हैं । के कार्यान्वयन पर एक नज़र डालकर 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। हम देखते हैं कि प्रदर्शन-क्लिक क्रिया वहाँ होती है। इसलिए, 's' trueमें वापस लौटना और परिणामस्वरूप कॉलिंग नहीं करना , 's' कॉल न करने का कारण बनता है ।OnTouchListeneronTouchonTouchEventOnClickListeneronClick

कॉल न करने के साथ एक और मुद्दा है 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)ड्रॉबल स्टेट को बदलने के लिए स्वयं को कॉल करके कोड बदल सकते हैं जहां घटना की कार्रवाई है 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: मैंने उत्तर अपडेट कर दिया है। कृपया इसे देखें, यार।
अमीनोग्राफी

यह एक अच्छा विस्तृत उत्तर है, और मैं इसे स्वीकार करूंगा। बदलने के लिए कुछ संकेत: 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 चेक करता है कि क्या यह झूठे से सेटिंग करने से पहले अशक्त है और यदि हम पूर्वगामी हैं तो भी रन करने योग्य निश्चित नहीं है। मुझे यह समझ में नहीं आया कि आप कैसे घटाते हैं कि इसे झूठे
व्हाइटबियर

onClickकहा जाता है क्योंकि आप बुला रहे हैं है v.performClick();। कृपया ऊपर दिए गए कोड को MotionEvent.ACTION_UPफिर से सेक्शन में देखें, setPressed(false)इसे वैसे भी कहा जाता है, चाहे mUnsetPressedStateअशक्त हो या न हो, prepressedसत्य है या नहीं। अंतर कॉलिंग के तरीके में है setPressed(false)जो post/ के माध्यम से postDelayedया सीधे हो सकता है ।
अमीनोग्राफी

2

आप घटनाओं touchऔर focusघटनाओं के आसपास गड़बड़ कर रहे हैं । एक ही रंग के साथ समझ व्यवहार के साथ शुरू करते हैं। डिफ़ॉल्ट रूप से, एंड्रॉइड में Selectorपृष्ठभूमि के रूप में असाइन किया Buttonगया है। तो बस बैकग्राउंड कलर बदलने से मेक स्टैटिक है (रंग नहीं बदलेगा)। लेकिन यह मूल व्यवहार नहीं है।

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। सेट करने से onTouchListenerआप स्पर्श घटनाओं को संभाल लेंगे, जिनका कोई लेना-देना नहीं है focus

Selectorबटन के बटन पर क्लिक घटना के दौरान के focusसाथ घटना को बदलना चाहिए touch। लेकिन आपके कोड के पहले भाग में आपने touch(कॉलबैक से सच लौटना) के लिए घटनाओं को रोक दिया था । रंग परिवर्तन आगे नहीं बढ़ सकता है और एक ही रंग के साथ ठंड है। और यही कारण है कि दूसरा संस्करण (बिना अवरोधन) ठीक काम कर रहा है और यह आपका भ्रम है।

अपडेट करें

आपको इसके लिए व्यवहार और रंग बदलने की आवश्यकता है 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>

फिर अच्छा काम करने के लिए आप पहला उदाहरण कैसे बदलेंगे?
व्हाइटबियर

Im मेरे कार्यान्वयन से स्पर्श श्रोता को हटाना नहीं चाह रहा है। जैसा कि मैं टच हैंडलर का उपयोग करके एक विशिष्ट दृश्य पर क्लिक पकड़ना चाहता हूं, और फिर उन्हें स्वयं (performClick का उपयोग करके) हैंडल करता हूं और अन्य हैंडलर को बताने के लिए टच श्रोता से सही वापस आता है कि आगे कोई हैंडलिंग नहीं हुई है। लॉग मेरे उदाहरण में ठीक मुद्रित हैं, फिर भी बटन अभी भी हाइलाइट किया गया है।
व्हाइटबियर

@Whitebear आपने उन प्रश्नों में उल्लेख नहीं किया है। किसी भी तरह से, आप जितना चाहें उतने onTouchListenerएस का उपयोग कर सकते हैं। आपको बस ईवेंट का उपभोग करने की आवश्यकता नहीं है return true
गेन्सेम्स

@Hitebear या चयनकर्ता निकालें और बटन के माध्यम से कच्चे रंग सेट करें backgroundColor
गेन्सेम्स

दोस्तों, आप अभी भी मुझे मूल पोस्ट में लिखे गए पते को संबोधित नहीं कर रहे हैं ..
व्हाइटबियर

0

यदि आप बटन को पृष्ठभूमि असाइन करते हैं, तो यह क्लिक पर रंग नहीं बदल सकता है।

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

और इसे अपने बटन के बैकग्राउंट के रूप में सेट करें

android:background="@color/myColor"

0

आप बस बटन दृश्य के बजाय सामग्री चिप्स का उपयोग कर सकते हैं। संदर्भ: https://material.io/develop/android/compenders/chip वहां वे उन hililghted घटनाओं को संभालते हैं और आप थीम को लागू करने के साथ अनुकूलित कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.