Eu preciso fazer uma coisa muito simples - descobrir se o teclado do software é mostrado. Isso é possível no Android?
Eu preciso fazer uma coisa muito simples - descobrir se o teclado do software é mostrado. Isso é possível no Android?
Respostas:
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
}
...
}
((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
android.R.id.content
), poderá dizer com mais confiança que, em System
vez 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.
heightDiff
sempre 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í.
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
}
}
});
TwoDScrollerView
semelhante ao stackoverflow.com/a/5224088/530513, embora com o zoom também. O filho não era um ImageView
layout 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!
ActionBar
e ActionBarSherlock
. Muito obrigado! By the way, existe um método r.height()
:) #
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.
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
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
getLastKeyboardHeightInPx()
não inclui a altura dessa linha. Você conhece uma maneira de levar isso em consideração também?
Algumas melhorias para evitar detectar erroneamente a visibilidade do teclado virtual em dispositivos de alta densidade:
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.
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;
}
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
A verificação da altura dos elementos não é confiável porque alguns teclados como o WifiKeyboard têm altura zero.
Em vez disso, você pode usar o resultado de retorno de chamada showSoftInput () e hideSoftInput () para verificar o status do teclado. Detalhes completos e código de exemplo em
https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android
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.
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
}
}
});
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);
}
}
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
}
}
});
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 LinearLayout
e precise ocultá-lo quando o teclado for exibido:
> LinearLayout
> ContentView
> BottomNavigationView
Tudo que você precisa fazer é estender LinearLayout
da 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 .bottom
valor bastante grande .
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()
}
}
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
}
}
});
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.
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
}
}
});
R.id.activityRoot
, você pode simplesmente usar android.R.id.content
exatamente o que você precisa.
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.
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 ... \\
}
}
});
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
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..
}
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 onSaveInstanceState
método do meu Fragment e o recuperei onCreate
. Então, mostrei o teclado à força onCreateView
se 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).
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();
}
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"
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
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;
}
}
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:windowTranslucentStatus
como 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
}
}
});
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.
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,
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!
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.