Chặn nút quay lại từ bàn phím mềm


84

Tôi có hoạt động với một số trường đầu vào. Khi hoạt động bắt đầu, bàn phím mềm được hiển thị. Khi nhấn nút quay lại, bàn phím mềm đóng và để đóng hoạt động, tôi cần nhấn nút quay lại một lần nữa.

Vì vậy, câu hỏi: có thể chặn nút quay lại để đóng bàn phím mềm và kết thúc hoạt động chỉ bằng một lần nhấn nút quay lại mà không cần tạo tùy chỉnh InputMethodServicekhông?

Tái bút Tôi biết cách chặn nút quay lại trong các trường hợp khác: onKeyDown()hoặc onBackPressed()nhưng nó không hoạt động trong trường hợp này: chỉ lần nhấn nút quay lại thứ hai bị chặn.

Câu trả lời:


76

Có, hoàn toàn có thể hiển thị và ẩn bàn phím và chặn các cuộc gọi đến nút quay lại. Đó là một chút nỗ lực hơn vì nó đã được đề cập là không có cách trực tiếp nào để thực hiện việc này trong API. Điều quan trọng là ghi đè boolean dispatchKeyEventPreIme(KeyEvent)trong một bố cục. Những gì chúng tôi làm là tạo bố cục của chúng tôi. Tôi đã chọn RelativeLayout vì nó là cơ sở cho Hoạt động của tôi.

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

Bên trong Activity của chúng tôi, chúng tôi thiết lập các trường đầu vào và gọi setActivity(...)hàm.

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

Rõ ràng, initInputField()hàm thiết lập trường đầu vào. Nó cũng cho phép phím enter để thực thi chức năng (trong trường hợp của tôi là tìm kiếm).

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

Vì vậy, khi onBackPressed()được gọi trong bố cục của chúng ta, chúng ta có thể làm bất cứ điều gì chúng ta muốn như ẩn bàn phím:

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

Dù sao, đây là ghi đè của tôi đối với 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);
    }
}

Thật không may, tôi không thể nhận tất cả các tín dụng. Nếu bạn kiểm tra nguồn Android cho hộp SearchDialog nhanh, bạn sẽ thấy ý tưởng đến từ đâu.


2
Điều này hoạt động rất tốt và không quá khó để thực hiện (mặc dù trông hơi đáng sợ).
Cesar

Cảm ơn bạn rất nhiều về điều này, tôi chỉ ước tôi có một cách để làm cho một cái gì đó tương tự như vậy để hoạt động với các phiên bản Android cũ hơn.
zidarsk

3
Mã có thể được đơn giản hóa bằng cách chỉ truyền getContext()sang Activity. Tất nhiên, với điều kiện bối cảnh của bố cục là hoạt động được đề cập. Nhưng tôi không biết nếu nó không thể được.
mxcl

3
mSearchActivity tĩnh - bị lỗi do sự cố. Điều gì sẽ xảy ra nếu bạn có hai trong số này trong bố cục? Sử dụng biến không tĩnh để thay thế.
Pointer Null

Hoạt động như một sự quyến rũ. Nhưng, tôi nghĩ rằng đó là tốt hơn để sử dụng giao diện tĩnh địa phương và thiết lập nó thực hiện từ hoạt động bên trong thay vì sử dụng acticty tĩnh ...
SpiralDev

80

onKeyDown ()onBackPressed () không hoạt động cho trường hợp này. Bạn phải sử dụng onKeyPreIme .

Ban đầu, bạn phải tạo văn bản chỉnh sửa tùy chỉnh mở rộng EditText. Và sau đó bạn phải triển khai phương thức onKeyPreIme điều khiển KeyEvent.KEYCODE_BACK . Sau đó, nhấn lại một lần là đủ để giải quyết vấn đề của bạn. Giải pháp này phù hợp với tôi một cách hoàn hảo.

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

Trong XML của bạn

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

Trong Hoạt động của bạn

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
Yêu câu trả lời này rất nhiều. Sử dụng điều này với một đoạn cũng như (trong vòng một thanh tìm kiếm mà thậm chí không hiển thị luôn luôn), và bằng cách gợi ý để ghi đè lên EditText, điều này dễ dàng đóng gói hành vi này chỉ khi nó cần thiết
Jawad

2
Lưu ý với mọi người: Thêm một giao diện nhanh chóng để điều này làm cho cuộc sống tốt hơn rất nhiều, và đó là may mắn khá dễ dàng để làm
Jawad

làm thế nào để gọi một phương thức trong onKeyPreIme () trong mainActivity.java?
Abhigyan

Bạn không cần phải gọi onKeyPreIme trong hoạt động. Nếu bạn sử dụng CustomEditText trong hoạt động của mình, phương thức sẽ được kích hoạt khi bạn nhấn bất kỳ nút nào trên bàn phím.
Alican Temel

1
Tôi nghĩ đây là câu trả lời tốt nhất, vì nó đơn giản hơn nhiều để thực hiện. trên thực tế, bạn chỉ cần sao chép lớp "CustomEditText" (và đổi tên văn bản chỉnh sửa của bạn trong XML) và thực hiện những gì bạn muốn trong vùng // VIỆC CẦN LÀM. Và chỉ cần thêm "return true" trong vùng TODO nếu bạn không muốn đóng hoạt động của mình. Cảm ơn !
Chrysotribax

10

Tôi đã phát hiện ra rằng việc ghi đè phương thức sendKeyEventPreIme của Lớp bố cục cũng hoạt động tốt. Chỉ cần đặt Hoạt động chính của bạn làm thuộc tính và khởi chạy một phương thức được xác định trước.

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

bạn có thể cho tôi xem xml của bạn cho bố cục này không? tôi đang cố gắng để kiểm tra phương pháp này và tôi có cái nhìn bề mặt trong cách bố trí custum của tôi, nhưng nó isnt chặn bất cứ điều gì
ZZZ

Về cơ bản, chế độ xem tùy chỉnh là phần tử xung quanh trong xml. <?xml version="1.0" encoding="utf-8"?> <view class="package.LinearLayoutGradient"...
Kirill Rakhman,

nó cũng hoạt động nếu chúng ta chỉ cần mở rộng một chế độ xem duy nhất và ghi đè lên nó sendKeyEventPreIme (). Không bắt buộc phải mở rộng bố cục.
faizal 19/1213

7

Tôi đã thành công bằng cách ghi đè dispatchKeyEvent :

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

Nó ẩn bàn phím và kết thúc hoạt động.


điều này dường như chỉ hoạt động trên một số thiết bị nhất định như HTC Desire.
Abhijit

5
không hoạt động trên samsung note 2. Công vănKeyEvent () không được gọi khi bật bàn phím mềm.
faizal

Chà, tôi đã thử điều này với Trình giả lập Nexus 6 (Lollipop 5.0) -> hoạt động và Note 4 (CM12 Lollipop 5.0.2) -> không hoạt động như vậy, không thực sự là giải pháp tốt nhất - nhưng dù sao .. Cảm ơn bạn :)
Martin Pfeffer

Nhấp vào nút được sử dụng khi bỏ qua bàn phím
slott

4

Làm thế nào bạn đang hiển thị bàn phím mềm?

Nếu bạn đang sử dụng InputMethodManager.showSoftInput(), bạn có thể thử chuyển vào ResultReceivervà triển khai onReceiveResult()để xử lýRESULT_HIDDEN

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


Âm thanh tốt, nhưng showSoftInput()không hiển thị bàn phím vào điện thoại và giả lập của tôi, vì vậy tôi sử dụngtoggleSoftInput()
Sergey Glotov

1
Không hiệu quả với tôi. onReceiveResult () được gọi với RESULT_SHOWN, nhưng không bao giờ muộn với RESULT_HIDDEN
Yulia Rogovaya

2

Tôi đã gặp vấn đề tương tự nhưng đã khắc phục được bằng cách chặn phím quay lại. Trong trường hợp của tôi (HTC Desire, Android 2.2, API Ứng dụng Cấp 4), nó đóng Bàn phím và ngay lập tức kết thúc Hoạt động. Không biết tại sao điều này cũng không hiệu quả với bạn:

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

này không hoạt động trên samsung galaxy note 2. onkeydown () hoặc onbackpressed () không được kích hoạt khi bàn phím mềm là có thể nhìn thấy và nút quay lại được nhấn
Faizal

1

Sử dụng onKeyPreIme(int keyCode, KeyEvent event)phương pháp và kiểm tra KeyEvent.KEYCODE_BACKsự kiện. Nó rất đơn giản mà không cần thực hiện bất kỳ mã hóa cầu kỳ nào.


0

Hãy thử mã này trong triển khai BackPressed của bạn ( Nút Chặn Quay lại trong android ):

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

Tôi khuyên bạn nên xem @ Đóng / ẩn Bàn phím mềm Android


1
Tôi đã thử điều này, nhưng nó vẫn cần hai lần nhấn vào nút quay lại. Theo tôi hiểu thì lần nhấp đầu tiên bị chặn bởi bàn phím mềm nên onBackPressed()không hoạt động. Và chỉ sau khi chương trình báo chí thứ hai rơi vào onBackPressed().
Sergey Glotov

@Sergey Glotov Tôi đã thử rất nhiều đề xuất cho vấn đề và cuối cùng đã đưa ra một giải pháp không tốt lắm khi tôi phải triển khai SoftKeyBoard của riêng mình. Nhưng tôi hy vọng cộng đồng Android sẽ sớm đưa ra giải pháp tốt hơn nhiều.
100rabh

0

phiên bản giải pháp @mhradek của tôi:

Bố trí

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

}

tệp 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>

Miếng

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (view as BazingaLayout).activity = activity
        super.onViewCreated(view, savedInstanceState)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.