Le clavier logiciel redimensionne l'image d'arrière-plan sur Android

133

Chaque fois que le clavier logiciel apparaît, il redimensionne l'image d'arrière-plan. Reportez-vous à la capture d'écran ci-dessous:

Comme vous pouvez le voir, l'arrière-plan est en quelque sorte pressé. N'importe qui peut expliquer pourquoi l'arrière-plan est redimensionné?

Ma mise en page est la suivante:

<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>
Andreas Wong
la source

Réponses:

190

Ok je l'ai corrigé en utilisant

android:windowSoftInputMode="stateVisible|adjustPan"

entrée à l'intérieur de la <Activity >balise dans le manifestfichier. Je pense que cela a été causé par le fait d'avoir ScrollView dans l'activité.

Andreas Wong
la source
3
Le problème est que le défilement est, lors de l'utilisation d'ajustPan, ne fonctionne pas ... (si vous avez un ScrollView), et c'est ennuyeux ...
Ted
4
Mais le scrollview ne fonctionne pas maintenant. est-il possible de l'éviter
Mr.G
J'ai besoin d'une vue de défilement. Avez-vous un moyen de conserver la vue de défilement et de garder l'arrière-plan non compressé.
Rana Ranvijay Singh
4
Ce n'est pas assez. Au moins, si vous n'utilisez pas de ScrollView. Une solution bien meilleure est ici stackoverflow.com/a/29750860/2914140 ou stackoverflow.com/a/18191266/2914140 .
CoolMind
1
si vous ne voulez pas afficher le clavier automatiquement, utilisez: android: windowSoftInputMode = "AdjustPan"
satvinder singh
110

J'ai rencontré le même problème en développant une application de chat, un écran de chat avec une image de fond. android:windowSoftInputMode="adjustResize"pressé mon image d'arrière-plan pour l'adapter à l'espace disponible après que le clavier virtuel a été affiché et "AdjustPan" a déplacé toute la disposition vers le haut pour ajuster le clavier logiciel. La solution à ce problème était de définir l'arrière-plan de la fenêtre au lieu d'un arrière-plan de mise en page dans un XML d'activité. Utilisez getWindow().setBackgroundDrawable()dans votre activité.

parulbe
la source
4
Très bien! Meilleure réponse! AdjustPan provoque des effets collatéraux comme: Le clavier remplace les composants mais aucune barre de défilement n'est affichée.
CelinHC
4
@parulb ou toute autre personne qui lit ceci, cela fonctionne très bien (et merci pour cela) tant que l'image d'arrière-plan ne contient pas d'informations pertinentes. Le problème que je rencontre de temps en temps est que mon arrière-plan contiendra quelque chose comme un logo, et la définition de l'arrière-plan de la fenêtre sur l'image masque le logo derrière l'ActionBar. Pour certains écrans, ce n'est pas un problème, mais parfois je suis obligé de conserver l'ActionBar (pas de masquage). Des idées sur la façon de gérer une sorte de remplissage tout en définissant l'arrière-plan de la fenêtre pour prendre en compte l'ActionBar?
zgc7009
1
Et si j'ai un fragment, comment le gérer? cela ne fonctionne pas pour les fragments
user2056563
Pour user2056563: Pour les fragments, utilisez simplement getActivity (). GetWindow (). SetBackgroundDrawable () ou getActivity (). GetWindow (). SetBackgroundDrawableResource ()
Andrei Aulaska
Merci, cela a fonctionné même avec scrollview. J'ai fait comme ça sur le nouveau AppCompat: getWindow (). SetBackgroundDrawable (ContextCompat.getDrawable (this, R.drawable.background));
Lucas Eduardo
34

Voici la meilleure solution pour éviter ce genre de problème.

Étape 1: créer un style

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

Étape 2: définissez votre style d'activité dans le AndroidManifestfichier

<activity
    android:name=".Chat"
    android:screenOrientation="portrait"
    android:theme="@style/ChatBackground" >
Ahsanwarsi
la source
agréable! solution la plus simple
Matias Elorriaga
28

Grâce à Android: windowSoftInputMode = "AdjustPan" donnant une mauvaise expérience utilisateur car à travers tout cet écran va en haut (décalage vers le haut) Donc, la suite est l'une des meilleures réponses.

J'ai le même problème mais après cela, j'ai trouvé des réponses géniales de @Gem

Dans Manifest

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

En xml

Ne définissez aucun arrière-plan ici et gardez votre vue sous ScrollView

En Java

Vous devez définir l'arrière-plan de la fenêtre:

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

Merci à @Gem.

Joseph Mekwan
la source
1
Merci pour la reconnaissance.
Gem du
Que faire si je souhaite utiliser le type d'échelle dans ce domaine? Parce que l'image de l'écran à encoche s'étire.
Nil
14

juste pour l'addition ...

si vous avez une vue de liste sur votre activité, vous devez l'ajouter android:isScrollContainer="false"dans vos propriétés de vue de liste ...

et n'oubliez pas d'ajouter android:windowSoftInputMode="adjustPan"votre manifeste xml à votre activité ...

si vous utilisez android:windowSoftInputMode="adjustUnspecified"une vue déroulante sur votre mise en page, votre arrière-plan sera toujours redimensionné par le clavier virtuel ...

il serait préférable d'utiliser la valeur "AdjustPan" pour empêcher le redimensionnement de votre arrière-plan ...

BolbazarMarme
la source
2
android: windowSoftInputMode = "AdjustPan" fait changer la vue entière avec le clavier. En général, nous avons un titre de l'écran en haut. En utilisant cet indicateur, il sortira également de la zone visible, ce qui entraînera une mauvaise expérience utilisateur.
Gem
@Gem: alors quelle est la solution?
@Copernic J'ai rencontré ce problème il y a longtemps lorsque je travaillais sur une application de chat. Finalement, j'ai corrigé cela, veuillez vous référer à ma réponse ici: stackoverflow.com/questions/5307264/…
Gem
7

Au cas où quelqu'un a besoin d'un adjustResizecomportement et ne veut pas que le sien ImageViewsoit redimensionné, voici une autre solution de contournement.

Mettez simplement à l' ImageViewintérieur ScrollView=> RelativeLayoutavec 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>
Konmik
la source
1
C'est une bonne solution au cas où vous auriez d'autres vues en arrière-plan autres qu'une image .... Merci
Dominic D'Souza
4

J'ai rencontré le problème principal en travaillant sur mon application. Dans un premier temps, j'utilise la méthode fournie par @parulb pour résoudre le problème. Merci beaucoup. Mais plus tard, j'ai remarqué que l'image de fond est partiellement masquée par la barre d'action (et la barre d'état j'en suis sûr). Ce petit numéro est déjà proposé par @ zgc7009 qui a commenté ci-dessous la réponse de @parulb il y a un an et demi mais personne n'a répondu.

J'ai travaillé toute une journée pour trouver un moyen et heureusement, je peux au moins résoudre parfaitement ce problème sur mon téléphone portable maintenant.

Nous avons d'abord besoin d'une ressource de liste de calques dans un dossier pouvant être dessiné pour ajouter un remplissage en haut de l'image d'arrière-plan:

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

Deuxièmement, nous définissons ce fichier comme ressource pour l'arrière-plan comme mentionné ci-dessus:

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

J'utilise Nexus 5. J'ai trouvé un moyen d'obtenir la hauteur de la barre d'action en xml mais pas la barre d'état, donc je dois utiliser une hauteur fixe de 75dp pour le rembourrage supérieur. J'espère que tout le monde pourra trouver la dernière pièce de ce puzzle.

RaymondRexxar
la source
3

J'ai souffert de problèmes similaires, mais il semble que l'utilisation de adjustPanavec android:isScrollContainer="false"ne corrige toujours pas ma mise en page (qui était un RecyclerView sous un LinearLayout). Le RecyclerView était bien, mais à chaque fois que le clavier virtuel apparaissait, le LinearLayout était réajusté.

Pour éviter ce comportement (je voulais simplement que le clavier passe par-dessus ma disposition), j'ai choisi le code suivant:

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

Cela indique à Android de laisser votre mise en page seule lorsque le clavier virtuel est appelé.

Vous trouverez plus de lecture sur les options possibles ici (bien que curieusement, il ne semble pas qu'il y ait une entrée pour adjustNothing).

welshk91
la source
3

Après avoir étudié et mis en œuvre toutes les réponses disponibles, j'ajoute ici une solution.

Cette réponse est une combinaison de code de:

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

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

Voici la AppCompatImageViewclasse personnalisée qui n'a montré aucun clavier logiciel d'étirement ou de défilement: -

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

Pour l'utiliser comme arrière-plan de ma Fragmentclasse, je l'ai défini comme premier élément sur 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
la source
2

Ajoutez cette ligne dans le fichier AndroidManifest.xml :

android:windowSoftInputMode=adjustUnspecified

reportez-vous à ce lien pour plus d'informations.

David
la source
Cela se produit encore même après avoir fait cela. Cela devient frustrant :(
Andreas Wong
1

J'ai été confronté à ce problème, lorsque mon image d'arrière-plan n'était qu'un ImageViewintérieur a Fragment, et elle a été redimensionnée par le clavier.

Ma solution était la suivante: utiliser la personnalisation ImageViewde cette réponse SO , modifiée pour être compatible avec 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);
    }

}
Andrew
la source
Il ne s'étire plus avec le code ci-dessus mais il fait toujours défiler un peu le clavier vers le haut / le bas.
Harpreet
0

Ajoutez simplement votre activité

getWindow().setBackgroundDrawable(R.drawable.your_image_name);
ariful islam arif
la source
0

J'ai fait face au même problème avant mais aucune solution n'a fonctionné pour moi si longtemps parce que j'utilisais un Fragment, et aussi getActivity().getWindow().setBackgroundDrawable() n'était pas non plus une solution pour moi.

La solution qui a fonctionné pour moi est de remplacer FrameLayoutpar une logique pour gérer le clavier qui devrait apparaître et changer le bitmap en déplacement.

Voici mon code 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)
        }
    }
}

Et je l'ai utilisé comme un arrière-plan FrameLayout habituel.

frameLayout.backgroundImage = Drawable.createFromPath(path)

J'espère que ça aide.

Astuces Yela
la source
0

Vous pouvez envelopper votre LinearLayout avec FrameLayout et ajouter ImageView avec arrière-plan:

<FrameLayout>

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

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

Et vous pouvez le définir en hauteur lorsque vous créez une activité / fragment (pour éviter la mise à l'échelle lorsque le clavier est ouvert). Un peu de code dans Kotlin de Fragment:

activity?.window?.decorView?.height?.let {
        backgroundImage.setHeight(it)
}
Rafols
la source
0

si vous définissez l'image comme arrière-plan de la fenêtre et que l'interface utilisateur va rester bloquée. alors il peut y avoir une possibilité que vous utilisiez drawable qui est dans un seul dossier dessinable, si oui, vous devez le coller dans drawable-nodpi ou drawable-xxxhdpi.

Nikul Vaghani
la source
0

Ma solution est de remplacer l'arrière-plan de la fenêtre par celui de la mise en page, puis de définir l'arrière-plan de la mise en page sur null. De cette façon, je garde l'image dans la fenêtre d'aperçu XML:

Alors gardez l'arrière-plan dans la mise en page et ajoutez un identifiant pour cela. Ensuite, dans l'activité onCreate (), mettez ce code:

    ConstraintLayout mainLayout = (ConstraintLayout)findViewById(R.id.mainLayout);
    getWindow().setBackgroundDrawable(mainLayout.getBackground());
    mainLayout.setBackground(null);
gfunk
la source