Je dois faire une chose très simple: savoir si le clavier logiciel est affiché. Est-ce possible sur Android?
Je dois faire une chose très simple: savoir si le clavier logiciel est affiché. Est-ce possible sur Android?
Réponses:
NOUVELLE RÉPONSE ajoutée le 25 janvier 2012
Depuis que j'ai écrit la réponse ci-dessous, quelqu'un m'a informé de l'existence de ViewTreeObserver et de ses amis, des API qui se cachent dans le SDK depuis la version 1.
Plutôt que d'exiger un type de mise en page personnalisé, une solution beaucoup plus simple consiste à donner à la vue racine de votre activité un ID connu, par exemple @+id/activityRoot
, raccordez un GlobalLayoutListener au ViewTreeObserver, et à partir de là, calculez la différence de taille entre la racine de la vue de votre activité et la taille de la fenêtre:
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
}
}
});
Utilisation d'un utilitaire tel que:
public static float dpToPx(Context context, float valueInDp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
Facile!
Remarque:
Votre application doit définir ce drapeau dans le manifeste Android, android:windowSoftInputMode="adjustResize"
sinon la solution ci-dessus ne fonctionnera pas.
RÉPONSE ORIGINALE
Oui, c'est possible, mais c'est beaucoup plus difficile qu'il ne devrait l'être.
Si je dois me soucier de l'apparition et de la disparition du clavier (ce qui est assez fréquent), je personnalise ma classe de disposition de niveau supérieur en une qui remplace onMeasure()
. La logique de base est que si la mise en page se trouve remplir considérablement moins que la surface totale de la fenêtre, alors un clavier virtuel s'affiche probablement.
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);
}
}
Ensuite, dans votre classe d'activité ...
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
), vous pourrez dire avec plus de certitude que System
l'entité change sa hauteur plutôt que votre application. Il serait beaucoup plus sûr pour l'équipe Android de nous donner une pause et de nous faire savoir au moins des choses de base sur l'entrée SoftKeyboard.
heightDiff
inclura toujours la hauteur de la barre d'action. Dans la nouvelle réponse qui a été ignorée en testant si cette hauteur est supérieure à une constante, mais 100 pixels ne sont pas suffisants pour les appareils xxhdpi tels que le Nexus 4. Envisagez de convertir cette valeur en DP si vous voulez vraiment utiliser ce travail hacky- environ.
J'espère donc que cela aide quelqu'un.
La nouvelle réponse donnée par Reuben Scratton est excellente et vraiment efficace, mais elle ne fonctionne vraiment que si vous définissez votre windowSoftInputMode sur adjustResize. Si vous le définissez sur adjustPan, il n'est toujours pas possible de détecter si le clavier est visible ou non à l'aide de son extrait de code. Pour contourner ce problème, j'ai apporté cette petite modification au code ci-dessus.
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
similaire à stackoverflow.com/a/5224088/530513 bien qu'avec le zoom aussi. L'enfant n'était pas une disposition simple ImageView
mais personnalisée (étend RelativeLayout
) mais n'a pas pu détecter le clavier en utilisant la solution recommandée malgré le réglage android:windowSoftInputMode="adjustResize"
. Merci!
ActionBar
et ActionBarSherlock
. Merci beaucoup! Soit dit en passant, il existe une méthode r.height()
:)
heightDiff > root.getRootView().getHeight() / 4
est une bonne valeur pour travailler avec un appareil haute résolution. 100px est trop court. dans Nexus 5 avec 1080x1920 res, 1920 - (996-75)>? 100 = 999 1920 - (1776-75)>? 100 = 219 // le clavier est en place dans la galaxie s2 avec 480x800 res, 800 - (800-38)>? 100 = 38 800 - (410-38)>? 100 = 428 // le clavier est en place donc le nombre magique 100px n'est pas assez bon.
Cela a toujours été le cas en termes informatiques, mais cette question est toujours incroyablement pertinente!
J'ai donc pris les réponses ci-dessus et les ai combinées et affinées un peu ...
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);
}
});
}
Travaille pour moi :)
REMARQUE: Si vous remarquez que le DefaultKeyboardDP ne correspond pas à votre appareil, jouez avec la valeur et postez un commentaire pour que tout le monde sache quelle devrait être la valeur ... nous obtiendrons finalement la valeur correcte pour s'adapter à tous les appareils!
Pour plus de détails, consultez l'implémentation sur Cyborg
Désolé pour la réponse tardive, mais j'avais créé une petite classe d'aide pour gérer les événements d'ouverture / fermeture avec des auditeurs avertis et d'autres choses utiles, peut-être que quelqu'un le trouverait utile:
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();
}
}
}
}
Exemple d'utilisation:
final SoftKeyboardStateWatcher softKeyboardStateWatcher
= new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);
// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks
getLastKeyboardHeightInPx()
ne comprend pas la hauteur de cette ligne. Connaissez-vous également un moyen de le prendre en compte?
Quelques améliorations pour éviter de détecter à tort la visibilité du clavier logiciel sur les appareils haute densité:
Le seuil de différence de hauteur doit être défini comme 128 dp , pas 128 pixels .
Reportez-vous au document de conception Google sur les mesures et la grille , 48 dp est une taille confortable pour un objet tactile et 32 dp est un minimum pour les boutons. Le clavier virtuel générique doit comprendre 4 rangées de boutons clés, la hauteur minimale du clavier doit donc être: 32 dp * 4 = 128 dp , ce qui signifie que la taille du seuil doit être transférée aux pixels en multipliant la densité de l'appareil. Pour les périphériques xxxhdpi (densité 4), le seuil de hauteur du clavier virtuel doit être de 128 * 4 = 512 pixels.
Différence de hauteur entre la vue racine et sa zone visible:
hauteur de la vue racine - hauteur de la barre d'état - hauteur du cadre visible = bas de la vue racine - bas du cadre visible, car la hauteur de la barre d'état est égale au haut du cadre visible de la vue racine.
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;
}
J'ai utilisé un peu de temps pour comprendre cela ... Je l'ai exécuté quelques CastExceptions, mais j'ai compris que vous pouvez remplacer LinearLayout dans le fichier layout.xml par le nom de la classe.
Comme ça:
<?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>
De cette façon, vous ne rencontrez aucun problème de distribution.
... et si vous ne voulez pas le faire sur chaque page, je vous recommande d'utiliser "MasterPage dans Android". Voir le lien ici: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx
La vérification de la hauteur des éléments n'est pas fiable car certains claviers comme WifiKeyboard ont une hauteur nulle.
Au lieu de cela, vous pouvez utiliser le résultat du rappel de showSoftInput () et hideSoftInput () pour vérifier l'état du clavier. Détails complets et exemple de code sur
https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android
L'idée est que si vous devez masquer votre clavier et vérifier simultanément l'état de la saisie logicielle, utilisez la solution suivante:
public boolean hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}
Cette méthode renvoie true si le clavier a été affiché avant de se cacher.
J'ai trouvé qu'une combinaison de la méthode de @ Reuben_Scratton avec la méthode de @ Yogesh semble fonctionner le mieux. La combinaison de leurs méthodes donnerait quelque chose comme ceci:
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
}
}
});
Vous pouvez observer le masquage du clavier logiciel à l'aide de décorView de l'activité.
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);
}
}
Au lieu de supposer la différence de codage, j'ai fait quelque chose comme ça, car j'avais des options de menu dans mon application.
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
}
}
});
Il existe également une solution avec des insertions système, mais cela ne fonctionne qu'avec API >= 21
( Android L
). Supposons que vous en ayez un BottomNavigationView
, LinearLayout
et que vous devez le masquer lorsque le clavier s'affiche:
> LinearLayout
> ContentView
> BottomNavigationView
Tout ce que vous devez faire est d'étendre LinearLayout
de cette manière:
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;
}
}
L'idée est que lorsque le clavier est affiché, les insertions système sont modifiées avec une .bottom
valeur assez importante .
Il y a une méthode cachée qui peut aider pour cela InputMethodManager.getInputMethodWindowVisibleHeight
,. Mais je ne sais pas pourquoi c'est caché.
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()
}
}
Aucune de ces solutions ne fonctionnera pour Lollipop en l'état. Dans Lollipop activityRootView.getRootView().getHeight()
comprend la hauteur de la barre de boutons, tandis que la mesure de la vue ne le fait pas. J'ai adapté la solution la meilleure / la plus simple ci-dessus pour travailler avec 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
}
}
});
Je viens de rencontrer un bogue en utilisant la plupart des solutions ci-dessus qui suggèrent d'ajouter un numéro fixe.
Le S4 a un dpi élevé, ce qui fait que la hauteur de la barre de navigation est de 100 pixels, donc mon application pense que le clavier est ouvert tout le temps.
Donc, avec tous les nouveaux téléphones haute résolution sortis, je pense que l'utilisation d'une valeur codée en dur n'est pas une bonne idée à long terme.
Une meilleure approche que j'ai trouvée après quelques tests sur divers écrans et appareils était d'utiliser le pourcentage. Obtenez la différence entre decorView et le contenu de votre application et vérifiez ensuite quel est le pourcentage de cette différence. D'après les statistiques que j'ai obtenues, la plupart des barres de navigation (quelle que soit la taille, la résolution, etc.) occuperont entre 3% et 5% de l'écran. Là où, comme si le clavier était ouvert, il occupait entre 47% et 55% de l'écran.
En conclusion, ma solution était de vérifier si le différentiel est supérieur à 10%, alors je suppose que c'est un clavier ouvert.
J'ai utilisé une légère variante de la réponse de Reuban, qui s'est avérée plus utile dans certaines circonstances, en particulier avec les appareils haute résolution.
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
, vous pouvez simplement utiliser android.R.id.content
ce qui est exactement ce dont vous avez besoin.
Cela a toujours été le cas pour l'ordinateur, mais cette question est toujours incroyablement pertinente! J'ai donc pris les réponses ci-dessus et les ai combinées et affinées un peu ...
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);
}
});
}
Ça marche pour moi.
Essaye ça:
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 ... \\
}
}
});
Ma réponse est fondamentalement la même que la réponse de Kachi, mais je l'ai enveloppée dans une belle classe d'aide pour nettoyer la façon dont elle est utilisée dans toute mon application.
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);
}
}
Vous pouvez l'utiliser pour détecter les modifications du clavier n'importe où dans l'application, comme ceci:
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
}
}
});
Remarque: n'utilisez qu'un seul des appels "enregistrer". Ils fonctionnent tous de la même manière et ne sont là que pour plus de commodité
vous pouvez essayer cela, fonctionne très bien pour moi:
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) {
//Software Keyboard was shown..
} else {
//Software Keyboard was not shown..
}
J'avais du mal à maintenir l'état du clavier lors du changement d'orientation des fragments dans un viseur. Je ne sais pas pourquoi, mais cela semble juste chancelant et agit différemment d'une activité standard.
Pour conserver l'état du clavier dans ce cas, vous devez d'abord ajouter android:windowSoftInputMode = "stateUnchanged"
à votre AndroidManifest.xml
. Vous remarquerez peut-être, cependant, que cela ne résout pas réellement tout le problème - le clavier ne s'est pas ouvert pour moi s'il a été précédemment ouvert avant le changement d'orientation. Dans tous les autres cas, le comportement semblait correct.
Ensuite, nous devons mettre en œuvre l'une des solutions mentionnées ici. Le plus propre que j'ai trouvé était celui de George Maisuradze - utilisez le rappel booléen de hideSoftInputFromWindow:
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
J'ai stocké cette valeur dans la onSaveInstanceState
méthode de mon fragment et je l'ai récupérée onCreate
. Ensuite, j'ai montré de force le clavier onCreateView
s'il avait une valeur de true
(il retourne vrai si le clavier est visible avant de le cacher avant la destruction du fragment).
Voici ma solution, et cela fonctionne. Au lieu de rechercher la taille des pixels, vérifiez simplement que la hauteur de la vue du contenu a changé ou non:
// 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();
}
Ne créez pas de code dur. Le meilleur moyen est de redimensionner vos vues pendant que vous vous concentrez sur EditText avec KeyBord Show. Vous pouvez le faire en ajoutant la propriété de redimensionnement de l'activité dans le fichier manifeste à l'aide du code ci-dessous.
android:windowSoftInputMode="adjustResize"
Il existe une méthode directe pour le savoir. Et, il ne nécessite aucune modification de mise en page.
Donc, cela fonctionne également en mode plein écran immersif.
L'astuce est que vous essayez de masquer ou d'afficher le clavier virtuel et de capturer le résultat de cet essai.
Pas de panique, cela ne montre ni ne masque vraiment le clavier. Nous demandons simplement l'État.
Pour rester à jour, vous pouvez simplement répéter l'opération, par exemple toutes les 200 millisecondes, à l'aide d'un gestionnaire.
Vous trouverez une implémentation ici: https://stackoverflow.com/a/27567074/2525452
Je pense que cette méthode vous aidera à découvrir si le clavier est visible ou non.
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;
}
}
La nouvelle réponse de Reuben Scratton (calculer le HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
) ne fonctionnera pas en activité si vous définissez le mode de barre d'état translucide.
si vous utilisez la barre d'état translucide, activityRootView.getHeight()
ne changera jamais la météo, le clavier logiciel est visible. il renverra toujours la hauteur de l'activité et la barre d'état.
Par exemple, Nexus 4, Android 5.0.1, défini android:windowTranslucentStatus
sur true, il renverra 1184 pour toujours, même si l'image s'est ouverte. Si vous définissez android:windowTranslucentStatus
sur false, il renverra la hauteur correctement, s'il est invisible, il renverra 1134 (n'inclut pas la barre d'état)
Je ne sais pas s'il s'agit d'un bug, j'ai essayé 4.4.4 et 5.0.1, le résultat est le même.
Donc, jusqu'à présent, la deuxième réponse la plus acceptée, la solution de Kachi sera le moyen le plus sûr de calculer la hauteur d'IME. En voici une copie:
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
}
}
});
Une méthode qui n'a pas besoin d'un LayoutListener
Dans mon cas, je voudrais sauvegarder l'état du clavier avant de remplacer mon Fragment. J'appelle la méthode hideSoftInputFromWindow à partir onSaveInstanceState
, qui ferme le clavier et retourne - moi si le clavier est visible ou non.
Cette méthode est simple mais peut changer l'état de votre clavier.
Je sais que c'est un vieux post mais je pense que c'est l'approche la plus simple que je connaisse et mon appareil de test est le Nexus 5. Je ne l'ai pas essayé sur d'autres appareils. J'espère que d'autres partageront leur approche s'ils trouvent que mon code n'est pas bon :)
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 renvoie un booléen.
Merci,
if (keyopen())
{
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);
}
La fonction ci-dessus est ce que j'utilise pour vérifier si un clavier est visible. Si c'est le cas, je le ferme.
Ci-dessous montre les deux méthodes requises.
Tout d'abord, définissez la hauteur de fenêtre réalisable dans 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;
//
}
Ensuite, ajoutez une méthode booléenne qui obtient la hauteur de la fenêtre à cette instance. S'il ne correspond pas à l'original (en supposant que vous ne le modifiez pas en cours de route ...), le clavier est ouvert.
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!
Je sais à quel point vous pouvez déterminer si le clavier est caché ou non.
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;
}
Cela fonctionne pour les tablettes. Lorsque la barre de navigation est affichée horizontalement.