首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android AlertDialog 取得 EditText 輸入值避免空指標

     編輯:程式設計綜合問答

問題現象

在 AlertDialog 中嵌入自訂佈局包含 EditText,點擊確定按鈕時嘗試取得輸入值,卻拋出 NullPointerException。問題在於 findViewById() 找不到對應的視圖元件。

錯誤代碼示例

new AlertDialog.Builder(ActSectors.this)
    .setTitle(R.string.menu_new_sector)
    .setView(inflater.inflate(R.layout.dlg_sector_add, null)) // 直接 inflate 並傳入
    .setPositiveButton(R.string.dlg_save, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
            // 錯誤:在 Activity 根佈局中尋找,但 EditText 在 Dialog 佈局中
            EditText mNewSectorName = (EditText) findViewById(R.id.dlg_edt_nsector); 
            String val = mNewSectorName.getText().toString(); // NPE
            grabVal(val);
        }
    })
    .create().show();

根本原因

AlertDialog.Builder.setView(View) 接收的是一個已建立的 View 物件。若直接傳入 inflater.inflate(...),雖然視圖會顯示在對話框中,但該 View 物件未被保存參考。後續在 onClick 中呼叫 findViewById() 時,預設在 Activity 的根視圖層級查找,自然找不到對話框內部的 EditText。

正確解決方案

核心修正:先保存 inflate 後的 View 參考

private void showAddSectorDlg() {
    if (BuildConfig.DEBUG) {
        Log.i(Constants.TAG_ACTSECTORS, "showAddSectorDialog() called.");
    }
    
    LayoutInflater inflater = getLayoutInflater();
    
    // 關鍵步驟:先 inflate 並保存參考,加上 final 供內部類使用
    final View dialogView = inflater.inflate(R.layout.dlg_sector_add, null);
    
    new AlertDialog.Builder(ActSectors.this)
        .setTitle(R.string.menu_new_sector)
        .setMessage(R.string.dlg_sect_add_msg)
        .setView(dialogView) // 使用保存的參考
        .setPositiveButton(R.string.dlg_save, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                // 關鍵:在 dialogView 上查找,而非 Activity
                EditText mNewSectorName = (EditText) dialogView.findViewById(R.id.dlg_edt_nsector);
                String val = mNewSectorName.getText().toString();
                
                if (BuildConfig.DEBUG) {
                    Log.i(Constants.TAG_ACTSECTORS, "New Sector Name: " + val);
                }
                grabVal(val);
                dialog.dismiss();
            }
        })
        .setNegativeButton(R.string.dlg_cancel, null)
        .create()
        .show();
}

進階技巧:自訂 DialogFragment(現代寫法)

Google 建議使用 DialogFragment 替代傳統 AlertDialog,可更好處理生命週期與配置變更:

public class AddSectorDialogFragment extends DialogFragment {
    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.dlg_sector_add, null);
        final EditText editText = view.findViewById(R.id.dlg_edt_nsector);
        
        return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.menu_new_sector)
            .setView(view)
            .setPositiveButton(R.string.dlg_save, (dialog, which) -> {
                String val = editText.getText().toString();
                ((ActSectors) getActivity()).grabVal(val);
            })
            .setNegativeButton(R.string.dlg_cancel, null)
            .create();
    }
}

// 使用方式
new AddSectorDialogFragment().show(getSupportFragmentManager(), "add_sector");

常見錯誤對照表

錯誤寫法正確寫法說明
.setView(inflater.inflate(...))先存參考 View v = inflater.inflate(...) 再 .setView(v)需保存 View 參考以便後續查找子元件
findViewById(R.id.xxx)dialogView.findViewById(R.id.xxx)在對話框視圖層級查找,而非 Activity
未加 final加上 final View dialogView匿名內部類需捕獲 final 或 effectively final 變數

總結

關鍵在於 先 inflate 保存 View 參考,再傳給 setView(),取值時在該 View 上呼叫 findViewById()。現代開發建議改用 DialogFragment 獲得更完整的生命週期管理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved