ตรวจจับเมื่อผู้ใช้ปิดแป้นพิมพ์อ่อน


114

ฉันมีวิดเจ็ต EditText ในมุมมองของฉัน เมื่อผู้ใช้เลือกวิดเจ็ต EditText ฉันจะแสดงคำแนะนำบางอย่างและซอฟต์คีย์บอร์ดจะปรากฏขึ้น

ฉันใช้ OnEditorActionListener เพื่อตรวจจับเมื่อผู้ใช้ป้อนข้อความเสร็จแล้วและฉันปิดแป้นพิมพ์ซ่อนคำแนะนำและดำเนินการบางอย่าง

ปัญหาของฉันคือเมื่อผู้ใช้ปิดแป้นพิมพ์โดยการกดปุ่ม BACK ระบบปฏิบัติการจะปิดแป้นพิมพ์ แต่คำแนะนำของฉัน (ซึ่งฉันต้องการซ่อน) ยังคงปรากฏให้เห็น

ฉันได้ลองลบล้าง OnKeyDown แล้ว แต่ดูเหมือนจะไม่ถูกเรียกเมื่อใช้ปุ่ม BACK เพื่อปิดแป้นพิมพ์

ฉันได้ลองตั้งค่า OnKeyListener บนวิดเจ็ต EditText แล้ว แต่ดูเหมือนจะไม่ถูกเรียกด้วย

ฉันจะตรวจจับได้อย่างไรเมื่อปิดแป้นพิมพ์แบบนิ่ม

คำตอบ:


160

ฉันรู้วิธีที่จะทำสิ่งนี้ คลาสย่อย EditText และใช้:

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
    // Do your thing.
    return true;  // So it is not propagated.
  }
  return super.dispatchKeyEvent(event);
}

นี่คือลิงค์เกี่ยวกับวิธีใช้มุมมองที่กำหนดเองของคุณ (สำหรับเมื่อคุณคลาสย่อย EditText): http://developer.android.com/guide/topics/ui/custom-components.html


2
ฉันได้รับรายงานจากผู้ใช้ Android ที่มีแป้นพิมพ์ฮาร์ดแวร์ว่าการทำเช่นนี้รบกวนการกดแป้น ฉันไม่มีข้อมูลเพิ่มเติมในขณะนี้
esilver

ฉันกำลังมองหาวิธีแก้ปัญหาหลายอย่างนี่เป็นวิธีที่ดีที่สุด!
Friesgaard

11
รอเดี๋ยวก่อนฉันเพิ่งดูครั้งนี้เป็นครั้งที่สามไม่ควรโทรไปที่ซูเปอร์onKeyPreIme? หรือมีเหตุผลเฉพาะที่ไม่เป็นเช่นนั้น?
Erhannis

ดูมีประโยชน์ยกเว้นในกรณีที่ EditText ไม่สามารถเป็นคลาสย่อยได้ (เช่นใน SearchView) นี่เป็นปัญหาเมื่อพยายามซ่อน SearchView หากว่างเปล่าเมื่อปิดแป้นพิมพ์ ฉันต้องสงสัยว่าทำไมคนที่เป็น Android ไม่เพียงแค่ให้ OSK API ที่ดีสำหรับสิ่งนี้
tbm

2
@tbm เพื่อให้ได้เอฟเฟกต์ที่คล้ายคลึงกันSearchViewโปรดดูที่stackoverflow.com/questions/9629313/…
Cheok Yan Cheng

124

เจทางออกของคุณดี! ขอบคุณ :)

public class EditTextBackEvent extends EditText {

    private EditTextImeBackListener mOnImeBack;

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

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

    public EditTextBackEvent(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && 
            event.getAction() == KeyEvent.ACTION_UP) {
            if (mOnImeBack != null) 
                mOnImeBack.onImeBack(this, this.getText().toString());
        }
        return super.dispatchKeyEvent(event);
    }

    public void setOnEditTextImeBackListener(EditTextImeBackListener listener) {
        mOnImeBack = listener;
    }

}

public interface EditTextImeBackListener {
    public abstract void onImeBack(EditTextBackEvent ctrl, String text);
}

มีเหตุผลใดที่เราต้องการตรวจสอบKeyEvent.ACTION_UPด้วย?
Cheok Yan Cheng

2
@CheokYanCheng เป็นเพราะการกระทำของผู้ใช้โดยปกติควรมีผลเมื่อปล่อยปุ่มไม่ใช่เมื่อเริ่มกด
jayeffkay

3
อย่าลืมขยายเวลาandroid.support.v7.widget.AppCompatEditTextการย้อมสี
Sanvywell

ขยายเวลา: AppCompatEditTextสำหรับ androidx
COYG

ที่ดี! ฉันขอแนะนำให้ปรับปรุงเพื่อระบุโซลูชันของคุณเท่านั้น ฉันจะส่งต่อข้อโต้แย้งจาก onKeyPreIme "ตามสภาพ" ไปยังผู้ฟังด้วยวิธีนี้คุณสามารถใช้ตรรกะของคุณในรูปแบบต่างๆที่คุณต้องการ
marcRDZ

17

ฉันทำการเปลี่ยนแปลงเล็กน้อยในโซลูชันของ Jay โดยโทรไปที่ super.onKeyPreIme ():

_e = new EditText(inflater.getContext()) {
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK){
            cancelTextInput();
        }
        return super.onKeyPreIme(keyCode, event);
    }
};

ทางออกที่ยอดเยี่ยมเจย์ +1!


14

นี่คือ EditText ที่กำหนดเองของฉันเพื่อตรวจสอบว่าแป้นพิมพ์กำลังแสดงอยู่หรือไม่

/**
 * Created by TheFinestArtist on 9/24/15.
 */
public class KeyboardEditText extends EditText {

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

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

    public KeyboardEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (listener != null)
            listener.onStateChanged(this, true);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
                && event.getAction() == KeyEvent.ACTION_UP) {
            if (listener != null)
                listener.onStateChanged(this, false);
        }
        return super.onKeyPreIme(keyCode, event);
    }

    /**
     * Keyboard Listener
     */
    KeyboardListener listener;

    public void setOnKeyboardListener(KeyboardListener listener) {
        this.listener = listener;
    }

    public interface KeyboardListener {
        void onStateChanged(KeyboardEditText keyboardEditText, boolean showing);
    }
}

8

มันเป็น 2019 ตอนนี้ ...
ดังนั้นผมจึงสร้างโซลูชั่นที่เรียบร้อยมากขึ้นด้วย Kotlin

1. สร้างฟังก์ชันส่วนขยาย:

fun Activity.addKeyboardToggleListener(onKeyboardToggleAction: (shown: Boolean) -> Unit): KeyboardToggleListener? {
    val root = findViewById<View>(android.R.id.content)
    val listener = KeyboardToggleListener(root, onKeyboardToggleAction)
    return root?.viewTreeObserver?.run {
        addOnGlobalLayoutListener(listener)
        listener
    }
}

2. ผู้ฟังสลับอยู่ที่ไหน:

open class KeyboardToggleListener(
        private val root: View?,
        private val onKeyboardToggleAction: (shown: Boolean) -> Unit
) : ViewTreeObserver.OnGlobalLayoutListener {
    private var shown = false
    override fun onGlobalLayout() {
        root?.run {
            val heightDiff = rootView.height - height
            val keyboardShown = heightDiff > dpToPx(200f)
            if (shown != keyboardShown) {
                onKeyboardToggleAction.invoke(keyboardShown)
                shown = keyboardShown
            }
        }
    }
}

fun View.dpToPx(dp: Float) = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics).roundToInt()

3. ใช้ในกิจกรรมใดก็ได้ง่ายๆดังนี้:

addKeyboardToggleListener {shown ->
          // hurray! Now you know when the keyboard is shown and hidden!!
      }

ขอบคุณสำหรับโซลูชัน Kotlin แม้ว่าเมื่อฉันใช้งานฉันสังเกตเห็นว่ามันทำให้ผู้ฟังเริ่มทำงานหลายครั้งสำหรับการเปลี่ยนแป้นพิมพ์หนึ่งครั้งและเมื่อเริ่มต้นใช้งาน ฉันต้องจัดเก็บสถานะเปิด / ไม่เปิดและเรียกใช้เฉพาะผู้ฟังเมื่อค่าแตกต่างกันจริง
RandomEngy

@RandomEngy แก้ไขใน KeyboardToggleListener ขอบคุณที่สังเกต
Leo Droidcoder

4

เพียงสร้างคลาสที่ขยาย Edittext และใช้ข้อความแก้ไขนั้นในโค้ดของคุณคุณควรจะแทนที่วิธีการต่อไปนี้ในข้อความแก้ไขที่กำหนดเอง:

@Override
 public boolean onKeyPreIme(int keyCode, KeyEvent event) {
 if (keyCode == KeyEvent.KEYCODE_BACK) {

    //Here it catch all back keys
    //Now you can do what you want.

} else if (keyCode == KeyEvent.KEYCODE_MENU) {
    // Eat the event
    return true;
}
return false;}

มีวิธีตรวจจับเมื่อคีย์บอร์ดเปิดขึ้นหรือไม่?
powder366

3

นี่เป็นวิธีแก้ปัญหาด้วยตัวฟังหลัก ฉันไม่รู้ว่าทำไมถึงใช้งานได้ แต่ OnKeyListener ใช้งานได้ถ้าคุณเพียงแค่แทนที่ onKeyPreIme บน EditText ที่กำหนดเองของคุณ

SomeClass.java

customEditText.setOnKeyListener((v, keyCode, event) -> {
            if(event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (keyCode) {
                    case KeyEvent.KEYCODE_BACK:
                        getPresenter().onBackPressed();
                        break;
                }
            }
            return false;
        }); 

CustomEditText.java

@Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        return super.dispatchKeyEvent(event);
    }

3

ใช้คำตอบของ @ olivier_sdg แต่แปลงเป็น Kotlin:

class KeyboardEditText : AppCompatEditText {

    var listener: Listener? = null
  
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  
    override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
        if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
            listener?.onImeBack(this)
        }
        return super.dispatchKeyEvent(event)
    }
  
    interface Listener {
        fun onImeBack(editText: KeyboardEditText)
    }

}

การใช้งาน:

keyboardEditText.listener = object : KeyboardEditText.Listener {
    override fun onImeBack(editText: KeyboardEditText) {
        //Back detected
    }
}

2

สำหรับใครก็ตามที่ต้องการทำเช่นเดียวกันใน Xamarin ฉันได้แปลคำตอบยอดนิยมบางส่วนเนื่องจากมันแตกต่างกันเล็กน้อย ฉันสร้างส่วนสำคัญที่นี่แต่สรุปคุณสร้าง EditText ที่กำหนดเองและแทนที่OnKeyPreImeดังนี้:

public class CustomEditText : EditText
{
    public event EventHandler BackPressed;

    // ...

    public override bool OnKeyPreIme([GeneratedEnum] Keycode keyCode, KeyEvent e)
    {
        if (e.KeyCode == Keycode.Back && e.Action == KeyEventActions.Up)
        {
            BackPressed?.Invoke(this, new EventArgs());
        }

        return base.OnKeyPreIme(keyCode, e);
    }
}

... แล้วในมุมมอง ...

editText = FindViewById<CustomEditText>(Resource.Id.MyEditText);
editText.BackPressed += (s, e) => 
{
    // <insert code here>
};

แม้ว่าจะเป็นเพียงตัวอย่างง่ายๆ แต่ฉันขอแนะนำว่าอย่าใช้วิธีการแบบไม่ระบุตัวตนในตัวจัดการเหตุการณ์เนื่องจากสิ่งเหล่านี้ทำให้เกิดการรั่วไหลของหน่วยความจำและหลายคนใช้ตัวอย่างที่พบที่นี่และเรียกใช้โดยไม่ทราบถึงสิ่งนี้ ที่มา: docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/…
Jared

0

hideSoftInputFromWindow คืนค่าจริงเมื่อปิดแป้นพิมพ์ใช้ค่านี้เพื่อตรวจจับการปิดแป้นพิมพ์ใน Android

InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

        if (imm.hideSoftInputFromWindow(findFocus().getWindowToken(),
                InputMethodManager.HIDE_NOT_ALWAYS)) {
            //keyboard is closed now do what you need here
        }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.