Программная клавиатура изменяет размер фонового изображения на Android

133

Всякий раз, когда появляется программная клавиатура, она изменяет размер фонового изображения. См. Снимок экрана ниже:

Как видите, фон вроде как сдавлен. Кто-нибудь может пролить свет на то, почему изменяется размер фона?

Мой макет выглядит следующим образом:

<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>
Андреас Вонг
источник

Ответы:

190

Хорошо, я исправил это с помощью

android:windowSoftInputMode="stateVisible|adjustPan"

запись внутри <Activity >тега в manifestфайле. Я думаю, это было вызвано наличием ScrollView внутри Activity.

Андреас Вонг
источник
3
Проблема в том, что прокрутка при использовании adjustPan не работает ... (если у вас есть ScrollView), и это раздражает ...
Тед
4
Но просмотр прокрутки сейчас не работает. есть любой способ избежать этого
Mr.G
Мне нужен режим прокрутки. У вас есть способ сохранить режим прокрутки и сохранить несжатый фон.
Рана Ранвиджай Сингх
4
Этого не достаточно. По крайней мере, если вы не используете ScrollView. Намного лучше решение здесь stackoverflow.com/a/29750860/2914140 или stackoverflow.com/a/18191266/2914140 .
CoolMind
1
если вы не хотите, чтобы клавиатура отображалась автоматически, используйте: android: windowSoftInputMode = "adjustPan"
satvinder singh
110

Я столкнулся с той же проблемой при разработке приложения для чата, экрана чата с фоновым изображением. android:windowSoftInputMode="adjustResize"сжал мое фоновое изображение, чтобы оно уместилось в доступном пространстве после отображения виртуальной клавиатуры, а «adjustPan» сместил всю раскладку вверх, чтобы настроить мягкую клавиатуру. Решением этой проблемы была установка фона окна вместо фона макета внутри XML-активности. Используйте getWindow().setBackgroundDrawable()в своей деятельности.

parulb
источник
4
Очень хорошо! Лучший ответ! AdjustPan вызывает побочные эффекты, такие как: клавиатура переопределяет компоненты, но полосы прокрутки не отображаются.
CelinHC 02
4
@parulb или любой другой, кто это прочитает, это отлично работает (и спасибо вам за это), пока фоновое изображение не содержит соответствующей информации. Проблема, с которой я сталкиваюсь время от времени, заключается в том, что мой фон будет содержать что-то вроде логотипа, а установка фона окна на изображение скрывает логотип за ActionBar. Для некоторых экранов это не проблема, но иногда от меня требуется сохранить ActionBar (без скрытия). Любые идеи о том, как обрабатывать какие-то отступы при настройке фона окна для учета ActionBar?
zgc7009
1
Что делать, если у меня есть фрагмент, как с ним бороться? он не работает для фрагментов
user2056563
Пользователю2056563: для фрагментов просто используйте getActivity (). GetWindow (). SetBackgroundDrawable () или getActivity (). GetWindow (). SetBackgroundDrawableResource ()
Андрей Ауласка
Спасибо, сработало даже с просмотром прокрутки. Мне понравилось это в новом AppCompat: getWindow (). SetBackgroundDrawable (ContextCompat.getDrawable (this, R.drawable.background));
Лукас Эдуардо
34

Вот лучшее решение, чтобы избежать подобных проблем.

Шаг 1. Создайте стиль

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

Шаг 2. Задайте стиль занятия в AndroidManifestфайле

<activity
    android:name=".Chat"
    android:screenOrientation="portrait"
    android:theme="@style/ChatBackground" >
Ahsanwarsi
источник
отлично! простейшее решение
Матиас Элорриага
28

Через android: windowSoftInputMode = "adjustPan" доставляет неудобства пользователю, потому что весь экран идет вверху (переход наверх) Итак, следующий - один из лучших ответов.

У меня такая же проблема, но после этого я нашел потрясающие ответы от @Gem

В манифесте

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

В xml

Не устанавливайте здесь какой-либо фон и держите вид под ScrollView

В Java

Вам нужно установить фон в окно:

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

Спасибо @Gem.

Джозеф Мекван
источник
1
Спасибо за признание.
Gem
Что делать, если я хочу использовать в этом тип шкалы? Потому что за вырезом на экране изображение растягивается.
Нил
14

просто для дополнения ...

если у вас есть список ваших действий, вам нужно добавить это android:isScrollContainer="false"в свои свойства списка ...

и не забудьте добавить android:windowSoftInputMode="adjustPan"в свой манифест xml в своей деятельности ...

если вы, ребята, используете android:windowSoftInputMode="adjustUnspecified"прокручиваемый вид на макете, тогда размер вашего фона все равно будет изменяться с помощью мягкой клавиатуры ...

было бы лучше, если бы вы использовали значение "adjustPan", чтобы предотвратить изменение размера фона ...

BolbazarMarme
источник
2
android: windowSoftInputMode = "adjustPan" переключить весь вид с помощью клавиатуры. Обычно у нас есть заголовок экрана вверху. Используя этот флаг, он также будет выходить из видимой области, что ухудшает взаимодействие с пользователем.
Gem
@Gem: так какое решение?
@Copernic Я столкнулся с этой проблемой давно, когда работал над приложением для чата. В конце концов я исправил это, пожалуйста, обратитесь к моему ответу здесь: stackoverflow.com/questions/5307264/…
Gem
7

В случае, если кому-то нужно adjustResizeповедение и он не хочет, чтобы его ImageViewразмер изменялся, есть еще один обходной путь.

Просто вставьте ImageViewвнутрь ScrollView=> RelativeLayoutс 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
источник
1
Это хорошее решение на случай, если у вас есть другие виды на заднем плане, кроме изображения .... Спасибо
Доминик Д'Суза
4

Я столкнулся с основной проблемой при работе над своим приложением. Сначала я использую метод, предоставленный @parulb, для решения проблемы. Большое ему спасибо. Но позже я заметил, что фоновое изображение частично скрыто панелью действий (и строкой состояния, я уверен). Этот небольшой вопрос уже предложен @ zgc7009, который прокомментировал ниже ответ @parulb полтора года назад, но никто не ответил.

Я работал целый день, чтобы найти способ, и, к счастью, теперь я могу, по крайней мере, идеально решить эту проблему на своем мобильном телефоне.

Сначала нам нужен ресурс списка слоев в папке с возможностью переноса, чтобы добавить отступ сверху к фоновому изображению:

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

Во-вторых, мы устанавливаем этот файл как ресурс для фона, как указано выше:

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

Я использую Nexus 5. Я нашел способ получить высоту панели действий в xml, но не строку состояния, поэтому я должен использовать фиксированную высоту 75dp для верхнего отступа. Надеюсь, кто-нибудь сможет найти последний кусочек этой головоломки.

RaymondRexxar
источник
3

У меня были аналогичные проблемы, но похоже, что использование adjustPanwith android:isScrollContainer="false"все еще не исправило мой макет (который был RecyclerView ниже LinearLayout). RecyclerView был в порядке, но каждый раз, когда появлялась виртуальная клавиатура, LinearLayout изменялся.

Чтобы предотвратить такое поведение (я просто хотел, чтобы клавиатура перемещалась по макету), я использовал следующий код:

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

Это говорит Android, что нужно оставить макет в покое при вызове виртуальной клавиатуры.

Больше информации о возможных вариантах можно найти здесь (хотя, как ни странно, не похоже, что там есть запись adjustNothing).

welshk91
источник
3

После изучения и реализации всех доступных ответов я добавляю решение.

Этот ответ представляет собой комбинацию кода из:

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

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

Вот настраиваемый AppCompatImageViewкласс, который не показывал растягивания или прокрутки мягкой клавиатуры: -

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

Чтобы использовать его в качестве фона для моего Fragmentкласса, я установил его в качестве первого элемента 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
источник
2

Добавьте эту строку в файл AndroidManifest.xml :

android:windowSoftInputMode=adjustUnspecified

обратитесь к этой ссылке для получения дополнительной информации.

Дэвид
источник
Это все еще происходит даже после того, как я это сделал. Это уже расстраивает :(
Андреас Вонг
1

Я столкнулся с этой проблемой, когда мое фоновое изображение было просто ImageViewвнутри Fragment, а его размер был изменен с помощью клавиатуры.

Мое решение было: использовать обычай ImageViewиз этого ответа SO , отредактированный для совместимости с 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);
    }

}
Андрей
источник
Он больше не растягивается с приведенным выше кодом, но по-прежнему немного прокручивается вверх / вниз по мягкой клавиатуре.
Harpreet
0

Просто добавьте свою активность

getWindow().setBackgroundDrawable(R.drawable.your_image_name);
напористый ислам ариф
источник
0

Я сталкивался с этой же проблемой раньше, но никакое решение не работало для меня так долго, потому что я использовал Fragment, а также getActivity().getWindow().setBackgroundDrawable() не было решением для меня.

Решение, которое сработало для меня, - переопределить FrameLayoutлогику для обработки клавиатуры, которая должна появиться, и изменить растровое изображение на ходу.

Вот мой код 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)
        }
    }
}

И я использовал его как обычный фон FrameLayout.

frameLayout.backgroundImage = Drawable.createFromPath(path)

Надеюсь, поможет.

Yela Tricks
источник
0

Вы можете обернуть свой LinearLayout FrameLayout и добавить ImageView с фоном:

<FrameLayout>

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

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

И вы можете установить его высоту при создании активности / фрагмента (чтобы предотвратить масштабирование при открытой клавиатуре). Некоторый код в Котлине из фрагмента:

activity?.window?.decorView?.height?.let {
        backgroundImage.setHeight(it)
}
Rafols
источник
0

если вы установили изображение в качестве фона окна, и пользовательский интерфейс застревает. тогда может быть возможность, что вы используете drawable, который находится в одной папке с возможностью рисования, если да, вам нужно вставить его в drawable-nodpi или drawable-xxxhdpi.

Никул Вагани
источник
0

Мое решение - заменить фон окна на один из макета, а затем установить для фона макета значение null. Таким образом я сохраняю изображение в окне предварительного просмотра XML:

Так что оставьте фон в макете и добавьте для него идентификатор. Затем в Activity onCreate () поместите этот код:

    ConstraintLayout mainLayout = (ConstraintLayout)findViewById(R.id.mainLayout);
    getWindow().setBackgroundDrawable(mainLayout.getBackground());
    mainLayout.setBackground(null);
джи-фанк
источник