swiftkey - Android Cómo ajustar el diseño en el modo de pantalla completa cuando el teclado está visible
teclado gboard (21)
I used Joseph Johnson created AndroidBug5497Workaround class but getting black space between softkeyboard and the view. I referred this link gist.github.com/grennis/2e3cd5f7a9238c59861015ce0a7c5584 . After doing some changes to the above this is my final working code.
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);
}
}
}
He investigado mucho para ajustar el diseño cuando el teclado está activo y lo he implementado con éxito, pero el problema viene cuando uso android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
esto en mi etiqueta de actividad en el archivo de manifiesto .
Para esto he usado android:windowSoftInputMode="adjustPan|adjustResize|stateHidden"
con diferentes opciones pero sin suerte.
Después de eso implementé FullScreen
programación e intenté varios diseños para trabajar con FullScreen
pero todo fue en vano.
Me referí a estos enlaces y he visto muchas publicaciones relacionadas con este tema:
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/
Aquí está el código 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>
Quiero que los botones de la parte inferior 2 vayan hacia arriba cuando el teclado aparezca en la imagen.
Acabo de encontrar una solución simple y confiable si está utilizando el enfoque de IU del sistema ( https://developer.android.com/training/system-ui/immersive.html ).
Funciona en el caso cuando está usando View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
, por ejemplo, si está utilizando CoordinatorLayout
.
No funcionará para WindowManager.LayoutParams.FLAG_FULLSCREEN
(El que también puede establecer en el tema con android:windowFullscreen
), pero puede lograr un efecto similar con SYSTEM_UI_FLAG_LAYOUT_STABLE
(que "tiene el mismo efecto visual" según los documentos ) y esto la solución debería funcionar de nuevo.
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)
Lo probé en mi dispositivo con Marshmallow.
La clave es que los teclados suaves también son una de las ventanas del sistema (como la barra de estado y la barra de navegación), por lo que los WindowInsets
enviados por el sistema contienen información precisa y confiable al respecto.
Para el caso de uso, como en DrawerLayout
donde intentamos dibujar detrás de la barra de estado, podemos crear un diseño que ignore solo el recuadro superior y aplique el recuadro inferior que representa el teclado virtual.
Aquí está mi FrameLayout
personalizado:
/**
* 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);
}
}
}
Y para usarlo:
<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>
Esto debería funcionar teóricamente para cualquier dispositivo sin modificación insana, mucho mejor que cualquier hack que intente tomar un tamaño de pantalla al azar de 1/3
o 1/4
como referencia.
(Requiere API 16+, pero estoy usando pantalla completa solo en Lollipop + para dibujar detrás de la barra de estado, así que es la mejor solución en este caso).
Como ya se ha elegido la respuesta y se sabe que un problema es un error, pensé que agregaría un "Posible trabajo alternativo".
Puede alternar el modo pantalla completa cuando se muestra el teclado virtual. Esto permite que "adjustPan" funcione correctamente.
En otras palabras, todavía uso @android: style / Theme.Black.NoTitleBar.Fullscreen como parte del tema de la aplicación y stateVisible | adjustResize como parte del modo de entrada suave de la ventana de actividad, pero para que funcionen juntos, debo activar el modo de pantalla completa. antes de que aparezca el teclado.
Use el siguiente código:
Desactivar el modo de pantalla completa
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
Activar el modo de pantalla completa
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
Nota: la inspiración vino de: Ocultar el título en un modo de pantalla completa
De hecho, la apariencia del teclado suave no parece afectar la Activity
de ninguna manera, sin importar qué windowSoftInputMode
seleccione en el modo windowSoftInputMode
.
Aunque no pude encontrar mucha documentación sobre esta propiedad, creo que el modo FullScreen
fue diseñado para aplicaciones de juegos que no requieren mucho uso del teclado virtual. Si la suya es una actividad que requiere la interacción del usuario a través del teclado virtual, reconsidere el uso de un tema que no sea de pantalla completa. Puede desactivar la NoTitleBar
título utilizando un tema NoTitleBar
. ¿Por qué querrías esconder la barra de notificaciones?
En base a la solución de yghm, codifiqué una clase de conveniencia que me permite resolver el problema con una sola línea (después de agregar la nueva clase a mi código fuente, por supuesto). El one-liner es:
AndroidBug5497Workaround.assistActivity(this);
Y la clase de implementación es:
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);
}
}
Espero que esto ayude a alguien.
Implementé la solución Joseph Johnson y funcionó bien, me di cuenta después de usar esta solución que a veces el cajón de la aplicación no se cierra correctamente. Agregué una funcionalidad para eliminar el oyente removeOnGlobalLayoutListener cuando el usuario cierra el fragmento donde se encuentran 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://.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);
}
}
usa la clase donde están mis edittexts localizados
@Override
public void onStart()
{
super.onStart();
AndroidBug5497Workaround.getInstance(getActivity()).setListener();
}
@Override
public void onStop()
{
super.onStop();
AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
Para que funcione con FullScreen:
Use el plugin del teclado iónico. Esto le permite escuchar cuando el teclado aparece y desaparece.
OnDeviceReady agrega estos detectores de eventos:
// Allow Screen to Move Up when Keyboard is Present
window.addEventListener(''native.keyboardshow'', onKeyboardShow);
// Reset Screen after Keyboard hides
window.addEventListener(''native.keyboardhide'', onKeyboardHide);
La lógica:
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'');
}
Básicamente, si la diferencia es negativa, entonces esa es la cantidad de píxeles que cubre su entrada. Entonces, si ajustas tu div principal por esto, eso debería contrarrestarlo.
Agregar tiempos de espera a la lógica dice que 300ms también deberían optimizar el rendimiento (ya que esto permitirá que aparezca la hora del teclado).
Probé la clase de Joseph Johnson, y funcionó, pero no satisfizo mis necesidades. En lugar de emular Android: windowSoftInputMode = "adjustResize", necesitaba emular Android: windowSoftInputMode = "adjustPan".
Estoy usando esto para una vista web de pantalla completa. Para desplazar la vista de contenido a la posición correcta, necesito usar una interfaz de JavaScript que proporcione detalles sobre la posición del elemento de página que tiene foco y, por lo tanto, recibe la entrada del teclado. He omitido esos detalles, pero proporcioné mi reescritura de la clase de Joseph Johnson. Proporcionará una base muy sólida para implementar una panorámica personalizada frente a su redimensionamiento.
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());
}
}
Probé la solución de Joseph Johnson , pero al igual que otros me encontré con el problema de la brecha entre el contenido y el teclado. El problema ocurre porque el modo de entrada suave siempre se desplaza cuando se usa el modo de pantalla completa. Esta panorámica interfiere con la solución de Joseph cuando activa un campo de entrada que estaría oculto por la entrada suave.
Cuando aparece la entrada suave, primero se analiza el contenido en función de su altura original, y luego se redimensiona según el diseño solicitado por la solución de Joseph. El cambio de tamaño y el diseño posterior no anulan la panoramización, lo que da como resultado la brecha. El orden completo de los eventos es:
- Oyente de diseño global
- Panorámica
- Diseño del contenido (= cambio de tamaño real del contenido)
No es posible desactivar la panoramización, pero es posible forzar que la compensación de panorama sea 0 cambiando la altura del contenido. Esto se puede hacer en el oyente, porque se ejecuta antes de que se produzca el paneo. Establecer la altura del contenido a la altura disponible da como resultado una experiencia de usuario fluida, es decir, sin parpadeos.
También hice estos cambios. Si alguno de estos introduce problemas, hágamelo saber:
- Se cambió la determinación de la altura disponible para usar
getWindowVisibleDisplayFrame
. ElRect
se almacena en caché para evitar un poco de basura innecesaria. - Permita que el oyente sea eliminado también. Esto es útil cuando reutiliza una actividad para diferentes fragmentos que tienen diferentes requisitos de pantalla completa.
- No distinga entre teclado mostrado u oculto, pero siempre configure la altura del contenido a la altura del marco de la pantalla visible.
Se ha probado en un Nexus 5 y emuladores que ejecutan niveles de API 16-24 con tamaños de pantalla que varían de pequeños a grandes.
El código ha sido portado a Kotlin, pero transportar mis cambios a Java es simple. Déjame saber si necesitas ayuda:
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 = { 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
}
}
}
Tenga en cuenta que android:windowSoftInputMode="adjustResize"
no funciona cuando WindowManager.LayoutParams.FLAG_FULLSCREEN
está configurado para una actividad. Tienes dos opciones.
Desactive el modo de pantalla completa para su actividad. La actividad no se vuelve a clasificar según el tamaño en modo de pantalla completa. Puede hacerlo en xml (cambiando el tema de la actividad) o en código Java. Agregue las siguientes líneas en su método onCreate ().
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);`
O
Use una forma alternativa para lograr el modo de pantalla completa. Agregue el siguiente código en su método 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);`
Tenga en cuenta que el método 2 solo funciona en Android 4.1 y versiones posteriores.
Tuve que enfrentar este problema también y tuve un trabajo en el cual revisé HTC uno, galaxy s1, s2, s3, note y HTC Sensation.
pon un oyente de diseño global en la vista raíz de tu diseño
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
public void onGlobalLayout() {
checkHeightDifference();
}
});
y allí revisé la diferencia de altura y si la diferencia de altura de la pantalla es más grande que una tercera en la altura de la pantalla, podemos suponer que el teclado está abierto. lo tomó de esta respuesta .
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;
}
}
Probablemente esto no sea a prueba de balas y tal vez en algunos dispositivos no funcione, pero funcionó para mí y espero que también te ayude.
solo use android:windowSoftInputMode="adjustResize|stateHidden
como usa AdjustPan luego deshabilita la propiedad de cambio de tamaño
-> https://groups.google.com/group/android-developers/msg/5690ac3a9819a53b?pli=1
-> modo de pantalla completa no cambia el tamaño
Add android:fitsSystemWindows="true"
to the layout, and this layout will resize.
I have tried out all the possible answers from , finally i solved after a week Long search . I have used the coordinate layout and i changed this with linearLayout and my problem is fixed. I dont know possibly the coordinate layout has bugs or anything my mistake.
I tried many solutions include Joseph Johnson''s and Johan Stuyts''s. But as a result I got a white space between content and keyboard on some devices (like Lenovo s820) in all cases. So I made some changes to their codes and finally got working solution.
My idea based on adding margin to top of content when keyboard is showing.
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;
}
Como puede ver, agrego 30 px a la diferencia porque hay un pequeño espacio en blanco entre la parte superior de la pantalla y la zona de contenido con margen. Y no sé de dónde viene, así que decidí limitar los márgenes y ahora funciona exactamente como lo necesitaba.
I was only using full screen mode to hide the status bar. However, I want the app to resize when keyboard is shown. All of the other solutions (likely due to age of post) were complicated or not possible for my use (want to avoid change Java code for sack of PhoneGap Build).
Instead of using Full screen, I modified my configure for Android to be non-fullscreen:
<preference name="fullscreen" value="false" />
And added the cordova-plugin-statusbar
, via command line:
cordova plugin add cordova-plugin-statusbar
When app has loaded, I simple call a method on the plugin to hide itself, like:
if (window.cordova && window.cordova.platformId == ''android'' && window.StatusBar)
window.StatusBar.hide();
This works like a charm. Only real downside is that the status bar is breifly visible while the app loads. For my needs, that wasn''t an issue.
In my case, this issue started happening once I added Crosswalk to my Cordova application. My app is not used in fullscreen and android:windowSoftInputMode="adjustPan".
I already had the ionic keyboard plugin in the application, so detecting if the keyboard was up or down was easy thanks to it:
// 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'');
}
I tried all of the fixes above but the simple line that ended up doing it for me was this bit of css:
&.keyboardUp {
overflow-y: scroll;
}
Hope this saves you the few days I spent on this. :)
Just keep as android:windowSoftInputMode="adjustResize"
. Because it is given to keep only one out of "adjustResize"
and "adjustPan"
(The window adjustment mode is specified with either adjustResize or adjustPan. It is highly recommended that you always specify one or the other). You can find it out here: http://developer.android.com/resources/articles/on-screen-inputs.html
It works perfectly for me.
Thank you Joseph for your answer. However, in the method possiblyResizeChildOfContent(), the portion
else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
was not working for me, as the lower portion of view became hidden. So I had to take a global variable restoreHeight, and in the constructor, I inserted the last line
restoreHeight = frameLayoutParams.height;
and then I replaced the former mentioned part with
else {
// keyboard probably just became hidden
frameLayoutParams.height = restoreHeight;
}
But I have no idea why your code didn''t work for me. It would be of great help, if someone can shed light on this.
You want the bottom bar to stick to the bottom of the view, but when the keyboard is displayed, they should move up to be placed above the keyboard, right?
You can try this code snippet:
<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>
The BottomBar will stick to the bottom of the view and the LinearLayout containing the ScrollView will take what''s left of the view after the top/bottom bar and the keyboard are displayed. Let me know if it works for you as well.