O teclado virtual abre e fecha o ouvinte em uma atividade no Android

136

Eu tenho um Activityonde existem 5 EditTexts. Quando o usuário clica no primeiro EditText, o teclado virtual é aberto para inserir algum valor nele. Desejo definir Viewa visibilidade de outras pessoas para Gonequando o teclado virtual abrir e também quando o usuário clicar no primeiro EditTexte também quando o teclado virtual fechar da mesma EditTextno botão Voltar. Quero definir Viewa visibilidade de outra pessoa como visível.

Existe algum ouvinte ou retorno de chamada ou hack para quando o teclado virtual é aberto a partir de um clique no primeiro EditTextno Android?

N Sharma
fonte
1
Não. Não existem esses ouvintes. Não são hacks para conseguir o que você está tentando. Aqui está uma abordagem possível: como enviar um evento de ponteiro no Android .
Vikram
@Vikram Eu não estou procurandotrying to detect the virtual keyboard height in Android.
N Sharma
Eu sei. Se você seguir o código, verá como a altura está sendo determinada. Um evento de ponteiro está sendo enviado -> dois casos => 1. se o teclado estiver aberto => & se o local Xe o ponteiro estiverem Ysobre / sobre o teclado => SecurityException=> decremento Ye tente novamente => até que nenhuma exceção seja lançada => O Yvalor atual é a altura do teclado. 2. se o teclado não estiver aberto => não SecurityException.
Vikram
Como isso se aplica ao seu cenário? Envie um evento de ponteiro em digamos 2/3 da altura da tela. Se um SecurityExceptioné acionado => o teclado está aberto. Senão, o teclado está fechado.
Vikram
@ Vikram Eu só quero isso primeiro, EditTextnão outro EditText. Como posso distinguir isso?
N Sharma

Respostas:

91

Isso funciona apenas quando android:windowSoftInputModesua atividade está definida adjustResizeno manifesto. Você pode usar um ouvinte de layout para ver se o layout raiz da sua atividade é redimensionado pelo teclado.

Uso algo como a seguinte classe base para minhas atividades:

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);
        }
    }
}

O exemplo de atividade a seguir usa isso para ocultar uma exibição quando o teclado é mostrado e mostrá-la novamente quando o teclado está oculto.

O layout 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>

E a atividade:

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
fonte
4
+1 Sim Esta é a solução perfeita para o meu problema.
N Sharma
18
Olá, você usou getTop () na janela.ID_ANDROID_CONTENT. Get top não funciona para mim. é sempre 0 aqui, funciona como deveria usar getHeight ().
Daniele Segato
1
de onde você rootLayout = (ViewGroup) findViewById(R.id.rootLayout);vem?
CommonSenseCode
1
Por não estar trabalhando para mim por algum motivo, ele sempre chama o ShowKeyboard para abri-lo ou fechá-lo. Estou usando o findViewById (android.R.id.content), talvez seja esse o problema?
McSullivan D'Ander
2
@tsig sua solução +100 depende da tela específica. Falha em tablets e telefones hdpi. Usei a correção como dez por cento da altura do dispositivo. Isso significa que, se a altura da visualização for menor que a altura da tela - 10%, o teclado estará aberto. outro teclado está fechado. Aqui está meu contentViewTop em onGlobalLayout: contentViewTop = (getWindow (). GetDecorView (). GetBottom () / 10)
ilker
93

Pedaço de bolo com a incrível biblioteca KeyboardVisibilityEvent

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

Créditos para Yasuhiro SHIMIZU

Gal Rom
fonte
Isso não funcionará porque os teclados não têm alturas estáticas e a altura nesta biblioteca é definida como 100dp.
milosmns
@milosmns é usada uma altura limite de 100dp para a detecção do teclado. Sem suposição é feita sobre a altura do teclado real
Nino van Hooff
11
Ainda é codificado. Janelas múltiplas? Samsung vista dividida? Imagem no modo de imagem? Também existe um teclado de uma linha mínimo que cairá abaixo de 100dp. Não há bala de prata aqui ...
milosmns 16/01
1
Porque não há captura tudo para este problema, este parece ser o mais fácil de implementar e apenas voltar para o código que você realmente deseja trabalhar :)
Máquina Tribe
1
Esta é de longe a melhor resposta, totalmente confiável em qualquer dispositivo
Pelanes
69

Como Vikram apontou nos comentários, detectar se o teclado virtual é exibido ou desapareceu só é possível com alguns hacks feios.

Talvez seja suficiente definir um ouvinte de foco no edittext :

yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            //got focus
        } else {
            //lost focus
        }
   }
});
CommonGuy
fonte
27
suponha que eu clique no edittext, em seguida, o setOnFocusChangeListenerouvinte será chamado, pressione novamente e feche o teclado, mas não cliquei em outras visualizações. Agora, novamente, clico no mesmo edittext que já tem foco e o que acontecerá?
N Sharma
3
@ Williams Não tenho muita certeza, mas suspeito que onFocusChange()não será chamado.
precisa saber é o seguinte
1
Esta não é minha pergunta. Por favor, leia minha pergunta novamente - Eu tenho Atividade onde existem 5 EditText. Quando o usuário clica no primeiro EditText, o teclado virtual é aberto para inserir algum valor. Eu quero definir outra visibilidade de exibição como Gone quando o teclado virtual é aberto quando o usuário clica no primeiro EditText e quando o teclado virtual é fechado do mesmo EditText na pressão traseira, em seguida, desejo definir outra visibilidade de visualização como visível. Existe algum ouvinte ou retorno de chamada ou hack quando o teclado virtual é aberto ao clicar no primeiro EditText no Android?
N Sharma
4
Caras não olham essa resposta porque ele está dizendo algo diferente, mesmo eu não entendo.
N Sharma
2
Enfim, ele não funciona para mim ... Quando o teclado virtual está oculto, não há nenhuma mudança de foco no EditText ... Portanto, não consigo receber notificações deste ouvinte.
Licenciatura Julius
50

Para atividade:

    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 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
fonte
3
Usou-o para atividade, mas em vez de comparar com a visualização, comparei com o tamanho da tela. funciona muito bem
Roee
melhor seria comparar heightDiff com uma altura em dp do que em pixels. Pode variar significativamente.
DROIDCODER Leo 13/11
Isso precisa do android:windowSoftInputMode="adjustResize"manifesto?
LiuWenbin_NO.
android: windowSoftInputMode = "AdjustResize" android: configChanges = "orientação | teclado | teclado
oculto
Funciona para mim, ainda assim, tenho uma pergunta. Custa muitos recursos?
Licenciatura Julius
32

A resposta de Jaap não funcionará para AppCompatActivity. Em vez disso, obtenha a altura da barra de status e da barra de navegação etc. e compare com o tamanho da janela do seu aplicativo.

Igual a:

    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);
        }
    }
};
Richard
fonte
Parece funcionar muito bem com uma exceção: quebras no modo de tela dividida. Caso contrário, é ótimo.
MCLLC
14

Você pode experimentá-lo:

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
fonte
Spasibo, Dolbik! :)
AlexS
4

Você pode usar minha função de extensão 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()
}

Exemplo:

(context as AppCompatActivity)
                    .keyboardVisibilityChanges()
                    .subscribeBy { isKeyboardOpen ->
                        // your logic
                    }
Vlad
fonte
Não funciona para mim. Não consigo encontrar métodos dip()egetScreenHeight()
Marcin Kunert
O @MarcinKunert é apenas uma função de extensão que ajuda você a converter pixels em dp e obter a altura da tela. Se você quiser, eu posso lhe dar exemplo de tais funções
Vlad
GenericLifecycleObserver está obsoleto? qualquer solução?
Zainal Fahrudin
4

O código abaixo está funcionando para mim,

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
fonte
2

Se possível, tente estender o EditText e substituir o 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);
}

Como você pode estendê-lo:

  1. Implemente a escuta onFocus e declare 'onKeyboardShown'
  2. declarar 'onKeyboardHidden'

Eu acho que esse recálculo da altura da tela não é 100% bem-sucedido, como mencionado anteriormente. Para ser claro, a substituição do 'onKeyPreIme' não é chamada nos métodos 'ocultar teclado programático', MAS se você estiver fazendo isso em qualquer lugar, deve fazer a lógica 'onKeyboardHidden' lá e não criar soluções abrangentes.

zegee29
fonte
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
fonte
suponha que eu clique no edittext, em seguida, o setOnFocusChangeListenerouvinte será chamado, pressione novamente e feche o teclado, mas não cliquei em outras visualizações. Agora, novamente, clico no mesmo edittext que já tem foco e o que acontecerá?
N Sharma
Esta não é minha pergunta. Por favor, leia minha pergunta novamente - Eu tenho Atividade onde existem 5 EditText. Quando o usuário clica no primeiro EditText, o teclado virtual é aberto para inserir algum valor. Eu quero definir outra visibilidade de exibição como Gone quando o teclado virtual é aberto quando o usuário clica no primeiro EditText e quando o teclado virtual é fechado do mesmo EditText ao pressionar novamente, em seguida, desejo definir outra visibilidade de visualização como visível. Existe algum ouvinte ou retorno de chamada ou hack quando o teclado virtual é aberto ao clicar no primeiro EditText no Android?
N Sharma
1
Quando você pressiona para trás, em seguida, descartar o teclado que o tempo onfocusouvinte nunca chamam isso de é que eu estou procurando não o que você está sugerindo
N Sharma
1

Use esta classe,

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();
        }
    }

}
}

Em Android Manifest, android:windowSoftInputMode="adjustResize"é necessário.

/*
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();
}

PS - completamente retirado daqui .

Shubham A.
fonte
1

Para o caso de adjustResizeFragmentActivity, a solução aceita do @Jaap não funciona para mim.

Aqui está a minha solução:

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
fonte
1

Uma abordagem diferente seria verificar quando o usuário parou de digitar ...

Quando um TextEdit está em foco (o usuário está / estava digitando), você pode ocultar as visualizações (ouvinte do foco)

e use um manipulador + executável e um ouvinte de alteração de texto para fechar o teclado (independentemente de sua visibilidade) e mostrar as visualizações após algum atraso.

O principal aspecto a ser observado é o atraso que você usa, que depende do conteúdo desses 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
fonte
1

Esse código funciona muito bem

use esta classe para a visualização raiz:

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);
}

}

e defina o ouvinte do teclado em atividade ou fragmento:

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

        }
    });
saleh gholamian
fonte
1

Você pode lidar com a visibilidade do teclado substituindo dois métodos em sua Atividade: onKeyUp()e onKeyDown()mais informações neste link: https://developer.android.com/training/keyboard-input/commands

Arthur Arzumanyan
fonte
1
Os documentos especificam exatamente que essas funções não devem ser usadas em referência ao teclado de entrada flexível.
Piotr Prus
0

Infelizmente, não tenho uma reputação suficientemente alta para comentar a resposta de Jaap van Hengstum. Mas li alguns comentários de pessoas, tendo o problema que contentViewTopé sempre 0e que onShowKeyboard(...)é sempre chamado.

Eu tive o mesmo problema e descobri o problema que eu tinha. Eu usei um em AppCompatActivityvez de um 'normal' Activity. Nesse caso, Window.ID_ANDROID_CONTENTrefere-se a um valor ContentFrameLayoute não ao FrameLayoutvalor superior correto. No meu caso, foi bom usar o 'normal' Activity; se você tiver que usar outro tipo de atividade (acabei de testar o AppCompatActivity, talvez também seja um problema com outros tipos de atividade como o FragmentActivity), é necessário acessar o FrameLayout, que é um ancestral do ContentFrameLayout.

agi
fonte
0

quando o teclado mostra

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

é verdade, senão esconda

余艳辉
fonte
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;
        }
    }
};

Eu modifiquei a resposta aceita do Jaap um pouco. Mas no meu caso, existem poucas suposições, como android:windowSoftInputMode=adjustResizeo teclado não aparece no início quando o aplicativo é iniciado. E também, presumo que a tela em relação à altura dos pais.

contentHeight > 0essa verificação fornece-me para saber se a tela relacionada está oculta ou é mostrada para aplicar a escuta de eventos do teclado para essa tela específica. Também passo a visualização de layout da tela referente no método attachKeyboardListeners(<your layout view here>)da minha atividade principal onCreate(). Sempre que a altura da tela em questão é alterada, eu a salvo em prevContentHeightvariável para verificar posteriormente se o teclado é mostrado ou oculto.

Para mim, até agora tem sido bem trabalhado. Espero que funcione para os outros também.

psicoplasma
fonte
0

A resposta de "Jaap van Hengstum" está funcionando para mim, mas não há necessidade de definir "android: windowSoftInputMode" como ele acabou de dizer!

Eu o reduzi (agora apenas detecta o que quero, na verdade, um evento ao mostrar e ocultar o 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);
    }
}

e não se esqueça de adicionar isso

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

Isso funcionará sem a necessidade de alterar as configurações de sua atividade. android:windowSoftInputMode

etapa 1: estenda a classe EditText e substitua estas duas:

@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);
}

etapa 2: crie esses dois em sua atividade:

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
}

*** lembre-se, a fim de fazer o clearFocustrabalho, você deve tornar o pai ou o primeiro filho na hierarquia dos pais focalizável.

    setFocusableInTouchMode(true);
    setFocusable(true);
Kasra caído
fonte
0

Isso não está funcionando como desejado ...

... vi muitos usarem cálculos de tamanho para verificar ...

Eu queria determinar se estava aberto ou não e achei isAcceptingText()

portanto, isso realmente não responde à pergunta, pois não trata de abrir ou fechar, mais ou menos como está aberto ou fechado; portanto, é um código relacionado que pode ajudar outras pessoas em vários cenários ...

em uma atividade

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

em um 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
fonte
0

verifique com o código abaixo:

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
fonte