安卓系统中的软键盘会改变背景图片的大小

141

每当软键盘出现时,它都会调整背景图像的大小。请参考下面的截图:

正如您所看到的,背景被挤压了。有人能解释一下为什么背景会重新调整大小吗?

我的布局如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/page_bg"
    android:isScrollContainer="false"
>
    <LinearLayout android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
    >
        <EditText android:id="@+id/CatName"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:inputType="textCapSentences"
            android:lines="1"
        />
        <Button android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/save"
            android:onClick="saveCat"
        />
    </LinearLayout>
    <ImageButton
        android:id="@+id/add_totalk"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:background="@null"
        android:src="@drawable/add_small"
        android:scaleType="center"
        android:onClick="createToTalk"
        android:layout_marginTop="5dp"
    />
</LinearLayout>
16个回答

199

好的,我通过使用

解决了这个问题。

android:windowSoftInputMode="stateVisible|adjustPan"

manifest文件中<Activity>标签内的条目。 我认为这是由于在活动内有ScrollView引起的。


3
问题在于,当使用 adjustPan 时,滚动不起作用(如果您有一个 ScrollView),这很烦人。 - Ted
4
但是ScrollView现在无法工作,有没有办法避免这种情况? - Mr.G
我需要滚动视图。你有什么方法可以保持滚动视图并保持背景不被压缩? - Rana Ranvijay Singh
4
这还不够。至少,如果您不使用ScrollView,情况会更好。在这里有更好的解决方案:https://dev59.com/sW855IYBdhLWcg3wiU31#29750860 或 https://dev59.com/zm025IYBdhLWcg3wf2OE#18191266。 - CoolMind
1
如果您不想自动显示键盘,则可以使用:android:windowSoftInputMode="adjustPan"。 - satvinder singh
显示剩余5条评论

114

在开发聊天应用时,我遇到了同样的问题,即聊天界面带有背景图片。android:windowSoftInputMode="adjustResize"会将我的背景图片挤压适配可用空间,而"adjustPan"会将整个布局向上移动以适应软键盘。解决这个问题的方法是在活动XML中设置窗口背景而不是布局背景。在您的活动中使用getWindow().setBackgroundDrawable()


4
非常好!最佳答案!但是,使用 adjustPan 会产生一些副作用,例如:键盘覆盖组件,但没有滚动条显示。 - CelinHC
4
@parulb或其他读者如果看到这个消息,请注意,只要背景图片不包含相关信息,这个方法运行得很好(感谢你)。我偶尔会遇到一个问题,那就是我的背景会包含像商标这样的东西,并且将窗口背景设置为该图像会将商标隐藏在操作栏后面。对于某些屏幕,这不是问题,但有时我需要保留操作栏而不进行隐藏操作。您有什么想法如何处理一些内边距以考虑ActionBar而同时设置窗口背景? - zgc7009
1
如果我有一个碎片,该怎么处理?它对于碎片不起作用。 - user2056563
给user2056563:对于片段,只需使用getActivity().getWindow().setBackgroundDrawable()或getActivity().getWindow().setBackgroundDrawableResource()。 - Andrei Aulaska
谢谢,即使使用ScrollView,它也可以正常工作。在新的AppCompat上我是这样做的:getWindow().setBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.background)); - Lucas Eduardo
显示剩余2条评论

39

这是避免此类问题的最佳解决方案。

步骤1:创建样式

<style name="ChatBackground" parent="AppBaseTheme">
    <item name="android:windowBackground">@drawable/bg_chat</item>
</style>

步骤2:AndroidManifest文件中设置您的活动样式

<activity
    android:name=".Chat"
    android:screenOrientation="portrait"
    android:theme="@style/ChatBackground" >

1
不错!最简单的解决方案 - Matias Elorriaga
1
非常好的解决方案,尤其是它不使用windowSoftInputMode,这只会带来更多问题! - bensadiku
1
你是个天才! - hvar90

32

通过 android:windowSoftInputMode="adjustPan" 会给用户带来糟糕的体验,因为整个屏幕都被顶到了最上面。因此,以下是其中一个最好的解决方法。

我也遇到了同样的问题,但在之后,我从 @Gem 那里找到了一个很棒的解决方案。

在清单文件中

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

在XML中

不要在此处设置任何背景,并将视图放置在ScrollView下方。

在Java中

您需要将背景设置为窗口:

    getWindow().setBackgroundDrawableResource(R.drawable.bg_wood) ;

感谢 @Gem 的帮助。


1
感谢您的认可。 - Gem
如果我想在这里使用比例类型(scale type),该怎么办?因为对于刘海屏幕,图片会被拉伸。 - Nil

14

仅供补充...

如果您的活动中有一个列表视图, 则需要在您的列表视图属性中添加此android:isScrollContainer="false"...

而且不要忘记在您的活动的清单xml中添加android:windowSoftInputMode="adjustPan"...

如果您在布局中使用滚动视图并使用android:windowSoftInputMode="adjustUnspecified",则软键盘仍会调整您的背景大小...

最好使用"adjustPan"值以防止您的背景被调整大小...


2
android:windowSoftInputMode="adjustPan" 使整个视图随键盘移动。通常屏幕上方有一个标题。使用此标志,标题也会移到可见区域外,这会导致不良用户体验。 - Gem
@Gem:那么解决方案是什么? - user1079425
@Copernic 我曾经在开发聊天应用时遇到过这个问题,最终我解决了它。请参考我的答案:https://dev59.com/4W435IYBdhLWcg3woRjG#23651818 - Gem

7

如果有人需要 adjustResize 行为,但不想让他的 ImageView 被调整大小,这里有另一个变通方法。

只需将 ImageView 放在 ScrollView => RelativeLayout 中,并设置 ScrollView.fillViewport = true

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <FrameLayout
            android:layout_width="100dp"
            android:layout_height="100dp">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="fitXY"
                android:src="@drawable/gift_checked" />

        </FrameLayout>
    </RelativeLayout>
</ScrollView>

1
这是一个很好的解决方案,如果您的背景中除了图像之外还有其他视图...谢谢。 - Dominic D'Souza

6

在学习和实施所有可用答案后,我在这里添加了一个解决方案。

这个答案是从以下代码组合而来:

https://dev59.com/sW855IYBdhLWcg3wiU31#45620231

https://dev59.com/q2w15IYBdhLWcg3w4_7k#27702210

这是自定义的AppCompatImageView类,相对于软键盘没有拉伸或滚动:-
public class TopCropImageView extends AppCompatImageView {

    public TopCropImageView(Context context) {
        super(context);
        setScaleType(ImageView.ScaleType.MATRIX);
    }

    public TopCropImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setScaleType(ImageView.ScaleType.MATRIX);
    }

    public TopCropImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setScaleType(ImageView.ScaleType.MATRIX);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        computeMatrix();
    }

    @Override
    protected boolean setFrame(int l, int t, int r, int b) {
        computeMatrix();
        return super.setFrame(l, t, r, b);
    }


    private void computeMatrix() {
        if (getDrawable() == null) return;
        Matrix matrix = getImageMatrix();
        float scaleFactor = getWidth() / (float) getDrawable().getIntrinsicWidth();
        matrix.setScale(scaleFactor, scaleFactor, 0, 0);
        setImageMatrix(matrix);
    }
}

为了将其用作我的Fragment类的背景,我将其设置为FrameLayout的第一个元素。
<FrameLayout 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:layout_width="match_parent"
    android:layout_height="match_parent">

    <complete.package.TopCropImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/my_app_background" />

    <!-- Enter other UI elements here to overlay them on the background image -->

<FrameLayout>

谢谢你提供简单的答案 :) 你节省了我的时间。 - Priyanka

5
我在开发我的应用时遇到了一个主要问题。起初,我使用@parulb提供的方法来解决这个问题,非常感谢他。但后来我注意到背景图片被操作栏(和状态栏)部分遮挡了。这个小问题已经由@zgc7009提出,在一年半前评论了@parulb的答案,但没有人回复。
我花了整整一天的时间去寻找方法,幸运的是,我现在至少可以在我的手机上完美地解决这个问题。
首先,我们需要在drawable文件夹中添加一个图层列表资源,以在顶部添加背景图片的填充:
<!-- my_background.xml -->
<?xml version="1.0" encoding="utf-8"?>

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:top="75dp">
        <bitmap android:src="@drawable/bg" />
    </item>
</layer-list>

其次,我们按照上述方法将此文件设置为背景资源:
getWindow().setBackgroundDrawableResource(R.drawable.my_background);

我正在使用 Nexus 5。我找到了一种在xml中获取actionbar高度的方法,但无法获取状态栏高度,因此我必须使用固定高度75dp作为顶部填充。希望有人能找到这个谜题的最后一块拼图。


3

我遇到了类似的问题,但是似乎使用adjustPanandroid:isScrollContainer="false"仍然无法解决我的布局问题(我的布局是在一个LinearLayout下面的RecyclerView)。RecyclerView没有问题,但每次虚拟键盘出现时,LinearLayout都会重新调整。

为了防止这种行为(我只是想让键盘覆盖我的布局),我使用了以下代码:

    <activity
        android:name=".librarycartridge.MyLibraryActivity"
        android:windowSoftInputMode="adjustNothing" />

这句话的意思是当虚拟键盘被调用时,Android会基本上不改变你的布局。可以在这里找到更多可能的选项(尽管奇怪的是,似乎没有adjustNothing的条目)。

2
请在 AndroidManifest.xml 文件中添加以下行:
android:windowSoftInputMode=adjustUnspecified

请参考此链接获取更多信息。


即使我已经这样做了,它仍在发生。这真让人沮丧 :( - Andreas Wong

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