Me gustaría alterar el diseño en función de si se muestra o no el teclado virtual. He buscado en la API y en varios blogs, pero parece que no puedo encontrar nada útil.
¿Es posible?
¡Gracias!
Me gustaría alterar el diseño en función de si se muestra o no el teclado virtual. He buscado en la API y en varios blogs, pero parece que no puedo encontrar nada útil.
¿Es posible?
¡Gracias!
Respuestas:
Esta solución no funcionará para teclados suaves y
onConfigurationChanged
no se llamará para teclados suaves (virtuales).
Tienes que manejar los cambios de configuración tú mismo.
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
Muestra:
// from the link above
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks whether a hardware keyboard is available
if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
} else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
}
}
Luego, simplemente cambie la visibilidad de algunas vistas, actualice un campo y cambie su archivo de diseño.
newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
~ Chris
Esta puede no ser la solución más efectiva. Pero esto funcionó para mí cada vez ... Llamo a esta función donde necesito escuchar el softKeyboard.
boolean isOpened = false;
public void setListenerToRootView() {
final View activityRootView = getWindow().getDecorView().findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.
Toast.makeText(getApplicationContext(), "Gotcha!!! softKeyboardup", 0).show();
if (isOpened == false) {
//Do two things, make the view top visible and the editText smaller
}
isOpened = true;
} else if (isOpened == true) {
Toast.makeText(getApplicationContext(), "softkeyborad Down!!!", 0).show();
isOpened = false;
}
}
});
}
Nota: Este enfoque causará problemas si el usuario usa un teclado flotante.
android:windowSoftInputMode="adjustPan"
, o adjustResize
con una ventana de pantalla completa, ya que el diseño nunca cambia de tamaño.
Si desea manejar mostrar / ocultar la ventana del teclado IMM (virtual) desde su Actividad, deberá subclasificar su diseño y anular el método onMesure (para que pueda determinar el ancho medido y la altura medida de su diseño). Después de eso, establezca el diseño subclasificado como vista principal para su Actividad mediante setContentView (). Ahora podrá manejar los eventos de ventana mostrar / ocultar IMM. Si esto suena complicado, en realidad no es así. Aquí está el código:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<EditText
android:id="@+id/SearchText"
android:text=""
android:inputType="text"
android:layout_width="fill_parent"
android:layout_height="34dip"
android:singleLine="True"
/>
<Button
android:id="@+id/Search"
android:layout_width="60dip"
android:layout_height="34dip"
android:gravity = "center"
/>
</LinearLayout>
Ahora dentro de su Actividad, declare la subclase para su diseño (main.xml)
public class MainSearchLayout extends LinearLayout {
public MainSearchLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.main, this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("Search Layout", "Handling Keyboard Window shown");
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();
if (actualHeight > proposedheight){
// Keyboard is shown
} else {
// Keyboard is hidden
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Puede ver en el código que inflamos el diseño de nuestra Actividad en el constructor de subclase
inflater.inflate(R.layout.main, this);
Y ahora solo configure la vista de contenido del diseño subclasificado para nuestra Actividad.
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainSearchLayout searchLayout = new MainSearchLayout(this, null);
setContentView(searchLayout);
}
// rest of the Activity code and subclassed layout...
}
android:windowSoftInputMode="adjustPan"
, o adjustResize
con una ventana de pantalla completa, ya que el diseño nunca cambia de tamaño.
Hice de esta manera:
Agregar OnKeyboardVisibilityListener
interfaz.
public interface OnKeyboardVisibilityListener {
void onVisibilityChanged(boolean visible);
}
HomeActivity.java :
public class HomeActivity extends Activity implements OnKeyboardVisibilityListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
// Other stuff...
setKeyboardVisibilityListener(this);
}
private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
final View parentView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private boolean alreadyOpen;
private final int defaultKeyboardHeightDP = 100;
private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect rect = new Rect();
@Override
public void onGlobalLayout() {
int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
parentView.getWindowVisibleDisplayFrame(rect);
int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
if (isShown == alreadyOpen) {
Log.i("Keyboard state", "Ignoring global layout change...");
return;
}
alreadyOpen = isShown;
onKeyboardVisibilityListener.onVisibilityChanged(isShown);
}
});
}
@Override
public void onVisibilityChanged(boolean visible) {
Toast.makeText(HomeActivity.this, visible ? "Keyboard is active" : "Keyboard is Inactive", Toast.LENGTH_SHORT).show();
}
}
Espero que esto te ayude.
Basado en el Código de Nebojsa Tomcic, he desarrollado la siguiente Subclase RelativeLayout:
import java.util.ArrayList;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
public class KeyboardDetectorRelativeLayout extends RelativeLayout {
public interface IKeyboardChanged {
void onKeyboardShown();
void onKeyboardHidden();
}
private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();
public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public KeyboardDetectorRelativeLayout(Context context) {
super(context);
}
public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
keyboardListener.add(listener);
}
public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
keyboardListener.remove(listener);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();
if (actualHeight > proposedheight) {
notifyKeyboardShown();
} else if (actualHeight < proposedheight) {
notifyKeyboardHidden();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void notifyKeyboardHidden() {
for (IKeyboardChanged listener : keyboardListener) {
listener.onKeyboardHidden();
}
}
private void notifyKeyboardShown() {
for (IKeyboardChanged listener : keyboardListener) {
listener.onKeyboardShown();
}
}
}
Esto funciona bastante bien ... Marque, que esta solución simplemente funcionará cuando el Modo de entrada suave de su Actividad esté configurado en "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"
android:windowSoftInputMode="adjustPan"
, o adjustResize
con una ventana de pantalla completa, ya que el diseño nunca cambia de tamaño.
Al igual que la respuesta de @ amalBit, registre un oyente en el diseño global y calcule la diferencia de la parte inferior visible de dectorView y su parte inferior propuesta, si la diferencia es mayor que algún valor (altura de IME adivinada), creemos que IME está arriba:
final EditText edit = (EditText) findViewById(R.id.edittext);
edit.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (keyboardShown(edit.getRootView())) {
Log.d("keyboard", "keyboard UP");
} else {
Log.d("keyboard", "keyboard Down");
}
}
});
private boolean keyboardShown(View rootView) {
final int softKeyboardHeight = 100;
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
int heightDiff = rootView.getBottom() - r.bottom;
return heightDiff > softKeyboardHeight * dm.density;
}
el umbral de altura 100 es la altura mínima adivinada de IME.
Esto funciona tanto para ajustarPan como para ajustarResize.
La solución de Nebojsa casi me funcionó. Cuando hice clic dentro de un EditText de varias líneas, sabía que se mostraba el teclado, pero cuando comencé a escribir dentro de EditText, la altura real y la altura propuesta seguían siendo las mismas, por lo que no sabía que todavía se mostraba el teclado. Hice una ligera modificación para almacenar la altura máxima y funciona bien. Aquí está la subclase revisada:
public class CheckinLayout extends RelativeLayout {
private int largestHeight;
public CheckinLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.checkin, this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
largestHeight = Math.max(largestHeight, getHeight());
if (largestHeight > proposedheight)
// Keyboard is shown
else
// Keyboard is hidden
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
No estoy seguro si alguien publica esto. ¡Encontré esta solución simple de usar! . La clase SoftKeyboard está en gist.github.com . Pero mientras la devolución de llamada del evento emergente / oculto del teclado necesitamos un controlador para hacer correctamente las cosas en la interfaz de usuario:
/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);
/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{
@Override
public void onSoftKeyboardHide()
{
// Code here
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
...
}
});
}
@Override
public void onSoftKeyboardShow()
{
// Code here
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
...
}
});
}
});
Resuelvo esto anulando onKeyPreIme (int keyCode, KeyEvent event) en mi EditText personalizado.
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
//keyboard will be hidden
}
}
Tengo una especie de truco para hacer esto. Aunque no parece haber una forma de detectar cuándo se ha mostrado u ocultado el teclado virtual, de hecho puede detectar cuándo está a punto de mostrarse u ocultarse configurando un botón OnFocusChangeListener
en el EditText
que está escuchando.
EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
{
@Override
public void onFocusChange(View view, boolean hasFocus)
{
//hasFocus tells us whether soft keyboard is about to show
}
});
NOTA: Una cosa a tener en cuenta con este truco es que esta devolución de llamada se dispara inmediatamente cuando EditText
gana o pierde el foco. Esto realmente disparará justo antes de que se muestre u oculte el teclado virtual. La mejor manera que he encontrado para hacer algo después de que el teclado muestra u oculta es usar Handler
ay retrasar algo ~ 400 ms, así:
EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
{
@Override
public void onFocusChange(View view, boolean hasFocus)
{
new Handler().postDelayed(new Runnable()
{
@Override
public void run()
{
//do work here
}
}, 400);
}
});
OnFocusChangeListener
solo dice si EditText
tiene foco después de que el estado cambió. Pero IME
puede estar oculto cuando EditText
tiene foco, ¿cómo detectar este caso?
Sander, creo que estás intentando mostrar la vista bloqueada por el teclado virtual. Pruebe esto http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html .
He resuelto el problema en la codificación posterior de vista de texto de una sola línea.
package com.helpingdoc;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
public class MainSearchLayout extends LinearLayout {
int hieght = 0;
public MainSearchLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.main, this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("Search Layout", "Handling Keyboard Window shown");
if(getHeight()>hieght){
hieght = getHeight();
}
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();
System.out.println("....hieght = "+ hieght);
System.out.println("....actualhieght = "+ actualHeight);
System.out.println("....proposedheight = "+ proposedheight);
if (actualHeight > proposedheight){
// Keyboard is shown
} else if(actualHeight<proposedheight){
// Keyboard is hidden
}
if(proposedheight == hieght){
// Keyboard is hidden
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
También puede verificar el primer relleno inferior infantil de DecorView. Se establecerá en un valor distinto de cero cuando se muestre el teclado.
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
View view = getRootView();
if (view != null && (view = ((ViewGroup) view).getChildAt(0)) != null) {
setKeyboardVisible(view.getPaddingBottom() > 0);
}
super.onLayout(changed, left, top, right, bottom);
}
Ocultar | Mostrar eventos para el teclado se puede escuchar a través de un simple hack en OnGlobalLayoutListener:
final View activityRootView = findViewById(R.id.top_root);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > 100) {
// keyboard is up
} else {
// keyboard is down
}
}
});
Aquí activityRootView es la vista raíz de su Actividad.
usando viewTreeObserver para obtener fácilmente el evento del teclado.
layout_parent.viewTreeObserver.addOnGlobalLayoutListener {
val r = Rect()
layout_parent.getWindowVisibleDisplayFrame(r)
if (layout_parent.rootView.height - (r.bottom - r.top) > 100) { // if more than 100 pixels, its probably a keyboard...
Log.e("TAG:", "keyboard open")
} else {
Log.e("TAG:", "keyboard close")
}
}
** layout_parent es tu vista comoedit_text.parent
La respuesta de Nebojsa Tomcic no fue útil para mí. Tengo RelativeLayout
con TextView
y AutoCompleteTextView
dentro de ella. Necesito desplazarme TextView
hacia la parte inferior cuando se muestra el teclado y cuando está oculto. Para lograr esto, anulé el onLayout
método y funciona bien para mí.
public class ExtendedLayout extends RelativeLayout
{
public ExtendedLayout(Context context, AttributeSet attributeSet)
{
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.main, this);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
if (changed)
{
int scrollEnd = (textView.getLineCount() - textView.getHeight() /
textView.getLineHeight()) * textView.getLineHeight();
textView.scrollTo(0, scrollEnd);
}
}
}