O teclado do software redimensiona a imagem de fundo no Android


133

Sempre que o teclado do software aparece, ele redimensiona a imagem de fundo. Consulte a captura de tela abaixo:

Como você pode ver, o fundo é meio espremido. Qualquer pessoa pode esclarecer por que o fundo é redimensionado?

Meu layout é o seguinte:

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

Respostas:


190

Ok, eu consertei usando

android:windowSoftInputMode="stateVisible|adjustPan"

entrada dentro da <Activity >tag no manifestarquivo. Eu acho que foi causado por ter ScrollView dentro da atividade.


3
O problema é que a rolagem é, ao usar adjustPan, não trabalhando ... (se você tiver um ScrollView), e isso é chato ...
Ted

4
Mas a visualização de rolagem não está funcionando agora. é thr alguma maneira de evitá-lo
Mr.G

Eu preciso da visualização de rolagem. Você tem uma maneira de manter a exibição de rolagem e manter o plano de fundo descompactado.
Rana Ranvijay Singh

4
Isto não é suficiente. Pelo menos, se você não usar um ScrollView. Muito melhor é uma solução aqui stackoverflow.com/a/29750860/2914140 ou stackoverflow.com/a/18191266/2914140 .
CoolMind 14/08/2015

1
se você não quiser exibir o teclado automaticamente, em seguida, usar: android: windowSoftInputMode = "adjustPan"
satvinder singh

110

Eu enfrentei o mesmo problema ao desenvolver um aplicativo de bate-papo, tela de bate-papo com uma imagem de fundo. android:windowSoftInputMode="adjustResize"apertou minha imagem de plano de fundo para ajustar o espaço disponível após a exibição do teclado virtual e "AdjustPan" mudou todo o layout para ajustar o teclado virtual. A solução para esse problema foi definir o plano de fundo da janela em vez de um plano de layout dentro de um XML de atividade. Use getWindow().setBackgroundDrawable()em sua atividade.


4
Muito bom! Melhor resposta! AdjustPan causa efeitos colaterais como: O teclado substitui os componentes, mas nenhuma barra de rolagem é exibida.
CelinHC

4
@parulb ou qualquer outra pessoa que leia isso, isso funciona muito bem (e obrigado por isso) desde que a imagem de plano de fundo não contenha informações relevantes. O problema que encontro de tempos em tempos é que meu plano de fundo conterá algo como um logotipo, e definir o plano de fundo da janela para a imagem oculta o logotipo atrás da ActionBar. Para certas telas, isso não é um problema, mas às vezes sou obrigado a manter o ActionBar (sem ocultar). Alguma idéia de como lidar com algum tipo de preenchimento ao definir o plano de fundo da janela para levar em consideração o ActionBar?
Zgc7009

1
E se eu estiver tendo um fragmento, como lidar com isso? não o seu trabalho em fragmentos
user2056563

.... Para user2056563: Para fragmentos apenas usar getActivity () GetWindow () setBackgroundDrawable () ou getActivity () GetWindow () setBackgroundDrawableResource ()
Andrei Aulaska

Obrigado, funcionou mesmo com o scrollview. Eu fiz isso no novo AppCompat: getWindow (). SetBackgroundDrawable (ContextCompat.getDrawable (this, R.drawable.background));
Lucas Eduardo

34

Aqui está a melhor solução para evitar esse tipo de problema.

Etapa 1: criar um estilo

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

Etapa 2: defina seu estilo de atividade no AndroidManifestarquivo

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

legais! solução mais simples
Matias Elorriaga

28

Através do android: windowSoftInputMode = "AdjustPan", fornecendo uma experiência ruim ao usuário, porque a tela inteira fica em cima (mude para o topo). Portanto, seguir é uma das melhores respostas.

Eu tenho o mesmo problema, mas depois disso, encontrei respostas impressionantes do @Gem

Em manifesto

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

Em xml

Não defina nenhum plano de fundo aqui e mantenha sua visualização em ScrollView

Em Java

Você precisa definir o plano de fundo para a janela:

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

Graças ao @Gem.


1
Obrigado pelo reconhecimento.
Gem

O que fazer se eu quiser usar o tipo de escala nisso? Porque a imagem da tela de entalhe está se esticando.
Nil

14

apenas para adição ...

se você tem uma listview em sua atividade, você precisa adicionar este android:isScrollContainer="false" nas propriedades da sua exibição de lista ...

e não se esqueça de adicionar android:windowSoftInputMode="adjustPan" seu xml de manifesto à sua atividade ...

se vocês estão usando android:windowSoftInputMode="adjustUnspecified" com a exibição rolável em seu layout, o plano de fundo ainda será redimensionado pelo teclado virtual ...

seria melhor se você usasse o valor "AdjustPan" para impedir o redimensionamento do plano de fundo ...


2
android: windowSoftInputMode = "AdjustPan" faz com que toda a visualização mude com o teclado. Geralmente, temos um título da tela no topo. Usando esse sinalizador, ele também sairá da área visível, o que torna a experiência do usuário ruim.
Gem

@ Ge: então, qual é a solução?

@ Copernic Eu enfrentei esse problema há muito tempo quando estava trabalhando em um aplicativo de bate-papo. Eventualmente, eu consertei que, por favor, consulte minha resposta aqui: stackoverflow.com/questions/5307264/…
Gem

7

Caso alguém precise de um adjustResizecomportamento e não queira que ele ImageViewseja redimensionado aqui, há outra solução alternativa.

Basta colocar ImageViewdentro ScrollView=> RelativeLayoutcom 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 é uma boa solução para o caso u têm outros pontos de vista no fundo que não seja uma imagem .... Graças
Dominic D'Souza

4

Encontrei o principal problema ao trabalhar no meu aplicativo. No começo, eu uso o método fornecido pelo @parulb para resolver o problema. Muito obrigado. Mais tarde, porém, notei que a imagem de fundo é parcialmente ocultada pela barra de ação (e pela barra de status, tenho certeza). Esse pequeno problema já foi proposto por @ zgc7009, que comentou abaixo a resposta do @parulb há um ano e meio, mas ninguém respondeu.

Trabalhei o dia inteiro para descobrir uma maneira e, felizmente, posso pelo menos resolver esse problema perfeitamente no meu celular agora.

Primeiro, precisamos de um recurso de lista de camadas na pasta drawable para adicionar preenchimento na parte superior à imagem de fundo:

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

Segundo, configuramos esse arquivo como recurso para segundo plano, como mencionado acima:

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

Estou usando o Nexus 5. Encontrei uma maneira de obter a altura da barra de ação em xml, mas não a barra de status; portanto, tenho que usar uma altura fixa de 75dp para o preenchimento superior. Espero que alguém possa encontrar a última peça deste quebra-cabeça.


3

Sofri problemas semelhantes, mas parece que o uso adjustPancom android:isScrollContainer="false"ainda não corrigiu meu layout (que era um RecyclerView abaixo de um LinearLayout). O RecyclerView estava bom, mas toda vez que o teclado virtual aparecia, o LinearLayout era reajustado.

Para evitar esse comportamento (eu simplesmente queria que o teclado passasse por cima do meu layout), fui com o seguinte código:

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

Isso diz ao Android para basicamente deixar seu layout em paz quando o teclado virtual é chamado.

Mais leitura sobre as opções possíveis pode ser encontrada aqui (embora, por incrível que pareça, não parece haver uma entrada para adjustNothing).


3

Depois de estudar e implementar todas as respostas disponíveis, aqui estou adicionando uma solução.

Esta resposta é uma combinação de código de:

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

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

Aqui está a AppCompatImageViewclasse personalizada que não mostrou nenhum teclado virtual de alongamento ou rolagem: -

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 usá-lo como plano de fundo para minha Fragmentclasse, eu o defino como primeiro 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

Adicione esta linha no arquivo AndroidManifest.xml :

android:windowSoftInputMode=adjustUnspecified

consulte este link para mais informações.


Ainda está acontecendo mesmo depois que eu fiz isso. Isso está ficando frustrante :(
Andreas Wong


1

Eu enfrentei esse problema, quando minha imagem de plano de fundo era apenas um ImageViewdentro de Fragmente foi redimensionada pelo teclado.

Minha solução foi: usando personalizado ImageViewa partir deste SO Resposta , editado para ser compatível com 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);
    }

}

Ele não estica mais com o código acima, mas ainda rola um pouco do teclado virtual para cima / para baixo.
Harpreet

0

Basta adicionar sua atividade

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

0

Eu enfrentei esse mesmo problema antes, mas nenhuma solução funcionou para mim por tanto tempo porque eu estava usando um Fragmente também getActivity().getWindow().setBackgroundDrawable() não era uma solução para mim.

A solução que funcionou para mim é substituir FrameLayoutuma lógica para lidar com o teclado que deve aparecer e alterar o bitmap em movimento.

Aqui está o meu 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)
        }
    }
}

E eu usei como um pano de fundo habitual do FrameLayout.

frameLayout.backgroundImage = Drawable.createFromPath(path)

Espero que ajude.


0

Você pode agrupar seu LinearLayout com FrameLayout e adicionar ImageView com Background:

<FrameLayout>

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

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

E você pode definir a altura ao criar atividade / fragmento (para impedir o dimensionamento quando o teclado estiver aberto). Algum código no Kotlin do Fragment:

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

0

se você definir a imagem como fundo da janela e a interface do usuário ficar presa. pode haver possibilidade de você usar o drawable, que está em uma única pasta drawable, se sim, será necessário colá-lo em drawable-nodpi ou drawable-xxxhdpi.


0

Minha solução é substituir o plano de fundo da janela pelo do layout e definir o plano de fundo como nulo. Dessa maneira, mantenho a imagem na janela de visualização XML:

Portanto, mantenha o plano de fundo no layout e adicione um ID para ele. Em seguida, na atividade onCreate (), coloque este código:

    ConstraintLayout mainLayout = (ConstraintLayout)findViewById(R.id.mainLayout);
    getWindow().setBackgroundDrawable(mainLayout.getBackground());
    mainLayout.setBackground(null);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.