소프트 키보드에서 뒤로 버튼 차단


84

여러 입력 필드가있는 활동이 있습니다. 활동이 시작되면 소프트 키보드가 표시됩니다. 뒤로 버튼을 누르면 소프트 키보드가 닫히고 활동을 닫으려면 뒤로 버튼을 한 번 더 눌러야합니다.

그래서 질문 : 소프트 키보드를 닫고 사용자 정의를 만들지 않고 뒤로 버튼을 한 번 눌러 작업을 완료하기 위해 뒤로 버튼을 가로 챌 수 InputMethodService있습니까?

추신 : 다른 경우에 뒤로 버튼을 가로채는 방법을 알고 있습니다. onKeyDown()또는 onBackPressed()이 경우에는 작동하지 않습니다. 뒤로 버튼을 두 번 누르는 것만 차단됩니다.

답변:


76

예, 키보드를 표시하거나 숨기고 뒤로 버튼에 대한 호출을 가로 챌 수 있습니다. 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">

활동 내에서 입력 필드를 설정하고 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()함수는 입력 필드를 설정합니다. 또한 Enter 키를 사용하여 기능을 실행할 수 있습니다 (제 경우에는 검색).

@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 소스를 확인 하면 아이디어의 출처를 볼 수 있습니다.


2
이것은 매우 잘 작동했으며 구현하기가 너무 어렵지 않았습니다 (조금 무섭게 보임에도 불구하고).
Cesar

이것에 대해 대단히 감사합니다. 이전 버전의 Android에서 이와 같은 작업을 수행 할 수있는 방법이 있었으면합니다.
zidarsk8

3
코드는 캐스팅에 의해 간단하게 할 수 있습니다 getContext()Activity. 물론 레이아웃의 컨텍스트가 문제의 활동이라면 제공됩니다. 하지만 그럴 수 없는지 모르겠습니다.
mxcl 2012 년

3
정적 mSearchActivity-문제로 인해 잘못된 냄새가납니다. 레이아웃에이 두 개가 있으면 어떨까요? 대신 비 정적 변수를 사용하십시오.
포인터 널

매력처럼 작동합니다. 그러나, 나는 ... 지역 정적 인터페이스를 사용하여 정적 acticty를 사용하여 내부 활동 대신에서 구현 그것을 설정하는 것이 좋습니다 생각
SpiralDev

80

이 경우 onKeyDown ()onBackPressed () 가 작동하지 않습니다. onKeyPreIme 를 사용해야 합니다 .

처음에는 EditText를 확장하는 사용자 정의 편집 텍스트를 만들어야합니다. 그리고 KeyEvent.KEYCODE_BACK 을 제어하는 ​​onKeyPreIme 메소드를 구현해야 합니다 . 그 후, 문제를 해결하기에 충분한 백 프레스. 이 솔루션은 저에게 완벽하게 작동합니다.

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
모두에게 참고 : 여기에 빠른 인터페이스를 추가하면 삶이 훨씬 더 좋아지고 다행스럽게도 매우 쉽습니다
Jawad

mainActivity.java에있는 onKeyPreIme ()에서 메소드를 호출하는 방법은 무엇입니까?
Abhigyan 2015

활동에서 onKeyPreIme를 호출 할 필요가 없습니다. 활동에서 CustomEditText를 사용하는 경우 키보드에서 아무 버튼이나 누르면 메서드가 트리거됩니다.
Alican Temel 2015

1
구현하는 것이 훨씬 더 간단하기 때문에 이것이 최선의 대답이라고 생각합니다. 사실 "CustomEditText"클래스를 복사하고 (그리고 XML에서 편집 텍스트의 이름을 변경) // TODO 영역에서 원하는 것을 만들면됩니다. 그리고 활동을 종료하지 않으려면 TODO 영역에 "return true"를 추가하십시오. 감사 !
Chrysotribax

10

레이아웃 클래스 의 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);
    }
}

이 레이아웃에 대한 xml을 보여줄 수 있습니까? 나는이 접근 방식을 확인하려고 노력하고 있고 내 custum 레이아웃에 표면보기가 있지만 아무것도 가로 채지 않습니다
ZZZ

기본적으로 사용자 정의보기는 xml의 주변 요소입니다. <?xml version="1.0" encoding="utf-8"?> <view class="package.LinearLayoutGradient"...
Kirill Rakhman 2011

단일 뷰를 확장하고 dispatchKeyEventPreIme ()를 재정의하는 경우에도 작동합니다. 레이아웃 자체를 확장 할 필요는 없습니다.
faizal

7

dispatchKeyEvent 를 재정 의하여 성공했습니다 .

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

키보드를 숨기고 활동을 완료합니다.


이것은 HTC Desire와 같은 특정 장치에서만 작동하는 것으로 보입니다.
Abhijit 2012 년

5
samsung note 2에서는 작동하지 않습니다. 소프트 키보드가 켜져있을 때 dispatchKeyEvent ()가 호출되지 않습니다.
faizal

음, Nexus 6 Emulator (Lollipop 5.0)-> 작동하고 Note 4 (CM12 Lollipop 5.0.2)-> 작동하지 않았습니다. 실제로 최상의 솔루션은 아닙니다. 어쨌든 .. 감사합니다 :)
Martin Pfeffer

키보드를
닫을

4

소프트 키보드를 어떻게 보여주고 있습니까?

을 사용 InputMethodManager.showSoftInput()하는 경우을 전달 ResultReceiver하고 구현 onReceiveResult()하여RESULT_HIDDEN

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


소리 좋은,하지만 showSoftInput()내가 사용하는, 그래서 내 전화 및 에뮬레이터에서 키보드를 표시하지 않습니다toggleSoftInput()
세르게이 Glotov에게

1
나를 위해 작동하지 않습니다. onReceiveResult ()는 RESULT_SHOWN으로 호출되지만 나중에 RESULT_HIDDEN으로 호출되지 않습니다
Yulia Rogovaya

2

나는 같은 문제가 있었지만 뒤로 키 누름을 가로 채서 문제를 해결했습니다. 제 경우에는 (HTC Desire, Android 2.2, Application API 레벨 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. onKeyDown에 () 또는 onbackpressed 작동하지 않습니다
FAIZAL

1

onKeyPreIme(int keyCode, KeyEvent event)방법을 사용 하고 KeyEvent.KEYCODE_BACK이벤트를 확인하십시오 . 멋진 코딩을하지 않고도 매우 간단합니다.


0

BackPressed 구현에서이 코드를 시도해보십시오 ( Android의 Block Back Button ).

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

Android 소프트 키보드 닫기 / 숨기기 @보기를 제안합니다.


1
나는 이미 이것을 시도했지만 여전히 뒤로 버튼을 두 번 눌러야합니다. 내가 이해했듯이 첫 번째 클릭은 소프트 키보드에 의해 차단되므로 onBackPressed()작동하지 않습니다. 그리고 두 번째 프레스 프로그램이 onBackPressed().
Sergey Glotov

@Sergey Glotov 나는 문제에 대한 많은 제안을 시도했고 마침내 자체 SoftKeyBoard를 구현 해야하는 그다지 좋지 않은 솔루션을 얻었습니다.하지만 안드로이드 커뮤니티가 곧 훨씬 더 나은 솔루션으로 나오기를 바랍니다.
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)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.