web-dev-qa-db-fra.com

Comment vérifier la visibilité du clavier logiciel sous Android?

J'ai besoin de faire une chose très simple - savoir si le clavier du logiciel est affiché. Est-ce possible dans Android?

475
fhucho

NOUVELLE REPONSE _ ​​ajouté le 25 janvier 2012

Depuis l’écriture de la réponse ci-dessous, une personne m’a informé de l’existence de ViewTreeObserver and friends, des API qui se dissimulaient dans le SDK depuis la version 1.

Plutôt que de demander un type de disposition personnalisé, une solution beaucoup plus simple consiste à attribuer un ID connu à la vue racine de votre activité, par exemple @+id/activityRoot, à lier un GlobalLayoutListener au ViewTreeObserver, puis à calculer 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
        }
     }
});

Utiliser 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 cet indicateur dans le manifeste Android Android:windowSoftInputMode="adjustResize" sinon la solution ci-dessus ne fonctionnera pas.

REPONSE ORIGINALE

Oui, c'est possible, mais c'est beaucoup plus difficile que cela ne devrait être.

Si je dois prendre en compte le moment où le clavier apparaît et disparaît (ce qui est assez souvent), je personnalise ma classe de présentation de niveau supérieur en une classe qui remplace la fonction onMeasure(). La logique de base est que si la disposition se remplit considérablement moins que la surface totale de la fenêtre, un clavier virtuel apparaîtra 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);       
    }

    }

Puis 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
    }

    ...
}
643
Reuben Scratton

Alors espérons que cela aide quelqu'un.

La nouvelle réponse donnée par Reuben Scratton est géniale et efficace, mais elle ne fonctionne vraiment que si vous configurez windowSoftInputMode pour ajuster la taille. Si vous le réglez 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 > 100) { // if more than 100 pixels, its probably a keyboard...
        ... do something here
    }
 }
}); 
291
Kachi

Désolé pour la réponse tardive, mais j'avais créé une petite classe d'assistance pour gérer les événements d'ouverture/fermeture avec notification aux auditeurs et 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
51
Artem Zinnatullin

Cela a toujours été informatique, 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 :)

NOTE: Si vous remarquez que DefaultKeyboardDP ne convient pas à votre appareil, jouez avec la valeur et postez un commentaire pour que tout le monde sache quelle devrait être la valeur ... valeur pour adapter tous les appareils!

Pour plus de détails, consultez la mise en œuvre sur Cyborg

50
TacB0sS

Quelques améliorations pour éviter de détecter à tort la visibilité du clavier logiciel sur les périphériques haute densité: 

  1. Le seuil de différence de hauteur doit être défini comme 128 dp et non pas 128 pixels.
    Reportez-vous à 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 logiciel générique doit comporter 4 rangées de touches. La hauteur minimale du clavier doit donc être: 32 dp * 4 = 128 dp, ce qui signifie que la taille de seuil doit être transférée en pixels en multipliant la densité du périphérique. Pour les périphériques xxxhdpi (densité 4), le seuil de hauteur du clavier logiciel doit être de 128 * 4 = 512 pixels.

  2. 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 de l’image visible, puisque la hauteur de la barre d’état est égale au sommet 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;
    }
    
31
Orchard Cafe

J'ai utilisé un peu de temps pour comprendre cela ... Je l'ai lancé quelques exceptions CastExceptions, mais j'ai compris que vous pouvez vous remplacer par 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 faire cela 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

8
Janus Kamp Hansen

La vérification de la hauteur des éléments n’est pas fiable car certains claviers tels que WifiKeyboard ont une hauteur nulle.

Au lieu de cela, vous pouvez utiliser le résultat du rappel de showSoftInput () et de 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

6
Roger Keays

L'idée est que, si vous devez masquer votre clavier et vérifier l'état de l'entrée logicielle en même temps, utilisez la solution suivante:

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

Cette méthode retourne true si le clavier était affiché avant de se cacher.

5
George Maisuradze

Au lieu d’assumer la différence de codage, j’ai fait quelque chose comme ceci, 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
        }
     }
});
4
Santhosh Shettigar

J'ai trouvé qu'une combinaison de la méthode de @ Reuben_Scratton avec celle de @ Yogesh semble fonctionner 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
    }
  }
});
3
cbradley

Il existe une méthode cachée qui peut aider, 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()
  }
}
3
Kevin Du

Il existe également une solution avec des insertions système, mais cela ne fonctionne qu'avec API >= 21 (Android L). Disons que vous avez BottomNavigationView, qui est l'enfant de LinearLayout et que vous devez le cacher lorsque le clavier est affiché:

> LinearLayout
  > ContentView
  > BottomNavigationView

Tout ce que vous avez à faire est d’étendre LinearLayout de la manière suivante:

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 incrustations du système sont modifiées avec une valeur .bottom assez grande.

3
nikis

Vous pouvez observer la peau du clavier logiciel à l'aide de la fonction décorView.

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);
    }
}
3
Zebulon Li

J'ai utilisé une légère variante de la réponse de Reuban, qui s'est révélée plus utile dans certaines circonstances, notamment 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
                }
            }
        });
2
PearsonArtPhoto

Aucune de ces solutions ne fonctionnera pour Lollipop en l'état. Dans Lollipop, activityRootView.getRootView().getHeight() inclut la hauteur de la barre de boutons, contrairement à la mesure de la vue. J'ai adapté la solution la meilleure/la plus simple ci-dessus pour fonctionner 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
    }
  }
});
2
nathanielwolf

Cela a toujours été l'ordinateur, mais cette question est toujours incroyablement pertinente!.

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.

2
Roselyn Soffer

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 ... \\
        }
    }
});
1
Ofek Ashery

J'avais du mal à conserver l'état du clavier lorsque je changeais l'orientation des fragments dans un viewpager. Je ne suis pas sûr de savoir pourquoi, mais cela semble tout simplement méchant et agit différemment d'une activité standard.

Pour maintenir 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 tout le problème: le clavier ne s’est pas ouvert pour moi s’il était ouvert avant le changement d’orientation. Dans tous les autres cas, le comportement semblait être correct.

Ensuite, nous devons implémenter l’une des solutions mentionnées ici .. La plus propre que j’ai trouvée est celle 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 méthode onSaveInstanceState de mon fragment et l'ai récupérée onCreate. Ensuite, j'ai montré de force le clavier dans 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).

1
Quantum Dot

Je viens de rencontrer un bug lors de l'utilisation de la plupart des solutions ci-dessus qui suggèrent d'ajouter un nombre fixe.

La résolution de la S4 est élevée, ce qui fait que la barre de navigation a une hauteur de 100 pixels. Mon application pense donc que le clavier est ouvert en permanence.

Donc, avec tous les nouveaux téléphones haute résolution qui sortent, je pense qu’utiliser 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 consistait à utiliser le pourcentage ........ Obtenez la différence entre le contenu de decorView et celui de votre application, puis vérifiez quel est le pourcentage de cette différence. D'après les statistiques que j'ai obtenues, la plupart des barres de navigation (quels que soient leur taille, leur résolution, etc.) occuperont entre 3% et 5% de l'écran. 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 est supérieur à 10%, alors je suppose que c'est un clavier ouvert.

1
N Jay

Ma réponse est fondamentalement la même que celle de Kachi, mais je l'ai intégrée dans une classe d'assistance Nice pour nettoyer la manière dont elle est utilisée dans l'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 utiliser ceci pour détecter les modifications apportées au 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: utilisez uniquement l'un des appels "Register". Ils fonctionnent tous de la même manière et ne sont là que pour votre commodité.

1
billylindeman

vous pouvez essayer cela, fonctionne 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..
}
1
IRvanFauziE

La solution proposée par Reuben Scratton et Kachi semble reposer sur la densité de pixels des périphériques. Si vous possédez un périphérique haute densité, la différence de hauteur peut être supérieure à 100, même avec le clavier enfoncé. Un peu de travail autour de cela serait de voir la différence de hauteur initiale (avec le clavier enfoncé), puis de la comparer à la différence actuelle.

boolean isOpened = false;
int firstHeightDiff = -1;

public void setListenerToRootView(){
    final View activityRootView = getActivity().getWindow().getDecorView().findViewById(Android.R.id.content);
    Rect r = new Rect();
    activityRootView.getWindowVisibleDisplayFrame(r);
    firstHeightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (isAdded()) {
                Rect r = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(r);
                int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
                isOpened = heightDiff>firstHeightDiff+100;
                if (isAdded())
                    if(isOpened) {
                        //TODO stuff for when it is up
                    } else {
                        //TODO stuf for when it is down
                    }
            }
        }
    });
}
0
ericmguimaraes

Penser a un moyen facile, comme ceci:

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

Vous pouvez également voir s'il est actif dans une vue spécifique:

imm.isActive(View v);
0
Ricardo Albuquerque
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.

Vous trouverez ci-dessous les deux méthodes requises.

Commencez par définir la hauteur de fenêtre utilisable 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;
//

} 

Ajoutez ensuite une méthode booléenne qui obtient la hauteur de la fenêtre à cette instance. S'il ne correspond pas à l'original (si 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!

0
Belboz

Une méthode qui n'a pas besoin d'un LayoutListener

Dans mon cas, j'aimerais sauvegarder l'état du clavier avant de remplacer mon fragment. J'appelle la méthode hideSoftInputFromWindow from onSaveInstanceState, qui ferme le clavier et me renvoie si le clavier était visible ou non. 

Cette méthode est simple mais peut changer l’état de votre clavier.

0
Gordak

Ce code fonctionne très bien Nice

utilisez cette classe pour la vue racine:

public class KeyboardConstraintLayout extends ConstraintLayout {

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

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

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

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

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

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

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

public boolean isShowKeyboard() {
    return isShow;
}

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

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

}

et définir le programme d'écoute du clavier en activité ou en fragment:

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

    }
});
0
saleh gholamian

La nouvelle réponse de Reuben Scratton (calculez le paramètre HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();) ne fonctionnera pas si vous définissez le mode de barre de statut translucide.

si vous utilisez une barre d'état translucide, activityRootView.getHeight() ne changera jamais lorsque le clavier virtuel est visible. il retournera toujours la hauteur d'activité et la barre d'état.

Par exemple, Nexus 4, Android 5.0.1, définissez Android:windowTranslucentStatus sur true, il renverra 1184 pour toujours, même si la mémoire est ouverte. Si vous définissez Android:windowTranslucentStatus sur false, il renverra correctement la hauteur. Si invisible, il renverra 1134 (sans inclure la barre d'état)

Je ne sais pas s'il s'agit d'un bug, j'ai essayé les versions 4.4.4 et 5.0.1, le résultat est identique.

Ainsi, jusqu'à présent, deuxième réponse la plus acceptée, la solution de Kachi sera le moyen le plus sûr de calculer la hauteur de l'image. 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
    }
 }
}); 
0
Loyea

Après avoir compris certains problèmes avec différentes résolutions, j'ai décidé d'utiliser une taille relative. Comme je l'ai remarqué, la différence entre les états visible et caché est d'environ 30%. J'ai donc décidé de remplacer 128 PX par 0,3.

Et j'ai ajouté cet auditeur de classe pour notifier tout changement.

Voici ma version

import Android.app.*;
import Android.graphics.*;
import Android.view.*;

public class SoftKeyboardState {
  public static final int HIDDEN = 0, VISIBLE = 1;
  private OnKeyboardStateChangedListener listener;
  private View decorView;

  public SoftKeyboardState(Activity activity) {
    this.decorView = activity.findViewById(Android.R.id.content);
    initKeyboardListener();
  }

  private void initKeyboardListener() {
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(
      new ViewTreeObserver.OnGlobalLayoutListener(){
        private final Rect windowVisibleDisplayFrame = new Rect();
        private int lastVisibleDecorViewHeight;

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

          if (lastVisibleDecorViewHeight != 0) {
            if ((lastVisibleDecorViewHeight > visibleDecorViewHeight) && (lastVisibleDecorViewHeight / visibleDecorViewHeight >= 0.3f)) {
              // visible
              if (listener != null)listener.onKeyboardStateChanged(VISIBLE);
            } else if ((lastVisibleDecorViewHeight < visibleDecorViewHeight) && (visibleDecorViewHeight / lastVisibleDecorViewHeight >= 0.3f)) {
              // hidden
              if (listener != null)listener.onKeyboardStateChanged(HIDDEN);
            }
          }
          lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
      });
  }

  public void setOnKeyboardStateChangedListener(OnKeyboardStateChangedListener listener) {
    this.listener = listener;
  }

  public interface OnKeyboardStateChangedListener {
    public void onKeyboardStateChanged(int state);
  }
}
0

Je sais qu'il s'agit d'un ancien billet mais je pense que c'est l'approche la plus simple que je connaisse et mon appareil de test est Nexus 5. Je ne l'ai pas essayé dans 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,

0
Fran Ceriu

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.

0
Valentin Baryshev

Ne faites pas de code dur. Le meilleur moyen est de redimensionner vos vues lorsque vous obtenez la mise au point sur EditText avec KeyBord Show . Vous pouvez le faire en ajoutant une propriété de redimensionnement lors de l’activité dans le fichier manifeste en utilisant le code ci-dessous.

Android:windowSoftInputMode="adjustResize"

0
Rahul Mandaliya

Voici ma solution et ça marche. Au lieu de rechercher la taille en pixels, vérifiez simplement que la hauteur de la vue 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();
}
0
Meanman

Il existe une méthode directe pour le découvrir. Et, il ne nécessite aucune modification de mise en page.
Donc, cela fonctionne aussi en mode plein écran immersif.

L'astuce consiste à essayer de masquer ou d'afficher le clavier virtuel et de capturer le résultat de cet essai.
Pas de panique, cela n’affiche ou ne masque pas vraiment le clavier. Nous demandons juste pour 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 trouvez une implémentation ici: https://stackoverflow.com/a/27567074/2525452

0
fies

Voici une solution de contournement pour savoir si le clavier virtuel est visible.

  1. Recherchez les services en cours d'exécution sur le système à l'aide de ActivityManager.getRunningServices (max_count_of_services);
  2. A partir des instances ActivityManager.RunningServiceInfo renvoyées, vérifiez clientCount value pour le service de clavier logiciel.
  3. Le clientCount susmentionné sera incrémenté à chaque fois, le clavier virtuel est affiché. Par exemple, si clientCount était Initialement 1, il serait 2 lorsque le clavier est affiché.
  4. Lors de la suppression du clavier, clientCount est décrémenté. Dans ce cas, il réinitialise à 1.

Certains des claviers populaires ont certains mots-clés dans leurs classNames:

Google AOSP = IME
Swype = IME
Swiftkey = KeyboardService
Fleksy = keyboard
Adaptxt = IME (KPTAdaptxtIME)
Smart = Keyboard (SmartKeyboard)

Dans ActivityManager.RunningServiceInfo, recherchez les modèles ci-dessus dans ClassNames. En outre, clientPackage = Android de ActivityManager.RunningServiceInfo, indiquant que le clavier est lié au système.

Les informations mentionnées ci-dessus peuvent être combinées pour un moyen strict de savoir si un clavier logiciel est visible.

0
Satishkumar

Peut-être que cela vous aidera:

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

je pense que cette méthode vous aidera à savoir 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;
    }

}
0
John smith