141

Whenever the software keyboard appears, it resizes the background image. Refer to the screenshot below:

As you can see, the background is sort of squeezed. Anyone can shed a light on why the background resizes?

My Layout is as follows:

<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>
Palec
  • 12,743
  • 8
  • 69
  • 138
Andreas Wong
  • 59,630
  • 19
  • 106
  • 123

16 Answers16

198

Ok I fixed it by using

android:windowSoftInputMode="stateVisible|adjustPan"

entry inside <Activity > tag in manifest file. I think it was caused by having ScrollView inside the Activity.

Basheer AL-MOMANI
  • 14,473
  • 9
  • 96
  • 92
Andreas Wong
  • 59,630
  • 19
  • 106
  • 123
  • 3
    The problem is that scrolling is, when using adjustPan, not working... (if you have a ScrollView), and thats annoying... – Ted Jan 13 '13 at 13:36
  • 4
    But the scrollview is not working now. is thr any way to avoid it – Mr.G May 13 '13 at 10:48
  • I need scroll view. Do you have a way to keep the scroll view and keep the background un compressed. – Rana Ranvijay Singh Oct 03 '14 at 13:02
  • 4
    That's not enough. At least, if you don't use a ScrollView. Much better is a solution here http://stackoverflow.com/a/29750860/2914140 or http://stackoverflow.com/a/18191266/2914140. – CoolMind Aug 14 '15 at 08:36
  • `stateVisisble` made my keyboard automatically pop up, so I just used `adjustPan` and still worked for me. – Adam Johns Oct 06 '15 at 03:30
  • 1
    if you don't want to display keyboard automatically then use : android:windowSoftInputMode="adjustPan" – satvinder singh Oct 26 '16 at 12:03
  • This solution worked kinda but caused other issues...using the solution at http://stackoverflow.com/a/16917160/1815624 which worked better for me. – CrandellWS Nov 07 '16 at 03:13
  • I used both stateVisible|adjustPan but the background still moves after the keyboard appears/hides. My main layout is a scrollview though – behelit May 11 '18 at 04:36
  • Needs to be in activity tag, won't work if it's in application tag. – DIRTY DAVE May 23 '21 at 05:55
  • Also ```stateVisible``` causes the keyboard to show onCreate. I removed that tag and just used ```adjustPan``` – DIRTY DAVE May 23 '21 at 06:24
113

I faced the same problem while developing a chat app, chat screen with a background image. android:windowSoftInputMode="adjustResize" squeezed my background image to fit the available space after the soft keyboard was displayed and "adjustPan" shifted the whole layout up to adjust the soft keyboard. The solution to this problem was setting the window background instead of a layout background inside an activity XML. Use getWindow().setBackgroundDrawable() in your activity.

Deepzz
  • 4,573
  • 1
  • 28
  • 52
parulb
  • 1,131
  • 1
  • 7
  • 2
  • 4
    Very good! Best answer! adjustPan causes collateral effects like: The keyboard overrides components but no scrollbar are displayed. – CelinHC Jan 02 '14 at 19:31
  • 4
    @parulb or anyone else that happens to read this, this works great (and thank you for it) as long as the background image doesn't contain relevant information. The problem I encounter from time to time is that my background will contain something like a logo, and setting the window background to the image hides the logo behind the ActionBar. For certain screens this isn't an issue, but sometimes I am required to keep the ActionBar (no hiding). Any ideas on how to handle some sort of padding while setting the window background to take the ActionBar into account? – zgc7009 Jul 22 '14 at 19:58
  • 1
    What if i am having a fragment how to deal with it ? its not working for fragments – user2056563 Apr 16 '15 at 10:28
  • To user2056563: For fragments just use getActivity().getWindow().setBackgroundDrawable() or getActivity().getWindow().setBackgroundDrawableResource() – Andrei Aulaska Sep 18 '15 at 07:56
  • Thank you, it worked even with scrollview. I did like this on new AppCompat: getWindow().setBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.background)); – Lucas Eduardo Sep 25 '15 at 01:50
  • This shoud be the right answeir! Adjust pan narrows your abilities to code (you can not use ither options - like adjust resize, ets). We showd only set background to activities window, in all chat apps – Anton Kizema Jan 16 '16 at 10:55
  • What to do if I want to use scale type in this? Because for notch screen image is stretching. – Nil Jan 10 '19 at 05:43
39

Here is the best solution to avoid such kind of problem.

Step 1: Create a style

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

Step 2: Set your activity style in the AndroidManifest file

<activity
    android:name=".Chat"
    android:screenOrientation="portrait"
    android:theme="@style/ChatBackground" >
MHSaffari
  • 858
  • 1
  • 16
  • 39
Ahsanwarsi
  • 1,013
  • 9
  • 18
32

Through android:windowSoftInputMode="adjustPan" giving bad user experience because through that entire screen goes on top (shift to top ) So, following is one of the best answeres.

I have same Problem but after that , i Found Awesome answeres from the @Gem

In Manifest

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

In xml

Dont Set any background here and keep your view under ScrollView

In Java

You need to set the background to window:

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

Thanks to @Gem.

kingston
  • 11,053
  • 14
  • 62
  • 116
Joseph Mekwan
  • 1,082
  • 1
  • 15
  • 28
14

just for addition...

if u have a listview on your activity u need to add this android:isScrollContainer="false" in your listview properties...

and don't forget to add android:windowSoftInputMode="adjustPan" in your manifest xml at your activity...

if you guys using android:windowSoftInputMode="adjustUnspecified" with scrollable view on your layout, then your background will still resized by the soft keyboard...

it would be better if you use "adjustPan" value to prevent your background from resizing...

BolbazarMarme
  • 1,221
  • 2
  • 13
  • 25
  • 2
    android:windowSoftInputMode="adjustPan" make the whole view shift with keyboard. Generally we have a title of the screen on the top. Using this flag it will also go out of the visible area, which make bad user experience. – Gem May 14 '14 at 10:03
  • @Gem : so what's the solution ? –  Aug 13 '15 at 19:15
  • @Copernic I faced this problem long time back when i was working on a chat application. Eventually i fixed that please refer my answer here: http://stackoverflow.com/questions/5307264/how-to-prevent-soft-keyboard-from-resizing-background-image/23651818#23651818 – Gem Aug 14 '15 at 07:14
7

In case if somebody need an adjustResize behavior and don't want his ImageView to be resized here is another workaround.

Just put ImageView inside ScrollView => RelativeLayout with 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>
MHSaffari
  • 858
  • 1
  • 16
  • 39
konmik
  • 3,150
  • 19
  • 15
6

After studying and implementing all available answers, here I am adding a solution.

This answer is combination of code from:

https://stackoverflow.com/a/45620231/1164529

https://stackoverflow.com/a/27702210/1164529

Here is the custom AppCompatImageView class which showed no stretching or scrolling w.r.t. soft keyboard:-

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);
    }
}

To use it as background to my Fragment class, I set it as first element to 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>
Harpreet
  • 2,990
  • 3
  • 38
  • 52
5

I encountered the main problem when working on my app. At first, I use the method provided by @parulb to solve the problem. Thank him a lot. But later I noticed that the background image is partially hided by actionbar (and statusbar I am sure). This small issue is already proposed by @zgc7009 who commented below the answer of @parulb one year and a half ago but no one replied.

I worked a whole day to find out a way and fortunately I can at least solve this problem perfectly on my cellphone now.

First we need a layer-list resource in drawable folder to add padding on the top to the background image:

<!-- 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>

Second we set this file as resource for background as mentioned above:

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

I am using Nexus 5. I found a way to get height of actionbar in xml but not the statusbar, so I have to use a fixed height 75dp for top padding. Hope anyone can find the last piece of this puzzle.

3

I suffered similar issues, but it seems like using adjustPan with android:isScrollContainer="false" still didn't fix my layout (which was a RecyclerView below a LinearLayout). The RecyclerView was fine, but every time the virtual keyboard showed up, the LinearLayout re-adjusted.

To prevent this behavior (I simply wanted to have the keyboard go over my layout), I went with the following code:

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

This tells Android to basically leave your layout alone when the virtual keyboard is called.

More reading about possible options can be found here (though oddly enough, it doesn't seem like there's an entry for adjustNothing).

welshk91
  • 1,613
  • 18
  • 16
2

Add this line in AndroidManifest.xml file:

android:windowSoftInputMode=adjustUnspecified

refer to this link for more info.

SMR
  • 6,628
  • 2
  • 35
  • 56
David
  • 2,103
  • 3
  • 21
  • 29
2

I faced with this problem, when my background image was just a ImageView inside a Fragment, and it was resized by the keyboard.

My solution was: using custom ImageView from this SO Answer, edited to be compatible with androidx.

import android.content.Context;
import android.graphics.Matrix;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatImageView;

/**
 * Created by chris on 7/27/16.
 */
public class TopCropImageView extends AppCompatImageView {

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

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

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

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

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

    private void recomputeImgMatrix() {
        if (getDrawable() == null) return;

        final Matrix matrix = getImageMatrix();

        float scale;
        final int viewWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        final int viewHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        final int drawableWidth = getDrawable().getIntrinsicWidth();
        final int drawableHeight = getDrawable().getIntrinsicHeight();

        if (drawableWidth * viewHeight > drawableHeight * viewWidth) {
            scale = (float) viewHeight / (float) drawableHeight;
        } else {
            scale = (float) viewWidth / (float) drawableWidth;
        }

        matrix.setScale(scale, scale);
        setImageMatrix(matrix);
    }

}
0x384c0
  • 2,256
  • 1
  • 17
  • 14
  • 1
    It don't stretch anymore with above code but it still scrolls a little up/down w.r.t. soft keyboard. – Harpreet Mar 20 '19 at 06:09
1

My solution is to substitute the background of the window with the one of the layout, then set the layout background to null. In this way I keep the image in the XML preview window:

So keep the background in the layout and add an id for it. Then in the activity onCreate() put this code:

    ConstraintLayout mainLayout = (ConstraintLayout)findViewById(R.id.mainLayout);
    getWindow().setBackgroundDrawable(mainLayout.getBackground());
    mainLayout.setBackground(null);
gfunk
  • 11
  • 2
0

I faced this same problem before but no solution worked for me so long because i was using a Fragment, and also getActivity().getWindow().setBackgroundDrawable() was not a solution for me.

Solution which worked for me is to override FrameLayout with a logic to handle keyboard which should appear and change the bitmap on the go.

Here is my FrameLayout code (Kotlin):

class FlexibleFrameLayout : FrameLayout {

    var backgroundImage: Drawable? = null
        set(bitmap) {
            field = bitmap
            invalidate()
        }
    private var keyboardHeight: Int = 0
    private var isKbOpen = false

    private var actualHeight = 0

    constructor(context: Context) : super(context) {
        init()
    }

    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
        init()
    }

    fun init() {
        setWillNotDraw(false)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        if (actualHeight == 0) {
            actualHeight = height
            return
        }
        //kb detected
        if (actualHeight - height > 100 && keyboardHeight == 0) {
            keyboardHeight = actualHeight - height
            isKbOpen = true
        }
        if (actualHeight - height < 50 && keyboardHeight != 0) {
            isKbOpen = false
        }
        if (height != actualHeight) {
            invalidate()
        }
    }

    override fun onDraw(canvas: Canvas) {
        if (backgroundImage != null) {
            if (backgroundImage is ColorDrawable) {
                backgroundImage!!.setBounds(0, 0, measuredWidth, measuredHeight)
                backgroundImage!!.draw(canvas)
            } else if (backgroundImage is BitmapDrawable) {
                val scale = measuredWidth.toFloat() / backgroundImage!!.intrinsicWidth.toFloat()
                val width = Math.ceil((backgroundImage!!.intrinsicWidth * scale).toDouble()).toInt()
                val height = Math.ceil((backgroundImage!!.intrinsicHeight * scale).toDouble()).toInt()
                val kb = if (isKbOpen) keyboardHeight else 0
                backgroundImage!!.setBounds(0, 0, width, height)
                backgroundImage!!.draw(canvas)
            }
        } else {
            super.onDraw(canvas)
        }
    }
}

And I used it as like an usual FrameLayout's background.

frameLayout.backgroundImage = Drawable.createFromPath(path)

Hope it helps.

Harpreet
  • 2,990
  • 3
  • 38
  • 52
fkdplc
  • 31
  • 1
  • 7
0

Just add in your activity

getWindow().setBackgroundDrawable(R.drawable.your_image_name);
0

You can wrap your LinearLayout with FrameLayout and add ImageView with Background:

<FrameLayout>

  <ImageView 
    android:background="@drawable/page_bg"
    android:id="@+id/backgroundImage" />

  <LinearLayout>
    ....
  </LinearLayout>
</FrameLayout>

And You can set it height when you creating activity/fragment (to prevent from scaling when open keyboard). Some code in Kotlin from Fragment:

activity?.window?.decorView?.height?.let {
        backgroundImage.setHeight(it)
}
Rafols
  • 1,299
  • 14
  • 12
0

if you are set image as windowbackground and ui going to stuck. then there may be possibility you are uses drawable which is in single drawable folder, if yes then you have to paste it in drawable-nodpi or drawable-xxxhdpi.

Nikul Vaghani
  • 21
  • 1
  • 1