จะซ่อนคีย์บอร์ดอ่อนบน Android หลังจากคลิกที่ EditText นอกได้อย่างไร


354

ตกลงทุกคนรู้ว่าการซ่อนแป้นพิมพ์ที่คุณต้องใช้:

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

แต่เรื่องใหญ่ที่นี่คือวิธีการซ่อนแป้นพิมพ์เมื่อผู้ใช้สัมผัสหรือเลือกสถานที่อื่นที่ไม่ใช่ EditTextหรือ softKeyboard?

ฉันพยายามใช้ onTouchEvent()บนบนพาเรนต์ของฉันActivityแต่ใช้งานได้เฉพาะเมื่อผู้ใช้สัมผัสนอกมุมมองอื่นและไม่มี scrollview

ฉันพยายามที่จะใช้สัมผัสคลิกโฟกัสฟังโดยไม่ประสบความสำเร็จ

ฉันพยายามใช้ scrollview ของตัวเองเพื่อสกัดกั้นเหตุการณ์แบบสัมผัส แต่ฉันสามารถรับพิกัดของเหตุการณ์เท่านั้นไม่ใช่มุมมองที่คลิก

มีวิธีมาตรฐานในการทำเช่นนี้หรือไม่? ใน iPhone มันง่ายมาก


ฉันรู้ว่า scrollview ไม่ได้เป็นปัญหาจริงๆ แต่เป็นฉลากที่มี มุมมองเป็นเลย์เอาต์แนวตั้งที่มีบางอย่างเช่น TextView, EditText, TextView, EditText และอื่น ๆ และ textViews จะไม่ยอมให้ edittext หลวมโฟกัสและซ่อนคีย์บอร์ด
htafoya

คุณสามารถหาวิธีแก้ปัญหาได้getFields()ที่นี่: stackoverflow.com/questions/7790487/…
Reto

แป้นพิมพ์สามารถปิดโดยปุ่มกดผลตอบแทนดังนั้นฉันบอกว่ามันเป็นที่น่าสงสัยว่านี่คือความคุ้มค่า
gerrytan

4
ฉันพบคำตอบนี้: stackoverflow.com/a/28939113/2610855คำตอบที่ดีที่สุด
Loenix

คำตอบ:


575

ตัวอย่างต่อไปนี้ซ่อนคีย์บอร์ด:

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = 
        (InputMethodManager) activity.getSystemService(
            Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(
        activity.getCurrentFocus().getWindowToken(), 0);
}

คุณสามารถวางสิ่งนี้ไว้ในคลาสยูทิลิตี้หรือถ้าคุณกำหนดไว้ในกิจกรรมให้หลีกเลี่ยงพารามิเตอร์กิจกรรมหรือการโทร hideSoftKeyboard(this)คุณสามารถใส่นี้ขึ้นมาในระดับยูทิลิตี้หรือหากคุณกำหนดได้ภายในกิจกรรมหลีกเลี่ยงพารามิเตอร์กิจกรรมหรือโทร

ส่วนที่ยากที่สุดคือเวลาที่จะเรียกมันว่า คุณสามารถเขียนวิธีที่วนซ้ำViewในทุกกิจกรรมของคุณและตรวจสอบว่าเป็นวิธีที่instanceof EditTextไม่ได้ลงทะเบียน a setOnTouchListenerไปยังองค์ประกอบนั้นหรือไม่และทุกอย่างจะเข้าที่ ในกรณีที่คุณสงสัยว่าจะทำอย่างไรจริง ๆ แล้วมันค่อนข้างง่าย นี่คือสิ่งที่คุณทำคุณเขียนวิธีแบบเรียกซ้ำเช่นในความเป็นจริงคุณสามารถใช้วิธีนี้เพื่อทำอะไรก็ได้เช่นการตั้งค่าแบบอักษรที่กำหนดเองเป็นต้น ... นี่คือวิธีการ

public void setupUI(View view) {

    // Set up touch listener for non-text box views to hide keyboard.
    if (!(view instanceof EditText)) {
        view.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(MyActivity.this);
                return false;
            }
        });
    }

    //If a layout container, iterate over children and seed recursion.
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View innerView = ((ViewGroup) view).getChildAt(i);
            setupUI(innerView);
        }
    }
}

นั่นคือทั้งหมดเพียงแค่เรียกวิธีนี้หลังจากที่คุณsetContentViewอยู่ในกิจกรรมของคุณ ในกรณีที่คุณสงสัยว่าคุณจะส่งผ่านพารามิเตอร์ใดมันเป็นidของคอนเทนเนอร์หลัก กำหนดให้idกับคอนเทนเนอร์หลักของคุณเช่น

<RelativeLayoutPanel android:id="@+id/parent"> ... </RelativeLayout>

และโทร setupUI(findViewById(R.id.parent))นั่นคือทั้งหมดที่

หากคุณต้องการใช้สิ่งนี้อย่างมีประสิทธิภาพคุณสามารถสร้างส่วนขยายActivityและใส่วิธีนี้และทำให้กิจกรรมอื่น ๆ ทั้งหมดในใบสมัครของคุณขยายกิจกรรมนี้และเรียกมันsetupUI()ในonCreate()วิธีการ

หวังว่ามันจะช่วย

หากคุณใช้มากกว่า 1 กิจกรรมให้กำหนดรหัสทั่วไปให้กับโครงร่างหลักเช่น <RelativeLayout android:id="@+id/main_parent"> ... </RelativeLayout>

จากนั้นขยายคลาสจากActivityและนิยามsetupUI(findViewById(R.id.main_parent))ภายในOnResume()และขยายคลาสนี้แทนกิจกรรม ``in your program


นี่คือฟังก์ชั่นด้านบนเวอร์ชั่น Kotlin:

@file:JvmName("KeyboardUtils")

fun Activity.hideSoftKeyboard() {
    currentFocus?.let {
        val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
        inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
    }
}

ฉันไม่ได้ทดสอบตัวเอง แต่ดูเหมือนว่ามันจะใช้ได้และมีรีวิวสูงฉันจะเปลี่ยนคำตอบที่ยอมรับได้
htafoya

4
ไม่ควรจะยากมาก? ฉันออกจากการเขียนโปรแกรม Android ทันทีดังนั้นแก้ไขให้ถูกต้องถ้าฉันผิด คุณสามารถติดตาม EditText ที่โฟกัสได้ตลอดเวลาและขอให้สูญเสียความสำคัญในช่วง OnTouchEvent หรือไม่
Navneeth G

25
ไม่แน่ใจว่ามีใครประสบปัญหานี้หรือไม่ แต่นี่เป็นสาเหตุให้แอปหยุดทำงานเมื่อคุณเรียกใช้ hideSoftKeyboard หากไม่มีการโฟกัสใด ๆ คุณสามารถแก้ปัญหานี้ได้โดยการล้อมรอบบรรทัดที่สองของวิธีการด้วยif(activity.getCurrentFocus() != null) {...}
Frank Cangialosi

14
ปัญหาของวิธีนี้คือสมมติว่ามุมมองอื่น ๆ ทั้งหมดไม่จำเป็นต้องตั้งค่าOnTouchListenerสำหรับมุมมองเหล่านั้น คุณสามารถตั้งค่าตรรกะนั้นในViewGroup.onInterceptTouchEvent(MotionEvent)มุมมองรูทได้
Alex.F

2
ไม่ทำงานเมื่อฉันคลิกปุ่มควบคุมอื่น ๆ ที่เปิดคีย์บอร์ดอยู่
mohitum

285

คุณสามารถทำได้โดยทำตามขั้นตอนต่อไปนี้:

  1. ทำให้มุมมองพาเรนต์ (มุมมองเนื้อหาของกิจกรรมของคุณ) สามารถคลิกได้และสามารถโฟกัสได้โดยการเพิ่มแอททริบิวต่อไปนี้

        android:clickable="true" 
        android:focusableInTouchMode="true" 
  2. ใช้วิธีการ hideKeyboard ()

        public void hideKeyboard(View view) {
            InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
  3. สุดท้ายตั้ง onFocusChangeListener edittext ของคุณ

        edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    hideKeyboard(v);
                }
            }
        });

ดังที่กล่าวไว้ในหนึ่งในความคิดเห็นด้านล่างสิ่งนี้อาจไม่ทำงานหากมุมมองพาเรนต์เป็น ScrollView สำหรับกรณีดังกล่าวอาจเพิ่ม clickable และ focusableInTouchMode ในมุมมองโดยตรงภายใต้ ScrollView


67
ในความเห็นของฉันนี่เป็นคำตอบที่ถูกต้อง รหัสหักไม่ซ้ำไม่จำเป็น ...
gattshjoty

14
ฉันชอบคำตอบนี้มาก สิ่งหนึ่งที่ควรทราบคือสิ่งนี้ไม่ได้ผลสำหรับฉันเมื่อเพิ่มclickableและองค์ประกอบfocusableInTouchModeรากของฉัน ScrollViewผมต้องเพิ่มให้กับผู้ปกครองโดยตรงของฉันซึ่งเป็นEditText LinearLayout
Adam Johns

10
ทำงานอย่างสมบูรณ์แบบสำหรับฉัน แม้ว่าคุณจะมีวิดเจ็ต edittext อยู่สองตัวคุณต้องตรวจสอบให้แน่ใจว่าคุณจัดการกับทั้งสองอย่างถูกต้องไม่เช่นนั้นคุณจะสลับการซ่อนคีย์บอร์ดโดยไม่จำเป็น
Marka

1
@MarkaA ฉันไม่มีปัญหากับเรื่องนี้เมื่อฉันคลิกที่ EditText อื่น ๆ คีย์บอร์ดจะยังคงอยู่หากคลิกที่พื้นหลังมันจะซ่อนอย่างที่มันควรจะเป็น ฉันมีการจัดการอื่น ๆ ที่ onFocusChange เพียงแค่เปลี่ยนพื้นหลังของ EditText เมื่อมันมีโฟกัสไม่มีอะไรน่าเชื่อถือ
CularBytes

3
@Shrikant - ฉันเห็นการสั่นไหวด้วยข้อความแก้ไขหลายรายการ ฉันเพิ่งตั้งค่า onFocusChangeListener บนพาเรนต์แทนที่จะเป็นบนแต่ละข้อความแก้ไขและเปลี่ยนเงื่อนไขเพื่อบอกว่า (hasFocus) {hideKeyboard (v); } ไม่สังเกตเห็นการกะพริบอีกต่อไปสลับข้อความแก้ไข btwn
manisha

69

เพียงแทนที่รหัสด้านล่างในกิจกรรม

 @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

4
วิธีง่ายๆเพิ่มกิจกรรมและจะดูแลส่วนย่อยด้วย
Rohit Maurya

1
อีกวิธีคือสร้าง BaseActivity และขยายในกิจกรรมทั้งหมด
sumit sonawane

1
วิธีแก้ปัญหาที่ยอดเยี่ยม
Ahmed Adel Ismail

1
คุณช่วยฉันจากการกะพริบหน้าจอของฉันปิดเมื่อปิดแป้นพิมพ์ ยกนิ้ว!
Dimas Mendes

1
ดี แต่มันก็ดีเกินกว่าที่จะเป็นจริง: เรียบง่ายสั้นมากและใช้งานได้ ... อนิจจามีปัญหา: เมื่อมีการแสดงแป้นพิมพ์ทุกครั้งที่เราสัมผัส EditText ซึ่งถามแป้นพิมพ์มันจะลงและ ขึ้นโดยอัตโนมัติ
Chrysotribax

60

ฉันพบว่าคำตอบที่ยอมรับนั้นค่อนข้างซับซ้อน

นี่คือทางออกของฉัน เพิ่มOnTouchListenerลงในเค้าโครงหลักของคุณเช่น:

findViewById(R.id.mainLayout).setOnTouchListener(this)

และใส่รหัสต่อไปนี้ในวิธีการสัมผัส

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

วิธีนี้คุณไม่ต้องทำซ้ำทุกมุมมอง


@roepit - ฉันได้รับ classCastexception เมื่อพยายามส่งเลย์เอาต์ไปยังมุมมอง ฉันพลาดอะไรไปรึเปล่า?
katzenhut

คุณสามารถอ้างอิงรหัสของคุณได้ที่ไหน? ฉันไม่สามารถบอกได้ว่ามีอะไรผิดปกติเมื่อฉันไม่สามารถดูเลย์เอาต์และกิจกรรม / ส่วนและรหัสของคุณ
roepit

คำตอบที่ดีที่สุดยังคงพยายามห่อหัวของฉันรอบวิธีการทำงานท่อ
user40797

มันจะทำงานถ้าคุณคลิกที่แถบชื่อเรื่องของแอพ
user1506104

1
มันทำงานได้อย่างสมบูรณ์แบบสำหรับการซ่อนคีย์บอร์ด! โปรดทราบว่าสิ่งนี้ไม่ได้เน้นที่ EditText แต่เพียงซ่อนคีย์บอร์ด ในการแยกโฟกัส EditText ให้เพิ่มเช่นandroid:onClick="stealFocusFromEditTexts"ไปยัง xml ของมุมมองพาเรนต์จากนั้นpublic void stealFocusFromEditTexts(View view) {}ไปยังกิจกรรม วิธีการคลิกไม่จำเป็นต้องทำอะไรมันแค่มีอยู่สำหรับมุมมองหลักที่จะโฟกัส / เลือกซึ่งจำเป็นสำหรับการขโมยโฟกัสจากเด็ก EditText
Jacob R

40

ฉันมีวิธีแก้ปัญหาเพิ่มเติมเพื่อซ่อนแป้นพิมพ์โดย:

InputMethodManager imm = (InputMethodManager) getSystemService(
    Activity.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);

นี่ผ่านHIDE_IMPLICIT_ONLYที่ตำแหน่งของshowFlagและที่ตำแหน่งของ0 hiddenFlagมันจะปิดคีย์บอร์ดอย่างนุ่มนวล


3
ขอขอบคุณการทำงานของมัน ..... ข้างต้นทั้งหมดที่ฉันได้พยายาม แต่ไม่ได้ทำงานในขณะที่ im รับค่าจากข้อความ DialogBox editext ครั้ง closoing DialogBox ...
PankajAndroid

ขอบคุณนี่ใช้งานได้เท่านั้นและมันก็สะอาดกว่านี้อีกมาก! +1
Edmond Tamas

ใช้งานได้ตามที่คาดหวังขอขอบคุณสำหรับวิธีแก้ไขปัญหาของคุณ +1
tryp

ทำงานเหมือนจับใจสำหรับฉัน +1 สำหรับโซลูชันที่หรูหรา
saintjab

2
ขออภัย แต่วิธีการนี้สลับไปมาดังนั้นหากสถานะแป้นพิมพ์ถูกปิดไปแล้วมันจะแสดงแป้นพิมพ์
HendraWD

16

ฉันจัดการเพื่อแก้ไขปัญหาได้ค่อนข้างดีฉันแทนที่ dispatchTouchEvent ในกิจกรรมของฉันฉันใช้สิ่งต่อไปนี้เพื่อซ่อนแป้นพิมพ์

 /**
 * Called to process touch screen events. 
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            touchDownTime = SystemClock.elapsedRealtime();
            break;

        case MotionEvent.ACTION_UP:
            //to avoid drag events
            if (SystemClock.elapsedRealtime() - touchDownTime <= 150){  

                EditText[] textFields = this.getFields();
                if(textFields != null && textFields.length > 0){

                    boolean clickIsOutsideEditTexts = true;

                    for(EditText field : textFields){
                        if(isPointInsideView(ev.getRawX(), ev.getRawY(), field)){
                            clickIsOutsideEditTexts = false;
                            break;
                        }
                    }

                    if(clickIsOutsideEditTexts){
                        this.hideSoftKeyboard();
                    }               
                } else {
                    this.hideSoftKeyboard();
                }
            }
            break;
    }

    return super.dispatchTouchEvent(ev);
}

แก้ไข:วิธีการ getFields () เป็นเพียงวิธีการที่ส่งกลับอาร์เรย์ที่มีฟิลด์ข้อความในมุมมอง เพื่อหลีกเลี่ยงการสร้างอาร์เรย์นี้ในทุกการสัมผัสฉันสร้างอาร์เรย์แบบสแตติกที่เรียกว่า sFields ซึ่งถูกส่งคืนที่เมธอด getFields () อาร์เรย์นี้เริ่มต้นได้ที่เมธอด onStart () เช่น:

sFields = new EditText[] {mUserField, mPasswordField};


มันไม่สมบูรณ์แบบเวลากิจกรรมลากจะขึ้นอยู่กับฮิวริสติกเท่านั้นดังนั้นบางครั้งมันก็ไม่ได้ซ่อนเมื่อใช้คำสั่งยาว ๆ และฉันก็เสร็จสิ้นโดยการสร้างวิธีที่จะได้รับ editTexts ต่อการดู; แป้นพิมพ์อื่นจะซ่อนและแสดงเมื่อคลิก EditText อื่น ๆ

ยังยินดีต้อนรับการแก้ไขที่สั้นกว่าและสะอาดกว่า


8
เพื่อช่วยเหลือผู้อื่นในอนาคตคุณจะพิจารณาแก้ไขรหัสในคำตอบเพื่อรวมgetFields()วิธีการของคุณหรือไม่ มันไม่จำเป็นต้องเป็นจริงตัวอย่างเพียงแค่อาจมีความคิดเห็นบางส่วนที่แสดงว่ามันส่งคืนอาร์เรย์ของEditTextวัตถุ
Squonk

14

ใช้OnFocusChangeListener

ตัวอย่างเช่น:

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
            hideKeyboard();
        }
    }
});

อัปเดต : คุณยังสามารถแทนที่onTouchEvent()กิจกรรมของคุณและตรวจสอบพิกัดของการสัมผัส หากพิกัดอยู่นอก EditText ให้ซ่อนคีย์บอร์ด


9
ปัญหาคือว่า edittext ไม่ได้มุ่งเน้นไปที่หลวมเมื่อฉันคลิกที่ฉลากหรือมุมมองอื่น ๆ ที่ไม่สามารถโฟกัสได้
htafoya

ในกรณีนี้ฉันมีทางออกอีกหนึ่งข้อ ฉันได้อัพเดตคำตอบแล้ว
Sergey Glotov

2
onTouchEvent โทรมาหลายครั้งดังนั้นนี่จึงไม่ใช่วิธีปฏิบัติที่ดีเช่นกัน
Jesus Dimrix

13

ฉันใช้ dispatchTouchEvent ในกิจกรรมเพื่อทำสิ่งนี้:

private EditText mEditText;
private Rect mRect = new Rect();
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int action = MotionEventCompat.getActionMasked(ev);

    int[] location = new int[2];
    mEditText.getLocationOnScreen(location);
    mRect.left = location[0];
    mRect.top = location[1];
    mRect.right = location[0] + mEditText.getWidth();
    mRect.bottom = location[1] + mEditText.getHeight();

    int x = (int) ev.getX();
    int y = (int) ev.getY();

    if (action == MotionEvent.ACTION_DOWN && !mRect.contains(x, y)) {
        InputMethodManager input = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        input.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

และฉันทดสอบมันใช้งานได้ดี!


ทำงานได้ แต่ปัญหาในเรื่องนี้คือถ้าเรามี EditText มากกว่าหนึ่งรายการเราก็ต้องพิจารณาด้วยเช่นกัน แต่ฉันชอบคำตอบของคุณ :-)
Lalit Poptani

getActionMasked (ev) เลิกใช้แล้วดังนั้นใช้ตอนนี้: การกระทำ int สุดท้าย = ev.getActionMasked (); สำหรับบรรทัดแรก
Andrew

12

อีกวิธีหนึ่งในการออกแบบKotlin & Materialโดยใช้TextInputEditText (วิธีการนี้ยังเข้ากันได้กับEditTextView ) ...

1. ทำให้มุมมองพาเรนต์ (มุมมองเนื้อหาของกิจกรรม / ส่วนของคุณ) สามารถคลิกได้และสามารถโฟกัสได้โดยการเพิ่มแอททริบิวต่อไปนี้

android:focusable="true"
android:focusableInTouchMode="true"
android:clickable="true"

2. สร้างส่วนขยายสำหรับทุกมุมมอง (ภายในไฟล์ ViewExtension.kt เช่น):

fun View.hideKeyboard(){
    val inputMethodManager = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}

3. สร้าง BaseTextInputEditText ที่สืบทอดของ TextInputEditText ใช้วิธีการ onFocusChanged เพื่อซ่อนแป้นพิมพ์เมื่อมุมมองไม่ได้โฟกัส:

class BaseTextInputEditText(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs){
    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        if (!focused) this.hideKeyboard()
    }
}

4. เพียงเรียกมุมมองที่กำหนดเองใหม่ของคุณใน XML ของคุณ:

<android.support.design.widget.TextInputLayout
        android:id="@+id/textInputLayout"
        ...>

        <com.your_package.BaseTextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            ... />

    </android.support.design.widget.TextInputLayout> 

นั่นคือทั้งหมดที่ ไม่จำเป็นต้องแก้ไขตัวควบคุมของคุณ (ส่วนหรือกิจกรรม) เพื่อจัดการกรณีซ้ำ


ใช่ดี แต่ฉันหวังว่าจะมีวิธีที่ง่ายกว่า!
devDeejay

11

แทนที่บูลีนสาธารณะ dispatchTouchEvent (เหตุการณ์ MotionEvent) ในกิจกรรมใด ๆ (หรือขยายคลาสของกิจกรรม)

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    View view = getCurrentFocus();
    boolean ret = super.dispatchTouchEvent(event);

    if (view instanceof EditText) {
        View w = getCurrentFocus();
        int scrcoords[] = new int[2];
        w.getLocationOnScreen(scrcoords);
        float x = event.getRawX() + w.getLeft() - scrcoords[0];
        float y = event.getRawY() + w.getTop() - scrcoords[1];

        if (event.getAction() == MotionEvent.ACTION_UP 
 && (x < w.getLeft() || x >= w.getRight() 
 || y < w.getTop() || y > w.getBottom()) ) { 
            InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
        }
    }
 return ret;
}

และนั่นคือทั้งหมดที่คุณต้องทำ


นี่เป็นวิธีที่ง่ายที่สุดที่ฉันพบว่าทำงานได้ ทำงานร่วมกับ EditTexts และ ScrollView หลายรายการ
RJH

ทดสอบกับ EditTexts หลายรายการ มันได้ผล! ข้อเสียเปรียบเพียงอย่างเดียวคือเมื่อคุณทำการเคลื่อนไหวลากมันก็ซ่อน
RominaV

9

ฉันแก้ไขโซลูชันของ Andre Luis IM ฉันได้รับสิ่งนี้:

ฉันสร้างวิธีการยูทิลิตี้เพื่อซ่อนคีย์บอร์ดนุ่ม ๆ เช่นเดียวกับ Andre Luiz IM:

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager)  activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}

แต่แทนที่จะลงทะเบียน OnTouchListener สำหรับทุกมุมมองที่ให้ประสิทธิภาพต่ำฉันลงทะเบียน OnTouchListener สำหรับเพียงแค่มุมมองรูท เนื่องจากเหตุการณ์นั้นทำให้เกิดฟองจนกว่าจะหมด (EditText เป็นหนึ่งในมุมมองที่ใช้เป็นค่าเริ่มต้น) หากมาถึงมุมมองรูตนั่นเป็นเพราะมันไม่ได้ถูกใช้ดังนั้นฉันจึงปิดแป้นพิมพ์นุ่ม

findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Utils.hideSoftKeyboard(activity);
        return false;
    }
});

1
ดูเหมือนว่าจะปลอดภัยกว่าสำหรับฉัน
superarts.org

9

ฉันรู้ว่าหัวข้อนี้ค่อนข้างเก่าคำตอบที่ถูกต้องดูเหมือนจะใช้ได้และมีวิธีแก้ไขปัญหาการทำงานมากมาย แต่ฉันคิดว่าวิธีการที่ระบุไว้ด้านล่างอาจมีประโยชน์เพิ่มเติมเกี่ยวกับประสิทธิภาพและความสง่างาม

ฉันต้องการพฤติกรรมนี้สำหรับกิจกรรมทั้งหมดของฉันดังนั้นฉันจึงสร้างCustomActivityระดับที่สืบทอดจากกิจกรรมในชั้นเรียนและ "ติดยาเสพติด" ฟังก์ชั่นdispatchTouchEvent มีสองเงื่อนไขหลักในการดูแล:

  1. หากไม่มีการเปลี่ยนแปลงโฟกัสและมีคนแตะที่ด้านนอกของช่องป้อนข้อมูลปัจจุบันให้ยกเลิก IME
  2. หากโฟกัสมีการเปลี่ยนแปลงและองค์ประกอบที่โฟกัสถัดไปไม่ใช่อินสแตนซ์ของฟิลด์อินพุตใด ๆ ให้ยกเลิก IME

นี่คือผลลัพธ์ของฉัน:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if(ev.getAction() == MotionEvent.ACTION_UP) {
        final View view = getCurrentFocus();

        if(view != null) {
            final boolean consumed = super.dispatchTouchEvent(ev);

            final View viewTmp = getCurrentFocus();
            final View viewNew = viewTmp != null ? viewTmp : view;

            if(viewNew.equals(view)) {
                final Rect rect = new Rect();
                final int[] coordinates = new int[2];

                view.getLocationOnScreen(coordinates);

                rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());

                final int x = (int) ev.getX();
                final int y = (int) ev.getY();

                if(rect.contains(x, y)) {
                    return consumed;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText) {
                return consumed;
            }

            final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

            inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);

            viewNew.clearFocus();

            return consumed;
        }
    }       

    return super.dispatchTouchEvent(ev);
}

หมายเหตุด้านข้าง: นอกจากนี้ฉันยังกำหนดคุณลักษณะเหล่านี้ให้กับมุมมองรูทเพื่อให้สามารถล้างโฟกัสในทุกฟิลด์อินพุตและป้องกันไม่ให้ฟิลด์อินพุตได้รับความสนใจในการเริ่มต้นกิจกรรม (ทำให้เนื้อหาดู "โฟกัสจับ"):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final View view = findViewById(R.id.content);

    view.setFocusable(true);
    view.setFocusableInTouchMode(true);
}

สุดยอดมันใช้งานได้ดีขอบคุณ !! ฉันใส่หนึ่ง upvote สำหรับคำตอบของคุณ
วีเจย์

2
ฉันคิดว่านั่นเป็นทางออกที่ดีที่สุดสำหรับรูปแบบที่ซับซ้อน แต่ฉันพบข้อเสียเปรียบ 2 ประการ: 1. เมนูบริบทของ EditText นั้นไม่สามารถคลิกได้ - การคลิกที่มันทำให้เกิดการสูญเสียโฟกัสจาก EditText 2. เมื่อ EditText ของเราอยู่ด้านล่างของมุมมองและเราคลิกที่มันนาน (เลือกคำ) หลังจากนั้น แป้นพิมพ์ที่แสดง "จุดคลิก" ของเราอยู่บนแป้นพิมพ์ไม่ใช่ใน EditText - ดังนั้นเราจึงเสียสมาธิอีกครั้ง: /
sosite

@sosite ฉันคิดว่าฉันได้พูดถึงข้อ จำกัด เหล่านั้นในคำตอบของฉันแล้ว ลองดูสิ.
Andy Dennie

6

ฉันชอบวิธีการโทรdispatchTouchEventโดย htafoya แต่:

  • ฉันไม่เข้าใจส่วนของตัวจับเวลา (ไม่ทราบว่าเพราะเหตุใดการวัดการหยุดทำงานจึงจำเป็นต้องใช้)
  • ฉันไม่ชอบที่จะลงทะเบียน / ถอนการลงทะเบียน EditTexts ทั้งหมดที่มีการเปลี่ยนแปลงมุมมอง (อาจจะเป็นจำนวนมาก viewchanges และ edittexts ในลำดับชั้นที่ซับซ้อน)

ดังนั้นฉันจึงแก้ปัญหานี้ง่ายกว่า:

@Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
    // all touch events close the keyboard before they are processed except EditText instances.
    // if focus is an EditText we need to check, if the touchevent was inside the focus editTexts
    final View currentFocus = getCurrentFocus();
    if (!(currentFocus instanceof EditText) || !isTouchInsideView(ev, currentFocus)) {
        ((InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE))
            .hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    }
    return super.dispatchTouchEvent(ev);
}

/**
 * determine if the given motionevent is inside the given view.
 * 
 * @param ev
 *            the given view
 * @param currentFocus
 *            the motion event.
 * @return if the given motionevent is inside the given view
 */
private boolean isTouchInsideView(final MotionEvent ev, final View currentFocus) {
    final int[] loc = new int[2];
    currentFocus.getLocationOnScreen(loc);
    return ev.getRawX() > loc[0] && ev.getRawY() > loc[1] && ev.getRawX() < (loc[0] + currentFocus.getWidth())
        && ev.getRawY() < (loc[1] + currentFocus.getHeight());
}

มีข้อเสียอย่างหนึ่ง:

การสลับจากอันหนึ่งEditTextไปอีกอันEditTextทำให้แป้นพิมพ์ซ่อนและ reshow - ในกรณีของฉันมันเป็นที่ต้องการเช่นนั้นเพราะมันแสดงให้เห็นว่าคุณสลับไปมาระหว่างสององค์ประกอบอินพุต


วิธีนี้ทำงานได้ดีที่สุดในแง่ของความสามารถในการใช้งานแบบ plug-and-play กับ FragmentActivity ของฉัน
BillyRayCyrus

ขอบคุณมันเป็นวิธีที่ดีที่สุด! ฉันเพิ่งเพิ่มการตรวจสอบกิจกรรมการกระทำ: int action = ev.getActionMasked (); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {... }
sergey.n

ขอบคุณมาก! คุณประหยัดเวลาของฉัน .. คำตอบที่ดีที่สุด
I.d007

6

ข้ออ้างคำร้องขอ:ฉันจำได้ว่าฉันไม่มีอิทธิพล แต่โปรดตอบอย่างจริงจัง

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

การแก้ไข:ห้องสมุดภายนอกเรียกว่าButterknife

โซลูชันหนึ่งบรรทัด:

@OnClick(R.id.activity_signup_layout) public void closeKeyboard() { ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); }

โซลูชันที่อ่านได้มากขึ้น:

@OnClick(R.id.activity_signup_layout) 
public void closeKeyboard() {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

คำอธิบาย: ผูก OnClick Listener กับ ID พาเรนต์ XML Layout ของกิจกรรมดังนั้นการคลิกที่เลย์เอาต์ (ไม่ได้อยู่ในข้อความแก้ไขหรือคีย์บอร์ด) จะเรียกใช้โค้ดขนาดสั้นซึ่งจะซ่อนแป้นพิมพ์

ตัวอย่าง: หากไฟล์เลย์เอาต์ของคุณคือ R.layout.my_layout และ id เลย์เอาต์ของคุณคือ R.id.my_layout_id ดังนั้นการโทร Butterknife ของคุณควรมีลักษณะดังนี้:

(@OnClick(R.id.my_layout_id) 
public void yourMethod {
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

ลิงค์เอกสารของ Butterknife: http://jakewharton.github.io/butterknife/

เสียบ: Butterknife จะปฏิวัติการพัฒนาแอนดรอยด์ของคุณ พิจารณามัน

หมายเหตุ:ผลลัพธ์เดียวกันสามารถทำได้โดยไม่ต้องใช้ Butterknife ไลบรารีภายนอก เพียงแค่ตั้งค่า OnClickListener เป็นเค้าโครงหลักตามที่อธิบายไว้ข้างต้น


สุดยอดโซลูชั่นที่สมบูรณ์แบบ! ขอบคุณ.
nullforlife

6

ใน kotlin เราสามารถทำสิ่งต่อไปนี้ ไม่จำเป็นต้องวนซ้ำทุกมุมมอง มันจะใช้งานได้สำหรับเศษยัง

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    currentFocus?.let {
        val imm: InputMethodManager = getSystemService(
            Context.INPUT_METHOD_SERVICE
        ) as (InputMethodManager)
        imm.hideSoftInputFromWindow(it.windowToken, 0)
    }
    return super.dispatchTouchEvent(ev)
}

ไม่ทำงานจากชิ้นส่วน
Nurseyit Tursunkulov

ใช้งานได้ แต่มีข้อบกพร่อง ตัวอย่างเช่นหากฉันต้องการวางข้อความในมุมมองข้อความแป้นพิมพ์จะซ่อนและแสดงขึ้น มันน่ารำคาญนิดหน่อย
George Shalvashvili

ดังนั้นบอกฉันทางออกที่ดีที่สุด ..
ไทร

4

นี่คือรูปแบบอื่นของคำตอบของ fje ที่จัดการกับปัญหาที่เกิดขึ้นโดย sosite

ความคิดที่นี่คือการจัดการทั้งลงและดำเนินการในกิจกรรมของ dispatchTouchEventวิธีการในการทำงานลงเราได้จดบันทึกมุมมองที่โฟกัสอยู่ (ถ้ามี) และดูว่าอยู่ด้านในหรือไม่โดยบันทึกทั้งสองบิตของข้อมูลในภายหลัง

ในการดำเนินการครั้งแรกเราจะจัดส่งเพื่ออนุญาตให้มุมมองอื่นสามารถโฟกัสได้ หากหลังจากนั้นมุมมองที่โฟกัสอยู่ในขณะนี้คือมุมมองที่โฟกัส แต่เดิมและการกดลงในมุมมองนั้นเราจะเปิดแป้นพิมพ์ค้างไว้

หากมุมมองที่มุ่งเน้นในปัจจุบันนั้นแตกต่างจากมุมมองที่มุ่งเน้น แต่เดิมและเป็นEditTextแล้วเรายังปล่อยให้เปิดแป้นพิมพ์

มิฉะนั้นเราจะปิดมัน

ดังนั้นเพื่อสรุปผลงานนี้เป็นดังนี้:

  • เมื่อสัมผัสกับสิ่งที่อยู่ในโฟกัส EditTextคีย์บอร์ดจะยังคงเปิดอยู่
  • เมื่อย้ายจากที่โฟกัสEditTextไปที่อื่นEditTextคีย์บอร์ดจะยังคงเปิดอยู่ (ไม่ปิด / เปิดใหม่)
  • เมื่อสัมผัสที่ใดก็ตามนอกโฟกัสEditTextที่อยู่ในขณะนั้นไม่ใช่อีกEditTextแป้นพิมพ์จะปิดลง
  • เมื่อกดเป็นเวลานานในการEditTextเปิดแถบการกระทำตามบริบท (ด้วยปุ่มตัด / คัดลอก / วาง) แป้นพิมพ์จะยังคงเปิดอยู่แม้ว่าการกระทำ UP จะเกิดขึ้นนอกโฟกัสEditText(ซึ่งย้ายลงมาเพื่อให้มีที่ว่างสำหรับ CAB) . อย่างไรก็ตามโปรดทราบว่าเมื่อคุณแตะที่ปุ่มใน CAB มันจะปิดแป้นพิมพ์ นั่นอาจเป็นหรือไม่เป็นที่ต้องการ หากคุณต้องการตัด / คัดลอกจากเขตข้อมูลหนึ่งและวางไปที่อีกเขตข้อมูลก็จะเป็นเช่นนั้น หากคุณต้องการที่จะวางกลับเข้าไปเหมือนเดิมEditTextมันจะไม่เป็นเช่นนั้น
  • เมื่อโฟกัสEditTextอยู่ที่ด้านล่างของหน้าจอและคุณคลิกที่ข้อความยาว ๆ เพื่อเลือกEditTextโฟกัสจะคงอยู่ดังนั้นคีย์บอร์ดจึงเปิดเหมือนที่คุณต้องการเพราะเราทำการตรวจสอบ "การสัมผัสภายในขอบเขตการมอง" ไม่ใช่การกระทำ

    private View focusedViewOnActionDown;
    private boolean touchWasInsideFocusedView;
    
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                focusedViewOnActionDown = getCurrentFocus();
                if (focusedViewOnActionDown != null) {
                    final Rect rect = new Rect();
                    final int[] coordinates = new int[2];
    
                    focusedViewOnActionDown.getLocationOnScreen(coordinates);
    
                    rect.set(coordinates[0], coordinates[1],
                            coordinates[0] + focusedViewOnActionDown.getWidth(),
                            coordinates[1] + focusedViewOnActionDown.getHeight());
    
                    final int x = (int) ev.getX();
                    final int y = (int) ev.getY();
    
                    touchWasInsideFocusedView = rect.contains(x, y);
                }
                break;
    
            case MotionEvent.ACTION_UP:
    
                if (focusedViewOnActionDown != null) {
                    // dispatch to allow new view to (potentially) take focus
                    final boolean consumed = super.dispatchTouchEvent(ev);
    
                    final View currentFocus = getCurrentFocus();
    
                    // if the focus is still on the original view and the touch was inside that view,
                    // leave the keyboard open.  Otherwise, if the focus is now on another view and that view
                    // is an EditText, also leave the keyboard open.
                    if (currentFocus.equals(focusedViewOnActionDown)) {
                        if (touchWasInsideFocusedView) {
                            return consumed;
                        }
                    } else if (currentFocus instanceof EditText) {
                        return consumed;
                    }
    
                    // the touch was outside the originally focused view and not inside another EditText,
                    // so close the keyboard
                    InputMethodManager inputMethodManager =
                            (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    inputMethodManager.hideSoftInputFromWindow(
                        focusedViewOnActionDown.getWindowToken(), 0);
                    focusedViewOnActionDown.clearFocus();
    
                    return consumed;
                }
                break;
        }
    
        return super.dispatchTouchEvent(ev);
    }

4

มันง่ายเกินไปเพียงทำให้เลย์เอาต์ล่าสุดของคุณสามารถคลิกได้โดยโค้ดนี้:

android:id="@+id/loginParentLayout"
android:clickable="true"
android:focusableInTouchMode="true"

จากนั้นเขียนวิธีการและ OnClickListner สำหรับเลย์เอาต์นั้นเพื่อที่ว่าเมื่อเลย์เอาต์รูปแบบบนสุดแตะใด ๆ ที่มันจะเรียกวิธีการที่คุณจะเขียนรหัสเพื่อยกเลิกคีย์บอร์ด ต่อไปนี้เป็นรหัสสำหรับทั้งสอง; // คุณต้องเขียนสิ่งนี้ใน OnCreate ()

 yourLayout.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View view) {
                    hideKeyboard(view);
                }
            });

วิธีการเรียกจาก listner: -

 public void hideKeyboard(View view) {
     InputMethodManager imm =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

4

ฉันพบคำตอบที่ซับซ้อนเล็กน้อยที่ยอมรับได้สำหรับความต้องการง่ายๆนี้ นี่คือสิ่งที่ทำงานสำหรับฉันโดยไม่ผิดพลาดใด ๆ

findViewById(R.id.mainLayout).setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            return false;
        }
    });

1
ถ้าคุณใช้NestedScrollViewหรือรูปแบบที่ซับซ้อนดูคำตอบที่ได้รับการยอมรับ: stackoverflow.com/a/11656129/2914140 คุณควรรู้ว่าภาชนะบรรจุอื่นอาจใช้สัมผัส
CoolMind

3

มีวิธีที่ง่ายกว่าโดยอ้างอิงจากปัญหาเดิมของ iPhone เพียงแค่แทนที่เค้าโครงของพื้นหลังในเหตุการณ์การสัมผัสที่มีการแก้ไขข้อความ เพียงใช้รหัสนี้ในกิจกรรมของ OnCreate (login_fondo เป็นเลย์เอาต์หลัก):

    final LinearLayout llLogin = (LinearLayout)findViewById(R.id.login_fondo);
    llLogin.setOnTouchListener(
            new OnTouchListener()
            {
                @Override
                public boolean onTouch(View view, MotionEvent ev) {
                    InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
                            android.content.Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0);
                    return false;
                }
            });

1
ดังที่ฉันพูดและฉันจำได้ว่าจะใช้งานได้เฉพาะถ้าแบบฟอร์มไม่ได้อยู่ใน ScrollView
htafoya

1
สิ่งนี้ใช้งานไม่ได้ดีหากเค้าโครงพื้นหลังมีเค้าโครงลูกอื่น ๆ
AxeEffect

ขอบคุณที่เตือนว่า onClick ไม่ใช่ตัวเลือกเดียว
Harpreet

3

วิธีการแสดง / ซ่อนคีย์บอร์ดอ่อน

InputMethodManager inputMethodManager = (InputMethodManager) currentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (isShow) {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
        } else {
            inputMethodManager.showSoftInput(currentActivity.getCurrentFocus(), InputMethodManager.SHOW_FORCED);    
        }

    } else {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS, 0);
        } else {
            inputMethodManager.hideSoftInputFromInputMethod(currentActivity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);    
        }

    }

ฉันหวังว่าพวกเขาจะมีประโยชน์


3

อันนี้เป็นทางออกที่ง่ายที่สุดสำหรับฉัน (และทำงานโดยฉัน)

นี่เป็นวิธีซ่อนคีย์บอร์ด

public void hideKeyboard(View view){
        if(!(view instanceof EditText)){
            InputMethodManager inputMethodManager=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
        }
    }

ตอนนี้ตั้งค่าแอตทริบิวต์ onclick ของโครงร่างหลักของกิจกรรมเป็นวิธีด้านบนhideKeyboardทั้งจากมุมมองออกแบบของไฟล์ XML ของคุณหรือการเขียนโค้ดด้านล่างในมุมมองข้อความของไฟล์ XML ของคุณ

android:onClick="hideKeyboard"

2

ฉันได้ปรับปรุงวิธีการให้ใส่รหัสต่อไปนี้ในคลาสยูทิลิตี้ UI (โดยเฉพาะอย่างยิ่งไม่จำเป็น) เพื่อให้สามารถเข้าถึงได้จากทุกกิจกรรมหรือคลาส Fragment ของคุณเพื่อให้บริการตามวัตถุประสงค์

public static void serachAndHideSoftKeybordFromView(View view, final Activity act) {
    if(!(view instanceof EditText)) {
        view.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(act);
                return false;
            }
        });
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View nextViewInHierarchy = ((ViewGroup) view).getChildAt(i);
            serachAndHideSoftKeybordFromView(nextViewInHierarchy, act);
        }
    }
}
public static void hideSoftKeyboard (Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}

จากนั้นพูดตัวอย่างที่คุณต้องการเรียกมันจากกิจกรรมเรียกมันว่าดังนี้

UIutils.serachAndHideSoftKeybordFromView(findViewById(android.R.id.content), YourActivityName.this);

แจ้งให้ทราบ

findViewById (android.R.id.content)

สิ่งนี้ทำให้เรามีมุมมองรูทของกลุ่มปัจจุบัน (คุณต้องไม่ตั้ง id บนมุมมองรูท)

ไชโย :)



2

กิจกรรม

 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
     ScreenUtils.hideKeyboard(this, findViewById(android.R.id.content).getWindowToken());
     return super.dispatchTouchEvent(ev);
 }

ScreenUtils

 public static void hideKeyboard(Context context, IBinder windowToken) {
     InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
     imm.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS);
 }

3
รหัสนี้จะง่าย แต่ก็มีปัญหาที่เห็นได้ชัด: ปิดแป้นพิมพ์เมื่อใดก็ได้สัมผัส นั่นคือถ้าคุณแตะที่ตำแหน่งอื่นของ EditText เพื่อย้ายเคอร์เซอร์อินพุตมันจะซ่อนคีย์บอร์ดและแป้นพิมพ์จะปรากฏขึ้นอีกครั้งโดยระบบ
ผักประณาม

2

เพียงเพิ่มรหัสนี้ในคลาส @Overide

public boolean dispatchTouchEvent(MotionEvent ev) {
    View view = getCurrentFocus();
    if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
        int scrcoords[] = new int[2];
        view.getLocationOnScreen(scrcoords);
        float x = ev.getRawX() + view.getLeft() - scrcoords[0];
        float y = ev.getRawY() + view.getTop() - scrcoords[1];
        if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
            ((InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((this.getWindow().getDecorView().getApplicationWindowToken()), 0);
    }
    return super.dispatchTouchEvent(ev);
}

3
ขณะนี้อาจตอบคำถามได้ดีกว่าที่จะอธิบายส่วนสำคัญของคำตอบและอาจเป็นปัญหากับรหัส OPs
pirho

ใช่ @ pirho ฉันเห็นด้วยกับคุณ Haseeb จำเป็นต้องมีสมาธิในการให้คำตอบที่เหมาะสม
Dilip

2
@Dilip คุณรู้หรือไม่ว่าคุณสามารถโหวตความคิดเห็นที่คุณเห็นด้วย? นี่เป็นเพียงเพื่อให้ส่วนความคิดเห็นสะอาดเพื่อไม่ให้มีความคิดเห็นจำนวนมากที่มีจุดเดียวกันจริง ๆ
pirho

2

แทนที่จะวนซ้ำทุกมุมมองหรือแทนที่ dispatchTouchEvent

ทำไมไม่เพียงแทนที่ onUserInteraction () ของกิจกรรมนี้จะทำให้แน่ใจว่าแป้นพิมพ์จะยกเลิกเมื่อใดก็ตามที่ผู้ใช้แตะนอก EditText

จะทำงานแม้ว่า EditText จะอยู่ใน scrollView

@Override
public void onUserInteraction() {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
}

นี่เป็นคำตอบที่ดีกว่า
23:08

ใช่ นั่นเป็นคำตอบที่สะอาดมาก และถ้าคุณสร้าง AbstractActivity ที่กิจกรรมอื่น ๆ ของคุณขยายคุณสามารถทำเช่นนั้นเป็นพฤติกรรมเริ่มต้นในแอปของคุณ
wildcat12

1

ฉันได้ทำงานนี้กับตัวแปรที่แตกต่างกันเล็กน้อยในโซลูชันของ Fernando Camarago ในวิธีการ onCreate ของฉันฉันแนบ onTouchListener เดียวกับมุมมองรูท แต่ส่งมุมมองแทนที่จะเป็นกิจกรรมเป็นอาร์กิวเมนต์

        findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {           
        public boolean onTouch(View v, MotionEvent event) {
            Utils.hideSoftKeyboard(v);
            return false;
        }
    });

ในชั้น Utils แยกเป็น ...

    public static void hideSoftKeyboard(View v) {
    InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}

1

อาจเก่า แต่ฉันได้งานนี้โดย implenting คลาสเอง

public class DismissKeyboardListener implements OnClickListener {

    Activity mAct;

    public DismissKeyboardListener(Activity act) {
        this.mAct = act;
    }

    @Override
    public void onClick(View v) {
        if ( v instanceof ViewGroup ) {
            hideSoftKeyboard( this.mAct );
        }
    }       
}

public void hideSoftKeyboard(Activity activity) {
        InputMethodManager imm = (InputMethodManager)
        getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
}

วิธีปฏิบัติที่ดีที่สุดในที่นี้คือการสร้างคลาสผู้ช่วยและทุกคอนเทนเนอร์เลย์เอาต์เชิงสัมพันธ์ / เชิงเส้นควรใช้สิ่งนี้

**** จดบันทึกเฉพาะคอนเทนเนอร์หลักควรใช้คลาสนี้ (สำหรับการปรับให้เหมาะสม) ****

และใช้มันเช่นนี้

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

คำหลักนี้ใช้สำหรับกิจกรรม ดังนั้นถ้าคุณอยู่ในส่วนที่คุณใช้เช่น getActivity ();

--- ยกนิ้วให้ถ้าช่วยคุณ ... --- ไชโย Ralph ---


1

นี่เป็นคำตอบของ fje ที่ได้รับการแก้ไขเล็กน้อยซึ่งส่วนใหญ่ทำงานได้อย่างสมบูรณ์แบบ

รุ่นนี้ใช้ ACTION_DOWN เพื่อให้การดำเนินการเลื่อนยังเป็นการปิดแป้นพิมพ์ และจะไม่เผยแพร่เหตุการณ์เว้นแต่คุณคลิกที่ EditText อื่น ซึ่งหมายความว่าการคลิกที่ใดก็ได้นอก EditText ของคุณแม้ในคลิกอื่นเพียงแค่ปิดแป้นพิมพ์

@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
    if(ev.getAction() == MotionEvent.ACTION_DOWN)
    {
        final View view = getCurrentFocus();

        if(view != null)
        {
            final View viewTmp = getCurrentFocus();
            final View viewNew = viewTmp != null ? viewTmp : view;

            if(viewNew.equals(view))
            {
                final Rect rect = new Rect();
                final int[] coordinates = new int[2];

                view.getLocationOnScreen(coordinates);

                rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());

                final int x = (int) ev.getX();
                final int y = (int) ev.getY();

                if(rect.contains(x, y))
                {
                    super.dispatchTouchEvent(ev);
                    return true;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText)
            {
                super.dispatchTouchEvent(ev);
                return true;
            }

            final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

            inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);

            viewNew.clearFocus();

            return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}

บางสิ่งดูไม่ตรงนี้ คุณกำลังกำหนดทั้งสองviewและviewTmpเพื่อgetCurrentFocus()ให้พวกเขาจะเป็นค่าเดียวกันเสมอ
Andy Dennie

1

ฉันทำแบบนี้แล้ว:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
   View view = getCurrentFocus();
   if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
            int scrcoords[] = new int[2];
            view.getLocationOnScreen(scrcoords);
            float x = ev.getRawX() + view.getLeft() - scrcoords[0];
            float y = ev.getRawY() + view.getTop() - scrcoords[1];
            if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
                hideKeyboard(this);
        }
    return super.dispatchTouchEvent(ev);
}

ซ่อนรหัสแป้นพิมพ์ :

public static void hideKeyboard(Activity act) {
    if(act!=null)
      ((InputMethodManager)act.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((act.getWindow().getDecorView().getApplicationWindowToken()), 0);
  }

เสร็จสิ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.