Como verificar a visibilidade do teclado do software no Android?


516

Eu preciso fazer uma coisa muito simples - descobrir se o teclado do software é mostrado. Isso é possível no Android?


9
Embora a resposta de Reuben Scratton seja boa, parece quebrada em um tablet. Substituí a verificação diff> 128 por diff> screenHeight / 3.
kingston

2
A resposta de Reuben Scratton foi boa, mas eu exigi o ajuste de KaChi para realmente usá-lo.
Cullan

1
Por que o Google não faz com que um método interno padrão funcione para todos os aplicativos de teclado?
Fruit

4
Ainda me irmãos, que este não é um sistema funciona ...
longi

1
É absolutamente louco que essa API ainda esteja faltando 10 anos depois . Muito feliz por me afastar do Android.
Reuben Scratton 27/01

Respostas:


674

Nova resposta adicionada em 25 de janeiro de 2012

Desde que escrevi a resposta abaixo, alguém me informou sobre a existência do ViewTreeObserver e dos amigos, APIs que estão ocultas no SDK desde a versão 1.

Em vez de exigir um tipo de layout personalizado, uma solução muito mais simples é fornecer um ID conhecido à visualização raiz da atividade, digamos @+id/activityRoot, conectar um GlobalLayoutListener ao ViewTreeObserver e, a partir daí, calcular o diferencial de tamanho entre a raiz da visualização da atividade e o tamanho da janela:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
        if (heightDiff > dpToPx(this, 200)) { // if more than 200 dp, it's probably a keyboard...
            // ... do something here
        }
     }
});

Usando um utilitário como:

public static float dpToPx(Context context, float valueInDp) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}

Fácil!

Nota: Seu aplicativo deve definir esse sinalizador no Android Manifest, android:windowSoftInputMode="adjustResize"caso contrário, a solução acima não funcionará.

RESPOSTA ORIGINAL

Sim, é possível, mas é muito mais difícil do que deveria ser.

Se eu precisar me preocupar quando o teclado aparecer e desaparecer (o que é frequente), o que faço é personalizar minha classe de layout de nível superior em uma que substitua onMeasure(). A lógica básica é que, se o layout estiver preenchendo significativamente menos que a área total da janela, provavelmente um teclado virtual estará aparecendo.

import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;

/*
 * LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when 
 * the soft keyboard is shown and hidden (something Android can't tell you, weirdly). 
 */

public class LinearLayoutThatDetectsSoftKeyboard extends LinearLayout {

    public LinearLayoutThatDetectsSoftKeyboard(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public interface Listener {
        public void onSoftKeyboardShown(boolean isShowing);
    }
    private Listener listener;
    public void setListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        Activity activity = (Activity)getContext();
        Rect rect = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        int statusBarHeight = rect.top;
        int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
        int diff = (screenHeight - statusBarHeight) - height;
        if (listener != null) {
            listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);       
    }

    }

Em seguida, na sua classe Activity ...

public class MyActivity extends Activity implements LinearLayoutThatDetectsSoftKeyboard.Listener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        LinearLayoutThatDetectsSoftKeyboard mainLayout = (LinearLayoutThatDetectsSoftKeyboard)findViewById(R.id.main);
        mainLayout.setListener(this);
        ...
    }


    @Override
    public void onSoftKeyboardShown(boolean isShowing) {
        // do whatever you need to do here
    }

    ...
}

62
Isso não estava trabalhando para mim até que eu percebi que você deve definir o seguinte atributo na sua atividade: android: windowSoftInputMode = "adjustResize"
ajh158

9
Parece estar fazendo o truque. Além disso, se você não souber o ID da visualização raiz, veja como você pode obtê-la:((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
Goldsmith

8
Se você tentar isso usando a visualização raiz real ( android.R.id.content), poderá dizer com mais confiança que, em Systemvez de seu aplicativo, a entidade está alterando sua altura. Seria muito mais seguro para a equipe do Android nos dar uma pausa e nos informar pelo menos coisas básicas sobre a entrada do SoftKeyboard.
Graeme

14
Cuidado que heightDiffsempre incluirá a altura da barra de ação. Na nova resposta que foi ignorada ao testar se essa altura é maior que alguma constante, mas 100 pixels não são suficientes para dispositivos xxhdpi como o Nexus 4. Considere converter esse valor em DPs se você realmente quiser usar esse trabalho hacky. por aí.
Paul Lammertsma

8
Aviso: não funciona com WindowManager.LayoutParams.FLAG_FULLSCREEN e com um tema de tela cheia.
VAV

303

Então, espero que isso ajude alguém.

A nova resposta que Reuben Scratton deu é ótima e muito eficiente, mas realmente só funciona se você definir o windowSoftInputMode para ajustarResize. Se você configurá-lo para ajustar o painel, ainda não é possível detectar se o teclado está visível ou não usando seu trecho de código. Para contornar isso, fiz essa pequena modificação no código acima.

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
    Rect r = new Rect();
    //r will be populated with the coordinates of your view that area still visible.
    activityRootView.getWindowVisibleDisplayFrame(r);

    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    if (heightDiff > 0.25*activityRootView.getRootView().getHeight()) { // if more than 25% of the screen, its probably a keyboard...
        ... do something here
    }
 }
}); 

1
Este é o que funcionou para mim. Eu estava tentando detectar o estado do teclado de um costume TwoDScrollerViewsemelhante ao stackoverflow.com/a/5224088/530513, embora com o zoom também. O filho não era um ImageViewlayout simples, mas personalizado (estende RelativeLayout), mas não conseguiu detectar o teclado usando a solução recomendada, apesar da configuração android:windowSoftInputMode="adjustResize". Obrigado!
22412 David O'Meara

1
Obrigado, obrigado, obrigado! AdjustResize simplesmente não é viável para o meu aplicativo e sua solução funcionou perfeitamente.
Dwemthy

1
Trabalha com ActionBare ActionBarSherlock. Muito obrigado! By the way, existe um método r.height():) #
555 Dmitry Zaytsev

1
Anexarei uma recompensa aqui dentro de 23 horas para marcar esta resposta de alguma forma.
Dmitry Zaytsev

9
heightDiff > root.getRootView().getHeight() / 4É um bom valor trabalhar com dispositivos de alta resolução. 100px é muito curto. no Nexus 5 com resolução 1080x1920, 1920 - (996-75)>? 100 = 999 1920 - (1776-75)>? 100 = 219 // o teclado está ativo na galaxy s2 com 480x800 res, 800 - (800-38)>? 100 = 38800 - (410-38)>? 100 = 428 // o teclado está ativado, o número mágico 100px não é bom o suficiente.
precisa saber é o seguinte

55

Sempre foi em termos de computador, mas essa questão ainda é incrivelmente relevante!

Então, eu peguei as respostas acima e as combinei e refinei um pouco ...

public interface OnKeyboardVisibilityListener {


    void onVisibilityChanged(boolean visible);
}

public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
    final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);

    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        private boolean wasOpened;

        private final int DefaultKeyboardDP = 100;

        // From @nathanielwolf answer...  Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
        private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);

        private final Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            // Convert the dp to pixels.
            int estimatedKeyboardHeight = (int) TypedValue
                    .applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, activityRootView.getResources().getDisplayMetrics());

            // Conclude whether the keyboard is shown or not.
            activityRootView.getWindowVisibleDisplayFrame(r);
            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == wasOpened) {
                Log.d("Keyboard state", "Ignoring global layout change...");
                return;
            }

            wasOpened = isShown;
            listener.onVisibilityChanged(isShown);
        }
    });
}

Funciona para mim :)

NOTA: Se você perceber que o DefaultKeyboardDP não se encaixa no seu dispositivo, reproduza o valor e publique um comentário para que todos saibam qual deve ser o valor ... eventualmente, obteremos o valor correto para todos os dispositivos!

Para mais detalhes, confira a implementação no Cyborg


2
+1 Muito obrigado !! Eu estava tentando as outras respostas, mas elas não funcionam. Então eu encontrei o seu e funciona como um encanto. Código incrível! : D
Kevin van Mierlo

isso funciona apenas se você adicionar: android: windowSoftInputMode = "stateHidden | AdjustPan" ou android: windowSoftInputMode = "stateHidden | AdjustResize" Obrigado !!!!
Lena Bru

Você tem certeza? Se servidores de memória, recebi os eventos corretamente também quando o android: windowSoftInputMode tinha seu valor padrão ... a única coisa que não deu certo é o comportamento da tela, então eu a encolhi manualmente ...
TacB0sS

2
uma pequena correção: private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP? 48: 0);
binaryKarmic

2
Excelente!! Não importa que "windowSoftInputMode" esteja definido como "AdjustPan" / "AdjustResize" / "AdjustPan | stateHidden" / "AdjustResize | stateHidden", ou mesmo sem essa opção, ele sempre funciona! Testado em XiaoMi 8.
Zhou Hongbo

52

Desculpe pela resposta tardia, mas eu criei uma turma auxiliar para lidar com eventos de abertura / fechamento com ouvintes notificadores e outras coisas úteis, pode ser que alguém ache útil:

import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;

import java.util.LinkedList;
import java.util.List;

public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {

    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);
        void onSoftKeyboardClosed();
    }

    private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
    private final View activityRootView;
    private int        lastSoftKeyboardHeightInPx;
    private boolean    isSoftKeyboardOpened;

    public SoftKeyboardStateWatcher(View activityRootView) {
        this(activityRootView, false);
    }

    public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) {
        this.activityRootView     = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    public void onGlobalLayout() {
        final Rect r = new Rect();
        //r will be populated with the coordinates of your view that area still visible.
        activityRootView.getWindowVisibleDisplayFrame(r);

        final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (!isSoftKeyboardOpened && heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff);
        } else if (isSoftKeyboardOpened && heightDiff < 100) {
            isSoftKeyboardOpened = false;
            notifyOnSoftKeyboardClosed();
        }
    }

    public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
    }

    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }

    /**
     * Default value is zero {@code 0}.
     *
     * @return last saved keyboard height in px
     */
    public int getLastSoftKeyboardHeightInPx() {
        return lastSoftKeyboardHeightInPx;
    }

    public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.add(listener);
    }

    public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.remove(listener);
    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
        this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;

        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardOpened(keyboardHeightInPx);
            }
        }
    }

    private void notifyOnSoftKeyboardClosed() {
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardClosed();
            }
        }
    }
}

Exemplo de uso:

final SoftKeyboardStateWatcher softKeyboardStateWatcher 
    = new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);

// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks

2
A classe não é pequena, mas a implementação certamente é :). Graças Vou tentar isso :)
Atul O Holic

1
Ping-me se você tem problemas com ele :) Eu usado com sucesso em 2 projectos
Artem Zinnatullin

Depois de tentar muitos exemplos acima e encontrar problemas menores, este foi o que melhor funcionou para mim em muitos dispositivos diferentes (incluindo xxhdpi). Além disso, está em sua própria classe reutilizável. Eu o convertei para ser usado no mono droid.
kheit

Alguns teclados, algumas vezes, têm uma linha extra de teclas personalizadas na parte superior (por exemplo, palavras previstas). Aparentemente, eles não fazem parte do próprio teclado, pois o uso do getLastKeyboardHeightInPx()não inclui a altura dessa linha. Você conhece uma maneira de levar isso em consideração também?
ygesher

Isso só funciona se você estiver pronto para comprometer a alteração da altura do layout quando o teclado aparecer. direita?
M. Usman Khan

33

Algumas melhorias para evitar detectar erroneamente a visibilidade do teclado virtual em dispositivos de alta densidade:

  1. O limite da diferença de altura deve ser definido como 128 dp , não 128 pixels .
    Consulte o documento de design do Google sobre Metrics and Grid , 48 dp é o tamanho confortável para o objeto de toque e 32 dp é o mínimo para os botões. O teclado virtual genérico deve incluir 4 linhas de botões, para que a altura mínima do teclado seja: 32 dp * 4 = 128 dp , o que significa que o tamanho do limite deve ser transferido para pixels multiplicando a densidade do dispositivo. Para dispositivos xxxhdpi (densidade 4), o limite de altura do teclado virtual deve ser 128 * 4 = 512 pixels.

  2. Diferença de altura entre a vista raiz e sua área visível:
    altura da vista raiz - altura da barra de status - altura do quadro visível = vista inferior da raiz - fundo visível do quadro, uma vez que a altura da barra de status é igual à parte superior do quadro visível da vista raiz.

    private final String TAG = "TextEditor";
    private TextView mTextEditor;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_editor);
        mTextEditor = (TextView) findViewById(R.id.text_editor);
        mTextEditor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                isKeyboardShown(mTextEditor.getRootView());
            }
        });
    }
    
    private boolean isKeyboardShown(View rootView) {
        /* 128dp = 32dp * 4, minimum button height 32dp and generic 4 rows soft keyboard */
        final int SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128;
    
        Rect r = new Rect();
        rootView.getWindowVisibleDisplayFrame(r);
        DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
        /* heightDiff = rootView height - status bar height (r.top) - visible frame height (r.bottom - r.top) */
        int heightDiff = rootView.getBottom() - r.bottom;
        /* Threshold size: dp to pixels, multiply with display density */
        boolean isKeyboardShown = heightDiff > SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density;
    
        Log.d(TAG, "isKeyboardShown ? " + isKeyboardShown + ", heightDiff:" + heightDiff + ", density:" + dm.density
                + "root view height:" + rootView.getHeight() + ", rect:" + r);
    
        return isKeyboardShown;
    }

4
Isso merece ser a resposta aceita. Ignorar a densidade me deu resultados muito diferentes em dispositivos com diferentes formatos, mas tamanhos de pixel semelhantes. Obrigado!
Ginger McMurray

2
Obrigado ... esta é uma condição melhor!
TbB0sS

1
Excelente. O método isKeyboardShown () é o que precisamos. Obrigado
Danylo Volokh

Também está funcionando em 2020. Resposta perfeita. Eu tentei todo o código, mas isso funciona perfeitamente.
Mitesh Jain

8

Usei um pouco de tempo para descobrir isso ... Eu executei algumas CastExceptions, mas descobri que você pode substituir o LinearLayout no layout.xml pelo nome da classe.

Como isso:

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llMaster">

<com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard android:background="@drawable/metal_background"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:id="@+id/rlMaster" >
    <LinearLayout android:layout_width="fill_parent"
        android:layout_height="1dip" android:background="@drawable/line"></LinearLayout>

          ....

</com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard>    


</LinearLayout>

Dessa forma, você não enfrenta nenhum problema de elenco.

... e se você não quiser fazer isso em todas as páginas, recomendo que você use "MasterPage no Android". Veja o link aqui: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx


Tenha cuidado ao colar isso em seu XML se você não tiver o mesmo nome de pacote / classe. O Eclipse decide congelar e você precisa desligá-lo. Um produto tão profissional. / s
Spencer Ruport

5
@SpencerRuport, é por isso que é grátis.
Cody

@DoctorOreo - obtenha o IntelliJ. É grátis e não é ruim.
26413 Mark

@ Mark - alguns meses depois que postei isso, eu realmente experimentei o IntelliJ. É muito melhor, IMO, do que o Eclipse. Todos os seus produtos (na maior parte) acho que são excelentes. Eu até comprei alguns.
Cody

Desculpe por reviver um tópico de comentário tão antigo. Fico feliz que você esteja usando e gostando. Adoro usar o IntelliJ, bem como o AppCode para iOS e o PyCharm para Python. Felicidades!
29413 Mark


5

A idéia é que, se você precisar ocultar o teclado e verificar o estado de entrada suave ao mesmo tempo, use a seguinte solução:

public boolean hideSoftInput() {
    InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
    return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}

Este método retorna true se o teclado foi mostrado antes de ocultar.


Esta é a única obras sem o uso de altura e todos ... Obrigado ... u salvou a minha vez ...
VJI

4

Descobri que uma combinação do método de @ Reuben_Scratton junto com o método de @ Yogesh parece funcionar melhor. A combinação de seus métodos produziria algo como isto:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
       // ... do something here
    }
  }
});

sempre Configuration.KEYBOARDHIDDEN_NO.
fantouch

4

Você pode observar a ocultação do teclado virtual usando decorView da atividade.

public final class SoftKeyboardUtil {
    public static final String TAG = "SoftKeyboardUtil";
    public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
        final View decorView = activity.getWindow().getDecorView();
        decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect rect = new Rect();
                decorView.getWindowVisibleDisplayFrame(rect);
                int displayHight = rect.bottom - rect.top;
                int hight = decorView.getHeight();
                boolean hide = (double)displayHight / hight > 0.8 ;
                if(Log.isLoggable(TAG, Log.DEBUG)){
                    Log.d(TAG ,"DecorView display hight = "+displayHight);
                    Log.d(TAG ,"DecorView hight = "+ hight);
                    Log.d(TAG, "softkeyboard visible = " + !hide);
                }

                listener.onSoftKeyBoardVisible(!hide);

            }
        });
    }



    public interface OnSoftKeyBoardHideListener{
        void onSoftKeyBoardVisible(boolean visible);
    }
}

4

Em vez de assumir a diferença de codificação, eu fiz algo assim, pois eu tinha opções de menu no meu aplicativo.

final View root= findViewById(R.id.myrootview); 
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
    public void onGlobalLayout() {
        int heightDiff = root.getRootView().getHeight() - root.getHeight();

        Rect rectgle= new Rect();
        Window window= getWindow();
        window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
        int contentViewTop=                     
          window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            //Soft KeyBoard Hidden
        }else{
            //Soft KeyBoard Shown
        }
     }
});

Não funciona no android: windowSoftInputMode = "AdjustPan". Eu queria que minha tela não ficasse reduzida depois que o teclado virtual aparecer. Pode dizer qualquer correção para que ele funciona mesmo para adjustPan
Shirish Herwade

4

Também existe solução com inserções do sistema, mas funciona apenas com API >= 21( Android L). Digamos que você tenha BottomNavigationView, que é filho LinearLayoute precise ocultá-lo quando o teclado for exibido:

> LinearLayout
  > ContentView
  > BottomNavigationView

Tudo que você precisa fazer é estender LinearLayoutda seguinte maneira:

public class KeyboardAwareLinearLayout extends LinearLayout {
    public KeyboardAwareLinearLayout(Context context) {
        super(context);
    }

    public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardAwareLinearLayout(Context context,
                                     @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public KeyboardAwareLinearLayout(Context context, AttributeSet attrs,
                                     int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        int childCount = getChildCount();
        for (int index = 0; index < childCount; index++) {
            View view = getChildAt(index);
            if (view instanceof BottomNavigationView) {
                int bottom = insets.getSystemWindowInsetBottom();
                if (bottom >= ViewUtils.dpToPx(200)) {
                    // keyboard is shown
                    view.setVisibility(GONE);
                } else {
                    // keyboard is hidden
                    view.setVisibility(VISIBLE);
                }
            }
        }
        return insets;
    }
}

A idéia é que, quando o teclado é mostrado, as inserções do sistema são alteradas com um .bottomvalor bastante grande .


4

Existe um método oculto que pode ajudar nisso InputMethodManager.getInputMethodWindowVisibleHeight,. Mas não sei por que está escondido.

import android.content.Context
import android.os.Handler
import android.view.inputmethod.InputMethodManager

class SoftKeyboardStateWatcher(private val ctx: Context) {
  companion object {
    private const val DELAY = 10L
  }

  private val handler = Handler()
  private var isSoftKeyboardOpened: Boolean = false

  private val height: Int
    get() {
      val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
      val method = imm.javaClass.getMethod("getInputMethodWindowVisibleHeight")
      method.isAccessible = true
      return method.invoke(imm) as Int
    }

  private val task: Runnable by lazy {
    Runnable {
      start()
      if (!isSoftKeyboardOpened && height > 0) {
        isSoftKeyboardOpened = true
        notifyOnSoftKeyboardOpened(height)
      } else if (isSoftKeyboardOpened && height == 0) {
        isSoftKeyboardOpened = false
        notifyOnSoftKeyboardClosed()
      }
    }
  }

  var listener: SoftKeyboardStateListener? = null

  interface SoftKeyboardStateListener {
    fun onSoftKeyboardOpened(keyboardHeightInPx: Int)
    fun onSoftKeyboardClosed()
  }

  fun start() {
    handler.postDelayed(task, DELAY)
  }

  fun stop() {
    handler.postDelayed({
      if (!isSoftKeyboardOpened) handler.removeCallbacks(task)
    }, DELAY * 10)
  }

  private fun notifyOnSoftKeyboardOpened(keyboardHeightInPx: Int) {
    listener?.onSoftKeyboardOpened(keyboardHeightInPx)
  }

  private fun notifyOnSoftKeyboardClosed() {
    listener?.onSoftKeyboardClosed()
  }
}

Se alguém precisar disso - ele funciona no Xamarin, o nome do método é exatamente o mesmo e precisa ser acessado da mesma maneira - através da propriedade Class no InputMethodManager.
Konstantin Severy

Tenha cuidado ao usar isso, pois é uma API não suportada (está oculta por um motivo) e, para iniciantes, não funciona no KitKat.
Daniele Ricci

3

Nenhuma dessas soluções funcionará para o Lollipop como está. No Lollipop activityRootView.getRootView().getHeight()inclui a altura da barra de botões, enquanto a medição da vista não. Eu adaptei a melhor / mais simples solução acima para trabalhar com o Lollipop.

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    Rect r = new Rect();
    //r will be populated with the coordinates of your view that area still visible.
    activityRootView.getWindowVisibleDisplayFrame(r);

    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    Resources res = getResources();
    // The status bar is 25dp, use 50dp for assurance
    float maxDiff =
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, res.getDisplayMetrics());

    //Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      float buttonBarHeight =
          TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, res.getDisplayMetrics());
      maxDiff += buttonBarHeight;
    }
    if (heightDiff > maxDiff) { // if more than 100 pixels, its probably a keyboard...
      ...do something here
    }
  }
});

Por que uma solução semelhante de stackoverflow.com/a/18992807/2914140 funcionou para você e como sua solução difere?
CoolMind

3

Acabei de encontrar um bug ao usar a maioria das soluções acima que sugerem a adição de um número fixo.

S4 é tem um dpi alto que resultou na altura da barra de navegação sendo 100px, portanto, meu aplicativo pensa que o teclado está aberto o tempo todo.

Portanto, com todos os novos telefones de alta resolução sendo lançados, acredito que usar um valor codificado não é uma boa idéia a longo prazo.

Uma abordagem melhor que encontrei após alguns testes em várias telas e dispositivos foi usar porcentagem. Obtenha a diferença entre o decorView e o conteúdo do seu aplicativo e depois verifique qual é a porcentagem dessa diferença. A partir das estatísticas que obtive, a maioria das barras de navegação (independentemente do tamanho, resolução etc.) levará entre 3% a 5% da tela. Onde, como se o teclado estivesse aberto, estava ocupando entre 47% a 55% da tela.

Como conclusão, minha solução foi verificar se o diff é superior a 10%, e presumo que seja um teclado aberto.


3

Usei uma pequena variante da resposta de Reuban, que provou ser mais útil em determinadas circunstâncias, especialmente com dispositivos de alta resolução.

final View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 3) {
                    //Make changes for Keyboard not visible
                } else {
                    //Make changes for keyboard visible
                }
            }
        });

O que é isso R.id.activityRoot
ranjith

2
em vez de criar e usar R.id.activityRoot, você pode simplesmente usar android.R.id.contentexatamente o que você precisa.
Marcin Orlowski

3

Sempre foi em termos de computador, mas essa questão ainda é incrivelmente relevante! Então, eu peguei as respostas acima e as combinei e refinei um pouco ...

public interface OnKeyboardVisibilityListener {
    void onVisibilityChanged(boolean visible);
}

public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
    final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        private boolean wasOpened;

    private final Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            activityRootView.getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isOpen = heightDiff > 100;
            if (isOpen == wasOpened) {
                logDebug("Ignoring global layout change...");
                return;
            }

            wasOpened = isOpen;
            listener.onVisibilityChanged(isOpen);
        }
    });
}

Funciona para mim.


3

Tente o seguinte:

final View activityRootView = getWindow().getDecorView().getRootView();
activityRootView.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.
        activityRootView.getWindowVisibleDisplayFrame(r);

        int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
             // ... do something here ... \\
        }
    }
});

2

Minha resposta é basicamente a mesma que a resposta de Kachi, mas envolvi-a em uma classe auxiliar agradável para limpar a maneira como é usada em todo o meu aplicativo.

import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

/**
 * Detects Keyboard Status changes and fires events only once for each change
 */
public class KeyboardStatusDetector {
    KeyboardVisibilityListener visibilityListener;

    boolean keyboardVisible = false;

    public void registerFragment(Fragment f) {
        registerView(f.getView());
    }

    public void registerActivity(Activity a) {
        registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
    }

    public KeyboardStatusDetector registerView(final View v) {
        v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                v.getWindowVisibleDisplayFrame(r);

                int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
                    /** Check this variable to debounce layout events */
                    if(!keyboardVisible) {
                        keyboardVisible = true;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
                    }
                } else {
                    if(keyboardVisible) {
                        keyboardVisible = false;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
                    }
                }
            }
        });

        return this;
    }

    public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
        visibilityListener = listener;
        return this;
    }

    public static interface KeyboardVisibilityListener {
        public void onVisibilityChanged(boolean keyboardVisible);
    }
}

Você pode usar isso para detectar alterações no teclado em qualquer lugar do aplicativo, como este:

    new KeyboardStatusDetector()
            .registerFragment(fragment)  //register to a fragment 
            .registerActivity(activity)  //or register to an activity
            .registerView(view)          //or register to a view
            .setVisibilityListener(new KeyboardVisibilityListener() {
                @Override
                public void onVisibilityChanged(boolean keyboardVisible) {
                    if(keyboardVisible) {
                       //Do stuff for keyboard visible
                    }else {
                       //Do stuff for keyboard hidden
                    }
                }
            });

Nota: use apenas uma das chamadas "registrar". Todos eles funcionam da mesma forma e estão lá apenas por conveniência


2

você pode tentar isso, funciona muito bem para mim:

InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);

if (imm.isAcceptingText()) {
    //Software Keyboard was shown..
} else {
    //Software Keyboard was not shown..
}

1
Não use isso, ele retorna true se o teclado estava escondida usando de volta, mas o ponto de vista tem o foco, pelo menos no Marshmallow
Maragues

2
sempre responde que é visível
jose920405

2

Eu estava tendo dificuldade em manter o estado do teclado ao alterar a orientação dos fragmentos em um viewpager. Não sei por que, mas parece ser instável e age de maneira diferente de uma atividade padrão.

Para manter o estado do teclado nesse caso, primeiro você deve adicionar android:windowSoftInputMode = "stateUnchanged"ao seuAndroidManifest.xml . Você pode notar, no entanto, que isso realmente não resolve todo o problema - o teclado não foi aberto para mim se estivesse aberto anteriormente antes da alteração da orientação. Em todos os outros casos, o comportamento parecia estar correto.

Então, precisamos implementar uma das soluções mencionadas aqui. O mais limpo que encontrei foi o de George Maisuradze - use o retorno de chamada booleano de hideSoftInputFromWindow:

InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);

Guardei esse valor no onSaveInstanceStatemétodo do meu Fragment e o recuperei onCreate. Então, mostrei o teclado à força onCreateViewse tivesse um valor de true(ele retorna true se o teclado estiver visível antes de realmente ocultá-lo antes da destruição do fragmento).


1

Aqui está a minha solução e funciona. Em vez de procurar o tamanho do pixel, verifique se a altura da exibição do conteúdo mudou ou não:

// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private int oldHeight;

        @Override
        public void onGlobalLayout() {
            int newHeight = commentsContent.getMeasuredHeight();
            if (newHeight < oldHeight) {
                // Check for the keyboard showing in case the height difference
                // is a result of orientation change
                if (isSoftKeyboardShowing(CommentsActivity.this)) {
                    // Keyboard is showing so scroll to the latest comment
                    scrollToLatestComment();
                }
            }
            oldHeight = newHeight;
        }

    });


public static boolean isSoftKeyboardShowing(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    return inputMethodManager.isActive();
}

inputMethodManager.isActive () é sempre retornando verdadeiro para mim, independentemente do teclado é se ou não
EionRobb

1

Não crie nenhum código rígido. A melhor maneira é redimensionar suas visualizações enquanto estiver no Get Focus on EditText with KeyBord Show. Você pode fazer isso adicionando propriedades de redimensionamento à atividade no arquivo Manifest usando o código abaixo.

android:windowSoftInputMode="adjustResize"


1

Existe um método direto para descobrir isso. E, não requer nenhuma alteração de layout.
Portanto, ele também funciona no modo imersivo em tela cheia.

O truque é tentar ocultar ou mostrar o teclado virtual e capturar o resultado dessa tentativa.
Sem pânico, isso realmente não mostra ou oculta o teclado. Nós apenas pedimos o estado.

Para manter-se atualizado, você pode simplesmente repetir a operação, por exemplo, a cada 200 milissegundos, usando um manipulador.

Você encontra uma implementação aqui: https://stackoverflow.com/a/27567074/2525452


1

Eu acho que esse método irá ajudá-lo a descobrir se o teclado é visível ou não.

 public Boolean isSoftKeyBoardVisible(){
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

    if (imm.isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
        return true;
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
        return false;
    }

}

sempre responde que é visível
jose920405

0

A nova resposta de Reuben Scratton (calcule o HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight(); ) não funcionará em atividade se você definir o modo translúcido da barra de status.

se você usar barra de status translúcida, activityRootView.getHeight() nunca mudará o clima, o teclado virtual estará visível. sempre retornará a altura da atividade e a barra de status.

Por exemplo, o Nexus 4, Android 5.0.1, definido android:windowTranslucentStatuscomo true, retornará 1184 para sempre, até mesmo os ope. Se você definirandroid:windowTranslucentStatus como false, ele retornará Height corretamente, se for invisível, retornará 1134 (não inclui a barra de status) .Feche o ime, retornará 5xx talvez (depende da altura do ime)

Eu não sei o tempo, isso é um erro, tentei 4.4.4 e 5.0.1, o resultado é o mesmo.

Portanto, até agora, a segunda resposta mais acertada, a solução da Kachi será a maneira mais segura de calcular a altura da imagem. Aqui está uma cópia:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new        OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);

int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
    ... do something here
    }
 }
}); 

0

Um método que não precisa de um LayoutListener

No meu caso, gostaria de salvar o estado do teclado antes de substituir o meu fragmento. Eu chamo o método hideSoftInputFromWindow deonSaveInstanceState , que fecha o teclado e me retorna se o teclado estava visível ou não.

Este método é simples, mas pode alterar o estado do seu teclado.


0

Sei que este é um post antigo, mas acho que essa é a abordagem mais simples que conheço e meu dispositivo de teste é o Nexus 5. Não tentei em outros dispositivos. Espero que outras pessoas compartilhem sua abordagem se acharem que meu código não é bom :)

public static boolean isKeyboardShown(Context context, View view) {
        if (context == null || view == null) {
            return false;
        }
        InputMethodManager imm = (InputMethodManager) context
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 
}

imm.hideSoftInputFromWindow retorna booleano.

Obrigado,


0
if (keyopen())
{
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);            
}

A função acima é o que eu uso para verificar se um teclado está visível. Se for, então eu fecho.

Abaixo mostra os dois métodos necessários.

Primeiro, defina a altura da janela viável no onCreate.

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

//  add to onCreate method
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    sheight= rectgle.bottom;
//

} 

Em seguida, adicione um método booleano que obtenha a altura da janela nessa instância. Se ele não corresponder ao original (supondo que você não o mude ao longo do caminho ...), o teclado estará aberto.

public boolean keyopen()
{
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    int curheight= rectgle.bottom;

    if (curheight!=sheight)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Frotz!


0

Eu sei o quão exato você pode determinar se o teclado está oculto ou não.

public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public int getNavigationBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public boolean isKeyboardHidden() {
    int delta = mRootView.getRootView().getHeight() - mRootView.getHeight() - getNavigationBarHeight() - getStatusBarHeight()
            - getSupportActionBar().getHeight();
    return delta <= 0;
}

Isso funciona para tablets. Quando a barra de navegação é mostrada horizontalmente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.