Android全屏模式下如何在软键盘可见时调整布局

197
我已经进行了大量的研究,以调整软键盘激活时的布局,并且已经成功实现了。但是当我在清单文件中的活动标记中使用android:theme="@android:style/Theme.NoTitleBar.Fullscreen"时,问题就出现了。
为此,我使用了不同选项的android:windowSoftInputMode="adjustPan|adjustResize|stateHidden",但没有成功。
之后,我通过编程方式实现了FullScreen,并尝试使用各种布局来与FullScreen配合使用,但都无济于事。
我参考了这些链接,并查看了许多与此问题相关的帖子:

http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html

http://davidwparker.com/2011/08/30/android-how-to-float-a-row-above-keyboard/

这里是XML代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/masterContainerView"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#ffffff">

    <ScrollView android:id="@+id/parentScrollView"
        android:layout_width="fill_parent" android:layout_height="wrap_content">

        <LinearLayout android:layout_width="fill_parent"
            android:layout_height="fill_parent" android:orientation="vertical">

            <TextView android:id="@+id/setup_txt" android:layout_width="wrap_content"
                android:layout_height="wrap_content" android:text="Setup - Step 1 of 3"
                android:textColor="@color/top_header_txt_color" android:textSize="20dp"
                android:padding="8dp" android:gravity="center_horizontal" />

            <TextView android:id="@+id/txt_header" android:layout_width="fill_parent"
                android:layout_height="40dp" android:text="AutoReply:"
                android:textColor="@color/top_header_txt_color" android:textSize="14dp"
                android:textStyle="bold" android:padding="10dp"
                android:layout_below="@+id/setup_txt" />

            <EditText android:id="@+id/edit_message"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:text="Some text here." android:textSize="16dp"
                android:textColor="@color/setting_editmsg_color" android:padding="10dp"
                android:minLines="5" android:maxLines="6" android:layout_below="@+id/txt_header"
                android:gravity="top" android:scrollbars="vertical"
                android:maxLength="132" />

            <ImageView android:id="@+id/image_bottom"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:layout_below="@+id/edit_message" />

        </LinearLayout>
    </ScrollView>

    <RelativeLayout android:id="@+id/scoringContainerView"
        android:layout_width="fill_parent" android:layout_height="50px"
        android:orientation="vertical" android:layout_alignParentBottom="true"
        android:background="#535254">

        <Button android:id="@+id/btn_save" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_alignParentRight="true"
            android:layout_marginTop="7dp" android:layout_marginRight="15dp"
            android:layout_below="@+id/edit_message"
            android:text = "Save" />

        <Button android:id="@+id/btn_cancel" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_marginTop="7dp"
            android:layout_marginRight="10dp" android:layout_below="@+id/edit_message"
            android:layout_toLeftOf="@+id/btn_save" android:text = "Cancel" />

    </RelativeLayout>
</RelativeLayout>

enter image description here

当软键盘出现时,我希望底部的两个按钮向上移动。

enter image description here


1
我认为你必须在ScrollView里面和EditText下面添加按钮。 - Balaji Khadake
我已经尝试了许多不起作用的选项... - Vineet Shukla
1
将您的按钮放在FrameLayout中,并将FrameLayout的权重设置为1,最后仅使用android:windowSoftInputMode="adjustPan"。请告诉我这是否有效。 - Sherif elKhatib
@VineetShukla,你找到全屏的工作了吗? - Muhammad Babar
4
请注意,根据android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE的文档,“你不能同时使用adjustResizeadjustPan”。这两个选项是互斥的。 - Denys Kniazhev-Support Ukraine
29个回答

0

我尝试了许多解决方案,包括Joseph Johnson和Johan Stuyts的。但是在某些设备(如联想s820)中,在所有情况下,我得到了内容和键盘之间的空白。

因此,我对他们的代码进行了一些更改,最终得到了可行的解决方案。

我的想法是在键盘显示时向内容顶部添加边距。

contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
    int usableHeightNow = contentAreaOfWindowBounds.height();

    if (usableHeightNow != usableHeightPrevious) {

        int difference = usableHeightNow - usableHeightPrevious;

        if (difference < 0 && difference < -150) {
            keyboardShowed = true;
            rootViewLayout.topMargin -= difference + 30;
            rootViewLayout.bottomMargin += 30;
        }
        else if (difference < 0 && difference > -150){
            rootViewLayout.topMargin -= difference + 30;
        }
        else if (difference > 0 && difference > 150) {
            keyboardShowed = false;
            rootViewLayout.topMargin = 0;
            rootViewLayout.bottomMargin = 0;
        }

        rootView.requestLayout();

        Log.e("Bug Workaround", "Difference: " + difference);

        usableHeightPrevious = usableHeightNow;
}

正如您所看到的,我在差异值上添加了30像素,因为屏幕顶部和带有边距的内容区之间有一个小的白色空间。我不知道它从哪里出现,所以我决定将边距缩小,现在它完全符合我的需求。


0
如果您想真正支持软输入的全屏:
private fun View.setStatusBarTransparent() {
    this@MainActivity.apply {
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
        this@setStatusBarTransparent.fitsSystemWindows = true
        WindowCompat.setDecorFitsSystemWindows(window, false)
        ViewCompat.setOnApplyWindowInsetsListener(this@setStatusBarTransparent) { root, windowInset ->
            val inset = windowInset.getInsets(WindowInsetsCompat.Type.systemBars())
            val inset2 = windowInset.getInsets(WindowInsetsCompat.Type.ime())
            root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
                leftMargin = inset.left
                bottomMargin = maxOf(inset.bottom, inset2.bottom)
                rightMargin = inset.right
            }
            WindowInsetsCompat.CONSUMED
        }
    }
}

如何使用?... - undefined

0

还有一种方法,无需创建自己的帮助程序类或函数来计算屏幕高度。而是使用ViewCompat.setOnApplyWindowInsetsListener。 通过侦听器,您可以检查键盘是否打开,并根据键盘高度设置底部填充。

// the root view of your webview, e.g FrameLayout or LinearLayout
    rootView = view.findViewById(R.id.whatever);
    
    ViewCompat.setOnApplyWindowInsetsListener(rootView, (webView, insets) -> {
        
        // checks if keyboard is visible, the Type.ime() stands for Input Method
        boolean isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
        
        // get the keyboard height and use the height as bottom padding for your view
        int bottomKeyboardPadding = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;

        
        if (isKeyboardVisible) { webView.setPadding(0, 0, 0, bottomKeyboardPadding); }
        else { webView.setPadding(0, 0, 0, 0); }

        return insets;
    });

0
你想让底部栏固定在视图的底部,但当键盘显示时,它们应该向上移动以放置在键盘上方,对吗?
你可以尝试这段代码片段:
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    ...>

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

    <LinearLayout
        android:id="@+id/LinearLayoutBottomBar"
        android:layout_alignParentBottom = true
        ...>
    </LinearLayout>

    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="390dp"
    android:orientation="vertical" 
    android:layout_above="@+id/LinearLayoutBottomBar"
    android:layout_below="@+id/RelativeLayoutTopBar"> 

    <ScrollView 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:id="@+id/ScrollViewBackground">

            ...

        </ScrollView>
     </LinearLayout>
  </RelativeLayout>

BottomBar 将会固定在视图的底部,包含 ScrollView 的 LinearLayout 将会占据除了顶部/底部栏和键盘显示后剩余的视图空间。如果这对您也有效,请告诉我。


1
非常奇怪,因为它在我的应用程序中已经运行了好几次。顺便说一下,RelativeLayout没有方向,所以你可以删除代码中的这些属性。我刚才意识到我可以将代码片段缩减到一行:android:layout_below="@+id/scoringContainerView",你需要将其添加到你的ScrollView中。 - banzai86
全屏?你是指顶部没有布局吗? - banzai86
不要有状态栏,也就是不要显示设备的电池寿命、连接等信息。 - Vineet Shukla
不,状态栏在我的应用程序中是可见的。你可以尝试改变布局的顺序,也就是将包含按钮的布局代码放在其他代码之上,然后再试一次吗?也许你需要先定义它们才能使用layout_below属性。 - banzai86
1
请仔细阅读问题......我已经提到我在全屏模式下遇到了麻烦...... - Vineet Shukla

0

我已经尝试了来自 stackOverflow 的所有可能答案,最终在长达一周的搜索后解决了问题。 我使用了 coordinate layout,并将其更改为 linearLayout,我的问题得到了解决。我不知道 coordinate layout 可能存在 Bug 或其他问题是我的错误。


0

我之前只是使用全屏模式来隐藏状态栏。然而,当键盘弹出时,我希望应用程序能够自适应大小。所有其他解决方案(可能是由于帖子的年龄)都很复杂或不适用于我的情况(想要避免为了 PhoneGap Build 而更改 Java 代码)。

因此,我修改了 Android 的配置,使其成为非全屏:

            <preference name="fullscreen" value="false" />

并通过命令行添加了cordova-plugin-statusbar

cordova plugin add cordova-plugin-statusbar

当应用程序加载完成后,我只需调用插件上的一个方法来隐藏它自己,例如:
    if (window.cordova && window.cordova.platformId == 'android' && window.StatusBar)
        window.StatusBar.hide();

这个东西非常好用。唯一的缺点是应用程序加载时状态栏会短暂可见。对于我的需求来说,这不是问题。


0

今天,全屏问题上的adjustResize在安卓SDK上无法工作。

我查到的答案如下:
解决方案 - 但是该解决方案存在以下图片问题:

然后我找到了解决方案并删除了一个不必要的操作:

this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

所以,看看我在 Kotlin 上的修复解决方案代码:

class AndroidBug5497Workaround constructor(val activity: Activity) {

    private val content = activity.findViewById<View>(android.R.id.content) as FrameLayout

    private val mChildOfContent = content.getChildAt(0)
    private var usableHeightPrevious: Int = 0
    private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
    private val rootView = contentContainer.getChildAt(0)
    private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams

    private val listener = {
        possiblyResizeChildOfContent()
    }

    fun addListener() {
        mChildOfContent.apply {
            viewTreeObserver.addOnGlobalLayoutListener(listener)

        }
    }

    fun removeListener() {
        mChildOfContent.apply {
            viewTreeObserver.removeOnGlobalLayoutListener(listener)
        }
    }

    private fun possiblyResizeChildOfContent() {
        val contentAreaOfWindowBounds = Rect()
        mChildOfContent.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
        val usableHeightNow = contentAreaOfWindowBounds.height()

        if (usableHeightNow != usableHeightPrevious) {
            rootViewLayout.height = usableHeightNow
            rootView.layout(contentAreaOfWindowBounds.left,
                    contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
            mChildOfContent.requestLayout()
            usableHeightPrevious = usableHeightNow
        }
    }
}

我的错误修复实现代码:

 class LeaveDetailActivity : BaseActivity(){

    private val keyBoardBugWorkaround by lazy {
        AndroidBug5497Workaround(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }

    override fun onResume() {
        keyBoardBugWorkaround.addListener()
        super.onResume()
    }

    override fun onPause() {
        keyBoardBugWorkaround.removeListener()
        super.onPause()
    }
}

-1

不要使用:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

因为工作不好。

所以,使用以下内容:

fun setFullScreen(fullScreen: Boolean) {
        val decorView = getWindow().getDecorView()
        val uiOptions : Int
        if(fullScreen){
            uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN // this hide statusBar
            toolbar.visibility = View.GONE // if you use toolbar
            tabs.visibility = View.GONE // if you use tabLayout
        } else {
            uiOptions = View.SYSTEM_UI_FLAG_VISIBLE // this show statusBar
            toolbar.visibility = View.VISIBLE
            tabs.visibility = View.VISIBLE
        }
        decorView.setSystemUiVisibility(uiOptions)
    }

-2
在我的情况下,这个问题是在我将Crosswalk添加到我的Cordova应用程序后开始出现的。我的应用程序没有在全屏模式下使用,并且android:windowSoftInputMode="adjustPan"。
我已经在应用程序中安装了ionic键盘插件,因此通过它检测键盘是否弹起或关闭非常容易:
// Listen for events to when the keyboard is opened and closed
window.addEventListener("native.keyboardshow", keyboardUp, false);
window.addEventListener('native.keyboardhide', keyboardDown, false);

function keyboardUp()
{
    $('html').addClass('keyboardUp');
}

function keyboardDown()
{
    $('html').removeClass('keyboardUp');
}

我尝试了上面所有的修复方法,但最终成功的只需要这一行CSS:

&.keyboardUp {
        overflow-y: scroll;
}

希望这篇文章能为你节省我花费的几天时间。 :)

我同时使用Cordova和Crosswalk,并且设置了android:windowSoftInputMode="adjustPan"。但是它没有起作用。我看到该类被添加到HTML元素中,但CSS对屏幕没有影响。你有其他的设置可以让屏幕移动吗? - darewreck
我必须设置add transform:translateY(0px)才能使其工作。然而,滚动根本不起作用。有什么想法吗? - darewreck

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