Software keyboard resizes background image on Android

你。 提交于 2019-11-26 15:00:40
Andreas Wong

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.

parulb

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.

Ahsanwarsi

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

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.

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

konmik

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>

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.

David

Add this line in AndroidManifest.xml file:

android:windowSoftInputMode=adjustUnspecified

refer to this link for more info.

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

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

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

}

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>

Just add in your activity

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

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.

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!