Android Lollipop 软键盘在 DialogFragment 中隐藏用户输入

Android Lollipop Soft Keyboard Hides User Inputs in DialogFragment

我正在编写我的第一个真正的 Android 应用程序。我有一个 Android DialogFragment 正在显示以允许用户添加或编辑所选项目的详细信息。 DialogFragment 弹出并在全屏模式下显示正常。但是,在 Lollipop 模拟器上,当我单击屏幕下方的 EditText 小部件时,软键盘会覆盖小部件,而不是向上滚动屏幕以便用户可以看到他们正在输入的内容。我在 Lollipop 之前的模拟器中没有遇到这种行为。在 KitKat 模拟器上,屏幕会调整,以便 EditText 仍然可见。 Whosebug 还不允许我 post 图片。所以,你必须发挥你的想象力。 :-( 我尝试了不同的方法来解决这个问题。不幸的是,none 曾为 Lollipop 工作过。

UPDATE - 我已经在 Sony Xperia Z3 Compact 运行 Android 5.0.2 上测试过,DialogFragment 没有选择下方 EditText 小部件时滚动或调整屏幕。

以下是正在使用的样式:

<!-- Base application theme. -->
<style name="AppTheme" parent="AppTheme.Base">
    <!-- Customize your theme here. -->
</style>

<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:textColorPrimary">@color/colorPrimaryText</item>
    <item name="android:textColorSecondary">@color/colorSecondaryText</item>
    <item name="android:dialogTheme">@style/DialogTheme</item>
    <item name="android:actionBarStyle">@style/AppActionBarTheme</item>
    <item name="actionBarStyle">@style/AppActionBarTheme</item>"
    <item name="actionBarTheme">@style/AppActionBarTheme</item>
    <item name="actionBarPopupTheme">@style/AppActionBarOverflowMenuTheme</item>
    <item name="windowActionModeOverlay">true</item>
</style>

<style name="DialogTheme" parent="Theme.AppCompat.Light.Dialog">
    <item name="android:clickable">true</item>
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowSoftInputMode">stateHidden|adjustResize</item>
    <item name="windowActionModeOverlay">true</item>
</style>

正如您在 DialogTheme 中看到的,我已经包含了 android:windowSoftInputMode 属性。我还尝试在最终包含 DialogFragment 的 Activity 下的 AndroidManifest.xml 文件中使用该属性。这是 DialogFragment:

的 XML 布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.ahappydevelopment.android.fishtales.views.LogEditView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="@dimen/dialog_margin_top"
    android:background="@color/background_material_light"
    android:theme="@style/DialogTheme">

    <include
        android:id="@+id/app_bar"
        layout="@layout/app_bar_edit" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/app_bar">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin">

            <Button
                android:id="@+id/btnCatchDate"
                style="?android:attr/borderlessButtonStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_alignParentTop="true"
                android:textAllCaps="false" />

            <Button
                android:id="@+id/btnCatchTime"
                style="?android:attr/borderlessButtonStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"
                android:layout_alignParentRight="true"
                android:layout_alignParentTop="true"
                android:textAllCaps="false" />

            <EditText
                android:id="@+id/editLatitudeLabel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@id/btnCatchDate"
                android:ems="10"
                android:hint="@string/label_latitude"
                android:inputType="numberDecimal" />


            <EditText
                android:id="@+id/editLongitudeLabel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@id/editLatitudeLabel"
                android:ems="10"
                android:hint="@string/label_longitude"
                android:inputType="numberDecimal" />

            <ImageView
                android:id="@+id/imgCatchPicture"
                android:layout_width="75dp"
                android:layout_height="75dp"
                android:layout_alignParentEnd="true"
                android:layout_alignParentRight="true"
                android:layout_alignTop="@id/editLatitudeLabel"
                android:background="@android:color/darker_gray"
                android:contentDescription="@string/imgcatchpicture_contentdescription"
                android:scaleType="centerInside"
                android:src="@android:drawable/ic_menu_camera" />

            <Spinner
                android:id="@+id/spinnerLocation"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@id/editLongitudeLabel" />

            <TextView
                android:id="@+id/txtFishHeader"
                style="?android:attr/listSeparatorTextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@id/spinnerLocation"
                android:text="@string/section_fish" />

            <Spinner
                android:id="@+id/spinnerSpeciesType"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@id/txtFishHeader" />

            <EditText
                android:id="@+id/editLengthLabel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@id/spinnerSpeciesType"
                android:ems="5"
                android:hint="@string/hint_length"
                android:inputType="numberDecimal" />

            <EditText
                android:id="@+id/editWeightLabel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"
                android:layout_alignParentRight="true"
                android:layout_below="@id/spinnerSpeciesType"
                android:ems="5"
                android:hint="@string/hint_weight"
                android:inputType="numberDecimal" />

            <EditText
                android:id="@+id/editLengthLabel2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@id/editLengthLabel"
                android:ems="5"
                android:hint="@string/hint_length"
                android:inputType="numberDecimal" />

            <EditText
                android:id="@+id/editWeightLabel2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"
                android:layout_alignParentRight="true"
                android:layout_below="@id/editWeightLabel"
                android:ems="5"
                android:hint="@string/hint_weight"
                android:inputType="numberDecimal" />

        </RelativeLayout>

    </ScrollView>

</com.ahappydevelopment.android.fishtales.views.LogEditView>

如您所见,我的布局确实包含 ScrollView。我什至在布局中定义了 android:theme="@style/DialogTheme"。我已经尝试在 ScrollView 上设置 android:fillViewport="true",正如我在另一个 Whosebug posting 中看到的那样。

在 Java 方面,我重写了 onCreateDialog 方法,定义如下:

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Dialog dialog = new Dialog(getActivity(), R.style.DialogTheme);
    //set to adjust screen height automatically, when soft keyboard appears on screen
    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    return dialog;
}

启动DialogFragmentFragment有一个看起来像这样的方法:

public void addLogButtonOnClickEvent() {
    LogEditFragment addLog = LogEditFragment.get(0l);
    if(mIsLargeLayout) {
        // The device is using a large layout, so show the fragment as a dialog
        addLog.show(getFragmentManager(), "AddLog");
    } else {
        // The device is smaller, so show the fragment fullscreen
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        // For a little polish, specify a transition animation
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        // To make it fullscreen, use the 'content' root view as the container
        // for the fragment, which is always the root view for the activity
        transaction.add(android.R.id.content, addLog).addToBackStack(null).commit();
    }
}

正如我所说,这对 KitKat 模拟器来说工作正常。这不适用于 Lollipop 模拟器。您可以看到我非常努力地让 DialogFragment 调整调整大小。 :-) 有没有人看到我遗漏或做错了什么?此外,如果有我忽略包含的代码有助于找到解决方案,请告诉我,我也会 post。感谢您提供的任何见解。

我花了一段时间才发现这个答案。我实际上已经将 DialogFragment 重构为 AppCompatActivity。然后我开始想,也许为 Android Lollipop 设置的导航抽屉是我在 DialogFragment 上遇到的问题的罪魁祸首。我的 API 级别 21 及更高级别的 MainActivity 布局文件使用 DrawerLayout 小部件和 ScrimInsetsFrameLayout 导航抽屉视图。因此,DrawerLayoutScrimInsetsFrameLayout 在其定义中都使用了 android:fitsSystemWindows="true" 作为实现导航抽屉的 Material 设计 UI 规范的一部分。 DrawerLayout 定义中的 android:fitsSystemWindows="true" 干扰了 AndroidManifest.xml 文件中为 MainActivity 定义的 android:windowSoftInputMode="stateHidden|adjustResize",导致对话框在启动软键盘时调整和调整大小。这是 MainActivity 的 XML 布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <!-- The Main Content View -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include
            android:id="@+id/app_bar"
            layout="@layout/app_bar_main" />

        <FrameLayout
            android:id="@+id/fragment_main_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/app_bar" />

    </RelativeLayout>

    <!-- The Navigation Drawer View -->
    <com.ahappydevelopment.android.fishtales.widgets.ScrimInsetsFrameLayout
        android:id="@+id/scrimInsetsFrameLayout"
        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:elevation="10dp"
        android:fitsSystemWindows="true"
        app:insetForeground="#4000">

        <FrameLayout
            android:id="@+id/fragment_drawer_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </com.ahappydevelopment.android.fishtales.widgets.ScrimInsetsFrameLayout>

</android.support.v4.widget.DrawerLayout>

当告诉 FragmentManager 添加 DialogFragment 时,我使用 android.R.id.content 作为视图容器,这是文档规定的。通过试验使用哪个视图容器,我能够找到一个给出预期结果的容器。在我的例子中,它是 MainActivity 布局文件中定义的 DrawerLayout 视图。

LogEditFragment addLog = LogEditFragment.get(0l);
if(mIsLargeLayout) {
    // The device is using a large layout, so show the fragment as a dialog
    addLog.show(getFragmentManager(), "AddLog");
} else {
    // The device is smaller, so show the fragment fullscreen
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    // For a little polish, specify a transition animation
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    // To make it fullscreen, use the 'drawer_layout' view 
    // as the container for the fragment
    transaction.add(R.id.drawer_layout, addLog).addToBackStack(null).commit();
}

完成这些更改并重构我的 LogEditFragment class 以再次扩展 DialogFragment 后,对话框和导航抽屉都按预期运行。