Software keyboard resizes background image on Android
Asked Answered
E

16

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>
Expunge answered 26/11, 2010 at 17:38 Comment(0)
E
199

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.

Expunge answered 27/11, 2010 at 7:4 Comment(10)
The problem is that scrolling is, when using adjustPan, not working... (if you have a ScrollView), and thats annoying...Converse
But the scrollview is not working now. is thr any way to avoid itAdobe
I need scroll view. Do you have a way to keep the scroll view and keep the background un compressed.Vada
That's not enough. At least, if you don't use a ScrollView. Much better is a solution here https://mcmap.net/q/159445/-software-keyboard-resizes-background-image-on-android or https://mcmap.net/q/161574/-how-to-keep-background-image-size-when-software-keyboard-show.Welt
stateVisisble made my keyboard automatically pop up, so I just used adjustPan and still worked for me.Eastlake
if you don't want to display keyboard automatically then use : android:windowSoftInputMode="adjustPan"Outhe
This solution worked kinda but caused other issues...using the solution at https://mcmap.net/q/159445/-software-keyboard-resizes-background-image-on-android which worked better for me.Convincing
I used both stateVisible|adjustPan but the background still moves after the keyboard appears/hides. My main layout is a scrollview thoughClog
Needs to be in activity tag, won't work if it's in application tag.Whitneywhitson
Also stateVisible causes the keyboard to show onCreate. I removed that tag and just used adjustPanWhitneywhitson
N
114

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.

Natelson answered 4/6, 2013 at 11:55 Comment(7)
Very good! Best answer! adjustPan causes collateral effects like: The keyboard overrides components but no scrollbar are displayed.Lanham
@Natelson 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?Fluorometer
What if i am having a fragment how to deal with it ? its not working for fragmentsAmeliorate
To user2056563: For fragments just use getActivity().getWindow().setBackgroundDrawable() or getActivity().getWindow().setBackgroundDrawableResource()Rodi
Thank you, it worked even with scrollview. I did like this on new AppCompat: getWindow().setBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.background));Dumbarton
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 appsHunchbacked
What to do if I want to use scale type in this? Because for notch screen image is stretching.Stealthy
P
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" >
Prismatoid answered 20/4, 2015 at 14:36 Comment(3)
nice! simplest solutionPaleobiology
Excellent solution,esp because it doesn't use the windowSoftInputMode which only brings more issues!Popsicle
you are a genius !Fortaleza
B
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.

Battement answered 9/7, 2015 at 10:7 Comment(2)
Thanks for the recognition.Fiesta
What to do if I want to use scale type in this? Because for notch screen image is stretching.Stealthy
P
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...

Payoff answered 2/5, 2011 at 3:46 Comment(3)
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.Fiesta
@Fiesta : so what's the solution ?Acrobatic
@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: #5307764Fiesta
M
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>
Malka answered 12/11, 2014 at 16:16 Comment(1)
This is a good solution in case u have other Views in the background other than an image....ThanksIliad
U
6

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

This answer is combination of code from:

https://mcmap.net/q/159445/-software-keyboard-resizes-background-image-on-android

https://mcmap.net/q/161220/-imageview-scaling-top_crop

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>
Unclassical answered 20/3, 2019 at 6:59 Comment(1)
Thanks for the easy answer :) you save my timeTackett
P
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.

Puzzler answered 18/1, 2016 at 20:4 Comment(0)
C
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).

Charteris answered 28/9, 2016 at 17:17 Comment(0)
A
2

Add this line in AndroidManifest.xml file:

android:windowSoftInputMode=adjustUnspecified

refer to this link for more info.

Ambulant answered 26/11, 2010 at 17:59 Comment(1)
It's still happening even after I did this. This is getting frustrating :(Expunge
S
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);
    }

}
Synesthesia answered 15/2, 2019 at 10:58 Comment(1)
It don't stretch anymore with above code but it still scrolls a little up/down w.r.t. soft keyboard.Unclassical
D
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);
Denishadenison answered 26/2, 2020 at 10:50 Comment(0)
O
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.

Octennial answered 10/8, 2017 at 17:39 Comment(0)
R
0

Just add in your activity

getWindow().setBackgroundDrawable(R.drawable.your_image_name);
Retene answered 26/4, 2018 at 19:18 Comment(0)
H
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)
}
Halm answered 27/3, 2019 at 6:31 Comment(0)
F
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.

Fennell answered 12/4, 2019 at 5:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.