El teclado del software cambia el tamaño de la imagen de fondo en Android


133

Cada vez que aparece el teclado del software, cambia el tamaño de la imagen de fondo. Consulte la captura de pantalla a continuación:

Como puede ver, el fondo está algo comprimido. ¿Alguien puede arrojar luz sobre por qué el fondo cambia de tamaño?

Mi diseño es el siguiente:

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

Respuestas:


190

Ok, lo arreglé usando

android:windowSoftInputMode="stateVisible|adjustPan"

entrada dentro de la <Activity >etiqueta en el manifestarchivo. Creo que fue causado por tener ScrollView dentro de la Actividad.


3
El problema es que el desplazamiento es, cuando se utiliza ajustarPan, no funciona ... (si tiene un ScrollView), y eso es molesto ...
Ted

44
Pero la vista de desplazamiento no funciona ahora. es cualquier forma de evitarlo
Mr.G

Necesito vista de desplazamiento. ¿Tiene alguna manera de mantener la vista de desplazamiento y mantener el fondo sin comprimir?
Rana Ranvijay Singh

44
Eso no es suficiente. Al menos, si no usa un ScrollView. Mucho mejor es una solución aquí stackoverflow.com/a/29750860/2914140 o stackoverflow.com/a/18191266/2914140 .
CoolMind

1
si no desea mostrar el teclado automáticamente, use: android: windowSoftInputMode = "ajustarPan"
satvinder singh

110

Enfrenté el mismo problema al desarrollar una aplicación de chat, pantalla de chat con una imagen de fondo. android:windowSoftInputMode="adjustResize"apreté mi imagen de fondo para que se ajustara al espacio disponible después de que se visualizara el teclado virtual y "ajustarPan" cambiara todo el diseño para ajustar el teclado virtual. La solución a este problema fue establecer el fondo de la ventana en lugar de un fondo de diseño dentro de una actividad XML. UtilizargetWindow().setBackgroundDrawable() en su actividad.


44
¡Muy bien! ¡La mejor respuesta! ajustePan provoca efectos colaterales como: El teclado anula los componentes pero no se muestra la barra de desplazamiento.
CelinHC el

44
@parulb o cualquier otra persona que lea esto, funciona muy bien (y gracias por ello) siempre que la imagen de fondo no contenga información relevante. El problema que encuentro de vez en cuando es que mi fondo contendrá algo así como un logotipo, y configurar el fondo de la ventana a la imagen oculta el logotipo detrás de la barra de acciones. Para ciertas pantallas esto no es un problema, pero a veces se me exige que mantenga la Barra de acciones (sin ocultar). ¿Alguna idea sobre cómo manejar algún tipo de relleno al configurar el fondo de la ventana para tener en cuenta la ActionBar?
zgc7009

1
¿Qué sucede si tengo un fragmento sobre cómo tratarlo? no funciona para fragmentos
user2056563

Al usuario 2056563: Para fragmentos, use getActivity (). GetWindow (). SetBackgroundDrawable () o getActivity (). GetWindow (). SetBackgroundDrawableResource ()
Andrei Aulaska

Gracias, funcionó incluso con scrollview. Me gustó esto en el nuevo AppCompat: getWindow (). SetBackgroundDrawable (ContextCompat.getDrawable (this, R.drawable.background));
Lucas Eduardo

34

Aquí está la mejor solución para evitar este tipo de problema.

Paso 1: crea un estilo

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

Paso 2: establece tu estilo de actividad en el AndroidManifestarchivo

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

¡bonito! solución más simple
Matias Elorriaga

28

A través de Android: windowSoftInputMode = "ajustarPan" que da una mala experiencia de usuario porque a través de toda la pantalla se pone en la parte superior (cambio a la parte superior) Entonces, la siguiente es una de las mejores respuestas.

Tengo el mismo problema, pero después de eso, encontré respuestas impresionantes de @Gem

En manifiesto

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

En xml

No establezca ningún fondo aquí y mantenga su vista en ScrollView

En java

Debe configurar el fondo en la ventana:

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

Gracias a @Gem.


1
Gracias por el reconocimiento
Gema

¿Qué hacer si quiero usar el tipo de escala en esto? Porque para la muesca la imagen de la pantalla se está estirando.
Nulo

14

solo por agregar ...

si tiene una vista de lista en su actividad, necesita agregarla android:isScrollContainer="false"en sus propiedades de vista de lista ...

y no olvides agregar android:windowSoftInputMode="adjustPan"tu manifiesto xml en tu actividad ...

si ustedes usan una android:windowSoftInputMode="adjustUnspecified"vista desplazable en su diseño, entonces su fondo aún cambiará de tamaño con el teclado virtual ...

sería mejor si utiliza el valor "ajustarPan" para evitar que el fondo cambie de tamaño ...


2
android: windowSoftInputMode = "ajustarPan" hace que toda la vista cambie con el teclado. Generalmente tenemos un título de la pantalla en la parte superior. Usando esta bandera también saldrá del área visible, lo que hace que la experiencia del usuario sea mala.
Gema

@Gem: ¿cuál es la solución?

@Copernic Enfrenté este problema hace mucho tiempo cuando estaba trabajando en una aplicación de chat. Eventualmente arreglé eso, por favor refiérase mi respuesta aquí: stackoverflow.com/questions/5307264/…
Gema

7

En caso de que alguien necesite un adjustResizecomportamiento y no quiera ImageViewque se cambie su tamaño aquí, hay otra solución.

Simplemente ponga ImageViewadentro ScrollView=> RelativeLayoutcon 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
Esta es una buena solución en caso de que tenga otras vistas en segundo plano que no sean una imagen ... Gracias
Dominic D'Souza

4

Encontré el problema principal cuando trabajaba en mi aplicación. Al principio, uso el método proporcionado por @parulb para resolver el problema. Muchas gracias Pero luego noté que la imagen de fondo está parcialmente oculta por la barra de acción (y la barra de estado, estoy seguro). Este pequeño problema ya lo propuso @ zgc7009, quien comentó a continuación la respuesta de @parulb hace un año y medio, pero nadie respondió.

Trabajé todo un día para encontrar una manera y, afortunadamente, al menos ahora puedo resolver este problema perfectamente en mi teléfono celular.

Primero necesitamos un recurso de lista de capas en una carpeta dibujable para agregar relleno en la parte superior de la imagen de fondo:

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

En segundo lugar, configuramos este archivo como recurso para el fondo como se mencionó anteriormente:

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

Estoy usando Nexus 5. Encontré una manera de obtener la altura de la barra de acción en xml pero no la barra de estado, por lo que tengo que usar una altura fija de 75dp para el relleno superior. Espero que alguien pueda encontrar la última pieza de este rompecabezas.


3

Sufrí problemas similares, pero parece que usar adjustPancon android:isScrollContainer="false"todavía no solucionó mi diseño (que era un RecyclerView debajo de LinearLayout). RecyclerView estaba bien, pero cada vez que aparecía el teclado virtual, LinearLayout se reajustaba.

Para evitar este comportamiento (simplemente quería que el teclado revisara mi diseño), utilicé el siguiente código:

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

Esto le dice a Android que básicamente deje su diseño solo cuando se llama al teclado virtual.

Puede encontrar más información sobre las posibles opciones aquí (aunque, por extraño que parezca, no parece que haya una entrada para adjustNothing).


3

Después de estudiar e implementar todas las respuestas disponibles, aquí estoy agregando una solución.

Esta respuesta es una combinación de código de:

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

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

Aquí está la AppCompatImageViewclase personalizada que no mostró el teclado suave wrt de estiramiento o desplazamiento: -

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

Para usarlo como fondo para mi Fragmentclase, lo configuré como primer elemento para 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>

2

Agregue esta línea en el archivo AndroidManifest.xml :

android:windowSoftInputMode=adjustUnspecified

consulte este enlace para más información.


Todavía está sucediendo incluso después de que hice esto. Esto se está volviendo frustrante :(
Andreas Wong


1

Me enfrenté a este problema, cuando mi imagen de fondo era solo un ImageViewinterior Fragment, y el teclado la redimensionó.

Mi solución fue: usar personalizado ImageViewde esta respuesta SO , editado para ser compatible con 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);
    }

}

Ya no se estira con el código anterior, pero aún se desplaza un poco hacia arriba / abajo con el teclado suave wrt.
Harpreet


0

Me enfrenté a este mismo problema antes, pero ninguna solución me funcionó tanto tiempo porque estaba usando un Fragment, y también getActivity().getWindow().setBackgroundDrawable() era una solución para mí.

La solución que funcionó para mí es anular FrameLayoutcon una lógica para manejar el teclado que debería aparecer y cambiar el mapa de bits sobre la marcha.

Aquí está mi código FrameLayout (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)
        }
    }
}

Y lo usé como el fondo habitual de FrameLayout.

frameLayout.backgroundImage = Drawable.createFromPath(path)

Espero eso ayude.


0

Puede ajustar LinearLayout con FrameLayout y agregar ImageView con fondo:

<FrameLayout>

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

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

Y puede establecer su altura cuando crea actividad / fragmento (para evitar que se escale al abrir el teclado). Algún código en Kotlin de Fragment:

activity?.window?.decorView?.height?.let {
        backgroundImage.setHeight(it)
}

0

si está configurada la imagen como fondo de ventana y ui va a atascarse. entonces puede existir la posibilidad de que use drawable que se encuentra en una sola carpeta dibujable, en caso afirmativo, debe pegarlo en drawable-nodpi o drawable-xxxhdpi.


0

Mi solución es sustituir el fondo de la ventana con el del diseño, luego establecer el fondo del diseño en nulo. De esta manera mantengo la imagen en la ventana de vista previa XML:

Así que mantenga el fondo en el diseño y agregue una identificación para ello. Luego, en la actividad onCreate () pon este código:

    ConstraintLayout mainLayout = (ConstraintLayout)findViewById(R.id.mainLayout);
    getWindow().setBackgroundDrawable(mainLayout.getBackground());
    mainLayout.setBackground(null);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.