¿Cómo capturar el evento "virtual keyboard show / hide" en Android?
Me gustaría cambiar el diseño basado en si el teclado virtual se muestra o no. He buscado en la API y en varios blogs, pero no puedo encontrar nada útil.
¿Es posible?
- Los eventos de clic no se acumulan en RecyclerView
- Comunicación personalizada de Ver a la actividad de Android
- El evento touchend no funciona en Android
- Hacer emergente la tecla pulsada en un teclado personalizado
- Android: ¿Por qué un clic largo también activa un clic normal?
¡Gracias!
- Android EditText borrar (retroceso) evento clave
- Configuración de TextView visible desde otro hilo o BeginInvoke en Android
- Abrir y mostrar el evento del calendario en android
- Cordova 3.4 - Detectar evento de teclado
- Obtener eventos táctiles (coordenadas, MotionEvents, etc.) en un servicio
- ¿Cuál es la alternativa al sensor de orientación de Android?
- Detectar cuando usable conectado / desconectado desde / hacia el teléfono Android
- Javascript: ¿Hay alguna solución para que Chrome para Android dispare a los oyentes de touchmove y touchend que no usen event.preventDefault ()?
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(); } }
A continuación, solo cambia la visibilidad de algunas vistas, actualiza un campo y cambia el archivo de diseño.
Nota
Esta solución no funcionará para los teclados blandos y onConfigurationChanged
no se llamará para teclados blandos.
Esta puede no ser la solución más efectiva. Pero esto funcionó para mí cada vez … Llamo a esta función donde siempre 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 utiliza un teclado flotante.
Si desea manejar mostrar / ocultar la ventana de teclado de IMM (virtual) de su Actividad, tendrá que subclasificar su diseño y anular el método onMesure (para determinar el ancho medido y la altura medida de su diseño). Después de que establece el diseño subclasificado como vista principal para su actividad por setContentView (). Ahora podrás manejar eventos de IMM show / hide window. Si esto suena complicado, no es eso realmente. 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 declarar 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); } }
Usted puede ver desde el código que inflar el diseño de nuestra actividad en el constructor de subclases
inflater.inflate(R.layout.main, this);
Y ahora acaba de establecer la vista de contenido de subclasificación de diseño 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... }
Basado en el código de Nebojsa Tomcic He desarrollado el siguiente RelativeLayout-Subclase:
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 sólo funcionará cuando el modo de entrada suave de su actividad esté establecido en "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"
La solución de Nebojsa casi funcionó para mí. Cuando hice clic dentro de un EditText de varias líneas, sabía que el teclado se visualizaba, pero cuando empecé a escribir dentro del EditText, el realHeight y el propuestoHeight seguían siendo los mismos, por lo que no sabía que el teclado todavía se mostraba. 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 de si alguien publicar esto. ¡Encontré esta solución sencilla de usar! . La clase SoftKeyboard está en gist.github.com . Pero mientras que el teclado popup / ocultar evento de devolución de llamada 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 invalidando 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 } }
Al igual que la respuesta de @ amalBit, registra a un oyente en el diseño global y calcula la diferencia de la parte inferior visible de dectorView y su fondo propuesto, si la diferencia es mayor que un valor (supusimos la altura de IME), 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 para adjustPan y adjustResize.
Tengo una especie de hack para hacer esto. Aunque no parece haber una forma de detectar cuando el teclado OnFocusChangeListener
ha mostrado u ocultado, de hecho puede detectar cuando está a punto de ser mostrado u ocultado mediante el establecimiento de 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 hack es que esta devolución de llamada se dispara inmediatamente cuando el EditText
gana o pierde foco. Esto se disparará justo antes de que el teclado suave muestre u oculte. La mejor manera que he encontrado para hacer algo después de que el teclado muestra u oculta es usar un Handler
y demorar algo ~ 400ms, 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); } });
He hecho de esta manera:
Agregue la interfaz OnKeyboardVisibilityListener
.
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.
Sander, creo que estás tratando de mostrar la vista bloqueada por el teclado suave. Pruebe este http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html .
Tengo resolver el problema en la sola línea textview detrás que codifica.
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 comprobar el primer relleno inferior de DecorView. Se establecerá 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); }
Mostrar eventos para el teclado se puede escuchar a través de 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.
La respuesta de Nebojsa Tomcic no fue útil para mí. Tengo RelativeLayout
con TextView
y AutoCompleteTextView
dentro de él. Necesito desplazar el TextView
a la parte inferior cuando se muestra el teclado y cuando está oculto. Para lograr esto, onLayout
método onLayout
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); } } }
- Android Studio: no existe la ruta local
- Android: elementos ListView con varios botones que se pueden hacer clic