Klawiatura oprogramowania zmienia rozmiar obrazu tła w systemie Android

136

Za każdym razem, gdy pojawia się klawiatura programowa, zmienia rozmiar obrazu tła. Zobacz poniższy zrzut ekranu:

Jak widać, tło jest jakby ściśnięte. Każdy może rzucić światło na to, dlaczego zmienia się rozmiar tła?

Mój układ wygląda następująco:

<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
źródło

Odpowiedzi:

193

Ok, naprawiłem to za pomocą

android:windowSoftInputMode="stateVisible|adjustPan"

wpis wewnątrz <Activity >tagu w manifestpliku. Myślę, że było to spowodowane posiadaniem ScrollView w działaniu.

Andreas Wong
źródło
3
Problem polega na tym, że przewijanie podczas korzystania z adjustPan nie działa ... (jeśli masz ScrollView) i to jest denerwujące ...
Ted
4
Ale teraz widok przewijania nie działa. jest w jakikolwiek sposób, aby tego uniknąć
Pan G
Potrzebuję widoku przewijania. Czy masz sposób, aby zachować widok przewijania i nie skompresować tła?
Rana Ranvijay Singh
4
To nie wystarczy. Przynajmniej jeśli nie używasz ScrollView. Znacznie lepsze jest tutaj rozwiązanie stackoverflow.com/a/29750860/2914140 lub stackoverflow.com/a/18191266/2914140 .
CoolMind
1
jeśli nie chcesz automatycznie wyświetlać klawiatury, użyj: android: windowSoftInputMode = "adjustPan"
satvinder singh
110

Napotkałem ten sam problem podczas tworzenia aplikacji do czatu, ekranu czatu z obrazem tła. android:windowSoftInputMode="adjustResize"ścisnąłem mój obraz tła, aby dopasować się do dostępnego miejsca po wyświetleniu miękkiej klawiatury, a „adjustPan” przesunął cały układ w górę, aby dostosować klawiaturę ekranową. Rozwiązaniem tego problemu było ustawienie tła okna zamiast tła układu wewnątrz pliku XML działania. Użyj getWindow().setBackgroundDrawable()w swojej działalności.

parulb
źródło
4
Bardzo dobrze! Najlepsza odpowiedź! AdjustPan powoduje dodatkowe efekty, takie jak: Klawiatura zastępuje komponenty, ale pasek przewijania nie jest wyświetlany.
CelinHC
4
@parulb lub ktokolwiek inny, kto to przeczyta, działa świetnie (i dziękuję za to), o ile obraz tła nie zawiera odpowiednich informacji. Problem, który napotykam od czasu do czasu, polega na tym, że moje tło będzie zawierało coś w rodzaju logo, a ustawienie tła okna na obraz powoduje ukrycie logo za ActionBar. W przypadku niektórych ekranów nie stanowi to problemu, ale czasami muszę zachować ActionBar (bez ukrywania). Jakieś pomysły, jak poradzić sobie z jakimś wypełnieniem podczas ustawiania tła okna, aby uwzględnić ActionBar?
zgc7009
1
Co jeśli mam fragment, jak sobie z tym poradzić? nie działa dla fragmentów
user2056563
Do użytkownika2056563: W przypadku fragmentów po prostu użyj getActivity (). GetWindow (). SetBackgroundDrawable () lub getActivity (). GetWindow (). SetBackgroundDrawableResource ()
Andrei Aulaska
Dziękuję, działało nawet z scrollviewem. Zrobiłem to na nowym AppCompat: getWindow (). SetBackgroundDrawable (ContextCompat.getDrawable (this, R.drawable.background));
Lucas Eduardo,
34

Oto najlepsze rozwiązanie, aby uniknąć tego rodzaju problemów.

Krok 1: Utwórz styl

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

Krok 2: Ustaw swój styl aktywności w AndroidManifestpliku

<activity
    android:name=".Chat"
    android:screenOrientation="portrait"
    android:theme="@style/ChatBackground" >
Ahsanwarsi
źródło
ładny! najprostsze rozwiązanie
Matias Elorriaga
28

W systemie Android: windowSoftInputMode = "adjustPan", co powoduje złe wrażenia użytkownika, ponieważ cały ekran przechodzi na górę (przesunięcie na górę). Oto jedna z najlepszych odpowiedzi.

Mam ten sam problem, ale potem znalazłem niesamowite odpowiedzi z @Gem

W Manifest

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

W xml

Nie ustawiaj tła tutaj i zachowaj widok pod ScrollView

W Javie

Musisz ustawić tło na okno:

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

Dzięki @Gem.

Joseph Mekwan
źródło
1
Dzięki za uznanie.
Gem
Co zrobić, jeśli chcę w tym celu użyć typu skali? Ponieważ dla notcha obraz na ekranie się rozciąga.
Brak
14

tylko jako dodatek ...

jeśli masz widok listy swojej aktywności, musisz dodać go android:isScrollContainer="false"we właściwościach widoku listy ...

i nie zapomnij dodać android:windowSoftInputMode="adjustPan"do swojego manifestu xml podczas swojej aktywności ...

jeśli korzystasz android:windowSoftInputMode="adjustUnspecified"z przewijalnego widoku w swoim układzie, twoje tło będzie nadal zmieniane za pomocą miękkiej klawiatury ...

byłoby lepiej, gdybyś użył wartości „adjustPan”, aby zapobiec zmianie rozmiaru tła ...

BolbazarMarme
źródło
2
android: windowSoftInputMode = "adjustPan" przesuwaj cały widok za pomocą klawiatury. Generalnie na górze mamy tytuł ekranu. Użycie tej flagi spowoduje również wyjście poza widoczny obszar, co powoduje złe wrażenia użytkownika.
Gem
@Gem: więc jakie jest rozwiązanie?
@Copernic Z tym problemem miałem do czynienia dawno temu, kiedy pracowałem nad aplikacją do czatu. Ostatecznie naprawiłem to, proszę skierować moją odpowiedź tutaj: stackoverflow.com/questions/5307264/ ...
Gem
7

W przypadku, gdy ktoś potrzebuje adjustResizezachowania i nie chce, ImageViewaby jego rozmiar został zmieniony, tutaj jest inne obejście.

Po prostu włóż do ImageViewśrodka ScrollView=> za RelativeLayoutpomocą 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
źródło
1
Jest to dobre rozwiązanie w przypadku, gdy w tle masz inne widoki niż obraz .... Dzięki
Dominic D'Souza
4

Podczas pracy nad moją aplikacją napotkałem główny problem. Na początku używam metody dostarczonej przez @parulb, aby rozwiązać problem. Dziękuję mu bardzo. Ale później zauważyłem, że obraz tła jest częściowo ukryty przez pasek akcji (i jestem pewien, że pasek stanu). Ten mały problem jest już proponowany przez @ zgc7009, który skomentował poniżej odpowiedź @parulb półtora roku temu, ale nikt nie odpowiedział.

Pracowałem cały dzień, aby znaleźć sposób i na szczęście teraz mogę przynajmniej idealnie rozwiązać ten problem na moim telefonie komórkowym.

Najpierw potrzebujemy zasobu listy warstw w folderze do rysowania, aby dodać dopełnienie u góry do obrazu tła:

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

Po drugie ustawiliśmy ten plik jako zasób tła, jak wspomniano powyżej:

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

Używam Nexusa 5. Znalazłem sposób na uzyskanie wysokości paska akcji w XML, ale nie paska stanu, więc muszę użyć stałej wysokości 75dp dla górnego wypełnienia. Mam nadzieję, że każdy znajdzie ostatni element tej układanki.

RaymondRexxar
źródło
3

Miałem podobne problemy, ale wydaje się, że użycie adjustPanz android:isScrollContainer="false"nadal nie naprawiło mojego układu (który był RecyclerView poniżej LinearLayout). RecyclerView był w porządku, ale za każdym razem, gdy pojawiała się klawiatura wirtualna, LinearLayout ponownie dostosowywany.

Aby zapobiec temu zachowaniu (po prostu chciałem, aby klawiatura przeszła przez mój układ), zastosowałem następujący kod:

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

To mówi Androidowi, aby zasadniczo zostawił twój układ w spokoju, gdy wywoływana jest wirtualna klawiatura.

Więcej informacji na temat możliwych opcji można znaleźć tutaj (choć, co dziwne, nie wydaje się, że istnieje wpis dla adjustNothing).

welshk91
źródło
3

Po przestudiowaniu i wdrożeniu wszystkich dostępnych odpowiedzi, tutaj dodaję rozwiązanie.

Ta odpowiedź to kombinacja kodu z:

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

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

Oto AppCompatImageViewklasa niestandardowa, która nie pokazywała rozciągania ani przewijania klawiatury programowej: -

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

Aby użyć go jako tła dla mojej Fragmentklasy, ustawiłem go jako pierwszy element na 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
źródło
2

Dodaj tę linię w pliku AndroidManifest.xml :

android:windowSoftInputMode=adjustUnspecified

skorzystaj z tego linku, aby uzyskać więcej informacji.

David
źródło
To wciąż się dzieje, nawet po tym, jak to zrobiłem. To staje się frustrujące :(
Andreas Wong
1

Miałem do czynienia z tym problemem, gdy mój obraz tła znajdował się tylko ImageViewwewnątrz a Fragment, a jego rozmiar został zmieniony przez klawiaturę.

Moje rozwiązanie brzmiało: używając niestandardowego ImageViewz tej odpowiedzi SO , edytowanej tak, aby była kompatybilna z 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);
    }

}
Andrzej
źródło
Nie rozciąga się już z powyższym kodem, ale nadal przewija się trochę w górę / w dół w stosunku do miękkiej klawiatury.
Harpreet
1

Moim rozwiązaniem jest zastąpienie tła okna tym z układu, a następnie ustawienie tła układu na null. W ten sposób zachowuję obraz w oknie podglądu XML:

Więc zachowaj tło w układzie i dodaj do niego identyfikator. Następnie w działaniu onCreate () umieść ten kod:

    ConstraintLayout mainLayout = (ConstraintLayout)findViewById(R.id.mainLayout);
    getWindow().setBackgroundDrawable(mainLayout.getBackground());
    mainLayout.setBackground(null);
gfunk
źródło
0

Po prostu dodaj swoją aktywność

getWindow().setBackgroundDrawable(R.drawable.your_image_name);
ariful islam arif
źródło
0

Miałem ten sam problem wcześniej, ale żadne rozwiązanie nie działało dla mnie tak długo, ponieważ używałem a Fragment, a także getActivity().getWindow().setBackgroundDrawable() nie było dla mnie rozwiązaniem.

Rozwiązaniem, które u mnie zadziałało, jest zastąpienie FrameLayoutlogiką obsługi klawiatury, która powinna się pojawić i zmienić bitmapę w podróży.

Oto mój kod 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)
        }
    }
}

Użyłem go jak zwykłego tła FrameLayout.

frameLayout.backgroundImage = Drawable.createFromPath(path)

Mam nadzieję, że to pomoże.

fkdplc
źródło
0

Możesz opakować LinearLayout za pomocą FrameLayout i dodać ImageView z Background:

<FrameLayout>

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

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

I możesz ustawić jego wysokość podczas tworzenia aktywności / fragmentu (aby zapobiec skalowaniu po otwarciu klawiatury). Trochę kodu w Kotlinie z Fragmentu:

activity?.window?.decorView?.height?.let {
        backgroundImage.setHeight(it)
}
Rafols
źródło
0

jeśli ustawisz obraz jako windowbackground i interfejs użytkownika utknie. wtedy może istnieć możliwość, że używasz do rysowania, który znajduje się w jednym folderze do rysowania, jeśli tak, musisz wkleić go w drawable-nodpi lub drawable-xxxhdpi.

Nikul Vaghani
źródło