Software keyboard resizes background image on Android

AndroidViewAndroid Linearlayout

Android Problem Overview


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>

Android Solutions


Solution 1 - Android

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.

Solution 2 - Android

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.

Solution 3 - Android

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

Solution 4 - Android

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.

Solution 5 - Android

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

Solution 6 - Android

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>

Solution 7 - Android

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>

Solution 8 - Android

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.

Solution 9 - Android

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).

Solution 10 - Android

Add this line in AndroidManifest.xml file:

android:windowSoftInputMode=adjustUnspecified

refer to [this link] 1 for more info.

Solution 11 - Android

Just use in your onCreate() this code:

protected void onCreate(Bundle savedInstanceState) {
...   
 getWindow().setBackgroundDrawableResource(R.drawable.your_image_resource);
...
}

and eliminate this line in your xml:

android:background="@drawable/background"

Read more at:

http://developer.android.com/reference/android/view/Window.html

thanks for : Adrian Cid Almaguer

Solution 12 - Android

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

}

Solution 13 - Android

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

Solution 14 - Android

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.

Solution 15 - Android

Just add in your activity

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

Solution 16 - Android

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

Solution 17 - Android

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndreas WongView Question on Stackoverflow
Solution 1 - AndroidAndreas WongView Answer on Stackoverflow
Solution 2 - AndroidparulbView Answer on Stackoverflow
Solution 3 - AndroidAhsanwarsiView Answer on Stackoverflow
Solution 4 - AndroidJoseph MekwanView Answer on Stackoverflow
Solution 5 - AndroidBolbazarMarmeView Answer on Stackoverflow
Solution 6 - AndroidkonmikView Answer on Stackoverflow
Solution 7 - AndroidHarpreetView Answer on Stackoverflow
Solution 8 - Androiduser5806798View Answer on Stackoverflow
Solution 9 - Androidwelshk91View Answer on Stackoverflow
Solution 10 - AndroidDavidView Answer on Stackoverflow
Solution 11 - AndroidM.A.RView Answer on Stackoverflow
Solution 12 - AndroidAndrewView Answer on Stackoverflow
Solution 13 - AndroidgfunkView Answer on Stackoverflow
Solution 14 - AndroidfkdplcView Answer on Stackoverflow
Solution 15 - Androidariful islam arifView Answer on Stackoverflow
Solution 16 - AndroidRafolsView Answer on Stackoverflow
Solution 17 - AndroidNikul VaghaniView Answer on Stackoverflow