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

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

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

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

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

13 ответов

Решение

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

android:windowSoftInputMode="stateVisible|adjustPan"

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

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

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

Шаг 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" >

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

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

В манифесте

android:windowSoftInputMode="adjustResize|stateAlwaysHidden"

В XML

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

На яве

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

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

Благодаря @Gem.

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

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

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

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

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

На случай, если кому-то понадобится 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>

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

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

/questions/43803195/programmnaya-klaviatura-izmenyaet-razmer-fonovogo-izobrazheniya-na-android/43803200#43803200

/questions/32272412/imageview-masshtabirovanie-topcrop/32272417#32272417

Вот обычай 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">

    <app.utils.view.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>

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

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

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

<!-- 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. Я нашел способ получить высоту actionbar в xml, но не статусбар, поэтому я должен использовать фиксированную высоту 75dp для верхнего заполнения. Надеюсь, кто-нибудь может найти последний кусок этой головоломки.

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

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

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

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

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

Просто используйте в своем onCreate() этот код:

protected void onCreate(Bundle savedInstanceState) {
...   
 getWindow().setBackgroundDrawableResource(R.drawable.your_image_resource);
...
}

и удалите эту строку в вашем XML:

android:background="@drawable/background"

Узнайте больше на:

http://%20%20%20%20http://developer.android.com/reference/android/view/Window.html

спасибо за: Adrian Cid Almaguer

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

android:windowSoftInputMode=adjustUnspecified

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

Я столкнулся с этой проблемой, когда мое фоновое изображение было просто 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);
    }

}

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

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

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

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

<FrameLayout>

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

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

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

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

Просто добавьте в свою деятельность

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

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

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

`класс 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)

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

Другие вопросы по тегам