簡體   English   中英

攔截軟鍵盤的返回鍵

[英]Intercept back button from soft keyboard

我有幾個輸入字段的活動。 當活動開始時顯示軟鍵盤。 當按下后退按鈕軟鍵盤關閉並關閉活動時,我需要再按一次后退按鈕。

所以問題是:是否可以在不創建自定義InputMethodService情況下攔截后退按鈕以關閉軟鍵盤並一鍵完成活動?

PS 我知道如何在其他情況下攔截后退按鈕: onKeyDown()onBackPressed()但在這種情況下它不起作用:僅第二次按下后退按鈕被攔截。

onKeyDown()onBackPressed()不適用於這種情況。 您必須使用onKeyPreIme

最初,您必須創建擴展 EditText 的自定義編輯文本。 然后你必須實現控制KeyEvent.KEYCODE_BACK 的onKeyPreIme 方法。 在此之后,一回按足以解決您的問題。 這個解決方案非常適合我。

自定義編輯文本

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

是的,完全可以顯示和隱藏鍵盤並攔截對后退按鈕的調用。 這是一個額外的努力,因為它已經提到在 API 中沒有直接的方法來做到這一點。 關鍵是在布局中覆蓋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">

在我們的 Activity 中,我們設置了輸入字段並調用了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);
    }
}

不幸的是,我不能承擔所有的責任。 如果您檢查快速搜索對話框的 Android 源代碼,您將看到這個想法的來源。

我發現,覆蓋布局類的dispatchKeyEventPreIme方法也很有效。 只需將您的主要活動設置為屬性並啟動預定義的方法。

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

我通過覆蓋dispatchKeyEvent取得了成功:

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

它隱藏鍵盤並完成活動。

你怎么顯示軟鍵盤?

如果您使用InputMethodManager.showSoftInput() ,您可以嘗試傳入ResultReceiver並實現onReceiveResult()來處理RESULT_HIDDEN

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

我遇到了同樣的問題,但通過攔截后退鍵來解決它。 在我的情況下(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();
}

使用onKeyPreIme(int keyCode, KeyEvent event)方法並檢查KeyEvent.KEYCODE_BACK事件。 它非常簡單,無需進行任何花哨的編碼。

在你的 BackPressed 實現中試試這個代碼( Android 中的 Block Back Button ):

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

建議你看看@ 關閉/隱藏Android軟鍵盤

我的@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)
}

這是我對@kirill-rakhman解決方案的變體。

我需要知道在鍵盤顯示時按下硬件或手勢返回按鈕的時間,以便我可以做出反應並顯示以前在任何編輯文本視圖獲得焦點時隱藏的按鈕。

  1. 首先聲明你需要的回調接口
interface KeyboardEventListener {
   fun onKeyBoardDismissedIme()
}
  1. 然后使用偵聽器為 pre ime 鍵事件創建自定義視圖
class KeyboardAwareConstraintLayout(context: Context, attrs: AttributeSet) :
    ConstraintLayout(context, attrs) {

    var listener: KeyboardEventListener? = null

    override fun dispatchKeyEventPreIme(event: KeyEvent?): Boolean {
        val imm: InputMethodManager =
            context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        if (imm.isActive && event?.keyCode == KeyEvent.KEYCODE_BACK) {
            listener?.onKeyBoardDismissedIme()
        }
        return super.dispatchKeyEventPreIme(event)
    }
}
  1. 使用自定義布局視圖包裝您的布局
<com.package_name.KeyboardAwareLayout 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:id="@+id/keyBoardAwareLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

{Your layout children here}    

</com.package_name.KeyboardAwareLayout>

  1. 接下來在您的活動或片段中實現回調接口並設置鍵盤感知布局
class MyFragment : Fragment, KeyboardEventListener {

    // TODO: Setup some viewbinding to retrieve the view reference

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.keyBoardAwareLayout.listener = this
    }

    override fun onKeyBoardDismissedIme() {
        // TODO: React to keyboard hidden with back button
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM