Question Comment vérifier la visibilité du clavier logiciel dans Android?


Je dois faire une chose très simple - savoir si le clavier logiciel est montré. Est-ce possible sur Android?


451
2018-01-27 20:39


origine


Réponses:


NOUVELLE RÉPONSE  ajouté le 25 janvier 2012

Depuis que j'ai écrit la réponse ci-dessous, quelqu'un m'a expliqué l'existence de ViewTreeObserver et amis, des API qui se cachent dans le SDK depuis la version 1.

Plutôt que de demander un type de mise en page personnalisé, une solution beaucoup plus simple consiste à donner à la vue racine de votre activité un identifiant connu, par exemple @+id/activityRoot, accrochez un GlobalLayoutListener dans ViewTreeObserver, et de là, calculez la différence de taille entre la racine de 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.

RÉPONSE ORIGINALE

Oui, c'est possible, mais c'est beaucoup plus difficile qu'il ne devrait l'être.

Si je dois m'intéresser au moment où le clavier apparaît et disparaît (ce qui est assez fréquent), alors je personnalise ma classe de mise en page de haut niveau en une classe qui remplace onMeasure(). La logique de base est que si la mise en page se remplit significativement moins que la surface totale de la fenêtre, un clavier virtuel est probablement affiché.

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
    }

    ...
}

620
2018-01-19 15:42



Donc, j'espère que cela aidera quelqu'un.

La nouvelle réponse de Reuben Scratton est géniale et très efficace, mais cela 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 à 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
    }
 }
}); 

287
2018-02-02 06:45



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 les auditeurs avertisseurs et d'autres choses utiles, peut-être que quelqu'un trouverait cela 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

52
2017-10-14 05:53



Il a toujours été en termes d'ordinateur mais cette question est toujours incroyablement pertinente!

J'ai donc pris les réponses ci-dessus et les ai combinées et raffiné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 jouer avec la valeur et poster un commentaire pour tout le monde pour savoir quelle devrait être la valeur ... finalement, nous obtiendrons la valeur correcte pour s'adapter à tous les appareils!


48
2017-09-24 21:58



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, ne pas 128 pixels.
    Faire référence à Document de conception Google sur les métriques et la grille, 48 dp est une taille confortable pour un objet tactile et 32 dp est minimum pour les boutons. Le clavier logiciel générique doit inclure 4 rangées de touches, la hauteur minimale du clavier doit donc être: 32 dp * 4 = 128 dpCela signifie que la taille du seuil doit être transférée aux pixels en multipliant la densité du dispositif. 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 visible du cadre = vue racine inférieure - fond visible du cadre, 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;
    }
    

30
2017-10-02 00:00



J'ai utilisé un peu de temps pour comprendre cela ... J'ai lancé quelques CastExceptions, mais j'ai compris que vous pouviez vous 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 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
2017-07-09 07:39



La vérification de la hauteur des éléments n'est pas fiable car certains claviers comme WifiKeyboard n'ont aucune hauteur.

Au lieu de cela, vous pouvez utiliser le résultat du rappel de showSoftInput () et hideSoftInput () pour vérifier l'état du clavier. Des détails complets et un exemple de code à

http://www.ninthavenue.com.au/how-to-check-if-the-software-keyboard-is-shown-in-android


6
2018-02-20 23:50