Android:对话框中的EditText无法弹出软键盘

89
所以我遇到了一个常见的问题,就是当我的对话框中的EditText获得焦点时,它不会显示出来。我看到过几种解决方法,比如在这个线程这个线程这个线程(还有很多),但我从来没有看到一个令人满意的解释为什么这一切会发生。

我更希望Android使用自己默认的EditText行为而不是构建自己的行为,但似乎每个人(在那些线程中)都接受了在对话框中EditText的默认行为只是给出光标而没有键盘。为什么会这样呢?

记录一下,这些解决方法似乎都不适用于我——我能做到的最接近的方法是强制一个键盘出现在对话框下面(使用InputMethodManager.toggleSoftKeyboard(*))。我的特定配置是API15,在AlertDialog中的ListView的页脚上显示EditText。EditText android:focusable="true"已设置,并且onFocusChangeListener正在接收焦点事件。

编辑:

根据要求,这里是我正在使用的特定代码片段。我不会费心去整个布局,但在这个特定的应用程序中,EditText是响应于对话框上的按钮(类似于操作视图)而出现的。它包含在一个默认可见性为“gone”的RelativeLayout中:

 <RelativeLayout 
       android:id="@+id/relLay"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_centerVertical="true"
       android:visibility="gone"
       android:layout_marginTop="5dp"
       android:layout_marginBottom="5dp">

        <ImageButton
            android:id="@+id/cancelBut"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:background="@color/transparent"
            android:src="@drawable/cancelButton" 
            android:layout_margin="5dp"/>

        <ImageButton
            android:id="@+id/okBut"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@id/cancelBut"
            android:background="@color/transparent"
            android:src="@drawable/okButton"
            android:layout_margin="5dp" />

        <EditText 
            android:id="@+id/editText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text"
            android:focusable="true"
            android:layout_toLeftOf="@id/okBut"/>
   </RelativeLayout>

构建此代码将相对布局的可见性设置为“Visible”(并隐藏其他UI元素)。根据我对EditText的经验,这应该足以在EditText获得焦点时拉起键盘。然而,由于某种原因,情况并非如此。我可以设置以下onFocusChangeListener:

    edit_text.setOnFocusChangeListener(new OnFocusChangeListener() {

            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                // For whatever reason we need to request a soft keyboard.
                    InputMethodManager imm = (InputMethodManager)dlg.getWindow().getContext().getSystemService(_Context.INPUT_METHOD_SERVICE);
                    if(hasFocus)
                        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
                    Log.v("DialogProblem", "Focus requested, " + (hasFocus?"has focus.":"doesn't have focus."));
                }
            }
        });

使用这个配置,当我第一次进入EditText时,onFocusChangedListener会触发,并生成一个日志,通常看起来像这样:
Focus requested, has focus.
Focus requested, doesn't have focus.
Focus requested, has focus.

键盘出现后又消失了,可能是因为我切换了两次,但即使我确保它保持打开状态,它仍然在对话框窗口的后面(在一个灰色区域中),没有办法在不关闭对话框的情况下访问它。
话虽如此,我想强调的是,尽管我可能能够让这个解决方法起作用,但我主要关心为什么EditText一开始就没有触发,以及为什么这似乎是如此普遍的问题!

您介意贴出相关的代码片段(在其中设置焦点、添加焦点监听器等)吗? - Alexander Lucas
我可以(并且在回到电脑时会这样做),但我认为我的更广泛的问题与焦点监听器无关。我想知道为什么似乎在对话框中编辑文本不会打开键盘是一个常见的问题。虽然如果这是解决问题的方法,我很乐意使用一个临时的解决方案,但我想知道为什么需要这样做。 - Paul
12个回答

205

好的,经过大量阅读后,我已经明白为什么这是一个问题了,并且我不需要使用任何变通方法。

问题似乎在于(至少在我的情况下),由于输入文本的位置最初是隐藏的(或嵌套的或其他原因),AlertDialog会自动设置标志WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM(或某些该标志和WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE的组合),以便事物不会触发软输入框出现。

我发现解决此问题的方法是在对话框创建后添加以下行:

dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);

完成此操作后,EditText 就像普通的 EditText 一样,不需要任何巧妙或变通的方法。


91
在我尝试在创建后清除标志时,它并没有起作用。只有在我像这样使用它时才有效:Dialog = builder.create(); Dialog.show(); Dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); Dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - Alex Fragotsis
1
@AlexanderFragotsis 在尝试了stackoverflow上许多不同的解决方案后,您的评论是唯一有效的!谢谢! - Bruce
23
SOFT_INPUT_STATE_VISIBLE对我不起作用。我使用了SOFT_INPUT_STATE_ALWAYS_VISIBLE,然后它就起作用了。如果其他人也遇到同样的问题,我在这里提供一个解决方案。 - kb_14
1
非常重要的是在show()之后执行clearFlags(),感谢@AlexanderFragotsis。 - Javier Mendonça
很好的答案,只要确保在AlertDialog可见时调用它,因此最好在OnShowListener中执行此方法。 - Mapsy
显示剩余3条评论

19

我在自己的应用程序中遇到了同样的问题。如果您正在开发API级别>= 8的应用程序,您可以使用以下代码片段:

dialog.setOnShowListener(new OnShowListener() {
    @Override
     public void onShow(DialogInterface dialog) {
         InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
         imm.showSoftInput(textEdit, InputMethodManager.SHOW_IMPLICIT);
    }
});

我还没有找到适用于较低API级别的解决方案......

顺便说一下:这段代码在模拟器上不总是起作用。我不知道为什么。


2
我对这个特定的解决方法并不是很感兴趣,我更关心的是为什么它一开始就不能正常工作。此外,我实际上并不希望在对话框中显示键盘,我希望它在EditText获得焦点时显示出来,就像普通EditText的行为一样。 - Paul
1
这对我有用。如果您正在使用AlertDialog.Builder,请在上面使用AlertDialog dialog = builder.create();,然后使用dialog.show(); - Matt Connolly
1
这是唯一对我有效的方法。我也不认为这是一个变通方法,当你阅读下面引用Android文档的答案时,它是有道理的,解释了为什么在对话框实际显示之前无法请求焦点。 - Mick Byrne

18

如果您阅读AlertDialog文档,您会发现以下内容:

AlertDialog类会根据对话框中的任何视图是否从View.onCheckIsTextEditor()返回true来自动设置*WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM*。一般情况下,您希望为没有文本编辑器的Dialog设置此标志,以便将其放置在当前输入法UI的顶部。您可以在调用onCreate之后强制设置标志以符合您的要求。

我曾经遇到过在对话框内ListView中的EditText问题。我通过重写自定义视图类(在我的情况下是ListView)并使用我自己的FocusableListView来解决它,其中只有一个方法被覆盖:

public class FocusableListView extends ListView {

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

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

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

    @Override
    public boolean onCheckIsTextEditor() {
        // this is where the magic happens
        return true;
    }
}

然后我在布局文件中这样使用它:

<?xml version="1.0" encoding="UTF-8"?>
<com.myexample.wiget.FocusableListView 
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:descendantFocusability="beforeDescendants"
android:layout_height="wrap_content" />

你可以以同样的方式覆盖RelativeLayout来解决你的问题。


工作完美,但Android Studio(版本1.5.1)无法渲染预览布局。 - Choletski

13

这是对我有用的方法。创建AlertDialog.Builder,设置标题、positiveButton和negativeButton。在完成此操作后:

    AlertDialog dialog = builder.create();
    dialog.getWindow().clearFlags( WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    dialog.show();
    editText.requestFocus();

您不需要使用builder.show();


谢谢André,你真是太棒了。我在Stack Overflow上尝试了很多解决方案,这是唯一一个有效的。 - productioncoder
完美解决方案!! - NEERAJ SWARNKAR

8

上面的代码非常有用。但是你必须在“create”方法之后调用“show”方法(我不知道为什么,但只有这样在我的带有EditText的ListView对话框中才有效)。 在onCreateDialog方法中:

@Override
protected Dialog onCreateDialog(int id) {
  switch (id) {
    case YOUR_DIALOG_ID: {
        //...
        AlertDialog a = new AlertDialog.Builder(this)./*
        ... set the properties here
        */
        .create();
        a.show(); //!!! this is very important to call the "show" method
        a.getWindow().clearFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
        return a;
    }
  //...
  }
  return null;
}

那段代码真的能工作吗?a被初始化为一个AlertDialog,但构造函数是一个builder。Builders有一个create方法,它返回一个dialog,dialogs有一个show方法来显示它们。 - Paul
@Paul 对不起,对话框创建部分的代码真的有问题。我已经纠正了它。 - iLya2IK

7
感谢您!我在警告对话框片段中嵌入了一个ListView的最后一行嵌入文本编辑器。我使用了您的清除标志解决方案作为后续可运行项,现在它完美地工作了。
    @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    builder.setTitle("My Title");
    m_adapter = new MyAdapter(getContext());
    builder.setAdapter(m_adapter, new OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            // TODO Auto-generated method stub

        }
    }); 

    final AlertDialog dialog = builder.create();
    final ListView listView = dialog.getListView();
    listView.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {

        }
    });

    listView.post(new Runnable() {

        @Override
        public void run() {
            dialog.getWindow().clearFlags(
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);              
        }
    });
    return dialog;
}

4
这里有一种方法可以实现:

以下是具体步骤:

    final Window dialogWindow = dialog.getWindow();
    dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
    dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);

4
我想补充 Paul 的答案Alexander 的评论
我自己有一个对话框在 `onCreateDialog()` 方法中创建,这似乎需要返回 `dialog.show();`。因此,在创建对话框的地方不能添加布局参数。为了解决这个问题,只需保持 `onCreateDialog()` 方法不变,并添加以下 `onResume()` 方法:
@Override
public void onResume() {
    super.onResume();
    Dialog dialog = getDialog();
    dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
}

这应该可以解决问题,对我来说有效,谢天谢地。我已经处理这个问题有一段时间了。


0
This worked for me ----
editText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
//dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
//dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
//dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
InputMethodManager mgr = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.showSoftInput(v, InputMethodManager.SHOW_FORCED);
editText.setFocusable(true);
}
});

0

在对话框中显示键盘的完整代码:

public void onFocusChange(View v, boolean hasFocus) {
    Log.v("onFocusChange", hasFocus + " " + showkeyboard);
    if (hasFocus) {
        if (showkeyboard++ == 0) {
            alertDialog.getWindow().clearFlags(
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
            alertDialog.getWindow().setSoftInputMode(
                    WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        } else {
            showkeyboard = 1;
        }
    }
}

1
尝试解释你的答案。不要只给出代码。对相应的代码给出具体的答案和解释。 - Sandip Armal Patil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接