सॉफ्ट कीबोर्ड से बैक बटन को इंटरसेप्ट करें


84

मेरे पास कई इनपुट फ़ील्ड के साथ गतिविधि है। जब गतिविधि शुरू हुई तो नरम कीबोर्ड दिखाया गया है। जब बैक बटन दबाया सॉफ्ट कीबोर्ड बंद हो जाता है और गतिविधि को बंद करने के लिए मुझे बैक बटन को एक बार और प्रेस करना होगा।

तो सवाल: क्या कस्टम बनाने के बिना बैक बटन के एक प्रेस में सॉफ्ट कीबोर्ड बंद करने और गतिविधि खत्म करने के लिए बैक बटन इंटरसेप्ट करना संभव है InputMethodService?

PS मुझे पता है कि अन्य मामलों में बैक बटन को कैसे इंटरसेप्ट करना है: onKeyDown()या onBackPressed()यह इस मामले में काम नहीं करता है: बैक बटन का केवल दूसरा प्रेस इंटरसेप्टेड है।

जवाबों:


76

हां, कीबोर्ड को दिखाना और छिपाना और कॉल को बैक बटन पर इंटरसेप्ट करना पूरी तरह से संभव है। यह थोड़ा अतिरिक्त प्रयास है क्योंकि यह उल्लेख किया गया है कि एपीआई में ऐसा करने का कोई सीधा तरीका नहीं है। कुंजी boolean dispatchKeyEventPreIme(KeyEvent)एक लेआउट के भीतर ओवरराइड करने के लिए है । हम जो करते हैं वह हमारा लेआउट बनाता है। मैंने RelativeLayout को चुना क्योंकि यह मेरी गतिविधि का आधार था।

<?xml version="1.0" encoding="utf-8"?>
<com.michaelhradek.superapp.utilities.SearchLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.michaelhradek.superapp"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white">

हमारी गतिविधि के अंदर हम अपने इनपुट फ़ील्ड सेट करते हैं और setActivity(...)फ़ंक्शन को कॉल करते हैं।

private void initInputField() {
    mInputField = (EditText) findViewById(R.id.searchInput);        

    InputMethodManager imm = 
        (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 
            InputMethodManager.HIDE_IMPLICIT_ONLY);

    mInputField.setOnEditorActionListener(new OnEditorActionListener() {

        @Override
        public boolean onEditorAction(TextView v, int actionId,
                KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                performSearch();
                return true;
            }

            return false;
        }
    });

    // Let the layout know we are going to be overriding the back button
    SearchLayout.setSearchActivity(this);
}

जाहिर है, initInputField()फ़ंक्शन इनपुट फ़ील्ड सेट करता है। यह कार्यक्षमता को निष्पादित करने के लिए एंटर कुंजी को सक्षम करता है (मेरे मामले में एक खोज)।

@Override
public void onBackPressed() {
    // It's expensive, if running turn it off.
    DataHelper.cancelSearch();
    hideKeyboard();
    super.onBackPressed();
}

इसलिए जब onBackPressed()हमारे लेआउट के भीतर कॉल किया जाता है तो हम कीबोर्ड को छिपाने के लिए जो चाहें कर सकते हैं:

private void hideKeyboard() {
    InputMethodManager imm = (InputMethodManager) 
        getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(mInputField.getWindowToken(), 0);
}

वैसे भी, यहाँ RelativeLayout का मेरा ओवरराइड है।

package com.michaelhradek.superapp.utilities;

import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.RelativeLayout;

/**
 * The root element in the search bar layout. This is a custom view just to 
 * override the handling of the back button.
 * 
 */
public class SearchLayout extends RelativeLayout {

    private static final String TAG = "SearchLayout";

    private static Activity mSearchActivity;;

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

    public SearchLayout(Context context) {
        super(context);
    }

    public static void setSearchActivity(Activity searchActivity) {
        mSearchActivity = searchActivity;
    }

    /**
     * Overrides the handling of the back key to move back to the 
     * previous sources or dismiss the search dialog, instead of 
     * dismissing the input method.
     */
    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        Log.d(TAG, "dispatchKeyEventPreIme(" + event + ")");
        if (mSearchActivity != null && 
                    event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            KeyEvent.DispatcherState state = getKeyDispatcherState();
            if (state != null) {
                if (event.getAction() == KeyEvent.ACTION_DOWN
                        && event.getRepeatCount() == 0) {
                    state.startTracking(event, this);
                    return true;
                } else if (event.getAction() == KeyEvent.ACTION_UP
                        && !event.isCanceled() && state.isTracking(event)) {
                    mSearchActivity.onBackPressed();
                    return true;
                }
            }
        }

        return super.dispatchKeyEventPreIme(event);
    }
}

दुर्भाग्य से मैं सभी क्रेडिट नहीं ले सकता। यदि आप त्वरित SearchDialog बॉक्स के लिए एंड्रॉइड स्रोत की जांच करते हैं, तो आप देखेंगे कि विचार कहां से आया है।


2
यह बहुत अच्छी तरह से काम किया, और लागू करने के लिए बहुत मुश्किल नहीं था (थोड़ा डरावना दिखने के बावजूद)।
— सीजर

इसके लिए बहुत-बहुत धन्यवाद, मैं बस यही चाहता हूं कि मेरे पास एंड्रॉइड के पुराने संस्करणों के साथ काम करने का एक तरीका है।
— zidarsk8

3
सिर्फ कास्टिंग के द्वारा कोड को सरल बनाया जा सकता getContext()है Activity। बशर्ते लेआउट का संदर्भ पाठ्यक्रम के प्रश्न में गतिविधि हो। लेकिन मुझे नहीं पता कि यह नहीं हो सकता है।
— mxcl

3
स्थैतिक mSearchActivity - जो एक समस्या से गलत गंध लेती है। यदि आपके पास लेआउट में इनमें से दो हैं तो क्या होगा? इसके बजाय गैर-स्थिर चर का उपयोग करें।
— पॉइंटर नल

एक जादू की तरह काम करता है। लेकिन, मुझे लगता है कि स्थानीय स्थिर इंटरफ़ेस का उपयोग करना बेहतर है और स्थैतिक अभिनय का उपयोग करने के बजाय इसे अंदर की गतिविधि से लागू करना है ...
— SpiralDev

80

onKeyDown () और onBackPressed () इस मामले के लिए काम नहीं करता है। आपको onKeyPreIme का उपयोग करना होगा ।

प्रारंभ में, आपको कस्टम संपादन पाठ बनाना होगा जो EditText का विस्तार करता है। और फिर आपको onKeyPreIme पद्धति को लागू करना होगा जो KeyEvent.KEYCODE_BACK को नियंत्रित करता है । इसके बाद, आपकी समस्या को हल करने के लिए एक बैक पर्याप्त है। यह समाधान मेरे लिए पूरी तरह से काम करता है।

CustomEditText.java

public class CustomEditText extends EditText {

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

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            // User has pressed Back key. So hide the keyboard
            InputMethodManager mgr = (InputMethodManager)         

           getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);
            // TODO: Hide your view as you do it in your activity
        }
        return false;
}

अपने XML में

<com.YOURAPP.CustomEditText
     android:id="@+id/CEditText"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"/> 

आपकी गतिविधि में

public class MainActivity extends Activity {
   private CustomEditText editText;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      editText = (CustomEditText) findViewById(R.id.CEditText);
   }
}

4
इस जवाब को इतना प्यार करो। इसका उपयोग एक खंड के साथ-साथ (एक खोज बार के भीतर जो हमेशा दिखाई भी नहीं देता है), और editText को ओवरराइड करने का सुझाव देकर, यह आसानी से इस व्यवहार को केवल तभी लागू करता है जब इसके आवश्यक
— Jawad

2
हर किसी के लिए ध्यान दें: इसमें एक त्वरित इंटरफ़ेस जोड़ना जीवन को इतना बेहतर बनाता है, और सौभाग्य से यह करना बहुत आसान है
— जावेद

onKeyPreIme () में एक विधि को कैसे कॉल करें जो mainActivity.java में है?
— अभिज्ञान

आपको गतिविधि में onKeyPreIme पर कॉल करने की आवश्यकता नहीं है। यदि आप अपनी गतिविधि में CustomEditText का उपयोग करते हैं, तो कीबोर्ड पर कोई भी बटन दबाने पर विधि चालू हो जाती है।
— एलिकन टेम्पल

1
मुझे लगता है कि यह सबसे अच्छा उत्तर है, क्योंकि इसे लागू करना अधिक सरल है। वास्तव में आपको बस क्लास "CustomEditText" की प्रतिलिपि बनानी होगी (और XML में अपने एडिटटेक्स्ट का नाम बदलना होगा) और // TODO क्षेत्र में जो आप चाहते हैं वह करें। और TODO क्षेत्र में बस "वापसी सच" जोड़ें यदि आप अपनी गतिविधि को बंद नहीं करना चाहते हैं। धन्यवाद !
— क्राइसोट्रिबैक्स

10

मुझे पता चला, कि डिस्पैच को ओवरराइड करना है लेआउट क्लास विधि को करने से भी अच्छा काम होता है। बस अपनी मुख्य गतिविधि को एक विशेषता के रूप में सेट करें और एक पूर्वनिर्धारित विधि लॉन्च करें।

public class LinearLayoutGradient extends LinearLayout {
    MainActivity a;

    public void setMainActivity(MainActivity a) {
        this.a = a;
    }

    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        if (a != null) {
            InputMethodManager imm = (InputMethodManager) a
                .getSystemService(Context.INPUT_METHOD_SERVICE);

            if (imm.isActive() && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                a.launchMethod;
            }
        }

        return super.dispatchKeyEventPreIme(event);
    }
}

क्या आप मुझे इस लेआउट के लिए अपना xml दिखा सकते हैं? मैं इस दृष्टिकोण की जाँच करने की कोशिश कर रहा हूँ और मेरे पास कस्ट्यूम लेआउट में सतह दृश्य है, लेकिन यह कुछ भी अवरोधन नहीं कर रहा है
— ZZZ

मूल रूप से कस्टम दृश्य xml में आसपास का तत्व है। <?xml version="1.0" encoding="utf-8"?> <view class="package.LinearLayoutGradient"...
— किरिल रहमान

यह भी काम करता है अगर हम केवल एक दृश्य का विस्तार करते हैं और इसे अलग कर देते हैं। स्वयं लेआउट का विस्तार करना आवश्यक नहीं है।
— फ़ैज़ल

7

मैं अधिभावी द्वारा सफलता मिली dispatchKeyEvent :

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        finish();
        return true;
    }
    return super.dispatchKeyEvent(event);
}

यह कीबोर्ड को छुपाता है और गतिविधि को पूरा करता है।


यह केवल कुछ उपकरणों जैसे कि एचटीसी डिजायर पर काम करने के लिए प्रतीत होता है।
— अभिजीत

5
सैमसंग नोट 2 पर काम नहीं करता है। नरम कीबोर्ड उठने पर डिस्पैचकेवेंट () नहीं कहा जाता है।
— फ़ैज़ल

ठीक है, मैंने नेक्सस 6 एमुलेटर (लॉलीपॉप 5.0) के साथ यह कोशिश की -> काम किया और नोट 4 (सीएम 12 लॉलीपॉप 5.0.2) -> ऐसा काम नहीं किया, वास्तव में सबसे अच्छा समाधान नहीं है - लेकिन फिर भी .. धन्यवाद :)
— मार्टिन Pfeider

बटन क्लिक सेवन किया जाता है जब कुंजीपटल खारिज
— slott

4

आप सॉफ्ट कीबोर्ड कैसे दिखा रहे हैं?

यदि आप उपयोग कर रहे हैं InputMethodManager.showSoftInput(), तो आप इसे संभालने के लिए पास करने ResultReceiverऔर कार्यान्वित onReceiveResult()करने का प्रयास कर सकते हैंRESULT_HIDDEN

http://developer.android.com/reference/android/view/inputmethod/InputMethodManager.html


अच्छा लगता है, लेकिन showSoftInput()अपने फोन और एमुलेटर पर कीबोर्ड नहीं दिखाता है, इसलिए मैं उपयोग करता हूंtoggleSoftInput()
— सेर्गेई ग्लोटोव

1
मेरे लिए काम नहीं करता है। onReceiveResult () को RESULT_SHOWN के साथ कहा जाता है, लेकिन बाद में RESULT_HIDDEN
— यूलिया रोगोवया के

2

मुझे भी यही समस्या थी लेकिन बैक की प्रेस को इंटरसेप्ट करके इसके आस-पास हो गया। मेरे मामले में (HTC Desire, Android 2.2, Application API Level 4) यह कीबोर्ड को बंद कर देता है और गतिविधि को तुरंत समाप्त कर देता है। पता नहीं क्यों यह आपके लिए भी काम नहीं करना चाहिए:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        onBackPressed();
        return true;
    }
    return super.onKeyUp(keyCode, event);
}

/**
 * Called when the activity has detected the user's press of the back key
 */
private void onBackPressed() {
    Log.e(TAG, "back pressed");
    finish();
}

यह सैमसंग गैलेक्सी नोट 2 पर काम नहीं करता है। सॉफ्ट कीबोर्ड दिखाई देने पर ऑनकीडाउन () या ऑनबैकप्रैड () ट्रिगर नहीं होते हैं और बैक बटन दबाया जाता है
— faizal

1

घटना के onKeyPreIme(int keyCode, KeyEvent event)लिए विधि और जाँच का उपयोग करें KeyEvent.KEYCODE_BACK। बिना किसी फैंसी कोडिंग के यह बहुत सरल है।


0

इस कोड को अपने BackPressed कार्यान्वयन ( Android में ब्लॉक बैक बटन ) में आज़माएँ :

InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(myEditText.getWindowToken(), 0);

मेरा सुझाव है कि आप एंड्रॉइड सॉफ्ट कीबोर्ड को बंद करें / छिपाएं


1
मैंने पहले ही यह कोशिश कर ली है, लेकिन इसे अभी भी बैक बटन पर दो प्रेस की आवश्यकता है। जैसा कि मैं समझता हूं कि यह पहला क्लिक सॉफ्ट कीबोर्ड द्वारा इंटरसेप्ट किया गया है इसलिए onBackPressed()यह काम नहीं करता है। और केवल दूसरे प्रेस कार्यक्रम में आने के बाद onBackPressed()।
— सर्गेई ग्लोटोव

@Sergey Glotov मैंने समस्या के लिए बहुत सारे सुझाव की कोशिश की और अंत में एक अच्छा समाधान नहीं आया, जहां मुझे अपने सॉफ्टकाइबर्ड को लागू करना है। लेकिन मुझे उम्मीद है कि जल्द ही एंड्रॉइड समुदाय बहुत बेहतर समाधान के साथ सामने आएगा।
— 100rabh

0

@mhradek समाधान का मेरा संस्करण:

ख़ाका

class BazingaLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    ConstraintLayout(context, attrs, defStyleAttr) {

var activity: Activity? = null

override fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
    activity?.let {
        if (event.keyCode == KeyEvent.KEYCODE_BACK) {
            val state = keyDispatcherState
            if (state != null) {
                if (event.action == KeyEvent.ACTION_DOWN
                    && event.repeatCount == 0) {
                    state.startTracking(event, this)
                    return true
                } else if (event.action == KeyEvent.ACTION_UP && !event.isCanceled && state.isTracking(event)) {
                    it.onBackPressed()
                    return true
                }
            }
        }
    }
    return super.dispatchKeyEventPreIme(event)
}

}

xml फ़ाइल

<com... BazingaLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/grey">
 </com... BazingaLayout>

टुकड़ा

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (view as BazingaLayout).activity = activity
        super.onViewCreated(view, savedInstanceState)
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.