Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

ปิด/ซ่อนคีย์บอร์ดอัตโนมัติใน Android


InputMethodManager: API หลัก

InputMethodManager (IMM) เป็น System Service จัดการ Virtual Keyboard (IME) เมธอดหลัก:

  • hideSoftInputFromWindow(IBinder windowToken, int flags) - ซ่อนคีย์บอร์ด
  • showSoftInput(View view, int flags) - แสดงคีย์บอร์ด
  • toggleSoftInput(int showFlags, int hideFlags) - Toggle
  • isAcceptingText() - เช็คว่ารับ Input อยู่ไหม

วิธีซ่อนคีย์บอร์ด (Common Patterns)

1. จาก View ใดๆ (Common)

public static void hideKeyboard(View view) {
    if (view == null) return;
    InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm != null) {
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
}

// ใช้งาน
hideKeyboard(editText); // หรือ button, rootView, any view in window

2. จาก Activity/Fragment

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

// Fragment
public void hideKeyboard() {
    View view = requireView().getRootView().findFocus(); // หรือ requireActivity().getCurrentFocus()
    if (view != null) {
        InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
}

3. Extension Function (Kotlin)

// View.kt
fun View.hideKeyboard() {
    val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
    imm.hideSoftInputFromWindow(windowToken, 0)
}

fun Activity.hideKeyboard() {
    currentFocus?.let { it.hideKeyboard() }
}

fun Fragment.hideKeyboard() {
    view?.let { it.hideKeyboard() }
}

// ใช้งาน
editText.hideKeyboard()
activity.hideKeyboard()
fragment.hideKeyboard()

การตั้งค่าใน Manifest / XML

1. ปิดคีย์บอร์ดตอนเริ่ม Activity


    
    

2. windowSoftInputMode Options

Valueคำอธิบาย
stateHiddenซ่อนคีย์บอร์ดตอน Activity เริ่ม (Default)
stateVisibleแสดงคีย์บอร์ดตอนเริ่ม
stateAlwaysHiddenซ่อนตลอดกาล (แม้ EditText Focus)
stateAlwaysVisibleแสดงตลอดกาล
stateUnchangedคงสถานะเดิม
adjustResizeResize Layout ให้พอดีพื้นที่เหลือ (แนะนำ)
adjustPanเลื่อน Layout ขึ้น (เก่า, ไม่แนะนำ)
adjustNothingไม่ปรับ Layout

ตัวอย่างครบถ้วน: EditText + Button ซ่อนคีย์บอร์ด

Layout (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <EditText
        android:id="@+id/etInput"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="พิมพ์ข้อความ..."
        android:inputType="text"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@+id/btnHideKeyboard"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="ซ่อนคีย์บอร์ด"
        app:layout_constraintTop_toBottomOf="@id/etInput"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/tvResult"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="ผลลัพธ์จะแสดงที่นี่"
        android:textSize="16sp"
        app:layout_constraintTop_toBottomOf="@id/btnHideKeyboard"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java

import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private EditText etInput;
    private Button btnHideKeyboard;
    private TextView tvResult;
    private View rootLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        etInput = findViewById(R.id.etInput);
        btnHideKeyboard = findViewById(R.id.btnHideKeyboard);
        tvResult = findViewById(R.id.tvResult);
        rootLayout = findViewById(R.id.rootLayout);

        // Auto focus และแสดงคีย์บอร์ด (ถ้าต้องการ)
        // etInput.requestFocus();
        // showKeyboard(etInput);

        btnHideKeyboard.setOnClickListener(v -> {
            hideKeyboard(v); // ใช้ View ใดก็ได้ใน Window เดียวกัน
            String text = etInput.getText().toString().trim();
            tvResult.setText("ข้อความที่พิมพ์: " + (text.isEmpty() ? "(ว่าง)" : text));
            Toast.makeText(this, "คีย์บอร์ดถูกซ่อนแล้ว", Toast.LENGTH_SHORT).show();
        });

        // กดที่พื้นที่ว่าง (Root Layout) -> ซ่อนคีย์บอร์ด
        rootLayout.setOnClickListener(v -> {
            hideKeyboard(v);
            etInput.clearFocus(); // เอา Focus ออก
        });
    }

    private void hideKeyboard(View view) {
        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }

    // Optional: แสดงคีย์บอร์ด
    private void showKeyboard(View view) {
        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
        if (imm != null) {
            view.requestFocus();
            imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
        }
    }
}

กรณีพิเศษและ Troubleshooting

1. Dialog/BottomSheet: ซ่อนคีย์บอร์ดเมื่อปิด

// DialogFragment
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
    super.onDismiss(dialog);
    hideKeyboard(); // ใช้ method ด้านบน
}

// BottomSheetDialogFragment
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
    View view = getView();
    if (view != null) hideKeyboard(view);
    super.onDismiss(dialog);
}

2. RecyclerView/ListView: ซ่อนคีย์บอร์ดเวลา Scroll

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
        if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
            hideKeyboard(recyclerView);
        }
    }
});

3. คีย์บอร์ดไม่ซ่อน (Common Issues)

  • Window Token ผิด: ใช้ View ที่ Attach กับ Window เดียวกัน (getWindowToken() ต้องไม่ null)
  • Flags: ลองใช้ InputMethodManager.HIDE_NOT_ALWAYS แทน 0
  • Focus: เรียก view.clearFocus() หลังซ่อน
  • Dialog: ใช้ dialog.getWindow().setSoftInputMode(...)

4. Android 11+ (API 30): IME Animations

// ควบคุม Animation การซ่อน/แสดง (Inset Animation)
WindowInsetsControllerCompat controller = 
    WindowCompat.getInsetsController(getWindow(), rootView);
controller.hide(WindowInsetsCompat.Type.ime()); // ซ่อนพร้อม Animation
controller.show(WindowInsetsCompat.Type.ime()); // แสดงพร้อม Animation

// ควบคุม Behavior
controller.setSystemBarsBehavior(
    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
);

สรุป Checklist

  1. ใช้ InputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0)
  2. View ใดก็ได้ใน Window เดียวกันใช้ได้ (Button, RootLayout, EditText เอง)
  3. Manifest: android:windowSoftInputMode="stateHidden|adjustResize"
  4. Kotlin: เขียน Extension Function ไว้ใช้ร่วม
  5. Fragment: ใช้ requireView() หรือ requireActivity()
  6. API 30+: ใช้ WindowInsetsControllerCompat สำหรับ Animation Control