Teclado suave para abrir y cerrar escucha en una actividad en Android

136

Tengo un Activitydonde hay 5 EditTexts. Cuando el usuario hace clic en el primero EditText, el teclado virtual se abre para ingresar algún valor. Quiero establecer Viewla visibilidad de otros paraGone cuando el teclado virtual se abre y también cuando el usuario hace clic en el primero EditTexty también cuando el teclado virtual se cierra desde el mismo EditTextal presionar el botón Atrás. Entonces quiero establecer Viewla visibilidad de otros en visible.

¿Hay algún oyente o devolución de llamada o algún truco para cuando el teclado virtual se abre con un clic en el primer EditText en Android?

N Sharma
fuente
1
No. No hay tales oyentes. No son hacks para lograr lo que usted está tratando de. Aquí hay un posible enfoque: cómo enviar eventos de puntero en Android .
Vikram
@Vikram que no estoy buscandotrying to detect the virtual keyboard height in Android.
N Sharma
Lo sé. Si revisa el código, verá cómo se determina la altura. Se está enviando un evento de puntero -> dos casos => 1. si el teclado está abierto => y si el puntero Xy la Yubicación caen sobre / sobre el teclado => SecurityException=> decremento Ye intente nuevamente => hasta que no se produzca ninguna excepción => El Yvalor actual es la altura del teclado. 2. si el teclado no está abierto => no SecurityException.
Vikram
¿Cómo se aplica a su escenario? Envíe un evento de puntero a 2/3 de la altura de la pantalla. Si SecurityExceptionse lanza a => el teclado está abierto. De lo contrario, el teclado está cerrado.
Vikram
@Vikram Solo quiero esto para el primero, EditTextno para el otro EditText. ¿Cómo puedo distinguir esto?
N Sharma

Respuestas:

91

Esto solo funciona cuando android:windowSoftInputModesu actividad está configurada enadjustResize en el manifiesto. Puede usar un escucha de diseño para ver si el teclado cambia el tamaño del diseño raíz de su actividad.

Utilizo algo como la siguiente clase base para mis actividades:

public class BaseActivity extends Activity {
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
            int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

            LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);

            if(heightDiff <= contentViewTop){
                onHideKeyboard();

                Intent intent = new Intent("KeyboardWillHide");
                broadcastManager.sendBroadcast(intent);
            } else {
                int keyboardHeight = heightDiff - contentViewTop;
                onShowKeyboard(keyboardHeight);

                Intent intent = new Intent("KeyboardWillShow");
                intent.putExtra("KeyboardHeight", keyboardHeight);
                broadcastManager.sendBroadcast(intent);
            }
        }
    };

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

    protected void onShowKeyboard(int keyboardHeight) {}
    protected void onHideKeyboard() {}

    protected void attachKeyboardListeners() {
        if (keyboardListenersAttached) {
            return;
        }

        rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

        keyboardListenersAttached = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (keyboardListenersAttached) {
            rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
        }
    }
}

La siguiente actividad de ejemplo usa esto para ocultar una vista cuando se muestra el teclado y mostrarlo nuevamente cuando el teclado está oculto.

El diseño xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/rootLayout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">              

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

        <!-- omitted for brevity -->

    </ScrollView>

    <LinearLayout android:id="@+id/bottomContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <!-- omitted for brevity -->

    </LinearLayout>

</LinearLayout>

Y la actividad:

public class TestActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);

        attachKeyboardListeners();
    }

    @Override
    protected void onShowKeyboard(int keyboardHeight) {
        // do things when keyboard is shown
        bottomContainer.setVisibility(View.GONE);
    }

    @Override
    protected void onHideKeyboard() {
        // do things when keyboard is hidden
        bottomContainer.setVisibility(View.VISIBLE);
    }        
}
Jaap van Hengstum
fuente
44
+1 Sí, esta es la solución perfecta para mi problema.
N Sharma
18
Hola, usaste getTop () en Window.ID_ANDROID_CONTENT. Obtener top no funciona para mí. siempre es 0 aquí, funciona como se supone que debe usar getHeight () en su lugar.
Daniele Segato
1
¿De dónde obtiene rootLayout = (ViewGroup) findViewById(R.id.rootLayout);a partir?
CommonSenseCode
1
No funciona para mí por alguna razón, siempre llama a ShowKeyboard, ya sea que lo abro o lo cierro. Estoy usando findViewById (android.R.id.content), ¿tal vez ese es el problema?
McSullivan D'Ander
2
@tsig su solución +100 depende de la pantalla específica. Falló en tabletas y teléfonos hdpi. Usé la corrección como el diez por ciento de la altura del dispositivo. Eso significa que si la altura de la vista es menor que la altura de la pantalla - 10%, el teclado está abierto. De lo contrario, el teclado está cerrado. Aquí está mi contentViewTop en onGlobalLayout: contentViewTop = (getWindow (). GetDecorView (). GetBottom () / 10)
ilker
94

Pedazo de pastel con la impresionante biblioteca KeyboardVisibilityEvent

KeyboardVisibilityEvent.setEventListener(
    getActivity(),
    new KeyboardVisibilityEventListener() {
        @Override
        public void onVisibilityChanged(boolean isOpen) {
            // write your code
        }
    });

Créditos de Yasuhiro SHIMIZU

Gal rom
fuente
Esto no funcionará porque los teclados no tienen alturas estáticas, y la altura en esta biblioteca está establecida en 100dp.
milosmns
@milosmns se utiliza una altura de umbral de 100dp para la detección del teclado. No se hace suposición sobre la altura real del teclado
Nino van Hooff
11
Todavía está codificado. ¿Multi-ventana? Samsung dividir la vista? Imagen en modo imagen? También hay un teclado mínimo de una fila que caerá por debajo de 100dp. No hay bala de plata aquí ...
milosmns
1
Debido a que no hay ningún problema para este problema, este parece ser el más fácil de implementar y simplemente volver al código en el que realmente desea trabajar :)
Machine Tribe
1
Esta es, con mucho, la mejor respuesta, totalmente confiable en cualquier dispositivo
Pelanes
70

Como Vikram señaló en los comentarios, detectar si el teclado virtual se muestra o ha desaparecido solo es posible con algunos trucos feos.

Tal vez sea suficiente establecer un oyente de enfoque en el texto de edición :

yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            //got focus
        } else {
            //lost focus
        }
   }
});
CommonGuy
fuente
28
supongamos que hago clic en el texto de edición, luego se setOnFocusChangeListenerllamará al oyente, luego presiono hacia atrás y luego cierra el teclado, pero no hice clic en otras vistas, ahora nuevamente hago clic en el mismo texto de edición que ya tiene el foco, ¿qué sucederá?
N Sharma
3
@Williams No estoy completamente seguro, pero sospecho que onFocusChange()no se llamará.
CommonGuy
1
Esta no es mi pregunta. Por favor, lea mi pregunta nuevamente: tengo Actividad donde hay 5 EditText. Cuando el usuario hace clic primero en EditText y luego en el teclado virtual, se abre para ingresar algún valor. Quiero establecer alguna otra visibilidad de Vista en Desaparecida cuando el teclado virtual se abre cuando el usuario hace clic en el primer EditText y cuando el teclado virtual se cierra desde el mismo EditText al presionar hacia atrás, luego quiero configurar la visibilidad de alguna otra Vista visible. ¿Hay algún oyente o devolución de llamada o algún truco cuando el teclado virtual se abre al hacer clic en el primer EditText en Android?
N Sharma
44
Los chicos no miran esta respuesta porque él está diciendo algo diferente, incluso yo no entiendo.
N Sharma
2
De todos modos, no funciona para mí ... Cuando el teclado virtual está oculto, no se produjo ningún cambio de enfoque en EditText ... Por lo tanto, no puedo recibir notificaciones de este oyente.
Licat Julius
51

Para la actividad:

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();

                activityRootView.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { 
                 //enter your code here
                }else{
                 //enter code for hid
                }
            }
        });

Para el fragmento:

    view = inflater.inflate(R.layout.live_chat_fragment, null);
view.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.
                view.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...

                }
            }
        });
M Singh Karnawat
fuente
3
Lo usé para la actividad, pero en lugar de compararlo con la vista, lo comparé con el tamaño de la pantalla. funciona muy bien
Roee
mejor sería comparar heightDiff con una altura en dp en lugar de en píxeles. Puede variar significativamente.
Leo Droidcoder
¿Necesita esto android:windowSoftInputMode="adjustResize"en manifiesto?
LiuWenbin_NO.
android: windowSoftInputMode = "ajustarResize" android: configChanges = "orientación | teclado | teclado
oculto
A mí me funciona, aún tengo una pregunta. ¿Cuesta muchos recursos?
Licat Julius
32

La respuesta de Jaap no funcionará para AppCompatActivity. En su lugar, obtenga la altura de la barra de estado y la barra de navegación, etc. y compárela con el tamaño de la ventana de su aplicación.

Al igual que:

    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // navigation bar height
        int navigationBarHeight = 0;
        int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // status bar height
        int statusBarHeight = 0;
        resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // display window size for the app layout
        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);

        // screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard
        int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height());

        if (keyboardHeight <= 0) {
            onHideKeyboard();
        } else {
            onShowKeyboard(keyboardHeight);
        }
    }
};
Ricardo
fuente
Parece funcionar bastante bien con una excepción: se rompe en modo de pantalla dividida. De lo contrario, es genial.
MCLLC
14

Puedes probarlo:

private void initKeyBoardListener() {
    // Минимальное значение клавиатуры. 
    // Threshold for minimal keyboard height.
    final int MIN_KEYBOARD_HEIGHT_PX = 150;
    // Окно верхнего уровня view. 
    // Top-level window decor view.
    final View decorView = getWindow().getDecorView();
    // Регистрируем глобальный слушатель. Register global layout listener.
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        // Видимый прямоугольник внутри окна. 
        // Retrieve visible rectangle inside window.
        private final Rect windowVisibleDisplayFrame = new Rect();
        private int lastVisibleDecorViewHeight;

        @Override
        public void onGlobalLayout() {
            decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
            final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();

            if (lastVisibleDecorViewHeight != 0) {
                if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
                    Log.d("Pasha", "SHOW");
                } else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
                    Log.d("Pasha", "HIDE");
                }
            }
            // Сохраняем текущую высоту view до следующего вызова.
            // Save current decor view height for the next call.
            lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
    });
}
Pavel Dolbik
fuente
Spasibo, Dolbik! :)
AlexS
4

Puede usar mi función de extensión Rx (Kotlin).

/**
 * @return [Observable] to subscribe of keyboard visibility changes.
 */
fun AppCompatActivity.keyboardVisibilityChanges(): Observable<Boolean> {

    // flag indicates whether keyboard is open
    var isKeyboardOpen = false

    val notifier: BehaviorSubject<Boolean> = BehaviorSubject.create()

    // approximate keyboard height
    val approximateKeyboardHeight = dip(100)

    // device screen height
    val screenHeight: Int = getScreenHeight()

    val visibleDisplayFrame = Rect()

    val viewTreeObserver = window.decorView.viewTreeObserver

    val onDrawListener = ViewTreeObserver.OnDrawListener {

        window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)

        val keyboardHeight = screenHeight - (visibleDisplayFrame.bottom - visibleDisplayFrame.top)

        val keyboardOpen = keyboardHeight >= approximateKeyboardHeight

        val hasChanged = isKeyboardOpen xor keyboardOpen

        if (hasChanged) {
            isKeyboardOpen = keyboardOpen
            notifier.onNext(keyboardOpen)
        }
    }

    val lifeCycleObserver = object : GenericLifecycleObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event?) {
            if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                source.lifecycle.removeObserver(this)
                notifier.onComplete()
            }
        }
    }

    viewTreeObserver.addOnDrawListener(onDrawListener)
    lifecycle.addObserver(lifeCycleObserver)

    return notifier
            .doOnDispose {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                lifecycle.removeObserver(lifeCycleObserver)
            }
            .onTerminateDetach()
            .hide()
}

Ejemplo:

(context as AppCompatActivity)
                    .keyboardVisibilityChanges()
                    .subscribeBy { isKeyboardOpen ->
                        // your logic
                    }
Vlad
fuente
No funciona para mi No puedo encontrar métodos dip()ygetScreenHeight()
Marcin Kunert
@MarcinKunert es solo una función de extensión que te ayuda a convertir píxeles a dp y obtener la altura de la pantalla. Si lo desea, puedo darle un ejemplo de tales funciones
Vlad
GenericLifecycleObserver está en desuso? ¿alguna solución?
Zainal Fahrudin el
4

El siguiente código me funciona,

mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (mainLayout != null) {
                int heightDiff = mainLayout.getRootView().getHeight() - mainLayout.getHeight();
                if (heightDiff > dpToPx(getActivity(), 200)) { 
                   //keyboard is open
                } else {
                   //keyboard is hide
                }
            }
        }
    });
Vinoj Vetha
fuente
2

Si puede, intente extender EditText y anule el método 'onKeyPreIme'.

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener; //keep it for later usage
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            //you can define and use custom listener,
            //OR define custom R.id.<imeId>
            //OR check event.keyCode in listener impl
            //* I used editor action because of ButterKnife @
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

¿Cómo puedes extenderlo?

  1. Implemente la escucha onFocus y declare 'onKeyboardShown'
  2. declarar 'onKeyboardHidden'

Creo que el recálculo de la altura de la pantalla no es 100% exitoso como se mencionó anteriormente. Para ser claros, la anulación de 'onKeyPreIme' no se llama a los métodos de 'ocultar teclado programado', PERO si lo está haciendo en cualquier lugar, debe hacer la lógica 'onKeyboardHidden' allí y no crear soluciones integrales.

zegee29
fuente
1
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mainactivity);
    attachKeyboardListeners();
    ....
    yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                yourEditText2.setVisibility(View.GONE);
                yourEditText3.setVisibility(View.GONE);
                yourEditText4.setVisibility(View.GONE);
                yourEditText5.setVisibility(View.GONE);
            } else {
                yourEditText2.setVisibility(View.VISIBLE);
                yourEditText3.setVisibility(View.VISIBLE);
                yourEditText4.setVisibility(View.VISIBLE);
                yourEditText5.setVisibility(VISIBLE);
            }
       }
    });
    }
}
Sagar Pilkhwal
fuente
supongamos que hago clic en el texto de edición, luego se setOnFocusChangeListenerllamará al oyente, luego presiono hacia atrás y luego cierra el teclado, pero no hice clic en otras vistas, ahora nuevamente hago clic en el mismo texto de edición que ya tiene el foco, ¿qué sucederá?
N Sharma
Esta no es mi pregunta. Por favor, lea mi pregunta nuevamente: tengo Actividad donde hay 5 EditText. Cuando el usuario hace clic primero en EditText y luego en el teclado virtual, se abre para ingresar algún valor. Quiero establecer alguna otra visibilidad de Vista en Desaparecida cuando el teclado virtual se abre cuando el usuario hace clic en el primer EditText y cuando el teclado virtual se cierra desde el mismo EditText al presionar hacia atrás, luego quiero configurar la visibilidad de alguna otra Vista visible. ¿Hay algún oyente o devolución de llamada o algún truco cuando el teclado virtual se abre al hacer clic en el primer EditText en Android?
N Sharma
1
Cuando presiona hacia atrás, cierra el teclado que el onfocusoyente nunca llama, eso es lo que estoy buscando, no lo que está sugiriendo
N Sharma
1

Usa esta clase,

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class SoftKeyboard implements View.OnFocusChangeListener
{
private static final int CLEAR_FOCUS = 0;

private ViewGroup layout;
private int layoutBottom;
private InputMethodManager im;
private int[] coords;
private boolean isKeyboardShow;
private SoftKeyboardChangesThread softKeyboardThread;
private List<EditText> editTextList;

private View tempView; // reference to a focused EditText

public SoftKeyboard(ViewGroup layout, InputMethodManager im)
{
    this.layout = layout;
    keyboardHideByDefault();
    initEditTexts(layout);
    this.im = im;
    this.coords = new int[2];
    this.isKeyboardShow = false;
    this.softKeyboardThread = new SoftKeyboardChangesThread();
    this.softKeyboardThread.start();
}

public void openSoftKeyboard()
{
    if(!isKeyboardShow)
    {
        layoutBottom = getLayoutCoordinates();
        im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
        softKeyboardThread.keyboardOpened();
        isKeyboardShow = true;
    }
}

public void closeSoftKeyboard()
{
    if(isKeyboardShow)
    {
        im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
        isKeyboardShow = false;
    }
}

public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback)
{
    softKeyboardThread.setCallback(mCallback);
}

public void unRegisterSoftKeyboardCallback()
{
    softKeyboardThread.stopThread();
}

public interface SoftKeyboardChanged 
{
    public void onSoftKeyboardHide();
    public void onSoftKeyboardShow();   
}

private int getLayoutCoordinates()
{
    layout.getLocationOnScreen(coords);
    return coords[1] + layout.getHeight();
}

private void keyboardHideByDefault()
{
    layout.setFocusable(true);
    layout.setFocusableInTouchMode(true);
}

/*
 * InitEditTexts now handles EditTexts in nested views
 * Thanks to Francesco Verheye ([email protected])
 */
private void initEditTexts(ViewGroup viewgroup) 
{
    if(editTextList == null)
        editTextList = new ArrayList<EditText>();

    int childCount = viewgroup.getChildCount();
    for(int i=0; i<= childCount-1;i++) 
    {
        View v = viewgroup.getChildAt(i);

        if(v instanceof ViewGroup) 
        {
            initEditTexts((ViewGroup) v);
        }

        if(v instanceof EditText) 
        {
            EditText editText = (EditText) v;
            editText.setOnFocusChangeListener(this);
            editText.setCursorVisible(true);
            editTextList.add(editText);
        }
    }
}

/*
 * OnFocusChange does update tempView correctly now when keyboard is still shown
 * Thanks to Israel Dominguez ([email protected])
 */
@Override
public void onFocusChange(View v, boolean hasFocus) 
{
    if(hasFocus) 
    {
        tempView = v;
        if(!isKeyboardShow) 
        {
            layoutBottom = getLayoutCoordinates();
            softKeyboardThread.keyboardOpened();
            isKeyboardShow = true;
        }
    }
}

// This handler will clear focus of selected EditText
private final Handler mHandler = new Handler()
{
    @Override
    public void handleMessage(Message m)
    {
        switch(m.what)
        {
        case CLEAR_FOCUS:
            if(tempView != null)
            {
                tempView.clearFocus();
                tempView = null;
            }
            break;
        }
    }
};

private class SoftKeyboardChangesThread extends Thread
{
    private AtomicBoolean started;
    private SoftKeyboardChanged mCallback;

    public SoftKeyboardChangesThread()
    {
        started = new AtomicBoolean(true);
    }

    public void setCallback(SoftKeyboardChanged mCallback)
    {
        this.mCallback = mCallback;
    }

    @Override
    public void run()
    {
        while(started.get())
        {
            // Wait until keyboard is requested to open
            synchronized(this)
            {
                try 
                {
                    wait();
                } catch (InterruptedException e) 
                {
                    e.printStackTrace();
                }
            }

            int currentBottomLocation = getLayoutCoordinates();

            // There is some lag between open soft-keyboard function and when it really appears.
            while(currentBottomLocation == layoutBottom && started.get())
            {
                currentBottomLocation = getLayoutCoordinates();
            }

            if(started.get())
                mCallback.onSoftKeyboardShow();

            // When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
            // and at some moment equals layoutBottom.
            // That broke the previous logic, so I added this new loop to handle this.
            while(currentBottomLocation >= layoutBottom && started.get())
            {
                currentBottomLocation = getLayoutCoordinates();
            }

            // Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
            while(currentBottomLocation != layoutBottom && started.get())
            {
                                    synchronized(this)
                {
                    try 
                    {
                        wait(500);
                    } catch (InterruptedException e) 
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                currentBottomLocation = getLayoutCoordinates();
            }

            if(started.get())
                mCallback.onSoftKeyboardHide();

            // if keyboard has been opened clicking and EditText.
            if(isKeyboardShow && started.get())
                isKeyboardShow = false;

            // if an EditText is focused, remove its focus (on UI thread)
            if(started.get())
                mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
        }   
    }

    public void keyboardOpened()
    {
        synchronized(this)
        {
            notify();
        }
    }

    public void stopThread()
    {
        synchronized(this)
        {
            started.set(false);
            notify();
        }
    }

}
}

En Android Manifest, android:windowSoftInputMode="adjustResize"es necesario.

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use the layout root
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
}

@Override
public void onSoftKeyboardShow() {
    // Code here
}   
});

/*
Open or close the soft keyboard easily
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();

/* Prevent memory leaks:*/
@Override
public void onDestroy() {
    super.onDestroy();
    softKeyboard.unRegisterSoftKeyboardCallback();
}

PD: Completamente tomado de aquí .

Shubham A.
fuente
1

Para el caso de adjustResizeFragmentActivity, la solución aceptada de @Jaap no funciona para mí.

Aquí está mi solución:

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    private int contentDiff;
    private int rootHeight;
    @Override
    public void onGlobalLayout() {
        View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
        if (rootHeight != mDrawerLayout.getRootView().getHeight()) {
            rootHeight = mDrawerLayout.getRootView().getHeight();
            contentDiff = rootHeight - contentView.getHeight();
            return;
        }
        int newContentDiff = rootHeight - contentView.getHeight();
        if (contentDiff != newContentDiff) {
            if (contentDiff < newContentDiff) {
                onShowKeyboard(newContentDiff - contentDiff);
            } else {
                onHideKeyboard();
            }
            contentDiff = newContentDiff;
        }
    }
};
AnoDest
fuente
1

Un enfoque diferente sería verificar cuándo el usuario dejó de escribir ...

Cuando un TextEdit está enfocado (el usuario está / estaba escribiendo), puede ocultar las vistas (escucha del foco)

y use un Handler + Runnable y un oyente de cambio de texto para cerrar el teclado (independientemente de su visibilidad) y mostrar las vistas después de algún retraso.

Lo principal a tener en cuenta sería la demora que use, que dependería del contenido de estos TextEdits.

Handler timeoutHandler = new Handler();
Runnable typingRunnable = new Runnable() {
    public void run() {
        // current TextEdit
        View view = getCurrentFocus();

        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        // reset focus
        view.clearFocus();
        // close keyboard (whether its open or not)
        imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);

        // SET VIEWS VISIBLE
    }
};

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            // SET VIEWS GONE

            // reset handler
            timeoutHandler.removeCallbacks(typingRunnable);
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});

editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Reset Handler...
        timeoutHandler.removeCallbacks(typingRunnable);
    }

    @Override
    public void afterTextChanged(Editable s) {
        // Reset Handler Cont.
        if (editText.getText().toString().trim().length() > 0) {
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});
Ullauri
fuente
1

Este código funciona muy bien

use esta clase para la vista raíz:

public class KeyboardConstraintLayout extends ConstraintLayout {

private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;

public KeyboardConstraintLayout(Context context) {
    super(context);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); //128dp
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (!isInEditMode()) {
        Activity activity = (Activity) getContext();
        @SuppressLint("DrawAllocation")
        Rect rect = new Rect();
        getWindowVisibleDisplayFrame(rect);

        int statusBarHeight = rect.top;
        int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;

        if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
            if (keyboardHeight > minKeyboardHeight) {
                if (!isShow) {
                    isShow = true;
                    keyboardListener.onKeyboardVisibility(true);
                }
            }else {
                if (isShow) {
                    isShow = false;
                    keyboardListener.onKeyboardVisibility(false);
                }
            }
        }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

public boolean isShowKeyboard() {
    return isShow;
}

public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
    this.targetEditText = targetEditText;
    this.keyboardListener = keyboardListener;
}

public interface KeyboardListener {
    void onKeyboardVisibility (boolean isVisible);
}

}

y configure el oyente del teclado en actividad o fragmento:

        rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
        @Override
        public void onKeyboardVisibility(boolean isVisible) {

        }
    });
Saleh gholamian
fuente
1

Puede manejar la visibilidad del teclado anulando dos métodos en su Actividad: onKeyUp()y onKeyDown()más información en este enlace: https://developer.android.com/training/keyboard-input/commands

Arthur Arzumanyan
fuente
1
Los documentos especifican exactamente que estas funciones no deben usarse en referencia al teclado de entrada de software.
Piotr Prus
0

Lamentablemente, no tengo una reputación lo suficientemente alta como para comentar la respuesta de Jaap van Hengstum. Pero leí algunos comentarios de personas, teniendo el problema de que contentViewTopsiempre es 0y queonShowKeyboard(...) siempre se llama.

Tuve el mismo problema y descubrí el problema que tenía. Usé un en AppCompatActivitylugar de un "normal" Activity. En este caso se Window.ID_ANDROID_CONTENTrefiere a un ContentFrameLayouty no al FrameLayoutvalor superior correcto. En mi caso, estaba bien usar el 'normal' Activity, si tienes que usar otro tipo de actividad (acabo de probar el AppCompatActivity, tal vez también es un problema con otros tipos de actividad como el FragmentActivity), tienes que acceder al FrameLayout, que es un antepasado de la ContentFrameLayout.

agi
fuente
0

cuando se muestra el teclado

rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight() 

es cierto, de lo contrario esconderse

余艳辉
fuente
0
private boolean isKeyboardShown = false;
private int prevContentHeight = 0;
private ViewGroup contentLayout;

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener =
        new ViewTreeObserver.OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        int contentHeight = contentLayout.getHeight();
        int rootViewHeight = contentLayout.getRootView().getHeight();

        if (contentHeight > 0) {

            if (!isKeyboardShown) {
                if (contentHeight < prevContentHeight) {
                    isKeyboardShown = true;
                    onShowKeyboard(rootViewHeight - contentHeight);
                }
            } else {
                if (contentHeight > prevContentHeight) {
                    isKeyboardShown = false;
                    onHideKeyboard();
                }
            }

            prevContentHeight = contentHeight;
        }
    }
};

He modificado un poco la respuesta aceptada de Jaap. Pero en mi caso, hay pocas suposiciones comoandroid:windowSoftInputMode=adjustResize y el teclado no aparece al principio cuando se inicia la aplicación. Y también, supongo que la pantalla en relación con la altura de los padres.

contentHeight > 0Esta comprobación me permite saber si la pantalla correspondiente está oculta o se muestra para aplicar la escucha de eventos de teclado para esta pantalla específica. También paso la vista de diseño de la pantalla correspondiente en attachKeyboardListeners(<your layout view here>)el onCreate()método de mi actividad principal . Cada vez que cambia la altura de la pantalla correspondiente, la guardo enprevContentHeight variable para verificar más adelante si el teclado se muestra u oculta.

Para mí, hasta ahora ha funcionado bastante bien. Espero que también funcione para otros.

psicoplasma
fuente
0

La respuesta de "Jaap van Hengstum" funciona para mí, pero no es necesario configurar "android: windowSoftInputMode" como acaba de decir.

Lo he hecho más pequeño (ahora solo detecta lo que quiero, en realidad un evento de mostrar y ocultar el teclado):

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
        int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            onHideKeyboard();
        } else {
            onShowKeyboard();
        }
    }
};

private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;

protected void onShowKeyboard() {}
protected void onHideKeyboard() {}

protected void attachKeyboardListeners() {
    if (keyboardListenersAttached) {
        return;
    }

    rootLayout = (ViewGroup) findViewById(R.id.CommentsActivity);
    rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

    keyboardListenersAttached = true;
}

@Override
protected void onDestroy() {
    super.onDestroy();

    if (keyboardListenersAttached) {
        rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
    }
}

y no olvides agregar esto

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_comments);
    attachKeyboardListeners();}
Khalil Al-rahman Yossefi
fuente
0

Esto funcionará sin necesidad de cambiar la actividad de usted. android:windowSoftInputMode

Paso 1: amplíe la clase EditText y anule estos dos:

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener;
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

Paso 2: crea estos dos en tu actividad:

private void initKeyboard() {
    final AppEditText editText = findViewById(R.id.some_id);
    editText.setOnFocusChangeListener(new OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            setKeyboard(hasFocus);
        }
    });
    editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (event == null || event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                editText.clearFocus();
            }
            return false;
        }
    });
}

public void setKeyboard(boolean isShowing) {
    // do something
}

*** recuerde para poder clearFocustrabajar, debe hacer que el padre o el primer hijo de la jerarquía principal se puedan enfocar.

    setFocusableInTouchMode(true);
    setFocusable(true);
kasra caida
fuente
0

Esto no funciona como se desea ...

... he visto muchos cálculos de tamaño de uso para verificar ...

Quería determinar si estaba abierto o no y encontré isAcceptingText()

así que esto realmente no responde a la pregunta, ya que no aborda la apertura o el cierre, sino que más bien está abierto o cerrado, por lo que es un código relacionado que puede ayudar a otros en varios escenarios ...

en una actividad

    if (((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
    }

en un fragmento

    if (((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");

    }
CrandellWS
fuente
0

consulte con el siguiente código:

CÓDIGO XML:

<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorParent"
    style="@style/parentLayoutPaddingStyle"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  .................


</android.support.constraint.ConstraintLayout>

CÓDIGO JAVA:

//Global Variable
android.support.constraint.ConstraintLayout activityRootView;
boolean isKeyboardShowing = false;
private  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
android.support.constraint.ConstraintLayout.LayoutParams layoutParams;




 //onCreate or onViewAttached
    activityRootView = view.findViewById(R.id.coordinatorParent);
        onGlobalLayoutListener = onGlobalLayoutListener();
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);


  //outside oncreate
  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener() {
        return new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(r);
                int screenHeight = activityRootView.getRootView().getHeight();
                int keypadHeight = screenHeight - r.bottom;

                if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
                    if (!isKeyboardShowing) {  // keyboard is opened
                        isKeyboardShowing = true;
                        onKeyboardVisibilityChanged(true);
                   }
                }
                else {
                    if (isKeyboardShowing) {   // keyboard is closed
                        isKeyboardShowing = false;
                        onKeyboardVisibilityChanged(false);
                    }
                }
            }//ends here
        };

    }


    void onKeyboardVisibilityChanged(boolean value) {
        layoutParams = (android.support.constraint.ConstraintLayout.LayoutParams)topImg.getLayoutParams();

        if(value){
           int length = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, getResources().getDisplayMetrics());
            layoutParams.height= length;
            layoutParams.width = length;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }else{
            int length1 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 175, getResources().getDisplayMetrics());
            layoutParams.height= length1;
            layoutParams.width = length1;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }
    }


    @Override
    public void onDetach() {
        super.onDetach();
        if(onGlobalLayoutListener != null) {
            activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
        }
    }
Abhishek kumar
fuente