web-dev-qa-db-fra.com

Comment capturer l'événement "afficher / masquer le clavier virtuel" sous Android?

J'aimerais modifier la disposition en fonction de l'affichage ou non du clavier virtuel. J'ai effectué une recherche sur l'API et divers blogs, mais je n'arrive pas à trouver quelque chose d'utile.

C'est possible?

Merci!

216
Sander Versluys

Remarque

Cette solution ne fonctionnera pas pour les claviers logiciels et onConfigurationChanged ne sera pas appelé pour les claviers logiciels (virtuels).


Vous devez gérer vous-même les changements de configuration.

http://developer.Android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

Échantillon:

// from the link above
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);


    // Checks whether a hardware keyboard is available
    if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
    } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
    }
}

Il vous suffit ensuite de modifier la visibilité de certaines vues, de mettre à jour un champ et de modifier votre fichier de présentation.

71
Pedro Loureiro

Cela peut ne pas être la solution la plus efficace. Mais cela a fonctionné pour moi à chaque fois ... J'appelle cette fonction là où j'ai besoin d'écouter le clavier virtuel.

boolean isOpened = false;

public void setListenerToRootView() {
    final View activityRootView = getWindow().getDecorView().findViewById(Android.R.id.content);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
            if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.
                Toast.makeText(getApplicationContext(), "Gotcha!!! softKeyboardup", 0).show();

                if (isOpened == false) {
                    //Do two things, make the view top visible and the editText smaller
                }
                isOpened = true;
            } else if (isOpened == true) {
                Toast.makeText(getApplicationContext(), "softkeyborad Down!!!", 0).show();
                isOpened = false;
            }
        }
    });
}

Remarque: cette approche posera des problèmes si l'utilisateur utilise un clavier flottant.

55
amalBit

Si vous souhaitez gérer l'affichage/le masquage de la fenêtre de clavier IMM (virtuelle) de votre activité, vous devez sous-classer votre présentation et remplacer la méthode onMesure (afin de pouvoir déterminer la largeur mesurée et la hauteur mesurée de votre présentation). Après cela, définissez la disposition sous-classée comme vue principale de votre activité avec setContentView (). Vous pourrez maintenant gérer les événements de la fenêtre d'affichage/masquage IMM. Si cela semble compliqué, ce n'est pas vraiment ça. Voici le code:

main.xml

   <?xml version="1.0" encoding="utf-8"?>
   <LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:layout_width="fill_parent"
        Android:layout_height="fill_parent"
        Android:orientation="horizontal" >
        <EditText
             Android:id="@+id/SearchText" 
             Android:text="" 
             Android:inputType="text"
             Android:layout_width="fill_parent"
             Android:layout_height="34dip"
             Android:singleLine="True"
             />
        <Button
             Android:id="@+id/Search" 
             Android:layout_width="60dip"
             Android:layout_height="34dip"
             Android:gravity = "center"
             />
    </LinearLayout>

Maintenant, dans votre activité, déclarez une sous-classe pour votre mise en page (main.xml)

    public class MainSearchLayout extends LinearLayout {

    public MainSearchLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");

        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight){
            // Keyboard is shown

        } else {
            // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

Vous pouvez voir dans le code que nous gonflons la présentation de notre constructeur Activité dans la sous-classe

inflater.inflate(R.layout.main, this);

Et maintenant, définissez simplement le contenu de la présentation sous-classée pour notre activité.

public class MainActivity extends Activity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MainSearchLayout searchLayout = new MainSearchLayout(this, null);

        setContentView(searchLayout);
    }

    // rest of the Activity code and subclassed layout...

}
37
Nebojsa Tomcic

J'ai fait comme ça:

Ajoutez l'interface OnKeyboardVisibilityListener.

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

HomeActivity.Java:

public class HomeActivity extends Activity implements OnKeyboardVisibilityListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sign_up);
    // Other stuff...
    setKeyboardVisibilityListener(this);
}

private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
    final View parentView = ((ViewGroup) findViewById(Android.R.id.content)).getChildAt(0);
    parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private boolean alreadyOpen;
        private final int defaultKeyboardHeightDP = 100;
        private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop ? 48 : 0);
        private final Rect rect = new Rect();

        @Override
        public void onGlobalLayout() {
            int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
            parentView.getWindowVisibleDisplayFrame(rect);
            int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == alreadyOpen) {
                Log.i("Keyboard state", "Ignoring global layout change...");
                return;
            }
            alreadyOpen = isShown;
            onKeyboardVisibilityListener.onVisibilityChanged(isShown);
        }
    });
}


@Override
public void onVisibilityChanged(boolean visible) {
    Toast.makeText(HomeActivity.this, visible ? "Keyboard is active" : "Keyboard is Inactive", Toast.LENGTH_SHORT).show();
  }
}

J'espère que cela vous aiderait.

27
Hiren Patel

Sur la base du code de Nebojsa Tomcic, j'ai développé la sous-classe RelativeLayout suivante:

import Java.util.ArrayList;

import Android.content.Context;
import Android.util.AttributeSet;
import Android.widget.RelativeLayout;

public class KeyboardDetectorRelativeLayout extends RelativeLayout {

    public interface IKeyboardChanged {
        void onKeyboardShown();
        void onKeyboardHidden();
    }

    private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();

    public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

    public KeyboardDetectorRelativeLayout(Context context) {
        super(context);
    }

    public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.add(listener);
    }

    public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.remove(listener);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight) {
            notifyKeyboardShown();
        } else if (actualHeight < proposedheight) {
            notifyKeyboardHidden();
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void notifyKeyboardHidden() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardHidden();
        }
    }

    private void notifyKeyboardShown() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardShown();
        }
    }

}

Cela fonctionne très bien ... Cette solution ne fonctionnera que si le mode de saisie logicielle de votre activité est défini sur "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE".

22
Stefan

Comme la réponse de @ amalBit, enregistrez un auditeur dans la présentation globale et calculez la différence entre le fond visible de dectorView et le fond proposé, si la différence est supérieure à une valeur (hauteur supposée de l'IME), nous pensons que l'IME est actif:

    final EditText edit = (EditText) findViewById(R.id.edittext);
    edit.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (keyboardShown(edit.getRootView())) {
                Log.d("keyboard", "keyboard UP");
            } else {
                Log.d("keyboard", "keyboard Down");
            }
        }
    });

private boolean keyboardShown(View rootView) {

    final int softKeyboardHeight = 100;
    Rect r = new Rect();
    rootView.getWindowVisibleDisplayFrame(r);
    DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
    int heightDiff = rootView.getBottom() - r.bottom;
    return heightDiff > softKeyboardHeight * dm.density;
}

le seuil de hauteur 100 est la hauteur minimale supposée de l'IME.

Cela fonctionne aussi bien pour adjustPan que pour adjustResize.

19
alexhilton

La solution de Nebojsa a presque fonctionné pour moi. Lorsque j'ai cliqué à l'intérieur d'un EditText multiligne, il savait que le clavier était affiché, mais lorsque j'ai commencé à taper à l'intérieur de EditText, les valeurs actualHeight et proposalHeight étaient toujours identiques, de sorte qu'elles ne savaient pas que leur clavier était toujours affiché. J'ai fait une légère modification pour stocker la hauteur maximale et cela fonctionne bien. Voici la sous-classe révisée:

public class CheckinLayout extends RelativeLayout {

    private int largestHeight;

    public CheckinLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.checkin, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        largestHeight = Math.max(largestHeight, getHeight());

        if (largestHeight > proposedheight)
            // Keyboard is shown
        else
            // Keyboard is hidden

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
12
Gary Foster

Je résous ce problème en remplaçant onKeyPreIme (int keyCode, événement KeyEvent) dans mon EditText personnalisé.

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        //keyboard will be hidden
    }
}
10
qbait

Pas sûr si quelqu'un poste ceci. Trouvé cette solution simple à utiliser! . Le la classe SoftKeyboard est sur Gist.github.com . Mais pour rappeler les événements contextuels/masquer du clavier, nous avons besoin d’un gestionnaire qui effectue correctement les opérations sur l’interface utilisateur:

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);

/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{

    @Override
    public void onSoftKeyboardHide() 
    {
        // Code here
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });
    }

    @Override
    public void onSoftKeyboardShow() 
    {
        // Code here
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });

    }   
});
10
Robert

J'ai en quelque sorte un bidouillage à faire. Bien qu'il ne semble pas y avoir de moyen de détecter quand le clavier virtuel est affiché ou caché, vous pouvez détecter le moment où il est sur à afficher ou à masquer en définissant un OnFocusChangeListener sur le EditText que vous écoutez.

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            //hasFocus tells us whether soft keyboard is about to show
        }
    });

REMARQUE: Une chose à savoir avec ce piratage est que ce rappel est déclenché immédiatement lorsque la EditText gagne ou perd la mise au point. Cela déclenchera juste avant l'affichage ou le masquage du clavier logiciel. Le meilleur moyen que j'ai trouvé de faire quelque chose après est d'afficher ou de masquer le clavier est d'utiliser un Handler et de retarder quelque chose ~ 400ms, comme alors:

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            new Handler().postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        //do work here
                    }
                }, 400);
        }
    });
4
poisondminds

Sander, je crois que vous essayez de montrer la vue bloquée par le clavier logiciel. Essayez ceci http://Android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html .

3
100rabh

J'ai résolu le problème sur le codage de retour textview sur une seule ligne.

package com.helpingdoc;

import Android.content.Context;
import Android.util.AttributeSet;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.widget.LinearLayout;

public class MainSearchLayout extends LinearLayout {
    int hieght = 0;
    public MainSearchLayout(Context context, AttributeSet attributeSet) {

        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");
       if(getHeight()>hieght){
           hieght = getHeight();
       }
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();
        System.out.println("....hieght = "+ hieght);
        System.out.println("....actualhieght = "+ actualHeight);
        System.out.println("....proposedheight = "+ proposedheight);
        if (actualHeight > proposedheight){
            // Keyboard is shown


        } else if(actualHeight<proposedheight){
            // Keyboard is hidden

        }

        if(proposedheight == hieght){
             // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
2
user2462737

Vous pouvez également vérifier le premier rembourrage du bas de l'enfant de DecorView. Il sera défini sur une valeur non nulle lorsque le clavier est affiché.

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    View view = getRootView();
    if (view != null && (view = ((ViewGroup) view).getChildAt(0)) != null) {
        setKeyboardVisible(view.getPaddingBottom() > 0);
    }
    super.onLayout(changed, left, top, right, bottom);
}
2
MatrixDev

Cacher | Afficher les événements pour le clavier peut être écouté par un simple piratage dans OnGlobalLayoutListener:

 final View activityRootView = findViewById(R.id.top_root);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();

                if (heightDiff > 100) {
                    // keyboard is up
                } else {
                    // keyboard is down
                }
            }
        });

Ici activityRootView est la vue racine de votre activité.

1
Varun Verma