Android Как настроить макет в полноэкранном режиме, когда отображается программная клавиатура

Я много исследовал, чтобы настроить макет, когда программная клавиатура активна, и я успешно реализовал это, но проблема возникает, когда я использую android:theme="@android:style/Theme.NoTitleBar.Fullscreen" this в моем теге активности в файле манифеста.

Для этого я использовал android:windowSoftInputMode="adjustPan|adjustResize|stateHidden" с разными вариантами, но безуспешно.

После этого я реализовал FullScreen программно и пробовал различные макеты для работы с FullScreen, но безуспешно.

Я сослался на эти ссылки и просмотрел здесь много сообщений, связанных с этой проблемой:

http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html

http://davidwparker.com/2011/08/30/android-how-to-float-a-row-above-keyboard/

Вот xml-код:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/masterContainerView"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#ffffff">

    <ScrollView android:id="@+id/parentScrollView"
        android:layout_width="fill_parent" android:layout_height="wrap_content">

        <LinearLayout android:layout_width="fill_parent"
            android:layout_height="fill_parent" android:orientation="vertical">

            <TextView android:id="@+id/setup_txt" android:layout_width="wrap_content"
                android:layout_height="wrap_content" android:text="Setup - Step 1 of 3"
                android:textColor="@color/top_header_txt_color" android:textSize="20dp"
                android:padding="8dp" android:gravity="center_horizontal" />

            <TextView android:id="@+id/txt_header" android:layout_width="fill_parent"
                android:layout_height="40dp" android:text="AutoReply:"
                android:textColor="@color/top_header_txt_color" android:textSize="14dp"
                android:textStyle="bold" android:padding="10dp"
                android:layout_below="@+id/setup_txt" />

            <EditText android:id="@+id/edit_message"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:text="Some text here." android:textSize="16dp"
                android:textColor="@color/setting_editmsg_color" android:padding="10dp"
                android:minLines="5" android:maxLines="6" android:layout_below="@+id/txt_header"
                android:gravity="top" android:scrollbars="vertical"
                android:maxLength="132" />

            <ImageView android:id="@+id/image_bottom"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:layout_below="@+id/edit_message" />

        </LinearLayout>
    </ScrollView>

    <RelativeLayout android:id="@+id/scoringContainerView"
        android:layout_width="fill_parent" android:layout_height="50px"
        android:orientation="vertical" android:layout_alignParentBottom="true"
        android:background="#535254">

        <Button android:id="@+id/btn_save" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_alignParentRight="true"
            android:layout_marginTop="7dp" android:layout_marginRight="15dp"
            android:layout_below="@+id/edit_message"
            android:text = "Save" />

        <Button android:id="@+id/btn_cancel" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_marginTop="7dp"
            android:layout_marginRight="10dp" android:layout_below="@+id/edit_message"
            android:layout_toLeftOf="@+id/btn_save" android:text = "Cancel" />

    </RelativeLayout>
</RelativeLayout>

введите описание изображения здесь

Я хочу, чтобы две нижние кнопки двигались вверх, когда появляется экранная клавиатура.

введите описание изображения здесь


person Vineet Shukla    schedule 14.09.2011    source источник
comment
Я думаю, вам нужно добавить кнопки внутри ScrollView и ниже EditText.   -  person Balaji Khadake    schedule 14.09.2011
comment
Я уже много вариантов перепробовал, что не работает ...   -  person Vineet Shukla    schedule 14.09.2011
comment
поместите кнопки ur в раскладку фрейма и установите его вес равным 1 и, наконец, используйте только android:windowSoftInputMode="adjustPan", скажите мне, работает ли это ..   -  person Sherif elKhatib    schedule 20.09.2011
comment
@VineetShukla вы нашли какую-нибудь работу с полноэкранным режимом ??   -  person Muhammad Babar    schedule 29.04.2013
comment
Обратите внимание, что вы не должны использовать adjustResize и adjustPan одновременно, из javadoc android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE: это не может быть объединено с {@link SOFT_INPUT_ADJUST_PAN}   -  person Denis Kniazhev    schedule 19.09.2014


Ответы (26)


Основываясь на обходном пути yghm, я закодировал вспомогательный класс, который позволяет мне решить проблему с однострочным кодом (конечно, после добавления нового класса в мой исходный код). Однострочный:

     AndroidBug5497Workaround.assistActivity(this);

А класс реализации:


public class AndroidBug5497Workaround {

    // For more information, see https://issuetracker.google.com/issues/36911528
    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.

    public static void assistActivity (Activity activity) {
        new AndroidBug5497Workaround(activity);
    }

    private View mChildOfContent;
    private int usableHeightPrevious;
    private FrameLayout.LayoutParams frameLayoutParams;

    private AndroidBug5497Workaround(Activity activity) {
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        mChildOfContent = content.getChildAt(0);
        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                possiblyResizeChildOfContent();
            }
        });
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
    }

    private void possiblyResizeChildOfContent() {
        int usableHeightNow = computeUsableHeight();
        if (usableHeightNow != usableHeightPrevious) {
            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
            if (heightDifference > (usableHeightSansKeyboard/4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            } else {
                // keyboard probably just became hidden
                frameLayoutParams.height = usableHeightSansKeyboard;
            }
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        return (r.bottom - r.top);
    }
}

Надеюсь, это кому-то поможет.

person Jo Jo    schedule 21.10.2013
comment
Спасибо, у меня это сработало ... У меня не было достаточно времени, чтобы протестировать его и посмотреть, есть ли какой-то негативный эффект, но на первое впечатление он работает нормально. Спасибо. - person Ionut Negru; 31.10.2013
comment
Это решение работает для меня, когда ‹item name = android: windowIsFloating› false ‹/item›, иначе при вводе появится новый столбец на клавиатуре и скроет тот, который я хочу отобразить. - person Neil Han; 05.11.2013
comment
Большое спасибо! Сэкономил и мое время;) - person vir us; 16.01.2014
comment
Да, это решение работает. Люди должны видеть это прежде всего. Это работает на S2, 4.1.1 и Nexus 4 (4.4) - person Cuong Thai; 17.03.2014
comment
Спасибо! Понятия не имею, почему, но мне пришлось заменить return (r.bottom - r.top); на return r.bottom, чтобы заставить его работать на моем HTC One Mini, иначе вид активности будет слишком высоко из-за размера строки состояния. Однако я еще не тестировал его на другом устройстве. Надеюсь, это поможет. - person Joan; 15.07.2014
comment
Когда мне следует позвонить в этот класс? - person TheGraduateGuy; 18.09.2014
comment
Привет, Джозеф Джонсон, я использовал ваш код, и он отлично сработал. Но теперь на некоторых небольших устройствах возникает проблема, заключающаяся в том, что на нем отображается разрыв (пустой экран) между клавиатурой и раскладкой. Вы знаете об этой проблеме? Я также пробовал вернуть r.bottom. - person Pankaj; 26.09.2014
comment
спасибо, работает почти идеально но одно нюанс. учитывает ли это решение потерю фокуса? в моей реализации он не восстанавливает макет, когда клавиатура отклоняется (например, с аппаратной кнопкой возврата). я сделал что-то не так? - person heximal; 30.09.2014
comment
onGlobalLayout не вызывается, когда редактирование текста теряет фокус - я проверил, установив точку останова. - person heximal; 30.09.2014
comment
Pankaj & heximal, извините, но у меня нет времени разбираться в ваших вопросах. Надеюсь, вы в этом разобрались, и если да, то сообщите о своих выводах. - person Jo Jo; 30.09.2014
comment
Джозеф Джонсон: Я реализовал ваш метод, он работает нормально, когда мы нажимаем на верхний текст редактирования, но когда мы нажимаем на нижний текст редактирования, весь дизайн идет вверх - person ranjith; 16.10.2014
comment
@ranjith, см. этот вопрос SO: stackoverflow.com/questions/11410257/ - person Jo Jo; 18.10.2014
comment
К сожалению, это не работает на Nexus 7 (2013). Он по-прежнему работает даже с установленным параметром adjustNothing. - person Le-roy Staines; 30.11.2014
comment
Сработало как шарм .. Даже фрагментами Спасибо @JosephJohnson - person Vigneshwaran.m; 11.12.2014
comment
У меня вопрос: поскольку экземпляр класса создается только в его конструкторе, когда сборщик мусора удалит этот объект? - person Scorpio; 20.01.2015
comment
@ Скорпион, хороший вопрос. Обратите внимание на вызов addOnGlobalLayoutListener (). Этот вызов присоединяет экземпляр AndroidBug5497Workaround к Activity. Таким образом, экземпляр AndroidBug5497Workaround не будет собирать мусор до тех пор, пока действие не будет собрано (например, если пользователь не использует действие в течение длительного времени). - person Jo Jo; 21.01.2015
comment
@JosephJohnson спасибо за ответ. Я думал, что addOnGlobalLayoutListener () на самом деле был прикреплен к представлению, представленному через mChildOfContent. Я действительно думал, что он будет собран, когда представление будет уничтожено (что не то же самое, что уничтожаемое действие, если вы используете setContentView или другие методы для изменения представлений, отображаемых действием). - person Scorpio; 22.01.2015
comment
@ Скорпион, ты прав. Слушатель прикреплен к View, а View принадлежит Activity. Таким образом, когда действие будет восстановлено, будет восстановлено и представление, а затем будет восстановлен и слушатель. - person Jo Jo; 22.01.2015
comment
Большое спасибо. очень-очень полезно. сэкономил много времени :) +1 - person Ripal Tamboli; 10.02.2015
comment
Это здорово и должно быть в репо - person Krtko; 12.03.2015
comment
Спасибо за ответ, это помогло мне решить мою проблему. Однако мне пришлось кое-что изменить. Я использую планшет Nexus с Android Lollipop, и даже если клавиатура видна, это работает так, как ожидалось, когда клавиатура скрыта, мой макет занимает весь экран устройства (что плохо, потому что есть программные кнопки для дома, назад и задачи). Как я исправил, это внутри метода possibleResizeChildOfContent (), вместо того, чтобы выполнять какие-либо условия, которые я установил rameLayoutParams.height = usableHeightNow. И мне кажется, что это везде работает правильно. - person Noel De Martin; 17.03.2015
comment
Причина, по которой я ненавижу Android: подобные ошибки .. Причина, по которой я люблю Android: разработчики, которые предоставляют подобные обходные пути. - person Nick; 23.11.2015
comment
После того, как я добавил панель действий, я должен вычесть ActionBar.getHeight() из usableHeightSansKeyboard - heightDifference. - person Christian d'Heureuse; 16.12.2015
comment
Это работает, НО есть некоторые qwerks. Если вы устанавливаете размер текста, отличный от значения по умолчанию в EditText, который использует alignParentBottom="true", это приведет к тому, что в представлении появится зазор над клавиатурой и под нижней частью макета. (Как уже упоминали другие) Используя этот ответ (stackoverflow.com/a/4737265/2415921), я добавил это условие, если клавиатура открыта. if (editText != null) { editText.setTextSize(32); } А теперь я могу спать :) - person Michael; 30.12.2015
comment
Пробовал в киоск-приложении с веб-просмотром (android 5) работал, вы классные - person Alvaro Lopez Guerra; 01.03.2016
comment
Где я могу передать AndroidBug5497Workaround.assistActivity (this); в onCreate? - person Manikandan; 17.05.2016
comment
Отличный ответ, большое спасибо. Он работает на Nexus 6, но вместо frameLayoutParams.height = usableHeightSansKeyboard; я должен использовать frameLayoutParams.height = usableHeightNow;. Если я этого не сделаю, некоторые элементы выпадают за пределы экрана. - person RobertoAllende; 19.05.2016
comment
над клавиатурой появится пустая часть. Как этого избежать? - person ARUNBALAN NV; 29.08.2016
comment
@JosephJohnson Отличный материал! Почему делится на 4 usableHeightSansKeyboard / 4? - person Don Box; 01.10.2016
comment
/ 4 говорит нам, что полезная высота уменьшилась на 1/4 или более, что является хорошим признаком того, что клавиатура поднялась, потому что клавиатуры обычно требуют изрядного количества места. Ясно, что это грязный прием, но он сработал довольно хорошо. - person Jo Jo; 06.10.2016
comment
Это работает только в первых полях. Если вся страница заполнена полями, значит, она не работает. - person IntoTheDeep; 14.12.2016
comment
Идеально! Спасибо! - person ricky; 21.06.2017
comment
он работает нормально, но при нажатии на него сжимается макет в xamarin android. как я могу предотвратить сжатие макета. - person Riddhi Shah; 10.08.2017
comment
Какой прекрасный фрагмент кода! Большое спасибо! Мне пришлось немного подправить его, чтобы он работал внутри фрагмента. Фрагмент удерживался FrameLayout, поэтому все, что я сделал, это заменил android.R.id.content на R.id.frameLayoutFragmentHolder, и он работал красиво. Спасибо еще раз - person N. Matic; 24.11.2018

Поскольку ответ уже был выбран и проблема была известна как ошибка, я подумал, что добавлю «Возможный обходной путь».

Вы можете переключить полноэкранный режим, когда отображается программная клавиатура. Это позволяет «adjustPan» работать правильно.

Другими словами, я все еще использую @android: style / Theme.Black.NoTitleBar.Fullscreen как часть темы приложения и stateVisible | adjustResize < / em> как часть режима мягкого ввода окна активности, но чтобы заставить их работать вместе, я должен переключить полноэкранный режим до того, как появится клавиатура.

Используйте следующий код:

Отключить полноэкранный режим

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

Включите полноэкранный режим

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

Примечание. Источником вдохновения послужило: Скрытие заголовка в полноэкранном режиме

person LEO    schedule 08.06.2012
comment
Я ценю, что вы уделили время проблеме, +1 за это. Я обязательно протестирую этот подход и скоро дам вам знать, сработает ли он для меня, спасибо. - person Vineet Shukla; 15.06.2012
comment
Работает, как предполагалось! Действительно хорошее решение! +1 с моей стороны. - person Mike; 12.12.2014
comment
stackoverflow.com/questions/4745988/ - person LEO; 20.10.2015
comment
клавиатура защелкнется, и фон клавиатуры станет черным. Эффект привязки выглядит не очень хорошо :( - person nibz; 06.12.2017
comment
вау, спасибо ... это работает для меня очень хорошо, объединив обходной путь, упомянутый AndroidBug5497Workaround ... Я загрузил объединенный исходный код на GitHub ... github.com/CrandellWS/AndroidBug5497Workaround/blob/master/ - person CrandellWS; 05.10.2018
comment
Этот ответ отлично работает! Большое спасибо !!!! ого-ого-ого - person Future Deep Gone; 03.06.2021

Я попробовал решение от Джозефа Джонсона, но, как и другие, столкнулся с проблемой разрыва между содержимым и клавиатурой. Проблема возникает из-за того, что программный режим ввода всегда pan при использовании полноэкранного режима. Это панорамирование мешает решению Джозефа, когда вы активируете поле ввода, которое будет скрыто программным вводом.

Когда появляется мягкий ввод, содержимое сначала панорамируется на основе его исходной высоты, а затем изменяется размер в соответствии с макетом, запрошенным решением Джозефа. Изменение размера и последующий макет не отменяют панорамирование, что приводит к разрыву. Полный порядок событий:

  1. Слушатель глобального макета
  2. Панорамирование
  3. Макет содержимого (= фактическое изменение размера содержимого)

Отключить панорамирование невозможно, но можно принудительно установить смещение панорамирования равным 0, изменив высоту содержимого. Это можно сделать в слушателе, потому что он запускается до панорамирования. Установка доступной высоты содержимого обеспечивает плавное взаимодействие с пользователем, т. Е. Отсутствие мерцания.

Я тоже внес эти изменения. Если в любом из этих случаев возникнут проблемы, дайте мне знать:

  • Переключено определение доступной высоты для использования getWindowVisibleDisplayFrame. Rect кэшируется, чтобы предотвратить ненужный мусор.
  • Разрешите также удалить слушателя. Это полезно, когда вы повторно используете действие для разных фрагментов, имеющих разные требования к полноэкранному режиму.
  • Не различайте отображаемую или скрытую клавиатуру, но всегда устанавливайте высоту содержимого равной высоте видимого кадра дисплея.

Он был протестирован на Nexus 5 и эмуляторах с уровнями API 16-24 с размерами экрана от крошечного до большого.

Код был перенесен на Kotlin, но перенести мои изменения обратно на Java очень просто. Дайте мне знать, если вам понадобится помощь:

class AndroidBug5497Workaround constructor(activity: Activity) {
    private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
    private val rootView = contentContainer.getChildAt(0)
    private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
    private val viewTreeObserver = rootView.viewTreeObserver
    private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }

    private val contentAreaOfWindowBounds = Rect()
    private var usableHeightPrevious = 0

    // I call this in "onResume()" of my fragment
    fun addListener() {
        viewTreeObserver.addOnGlobalLayoutListener(listener)
    }

    // I call this in "onPause()" of my fragment
    fun removeListener() {
        viewTreeObserver.removeOnGlobalLayoutListener(listener)
    }

    private fun possiblyResizeChildOfContent() {
        contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
        val usableHeightNow = contentAreaOfWindowBounds.height()
        if (usableHeightNow != usableHeightPrevious) {
            rootViewLayout.height = usableHeightNow
            // Change the bounds of the root view to prevent gap between keyboard and content, and top of content positioned above top screen edge.
            rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom)
            rootView.requestLayout()

            usableHeightPrevious = usableHeightNow
        }
    }
}
person Johan Stuyts    schedule 15.02.2017
comment
Кажется, это лучший ответ. Я портировал на java здесь gist.github.com/grennis/2e3cd5f7a9238c5986101584ce0. Обратите внимание, что я получал исключения, что наблюдатель не был жив, и мне также приходилось проверять это. - person Greg Ennis; 27.04.2017
comment
О мой Бог! просматривал всю иерархию системных представлений в поисках этого призрачного пространства. Я был близок к тому, чтобы отказаться от компьютеров в пользу фургона с едой, но в последнюю минуту увидел твой ответ. Оно работает :) - person rupps; 16.03.2018
comment
@Greg Ennis Спасибо за порт Java. Сэкономлено много сил и времени. - person Ikun; 26.04.2018
comment
@GregEnnis, спасибо, ваше решение работает с onResume (), onPause (), onDestroy () (см. Комментарии в коде GitHub). - person CoolMind; 17.10.2018
comment
Это работает для меня, за исключением того, что вызов removeListener, похоже, не работает. Я помещаю точки останова как в вызов possiblyResizeChildOfContent, так и в вызов removeListener, и даже после того, как я попал в точку останова removeListener, possiblyResizeChildOfContent все еще вызывается. Кто-нибудь еще с этим вопросом? - person Quinn; 06.03.2019
comment
Хорошо, я исправил свою проблему, просто указав тип слушателя вот так private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }. Я предполагаю, что без типа он не удалялся должным образом. - person Quinn; 06.03.2019
comment
Как убрать эти настройки окна при переходе к новому фрагменту? Он отлично работает во фрагменте, где у меня есть полноэкранный режим, но когда я возвращаюсь к предыдущему фрагменту, который не является полноэкранным, нижний вид навигации теперь скрывается за навигацией устройства. Я предполагаю, что любые вставки, выполняемые в полноэкранном режиме, переносятся на предыдущий фрагмент, который не требует каких-либо изменений окна / вставки. - person AndroidDev123; 29.12.2020

Я только что нашел простое и надежное решение, если вы используете подход системного пользовательского интерфейса (https://developer.android.com/training/system-ui/immersive.html).

Это работает в том случае, если вы используете View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, например. если вы используете CoordinatorLayout.

Он не будет работать для WindowManager.LayoutParams.FLAG_FULLSCREEN (тот, который вы также можете установить в теме с помощью android:windowFullscreen), но вы можете добиться аналогичного эффекта с помощью SYSTEM_UI_FLAG_LAYOUT_STABLE (который "имеет такой же визуальный эффект" согласно документации), и это решение должно снова работать.

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION /* If you want to hide navigation */
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE)

Я тестировал его на своем устройстве под управлением Marshmallow.

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

Для случая использования, такого как в DrawerLayout, где мы пытаемся рисовать за строкой состояния, мы можем создать макет, который игнорирует только верхнюю вставку и применяет нижнюю вставку, которая учитывает программную клавиатуру.

Вот мой обычай FrameLayout:

/**
 * Implements an effect similar to {@code android:fitsSystemWindows="true"} on Lollipop or higher,
 * except ignoring the top system window inset. {@code android:fitsSystemWindows="true"} does not
 * and should not be set on this layout.
 */
public class FitsSystemWindowsExceptTopFrameLayout extends FrameLayout {

    public FitsSystemWindowsExceptTopFrameLayout(Context context) {
        super(context);
    }

    public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
                                                 int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
                                                 int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(),
                    insets.getSystemWindowInsetBottom());
            return insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
        } else {
            return super.onApplyWindowInsets(insets);
        }
    }
}

И использовать это:

<com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Your original layout here -->
</com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout>

Теоретически это должно работать для любого устройства без безумных модификаций, намного лучше, чем любой взлом, который пытается взять случайный 1/3 или 1/4 размер экрана в качестве эталона.

(Для этого требуется API 16+, но я использую полноэкранный режим только на Lollipop + для рисования за строкой состояния, поэтому в данном случае это лучшее решение.)

person Hai Zhang    schedule 19.02.2017
comment
@Dilip Работает по API 16+ при соблюдении вышеупомянутых условий. - person Hai Zhang; 07.06.2017

Обратите внимание, что android:windowSoftInputMode="adjustResize" не работает, если для действия задано WindowManager.LayoutParams.FLAG_FULLSCREEN. У вас есть два варианта.

  1. Либо отключите полноэкранный режим для своей деятельности. Размер активности не изменяется в полноэкранном режиме. Вы можете сделать это либо в xml (изменив тему действия), либо в коде Java. Добавьте следующие строки в свой метод onCreate ().

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);   
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);`
    

ИЛИ

  1. Используйте альтернативный способ перехода в полноэкранный режим. Добавьте следующий код в свой метод onCreate ().

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    View decorView = getWindow().getDecorView();
    // Hide the status bar.
    int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
    decorView.setSystemUiVisibility(uiOptions);`
    

Обратите внимание, что способ 2 работает только в Android 4.1 и более поздних версиях.

person Abhinav Chauhan    schedule 06.03.2015
comment
@AnshulTyagi method-2 работает только в Android 4.1 и выше. - person Abhinav Chauhan; 30.06.2015
comment
Проверено на 5.0 и 4.4.2, Nexus 9 и Samsung s4 соответственно, 2-й метод не работает. - person RobVoisey; 29.10.2015
comment
2-й метод просто не работает, и я потратил на него много времени. - person Greg Ennis; 27.04.2017
comment
Спасибо, спаси мой день. - person Deni Rohimat; 09.01.2020

Мне тоже пришлось столкнуться с этой проблемой, и у меня была работа, которую я проверил на HTC one, galaxy s1, s2, s3, note и HTC sensation.

поместите прослушиватель глобального макета в корневое представление вашего макета

mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
            public void onGlobalLayout() {
                checkHeightDifference();
            }
    });

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

private void checkHeightDifference(){
    // get screen frame rectangle 
    Rect r = new Rect();
    mRootView.getWindowVisibleDisplayFrame(r);
    // get screen height
    int screenHeight = mRootView.getRootView().getHeight();
    // calculate the height difference
    int heightDifference = screenHeight - (r.bottom - r.top);

    // if height difference is different then the last height difference and
    // is bigger then a third of the screen we can assume the keyboard is open
    if (heightDifference > screenHeight/3 && heightDifference != mLastHeightDifferece) {
        // keyboard visiblevisible
        // get root view layout params
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
        // set the root view height to screen height minus the height difference
        lp.height = screenHeight - heightDifference;
        // call request layout so the changes will take affect
        .requestLayout();
        // save the height difference so we will run this code only when a change occurs.
        mLastHeightDifferece = heightDifference;
    } else if (heightDifference != mLastHeightDifferece) {
        // keyboard hidden
        PFLog.d("[ChatroomActivity] checkHeightDifference keyboard hidden");
        // get root view layout params and reset all the changes we have made when the keyboard opened.
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
        lp.height = screenHeight;
        // call request layout so the changes will take affect
        mRootView.requestLayout();
        // save the height difference so we will run this code only when a change occurs.
        mLastHeightDifferece = heightDifference;
    }
}

это, вероятно, не является пуленепробиваемым и, возможно, на некоторых устройствах это не сработает, но у меня это сработало и надеюсь, что это поможет и вам.

person yghm    schedule 15.08.2013
comment
Требовались некоторые корректировки, но это сработало. В Nexus 7 2013 мне пришлось на несколько пикселей уменьшить высоту клавиатуры (screenHeight / 3). Хорошая идея, спасибо! - person Joao Sousa; 04.11.2013

Добавьте android:fitsSystemWindows="true" в макет, и размер этого макета изменится.

person zayn    schedule 20.10.2017
comment
Вот что решило это для меня. Вдобавок подумайте, убедитесь, что вы установили правильный вид. Если у вас есть фон, который должен располагаться под строкой состояния, не устанавливайте его там, а на макете внутри. Вероятно, представления EditText и т. Д. Должны быть внутри этого второго макета внутри. Также посмотрите этот разговор, он проясняет ситуацию: youtube.com/watch?v=_mGDMVRO3iE < / а> - person Stan; 30.12.2019
comment
У меня тоже сработало. Благодаря комментарию @Stan я также смог заставить его работать с темой FULLSCREEN, разместив этот атрибут на ViewPager вместо макета активности / фрагмента. - person marcouberti; 03.03.2020
comment
У меня отлично сработало. Я поместил его поверх макета в моем фрагменте, и теперь полноэкранная тема отлично работает с флагом adjustResize. Вонки. - person Alex Sullivan; 02.07.2021

Я реализовал решение Джозефа Джонсона, и оно работало хорошо, я заметил после использования этого решения, что иногда ящик в приложении не закрывается должным образом. Я добавил функциональность для удаления слушателя removeOnGlobalLayoutListener, когда пользователь закрывает фрагмент, в котором расположены edittexts.

    //when the application uses full screen theme and the keyboard is shown the content not scrollable! 
//with this util it will be scrollable once again
//http://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible
public class AndroidBug5497Workaround {


    private static AndroidBug5497Workaround mInstance = null;
    private View mChildOfContent;
    private int usableHeightPrevious;
    private FrameLayout.LayoutParams frameLayoutParams;
    private ViewTreeObserver.OnGlobalLayoutListener _globalListener;

    // For more information, see https://code.google.com/p/android/issues/detail?id=5497
    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.

    public static AndroidBug5497Workaround getInstance (Activity activity) {
        if(mInstance==null)
        {
            synchronized (AndroidBug5497Workaround.class)
            {
                mInstance = new AndroidBug5497Workaround(activity);
            }
        }
        return mInstance;
    }

    private AndroidBug5497Workaround(Activity activity) {
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        mChildOfContent = content.getChildAt(0);
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();

        _globalListener = new ViewTreeObserver.OnGlobalLayoutListener()
        {

            @Override
            public void onGlobalLayout()
            {
                 possiblyResizeChildOfContent();
            }
        };
    }

    public void setListener()
    {
         mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(_globalListener);
    }

    public void removeListener()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(_globalListener);
        } else {
            mChildOfContent.getViewTreeObserver().removeGlobalOnLayoutListener(_globalListener);
        }
    }

    private void possiblyResizeChildOfContent() {
        int usableHeightNow = computeUsableHeight();
        if (usableHeightNow != usableHeightPrevious) {
            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
            if (heightDifference > (usableHeightSansKeyboard/4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            } else {
                // keyboard probably just became hidden
                frameLayoutParams.height = usableHeightSansKeyboard;
            }
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        return (r.bottom - r.top);
    } 
}

использует класс, в котором находятся мои тексты редактирования

@Override
public void onStart()
{
    super.onStart();
    AndroidBug5497Workaround.getInstance(getActivity()).setListener();
}

@Override
public void onStop()
{
    super.onStop();
    AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
person visionix visionix    schedule 13.04.2016

Чтобы заставить его работать с FullScreen:

Используйте плагин ionic keyboard. Это позволяет вам отслеживать, когда клавиатура появляется и исчезает.

OnDeviceReady добавляет эти прослушиватели событий:

// Allow Screen to Move Up when Keyboard is Present
window.addEventListener('native.keyboardshow', onKeyboardShow);
// Reset Screen after Keyboard hides
window.addEventListener('native.keyboardhide', onKeyboardHide);

Логика:

function onKeyboardShow(e) {
    // Get Focused Element
    var thisElement = $(':focus');
    // Get input size
    var i = thisElement.height();
    // Get Window Height
    var h = $(window).height()
    // Get Keyboard Height
    var kH = e.keyboardHeight
    // Get Focused Element Top Offset
    var eH = thisElement.offset().top;
    // Top of Input should still be visible (30 = Fixed Header)
    var vS = h - kH;
    i = i > vS ? (vS - 30) : i;
    // Get Difference
    var diff = (vS - eH - i);
    if (diff < 0) {
        var parent = $('.myOuter-xs.myOuter-md');
        // Add Padding
        var marginTop = parseInt(parent.css('marginTop')) + diff - 25;
        parent.css('marginTop', marginTop + 'px');
    }
}

function onKeyboardHide(e) {
  // Remove All Style Attributes from Parent Div
  $('.myOuter-xs.myOuter-md').removeAttr('style');
}

В основном, если разница между ними отрицательная, то это количество пикселей, которое клавиатура покрывает ваш ввод. Итак, если вы настроите свой родительский div этим, это должно противодействовать этому.

Добавление таймаутов к логике говорит, что 300 мс также должны оптимизировать производительность (так как это позволит отображать время клавиатуры.

person tyler_mitchell    schedule 11.09.2015

Я попробовал уроки Джозефа Джонсона, и они сработали, но не совсем соответствовали моим потребностям. Вместо эмуляции android: windowSoftInputMode = "adjustResize" мне нужно было эмулировать android: windowSoftInputMode = "adjustPan".

Я использую это для полноэкранного просмотра в Интернете. Чтобы переместить представление содержимого в правильное положение, мне нужно использовать интерфейс javascript, который предоставляет подробную информацию о положении элемента страницы, который имеет фокус и, таким образом, получает ввод с клавиатуры. Я опустил эти детали, но предоставил свой переписанный курс Джозефа Джонсона. Это обеспечит вам очень прочную основу для реализации кастомного панорамирования по сравнению с его изменением размера.

package some.package.name;

import some.package.name.JavaScriptObject;

import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;

//-------------------------------------------------------
// ActivityPanner Class
//
// Convenience class to handle Activity attributes bug.
// Use this class instead of windowSoftInputMode="adjustPan".
//
// To implement, call enable() and pass a reference
// to an Activity which already has its content view set.
// Example:
//      setContentView( R.layout.someview );
//      ActivityPanner.enable( this );
//-------------------------------------------------------
//
// Notes:
//
// The standard method for handling screen panning
// when the virtual keyboard appears is to set an activity
// attribute in the manifest.
// Example:
// <activity
//      ...
//      android:windowSoftInputMode="adjustPan"
//      ... >
// Unfortunately, this is ignored when using the fullscreen attribute:
//      android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
//
//-------------------------------------------------------
public class ActivityPanner {

    private View contentView_;
    private int priorVisibleHeight_;

    public static void enable( Activity activity ) {
        new ActivityPanner( activity );
    }

    private ActivityPanner( Activity activity ) {
        FrameLayout content = (FrameLayout)
            activity.findViewById( android.R.id.content );
        contentView_ = content.getChildAt( 0 );
        contentView_.getViewTreeObserver().addOnGlobalLayoutListener(
            new ViewTreeObserver.OnGlobalLayoutListener() {
                public void onGlobalLayout() { panAsNeeded(); }
        });
    }

    private void panAsNeeded() {

        // Get current visible height
        int currentVisibleHeight = visibleHeight();

        // Determine if visible height changed
        if( currentVisibleHeight != priorVisibleHeight_ ) {

            // Determine if keyboard visiblity changed
            int screenHeight =
                contentView_.getRootView().getHeight();
            int coveredHeight =
                screenHeight - currentVisibleHeight;
            if( coveredHeight > (screenHeight/4) ) {
                // Keyboard probably just became visible

                // Get the current focus elements top & bottom
                // using a ratio to convert the values
                // to the native scale.
                float ratio = (float) screenHeight / viewPortHeight();
                int elTop = focusElementTop( ratio );
                int elBottom = focusElementBottom( ratio );

                // Determine the amount of the focus element covered
                // by the keyboard
                int elPixelsCovered = elBottom - currentVisibleHeight;

                // If any amount is covered
                if( elPixelsCovered > 0 ) {

                    // Pan by the amount of coverage
                    int panUpPixels = elPixelsCovered;

                    // Prevent panning so much the top of the element
                    // becomes hidden
                    panUpPixels = ( panUpPixels > elTop ?
                                    elTop : panUpPixels );

                    // Prevent panning more than the keyboard height
                    // (which produces an empty gap in the screen)
                    panUpPixels = ( panUpPixels > coveredHeight ?
                                    coveredHeight : panUpPixels );

                    // Pan up
                    contentView_.setY( -panUpPixels );
                }
            }
            else {
                // Keyboard probably just became hidden

                // Reset pan
                contentView_.setY( 0 );
            }

            // Save usabale height for the next comparison
            priorVisibleHeight_ = currentVisibleHeight;
        }
    }

    private int visibleHeight() {
        Rect r = new Rect();
        contentView_.getWindowVisibleDisplayFrame( r );
        return r.bottom - r.top;
    }

    // Customize this as needed...
    private int viewPortHeight() { return JavaScriptObject.viewPortHeight(); }
    private int focusElementTop( final float ratio ) {
        return (int) (ratio * JavaScriptObject.focusElementTop());
    }
    private int focusElementBottom( final float ratio ) {
        return (int) (ratio * JavaScriptObject.focusElementBottom());
    }

}
person BuvinJ    schedule 05.08.2014
comment
кажется, это то, что мне нужно, не могли бы вы добавить полный образец? спасибо за вашу работу! - person vilicvane; 28.01.2015
comment
Я не собирался публиковать весь проект. То, что я предоставил, приведет вас очень долгий путь к идеально работающему решению. Что вам нужно определить самостоятельно: создайте класс JavaScriptObject и вставьте его в свой веб-просмотр как интерфейс js (для этого см. Документацию по веб-просмотру). Есть хороший шанс, что вы уже это сделали, если пишете что-то, что исчерпывающе использует веб-просмотр. Добавьте JavaScript в веб-просмотр, чтобы прослушивать события фокуса и передавать данные в свой класс JavaScriptObject о позиционировании элемента фокуса. - person BuvinJ; 07.03.2016

В настоящее время я использую этот подход, и он отлично работает. Хитрость в том, что мы получаем высоту клавиатуры разными методами на 21 выше и ниже, а затем используем ее в качестве нижнего отступа нашего корневого представления в нашей деятельности. Я предположил, что ваш макет не требует верхнего отступа (идет под строкой состояния), но если вы это сделаете, сообщите мне, чтобы я обновил свой ответ.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RelativeLayout mainLayout = findViewById(R.id.main_layout);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ViewCompat.setOnApplyWindowInsetsListener(mainLayout , new OnApplyWindowInsetsListener() {
                @Override
                public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
                    v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
                    return insets;
                }
            });
        } else {
            View decorView = getWindow().getDecorView();
            final View contentView = mainLayout;
            decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    Rect r = new Rect();
                    //r will be populated with the coordinates of your view that area still visible.
                    decorView.getWindowVisibleDisplayFrame(r);

                    //get screen height and calculate the difference with the useable area from the r
                    int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
                    int diff = height - r.bottom;

                    //if it could be a keyboard add the padding to the view
                    if (diff != 0) {
                        // if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
                        //check if the padding is 0 (if yes set the padding for the keyboard)
                        if (contentView.getPaddingBottom() != diff) {
                            //set the padding of the contentView for the keyboard
                            contentView.setPadding(0, 0, 0, diff);
                        }
                    } else {
                        //check if the padding is != 0 (if yes reset the padding)
                        if (contentView.getPaddingBottom() != 0) {
                            //reset the padding of the contentView
                            contentView.setPadding(0, 0, 0, 0);
                        }
                    }
                }
            });
        }
    }
...
}

Не забудьте обратиться к корневому представлению с помощью идентификатора:

activity_main.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

Надеюсь, это кому-то поможет.

person Sdghasemi    schedule 03.10.2018
comment
Не могу понять, почему этот ответ до сих пор не в топе. Другие глючат, мигают, но этот великолепен, особенно если у вас 5+ api. - person Anton Shkurenko; 07.07.2020
comment
Я пробовал много решений, ни одно из которых не работает, кроме этого. Спасибо, Sdghasemi. - person Farzad; 13.01.2021

На самом деле внешний вид мягкой клавиатуры никоим образом не влияет на Activity, независимо от того, что windowSoftInputMode я выбираю в режиме FullScreen.

Хотя мне не удалось найти много документации по этому свойству, я думаю, что режим FullScreen был разработан для игровых приложений, которые не требуют большого использования программной клавиатуры. Если ваше действие требует взаимодействия с пользователем через программную клавиатуру, попробуйте использовать не полноэкранную тему. Вы можете отключить TitleBar, используя тему NoTitleBar. Зачем вам скрывать панель уведомлений?

person Arnab Chakraborty    schedule 26.09.2011

Просто оставьте как android:windowSoftInputMode="adjustResize". Потому что дано оставить только один из "adjustResize" и "adjustPan" (режим настройки окна указывается либо с помощью adjustResize, либо с помощью adjustPan. Настоятельно рекомендуется всегда указывать одно или другое). Вы можете найти его здесь: http://developer.android.com/resources/articles/on-screen-inputs.html

У меня он отлично работает.

person Balaji Khadake    schedule 15.09.2011
comment
У меня нет проблем ... Я тоже пробовал ваш XML. Этот тоже работает ... я использую Os версии 2.2 - person Balaji Khadake; 15.09.2011
comment
Я пробовал использовать только полноэкранный режим ... м тестировал его на своих Nexus One и Nexus S .... Работает. - person Balaji Khadake; 15.09.2011
comment
Я пробовал с Galaxy S, HTC wildfire, HTC Hero, Motorola Deify и Sony XPeria. Не работает ни на одном устройстве. - person Vineet Shukla; 15.09.2011
comment
позвольте нам продолжить обсуждение в чате - person Balaji Khadake; 15.09.2011

1) Создайте KeyboardHeightHelper:

public class KeyboardHeightHelper {

    private final View decorView;
    private int lastKeyboardHeight = -1;

    public KeyboardHeightHelper(Activity activity, View activityRootView, OnKeyboardHeightChangeListener listener) {
        this.decorView = activity.getWindow().getDecorView();
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
            int keyboardHeight = getKeyboardHeight();
            if (lastKeyboardHeight != keyboardHeight) {
                lastKeyboardHeight = keyboardHeight;
                listener.onKeyboardHeightChange(keyboardHeight);
            }
        });
    }

    private int getKeyboardHeight() {
        Rect rect = new Rect();
        decorView.getWindowVisibleDisplayFrame(rect);
        return decorView.getHeight() - rect.bottom;
    }

    public interface OnKeyboardHeightChangeListener {
        void onKeyboardHeightChange(int keyboardHeight);
    }
}

2) Пусть ваша активность будет в полноэкранном режиме:

activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

3) Прислушайтесь к изменениям высоты клавиатуры и добавьте нижний отступ для вашего вида:

View rootView = activity.findViewById(R.id.root); // your root view or any other you want to resize
KeyboardHeightHelper effectiveHeightHelper = new KeyboardHeightHelper(
        activity, 
        rootView,
        keyboardHeight -> rootView.setPadding(0, 0, 0, keyboardHeight));

Итак, каждый раз, когда на экране появляется клавиатура - нижнее поле для вашего представления будет меняться, а содержимое - переупорядочиваться.

person repitch    schedule 22.10.2018

используйте только android:windowSoftInputMode="adjustResize|stateHidden, поскольку вы используете AdjustPan, тогда он отключает свойство изменения размера

person Mohammed Azharuddin Shaikh    schedule 20.09.2011
comment
Я тоже им пользовался .... пожалуйста, сделайте так, что вы делаете это в полноэкранном режиме и на каком устройстве вы тестируете? - person Vineet Shukla; 20.09.2011
comment
HTC NEXUS one, хорошо, я не могу добавить полноэкранный режим - person Mohammed Azharuddin Shaikh; 20.09.2011
comment
можете ли вы использовать getWindow (). requestFeature (Window.FEATURE_NO_TITLE); onCreate () вместо использования темы? - person Mohammed Azharuddin Shaikh; 20.09.2011
comment
приведенный выше код работает нормально без полноэкранного режима, но добавляет полноэкранный режим либо из xml, либо из кода ... Он не работает ... Внимательно прочтите вопрос. - person Vineet Shukla; 20.09.2011

Я использовал Джозеф Джонсон, созданный классом AndroidBug5497Workaround, но между программной клавиатурой и представлением появилось черное пространство. Я сослался на эту ссылку Грега Энниса. После внесения некоторых изменений в вышеперечисленное, это мой последний рабочий код.

 public class SignUpActivity extends Activity {

 private RelativeLayout rlRootView; // this is my root layout
 private View rootView;
 private ViewGroup contentContainer;
 private ViewTreeObserver viewTreeObserver;
 private ViewTreeObserver.OnGlobalLayoutListener listener;
 private Rect contentAreaOfWindowBounds = new Rect();
 private FrameLayout.LayoutParams rootViewLayout;
 private int usableHeightPrevious = 0;

 private View mDecorView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_sign_up);
  mDecorView = getWindow().getDecorView();
  contentContainer =
   (ViewGroup) this.findViewById(android.R.id.content);

  listener = new OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    possiblyResizeChildOfContent();
   }
  };

  rootView = contentContainer.getChildAt(0);
  rootViewLayout = (FrameLayout.LayoutParams)
  rootView.getLayoutParams();

  rlRootView = (RelativeLayout) findViewById(R.id.rlRootView);


  rlRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    int heightDiff = rlRootView.getRootView().getHeight() - rlRootView.getHeight();
    if (heightDiff > Util.dpToPx(SignUpActivity.this, 200)) {
     // if more than 200 dp, it's probably a keyboard...
     //  Logger.info("Soft Key Board ", "Key board is open");

    } else {
     Logger.info("Soft Key Board ", "Key board is CLOSED");

     hideSystemUI();
    }
   }
  });
 }

 // This snippet hides the system bars.
 protected void hideSystemUI() {
  // Set the IMMERSIVE flag.
  // Set the content to appear under the system bars so that the 
  content
  // doesn't resize when the system bars hide and show.
  mDecorView.setSystemUiVisibility(
   View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
 }
 @Override
 protected void onPause() {
  super.onPause();
  if (viewTreeObserver.isAlive()) {
   viewTreeObserver.removeOnGlobalLayoutListener(listener);
  }
 }

 @Override
 protected void onResume() {
  super.onResume();
  if (viewTreeObserver == null || !viewTreeObserver.isAlive()) {
   viewTreeObserver = rootView.getViewTreeObserver();
  }
  viewTreeObserver.addOnGlobalLayoutListener(listener);
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  rootView = null;
  contentContainer = null;
  viewTreeObserver = null;
 }
 private void possiblyResizeChildOfContent() {
  contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);

  int usableHeightNow = contentAreaOfWindowBounds.height();

  if (usableHeightNow != usableHeightPrevious) {
   rootViewLayout.height = usableHeightNow;
   rootView.layout(contentAreaOfWindowBounds.left,
    contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
   rootView.requestLayout();

   usableHeightPrevious = usableHeightNow;
  } else {

   this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
  }
 }
}
person Venkat    schedule 29.05.2017

на основе https://stackoverflow.com/a/19494006/1815624 и желания осуществить это ...

обновленная идея


объединяя ответы от

Соответствующий код:

        if (heightDifference > (usableHeightSansKeyboard / 4)) {

            // keyboard probably just became visible
            frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        } else {

            // keyboard probably just became hidden
            if(usableHeightPrevious != 0) {
                frameLayoutParams.height = usableHeightSansKeyboard;
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
                activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

            }

Полный исходный код см. На https://github.com/CrandellWS/AndroidBug5497Workaround/blob/master/AndroidBug5497Workaround.java

старая идея

Создайте статическое значение высоты контейнеров перед открытием клавиатуры. Установите высоту контейнера на основе usableHeightSansKeyboard - heightDifference при открытии клавиатуры и установите для него сохраненное значение при закрытии.

if (heightDifference > (usableHeightSansKeyboard / 4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
                int mStatusHeight = getStatusBarHeight();
                frameLayoutParams.topMargin = mStatusHeight;
                ((MainActivity)activity).setMyMainHeight(usableHeightSansKeyboard - heightDifference);

                if(BuildConfig.DEBUG){
                    Log.v("aBug5497", "keyboard probably just became visible");
                }
            } else {
                // keyboard probably just became hidden
                if(usableHeightPrevious != 0) {
                    frameLayoutParams.height = usableHeightSansKeyboard;
                    ((MainActivity)activity).setMyMainHeight();    
                }
                frameLayoutParams.topMargin = 0;

                if(BuildConfig.DEBUG){
                    Log.v("aBug5497", "keyboard probably just became hidden");
                }
            }

Методы в MainActivity

public void setMyMainHeight(final int myMainHeight) {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            ConstraintLayout.LayoutParams rLparams =  (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
            rLparams.height = myMainHeight;

            myContainer.setLayoutParams(rLparams);
        }

    });

}

int mainHeight = 0;
public void setMyMainHeight() {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            ConstraintLayout.LayoutParams rLparams =  (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
            rLparams.height = mainHeight;

            myContainer.setLayoutParams(rLparams);
        }

    });

}

Пример контейнера XML

<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
        <android.support.constraint.ConstraintLayout
            android:id="@+id/my_container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintHeight_percent=".8">

аналогично поля могут быть добавлены при необходимости ...

Еще одно соображение - использовать отступы. Пример этого можно найти по адресу:

https://github.com/mikepenz/MaterialDrawer/issues/95#issuecomment-80519589

person Community    schedule 05.10.2018

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

Вы можете попробовать этот фрагмент кода:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    ...>

    <RelativeLayout
        android:id="@+id/RelativeLayoutTopBar"
    ...>
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/LinearLayoutBottomBar"
        android:layout_alignParentBottom = true
        ...>
    </LinearLayout>

    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="390dp"
    android:orientation="vertical" 
    android:layout_above="@+id/LinearLayoutBottomBar"
    android:layout_below="@+id/RelativeLayoutTopBar"> 

    <ScrollView 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:id="@+id/ScrollViewBackground">

            ...

        </ScrollView>
     </LinearLayout>
  </RelativeLayout>

BottomBar будет придерживаться нижней части представления, а LinearLayout, содержащий ScrollView, займет то, что осталось от представления после отображения верхней / нижней панели и клавиатуры. Сообщите мне, работает ли это и для вас.

person banzai86    schedule 20.09.2011
comment
Очень странно, так как в моих приложениях он работал несколько раз. Кстати, RelativeLayout не имеет ориентации, поэтому вы можете удалить эти атрибуты в своем коде. Я только что понял, что мог бы сократить фрагмент кода до строки: android: layout_below = @ + id / scoringContainerView, которую вы должны добавить в свой ScrollView - person banzai86; 20.09.2011
comment
Полноэкранный? Вы имеете в виду без макета вверху? - person banzai86; 20.09.2011
comment
нет ..... Я имею в виду отсутствие строки состояния, которая показывает время автономной работы, возможность подключения к устройству и т. д. - person Vineet Shukla; 20.09.2011
comment
нет, в моем приложении отображается строка состояния. Можете ли вы попробовать изменить порядок ваших макетов, что означает, что вы поместите код для своего макета с помощью кнопок над другим кодом, а затем повторите попытку? возможно, вам нужно сначала определить их, чтобы использовать layout_below - person banzai86; 20.09.2011
comment
пожалуйста, внимательно прочтите вопрос ... Я уже упоминал, что у меня проблемы с полноэкранным режимом ... - person Vineet Shukla; 20.09.2011

Спасибо, Джозеф, за ответ. Однако в методе возможноResizeChildOfContent () часть

else {
            // keyboard probably just became hidden
            frameLayoutParams.height = usableHeightSansKeyboard;
        }

у меня не работал, так как нижняя часть обзора стала скрытой. Поэтому мне пришлось взять глобальную переменную restoreHeight, и в конструкторе я вставил последнюю строку

restoreHeight = frameLayoutParams.height;

а затем я заменил ранее упомянутую часть на

else {
            // keyboard probably just became hidden
            frameLayoutParams.height = restoreHeight;
        }

Но я понятия не имею, почему ваш код не работал у меня. Было бы очень полезно, если бы кто-нибудь мог пролить свет на это.

person Debanjan    schedule 27.01.2016

Я использовал только полноэкранный режим, чтобы скрыть строку состояния. Однако я хочу, чтобы размер приложения изменялся при отображении клавиатуры. Все другие решения (вероятно, из-за возраста публикации) были сложными или невозможными для моего использования (хочу избежать изменения кода Java для мешка PhoneGap Build).

Вместо того, чтобы использовать полноэкранный режим, я изменил свою конфигурацию для Android, чтобы он не был полноэкранным:

            <preference name="fullscreen" value="false" />

И добавил cordova-plugin-statusbar через командную строку:

cordova plugin add cordova-plugin-statusbar

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

    if (window.cordova && window.cordova.platformId == 'android' && window.StatusBar)
        window.StatusBar.hide();

Это работает как шарм. Единственным реальным недостатком является то, что строка состояния видна во время загрузки приложения. Для моих нужд это не было проблемой.

person raider33    schedule 25.03.2016

Я испробовал все возможные ответы из stackOverflow, наконец, решил после недели долгого поиска. Я использовал раскладку координат и изменил ее с помощью linearLayout, и моя проблема исправлена. Я не знаю, возможно, в компоновке координат есть ошибки или что-то в этом роде.

person yubaraj poudel    schedule 26.06.2016

Я пробовал много решений, включая Джозефа Джонсона и Йохана Стуйца. Но в результате у меня на некоторых устройствах (например, Lenovo s820) во всех случаях возникает пробел между содержимым и клавиатурой. Поэтому я внес некоторые изменения в их коды и, наконец, получил рабочее решение.

Моя идея основана на добавлении поля поверх содержимого при отображении клавиатуры.

contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
    int usableHeightNow = contentAreaOfWindowBounds.height();

    if (usableHeightNow != usableHeightPrevious) {

        int difference = usableHeightNow - usableHeightPrevious;

        if (difference < 0 && difference < -150) {
            keyboardShowed = true;
            rootViewLayout.topMargin -= difference + 30;
            rootViewLayout.bottomMargin += 30;
        }
        else if (difference < 0 && difference > -150){
            rootViewLayout.topMargin -= difference + 30;
        }
        else if (difference > 0 && difference > 150) {
            keyboardShowed = false;
            rootViewLayout.topMargin = 0;
            rootViewLayout.bottomMargin = 0;
        }

        rootView.requestLayout();

        Log.e("Bug Workaround", "Difference: " + difference);

        usableHeightPrevious = usableHeightNow;
}

Как видите, я добавил 30 пикселей к разнице, потому что между верхней частью экрана и зоной содержимого с полями есть небольшое белое пространство. И я не знаю, откуда это появляется, поэтому я решил просто уменьшить поля, и теперь он работает именно так, как мне нужно.

person IDmikael    schedule 06.12.2017

Сегодня не работает AdjustResize в полноэкранном режиме. Проблема актуальна для android sdk.

Из ответов, которые я нашел:
решение, но решение имеет это на изображении проблемы:

Затем я нашел решение и удалил одно ненужное действие:

this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

Итак, посмотрите мой фиксированный код решения на Kotlin:

class AndroidBug5497Workaround constructor(val activity: Activity) {

    private val content = activity.findViewById<View>(android.R.id.content) as FrameLayout

    private val mChildOfContent = content.getChildAt(0)
    private var usableHeightPrevious: Int = 0
    private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
    private val rootView = contentContainer.getChildAt(0)
    private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams

    private val listener = {
        possiblyResizeChildOfContent()
    }

    fun addListener() {
        mChildOfContent.apply {
            viewTreeObserver.addOnGlobalLayoutListener(listener)

        }
    }

    fun removeListener() {
        mChildOfContent.apply {
            viewTreeObserver.removeOnGlobalLayoutListener(listener)
        }
    }

    private fun possiblyResizeChildOfContent() {
        val contentAreaOfWindowBounds = Rect()
        mChildOfContent.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
        val usableHeightNow = contentAreaOfWindowBounds.height()

        if (usableHeightNow != usableHeightPrevious) {
            rootViewLayout.height = usableHeightNow
            rootView.layout(contentAreaOfWindowBounds.left,
                    contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
            mChildOfContent.requestLayout()
            usableHeightPrevious = usableHeightNow
        }
    }
}

Мой код реализации исправления ошибок:

 class LeaveDetailActivity : BaseActivity(){

    private val keyBoardBugWorkaround by lazy {
        AndroidBug5497Workaround(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }

    override fun onResume() {
        keyBoardBugWorkaround.addListener()
        super.onResume()
    }

    override fun onPause() {
        keyBoardBugWorkaround.removeListener()
        super.onPause()
    }
}
person Serg Burlaka    schedule 14.01.2019

private void resizeWindowOnKeyboardVisible() {
            RelativeLayout rootLayout;
            rootLayout = findViewById(R.id.rootLayout);
            this.getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    
                ViewGroup.LayoutParams layoutParams = rootLayout.getLayoutParams();
                int height ;
    
                @Override
                public void onGlobalLayout() {
                    Rect r = new Rect();
                    getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
                    int screenHeight = rootLayout.getContext().getResources().getDisplayMetrics().heightPixels;
                    int heightDiff = screenHeight - r.bottom;
    
                    if (heightDiff > screenHeight*0.15)
                    {
                        height = screenHeight - heightDiff;
                        layoutParams.height=height;
                        rootLayout.setLayoutParams(layoutParams);
                    }else{
                        height=ViewGroup.LayoutParams.MATCH_PARENT;
                        if( height!=layoutParams.height) {
                            layoutParams.height = height;
                            rootLayout.setLayoutParams(layoutParams);
                        }
                    }
                }
            });
        }

Использование android: windowSoftInputMode = adjustResize | stateHidden может работать не во всех случаях, а также android: fitsSystemWindows = true не помогает при использовании тегов SYSTEM_UI_FLAG_FULLSCREEN. Чтобы настроить вид / окно / веб-просмотр при отображении клавиатуры, выполните следующие действия.

  • Используйте RelativeLayout в качестве корневого макета.
  • Объявите указанный выше метод resizeWindowOnKeyboardVisible () в действии и вызовите его после setContentView () в методе onCreate ().

Он также работает в Android 11 (API 30).

person Nag Kosanam    schedule 18.05.2021

В моем случае эта проблема возникла после того, как я добавил Crosswalk в свое приложение Cordova. Мое приложение не используется в полноэкранном режиме и в android: windowSoftInputMode = "adjustPan".

У меня уже был плагин ionic keyboard в приложении, поэтому определить, находится ли клавиатура вверх или вниз, было легко благодаря ему:

// Listen for events to when the keyboard is opened and closed
window.addEventListener("native.keyboardshow", keyboardUp, false);
window.addEventListener('native.keyboardhide', keyboardDown, false);

function keyboardUp()
{
    $('html').addClass('keyboardUp');
}

function keyboardDown()
{
    $('html').removeClass('keyboardUp');
}

Я попробовал все исправления, указанные выше, но простая строка, которая в итоге сделала это за меня, была эта часть css:

&.keyboardUp {
        overflow-y: scroll;
}

Надеюсь, это сэкономит вам несколько дней, которые я потратил на это. :)

person J R    schedule 06.07.2016
comment
Я использую пешеходный переход с кордовой, а также с android: windowSoftInputMode = adjustPan. Однако это не работает. Я вижу, что класс добавляется к элементу html, но CSS не влияет на экран. Есть ли у вас какие-то другие настройки, позволяющие перемещать экран? - person darewreck; 18.10.2016
comment
Мне нужно установить добавление преобразования: translateY (0px), чтобы оно работало. Однако прокрутка вообще не работает. Любые идеи? - person darewreck; 18.10.2016

Не используйте:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

потому что плохо работает. Вместо этого используйте:

fun setFullScreen(fullScreen: Boolean) {
        val decorView = getWindow().getDecorView()
        val uiOptions : Int
        if(fullScreen){
            uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN // this hide statusBar
            toolbar.visibility = View.GONE // if you use toolbar
            tabs.visibility = View.GONE // if you use tabLayout
        } else {
            uiOptions = View.SYSTEM_UI_FLAG_VISIBLE // this show statusBar
            toolbar.visibility = View.VISIBLE
            tabs.visibility = View.VISIBLE
        }
        decorView.setSystemUiVisibility(uiOptions)
    }
person Alberto    schedule 04.03.2019