Le clavier virtuel ouvre et ferme l'auditeur dans une activité sous Android


136

J'ai un Activityoù il y a 5 EditTexts. Lorsque l'utilisateur clique sur le premier EditText, le clavier virtuel s'ouvre pour y entrer une valeur. Je veux définir Viewla visibilité de certains autres sur le Gonemoment où le clavier virtuel s'ouvre et aussi lorsque l'utilisateur clique sur le premier EditTextet aussi lorsque le clavier virtuel se ferme du même EditTexten appuyant sur le bouton arrière. Ensuite, je veux définir Viewla visibilité de certains autres sur visible.

Y a-t-il un auditeur, un rappel ou un piratage lorsque le clavier virtuel s'ouvre à partir d'un clic sur le premier EditTextsous Android?


1
Non, il n'y a pas de tels auditeurs. Il existe des hacks pour réaliser ce que vous essayez de faire. Voici une approche possible: Comment envoyer un événement de pointeur dans Android .
Vikram

@Vikram je ne recherche pastrying to detect the virtual keyboard height in Android.
N Sharma

Je connais. Si vous parcourez le code, vous verrez comment la hauteur est déterminée. Un événement de pointeur est en cours d'envoi -> deux cas => 1. si le clavier est ouvert => & si le pointeur Xet la Yposition tombent sur / au-dessus du clavier => SecurityException=> décrémentez Yet réessayez => jusqu'à ce qu'aucune exception ne soit levée => la Yvaleur actuelle est la hauteur du clavier. 2. si le clavier n'est pas ouvert => non SecurityException.
Vikram

Comment cela s'applique-t-il à votre scénario? Envoyez un événement de pointeur aux 2/3 de la hauteur de l'écran. Si un SecurityExceptionest lancé => le clavier est ouvert. Sinon, le clavier est fermé.
Vikram

@Vikram Je veux seulement ça pour le premier EditTextpas pour les autres EditText. Comment puis-je distinguer cela?
N Sharma

Réponses:


91

Cela ne fonctionne que lorsque android:windowSoftInputModevotre activité est définie sur adjustResizedans le manifeste. Vous pouvez utiliser un écouteur de disposition pour voir si la disposition racine de votre activité est redimensionnée par le clavier.

J'utilise quelque chose comme la classe de base suivante pour mes activités:

public class BaseActivity extends Activity {
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
            int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

            LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);

            if(heightDiff <= contentViewTop){
                onHideKeyboard();

                Intent intent = new Intent("KeyboardWillHide");
                broadcastManager.sendBroadcast(intent);
            } else {
                int keyboardHeight = heightDiff - contentViewTop;
                onShowKeyboard(keyboardHeight);

                Intent intent = new Intent("KeyboardWillShow");
                intent.putExtra("KeyboardHeight", keyboardHeight);
                broadcastManager.sendBroadcast(intent);
            }
        }
    };

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

    protected void onShowKeyboard(int keyboardHeight) {}
    protected void onHideKeyboard() {}

    protected void attachKeyboardListeners() {
        if (keyboardListenersAttached) {
            return;
        }

        rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

        keyboardListenersAttached = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (keyboardListenersAttached) {
            rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
        }
    }
}

L'exemple d'activité suivant l'utilise pour masquer une vue lorsque le clavier est affiché et l'afficher à nouveau lorsque le clavier est masqué.

La mise en page xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/rootLayout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">              

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

        <!-- omitted for brevity -->

    </ScrollView>

    <LinearLayout android:id="@+id/bottomContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <!-- omitted for brevity -->

    </LinearLayout>

</LinearLayout>

Et l'activité:

public class TestActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);

        attachKeyboardListeners();
    }

    @Override
    protected void onShowKeyboard(int keyboardHeight) {
        // do things when keyboard is shown
        bottomContainer.setVisibility(View.GONE);
    }

    @Override
    protected void onHideKeyboard() {
        // do things when keyboard is hidden
        bottomContainer.setVisibility(View.VISIBLE);
    }        
}

4
+1 Ouais C'est la solution parfaite pour mon problème.
N Sharma le

18
Salut, vous avez utilisé getTop () sur Window.ID_ANDROID_CONTENT. Get top ne fonctionne pas pour moi. c'est toujours 0 ici, cela fonctionne comme s'il était censé utiliser getHeight () à la place.
Daniele Segato

1
d'où viens-tu rootLayout = (ViewGroup) findViewById(R.id.rootLayout);?
CommonSenseCode

1
Ne fonctionnant pas pour moi pour une raison quelconque, il appelle toujours onShowKeyboard que je l'ouvre ou je le ferme. J'utilise findViewById (android.R.id.content), c'est peut-être le problème?
McSullivan D'Ander

2
@tsig votre solution +100 dépend de l'écran spécifique. Échec sur les tablettes et les téléphones hdpi. J'ai utilisé la correction comme dix pour cent de la hauteur de l'appareil. Cela signifie que si la hauteur de vue est inférieure à screenHeight - 10%, le clavier est ouvert. sinon le clavier est fermé. Voici mon contentViewTop dans onGlobalLayout: contentViewTop = (getWindow (). GetDecorView (). GetBottom () / 10)
ilker

94

Du gâteau avec l'impressionnante bibliothèque KeyboardVisibilityEvent

KeyboardVisibilityEvent.setEventListener(
    getActivity(),
    new KeyboardVisibilityEventListener() {
        @Override
        public void onVisibilityChanged(boolean isOpen) {
            // write your code
        }
    });

Crédits pour Yasuhiro SHIMIZU


Cela ne fonctionnera pas car les claviers n'ont pas de hauteurs statiques et la hauteur dans cette bibliothèque est définie sur 100dp.
milosmns

@milosmns une hauteur de seuil de 100dp est utilisée pour la détection du clavier. Aucune hypothèse n'est faite sur la hauteur réelle du clavier
Nino van Hooff

11
C'est toujours codé en dur. Multi fenêtre? Vue partagée de Samsung? Image en mode image? Il existe également un clavier minimal à une ligne qui tombera en dessous de 100dp. Il n'y a pas de
solution

1
Parce qu'il n'y a pas de fourre-tout pour ce problème, cela semble être le plus simple à implémenter et il suffit de revenir au code sur lequel vous voulez réellement travailler :)
Machine Tribe

1
C'est de loin la meilleure réponse, totalement fiable sur n'importe quel appareil
Pelanes

70

Comme Vikram l'a souligné dans les commentaires, détecter si le clavier logiciel est affiché ou a disparu n'est possible qu'avec quelques horribles hacks.

Il suffit peut-être de définir un auditeur de focus sur l'edittext :

yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            //got focus
        } else {
            //lost focus
        }
   }
});

28
Supposons que je clique sur le edittext, alors l' setOnFocusChangeListenerécouteur sera appelé puis j'appuie sur en arrière puis il ferme le clavier et mais je n'ai pas cliqué sur d'autres vues, maintenant à nouveau je clique sur le même edittext qui a déjà le focus alors que se passera-t-il?
N Sharma

3
@Williams Je ne suis pas entièrement sûr, mais je soupçonne que onFocusChange()cela ne sera pas appelé.
CommonGuy

1
ce n'est pas ma question. Veuillez relire ma question - J'ai une activité où il y a 5 EditText. Lorsque l'utilisateur clique d'abord sur EditText, puis le clavier virtuel s'ouvre pour y entrer une valeur. Je veux définir une autre visibilité de la vue sur Fini lorsque le clavier virtuel est ouvert lorsque l'utilisateur clique sur le premier EditText et lorsque le clavier virtuel se ferme du même EditText au dos, je veux définir une autre visibilité de la vue sur visible. Y a-t-il un auditeur, un rappel ou un piratage lorsque le clavier virtuel est ouvert en cliquant sur le premier EditText dans Android?
N Sharma le

4
Les gars ne regardent pas cette réponse parce qu'il dit quelque chose de différent même si je ne comprends pas.
N Sharma le

2
Quoi qu'il en soit, cela ne fonctionne pas pour moi ... Lorsque le clavier virtuel est masqué, il n'y a pas de changement de focus sur le EditText ... Donc je ne peux pas recevoir de notification de cet écouteur.
Licat Julius

51

Pour l'activité:

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();

                activityRootView.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { 
                 //enter your code here
                }else{
                 //enter code for hid
                }
            }
        });

Pour le fragment:

    view = inflater.inflate(R.layout.live_chat_fragment, null);
view.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.
                view.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...

                }
            }
        });

3
Je l'ai utilisé pour l'activité, mais au lieu de comparer avec la vue i par rapport à la taille de l'écran. fonctionne très bien
Roee

mieux serait de comparer heightDiff avec une hauteur en dp plutôt qu'en pixels. Cela peut varier considérablement.
Leo Droidcoder

Cela a-t-il besoin du android:windowSoftInputMode="adjustResize"manifeste?
LiuWenbin_NO.

android: windowSoftInputMode = "AdjustResize" android: configChanges = "orientation | keyboard | keyboardHidden"
M Singh Karnawat

Cela fonctionne pour moi, encore, j'ai une question. Cela coûte-t-il beaucoup de ressources?
Licat Julius

32

La réponse de Jaap ne fonctionnera pas pour AppCompatActivity. Au lieu de cela, obtenez la hauteur de la barre d'état et de la barre de navigation, etc. et comparez-la à la taille de la fenêtre de votre application.

Ainsi:

    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // navigation bar height
        int navigationBarHeight = 0;
        int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // status bar height
        int statusBarHeight = 0;
        resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // display window size for the app layout
        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);

        // screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard
        int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height());

        if (keyboardHeight <= 0) {
            onHideKeyboard();
        } else {
            onShowKeyboard(keyboardHeight);
        }
    }
};

Semble fonctionner plutôt bien à une exception près: les pauses en mode écran partagé. Sinon c'est super.
MCLLC

14

Tu peux l'essayer:

private void initKeyBoardListener() {
    // Минимальное значение клавиатуры. 
    // Threshold for minimal keyboard height.
    final int MIN_KEYBOARD_HEIGHT_PX = 150;
    // Окно верхнего уровня view. 
    // Top-level window decor view.
    final View decorView = getWindow().getDecorView();
    // Регистрируем глобальный слушатель. Register global layout listener.
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        // Видимый прямоугольник внутри окна. 
        // Retrieve visible rectangle inside window.
        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 + MIN_KEYBOARD_HEIGHT_PX) {
                    Log.d("Pasha", "SHOW");
                } else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
                    Log.d("Pasha", "HIDE");
                }
            }
            // Сохраняем текущую высоту view до следующего вызова.
            // Save current decor view height for the next call.
            lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
    });
}

Spasibo, Dolbik! :)
AlexS

4

Vous pouvez utiliser ma fonction d'extension Rx (Kotlin).

/**
 * @return [Observable] to subscribe of keyboard visibility changes.
 */
fun AppCompatActivity.keyboardVisibilityChanges(): Observable<Boolean> {

    // flag indicates whether keyboard is open
    var isKeyboardOpen = false

    val notifier: BehaviorSubject<Boolean> = BehaviorSubject.create()

    // approximate keyboard height
    val approximateKeyboardHeight = dip(100)

    // device screen height
    val screenHeight: Int = getScreenHeight()

    val visibleDisplayFrame = Rect()

    val viewTreeObserver = window.decorView.viewTreeObserver

    val onDrawListener = ViewTreeObserver.OnDrawListener {

        window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)

        val keyboardHeight = screenHeight - (visibleDisplayFrame.bottom - visibleDisplayFrame.top)

        val keyboardOpen = keyboardHeight >= approximateKeyboardHeight

        val hasChanged = isKeyboardOpen xor keyboardOpen

        if (hasChanged) {
            isKeyboardOpen = keyboardOpen
            notifier.onNext(keyboardOpen)
        }
    }

    val lifeCycleObserver = object : GenericLifecycleObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event?) {
            if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                source.lifecycle.removeObserver(this)
                notifier.onComplete()
            }
        }
    }

    viewTreeObserver.addOnDrawListener(onDrawListener)
    lifecycle.addObserver(lifeCycleObserver)

    return notifier
            .doOnDispose {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                lifecycle.removeObserver(lifeCycleObserver)
            }
            .onTerminateDetach()
            .hide()
}

Exemple:

(context as AppCompatActivity)
                    .keyboardVisibilityChanges()
                    .subscribeBy { isKeyboardOpen ->
                        // your logic
                    }

Ça ne marche pas pour moi. Impossible de trouver les méthodes dip()etgetScreenHeight()
Marcin Kunert

@MarcinKunert c'est juste une fonction d'extension qui vous aide à convertir des pixels en dp et à obtenir la hauteur de l'écran. Si vous le souhaitez, je peux vous donner un exemple de telles fonctions
Vlad

GenericLifecycleObserver est obsolète? Toute solution?
Zainal Fahrudin

4

Le code ci-dessous fonctionne pour moi,

mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (mainLayout != null) {
                int heightDiff = mainLayout.getRootView().getHeight() - mainLayout.getHeight();
                if (heightDiff > dpToPx(getActivity(), 200)) { 
                   //keyboard is open
                } else {
                   //keyboard is hide
                }
            }
        }
    });

2

Si vous le pouvez, essayez d'étendre EditText et de remplacer la méthode 'onKeyPreIme'.

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener; //keep it for later usage
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            //you can define and use custom listener,
            //OR define custom R.id.<imeId>
            //OR check event.keyCode in listener impl
            //* I used editor action because of ButterKnife @
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

Comment pouvez-vous l'étendre:

  1. Implémentez l'écoute onFocus et déclarez 'onKeyboardShown'
  2. déclarer 'onKeyboardHidden'

Je pense que le recalcul de la hauteur de l'écran n'est pas réussi à 100% comme mentionné précédemment. Pour être clair, le remplacement de «onKeyPreIme» n'est pas appelé sur les méthodes «masquer le clavier logiciel par programme», MAIS si vous le faites n'importe où, vous devriez y faire la logique «onKeyboardHidden» et ne pas créer de solutions complètes.


1
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mainactivity);
    attachKeyboardListeners();
    ....
    yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                yourEditText2.setVisibility(View.GONE);
                yourEditText3.setVisibility(View.GONE);
                yourEditText4.setVisibility(View.GONE);
                yourEditText5.setVisibility(View.GONE);
            } else {
                yourEditText2.setVisibility(View.VISIBLE);
                yourEditText3.setVisibility(View.VISIBLE);
                yourEditText4.setVisibility(View.VISIBLE);
                yourEditText5.setVisibility(VISIBLE);
            }
       }
    });
    }
}

Supposons que je clique sur l'edittext alors l' setOnFocusChangeListenerécouteur sera appelé puis j'appuie sur en arrière puis il ferme le clavier et mais je n'ai pas cliqué sur d'autres vues, maintenant à nouveau je clique sur le même edittext qui a déjà le focus alors que se passera-t-il?
N Sharma

ce n'est pas ma question. Veuillez relire ma question - J'ai une activité où il y a 5 EditText. Lorsque l'utilisateur clique d'abord sur EditText, puis le clavier virtuel s'ouvre pour y entrer une valeur. Je veux définir une autre visibilité de la vue sur Fini lorsque le clavier virtuel est ouvert lorsque l'utilisateur clique sur le premier EditText et lorsque le clavier virtuel se ferme du même EditText au dos, je veux définir une autre visibilité de la vue sur visible. Y a-t-il un auditeur, un rappel ou un piratage lorsque le clavier virtuel est ouvert en cliquant sur le premier EditText dans Android?
N Sharma le

1
Lorsque vous appuyez sur en arrière, cela supprime le clavier que l' onfocusauditeur de temps n'appelle jamais, c'est que je ne regarde pas ce que vous suggérez
N Sharma

1

Utilisez cette classe,

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class SoftKeyboard implements View.OnFocusChangeListener
{
private static final int CLEAR_FOCUS = 0;

private ViewGroup layout;
private int layoutBottom;
private InputMethodManager im;
private int[] coords;
private boolean isKeyboardShow;
private SoftKeyboardChangesThread softKeyboardThread;
private List<EditText> editTextList;

private View tempView; // reference to a focused EditText

public SoftKeyboard(ViewGroup layout, InputMethodManager im)
{
    this.layout = layout;
    keyboardHideByDefault();
    initEditTexts(layout);
    this.im = im;
    this.coords = new int[2];
    this.isKeyboardShow = false;
    this.softKeyboardThread = new SoftKeyboardChangesThread();
    this.softKeyboardThread.start();
}

public void openSoftKeyboard()
{
    if(!isKeyboardShow)
    {
        layoutBottom = getLayoutCoordinates();
        im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
        softKeyboardThread.keyboardOpened();
        isKeyboardShow = true;
    }
}

public void closeSoftKeyboard()
{
    if(isKeyboardShow)
    {
        im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
        isKeyboardShow = false;
    }
}

public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback)
{
    softKeyboardThread.setCallback(mCallback);
}

public void unRegisterSoftKeyboardCallback()
{
    softKeyboardThread.stopThread();
}

public interface SoftKeyboardChanged 
{
    public void onSoftKeyboardHide();
    public void onSoftKeyboardShow();   
}

private int getLayoutCoordinates()
{
    layout.getLocationOnScreen(coords);
    return coords[1] + layout.getHeight();
}

private void keyboardHideByDefault()
{
    layout.setFocusable(true);
    layout.setFocusableInTouchMode(true);
}

/*
 * InitEditTexts now handles EditTexts in nested views
 * Thanks to Francesco Verheye (verheye.francesco@gmail.com)
 */
private void initEditTexts(ViewGroup viewgroup) 
{
    if(editTextList == null)
        editTextList = new ArrayList<EditText>();

    int childCount = viewgroup.getChildCount();
    for(int i=0; i<= childCount-1;i++) 
    {
        View v = viewgroup.getChildAt(i);

        if(v instanceof ViewGroup) 
        {
            initEditTexts((ViewGroup) v);
        }

        if(v instanceof EditText) 
        {
            EditText editText = (EditText) v;
            editText.setOnFocusChangeListener(this);
            editText.setCursorVisible(true);
            editTextList.add(editText);
        }
    }
}

/*
 * OnFocusChange does update tempView correctly now when keyboard is still shown
 * Thanks to Israel Dominguez (dominguez.israel@gmail.com)
 */
@Override
public void onFocusChange(View v, boolean hasFocus) 
{
    if(hasFocus) 
    {
        tempView = v;
        if(!isKeyboardShow) 
        {
            layoutBottom = getLayoutCoordinates();
            softKeyboardThread.keyboardOpened();
            isKeyboardShow = true;
        }
    }
}

// This handler will clear focus of selected EditText
private final Handler mHandler = new Handler()
{
    @Override
    public void handleMessage(Message m)
    {
        switch(m.what)
        {
        case CLEAR_FOCUS:
            if(tempView != null)
            {
                tempView.clearFocus();
                tempView = null;
            }
            break;
        }
    }
};

private class SoftKeyboardChangesThread extends Thread
{
    private AtomicBoolean started;
    private SoftKeyboardChanged mCallback;

    public SoftKeyboardChangesThread()
    {
        started = new AtomicBoolean(true);
    }

    public void setCallback(SoftKeyboardChanged mCallback)
    {
        this.mCallback = mCallback;
    }

    @Override
    public void run()
    {
        while(started.get())
        {
            // Wait until keyboard is requested to open
            synchronized(this)
            {
                try 
                {
                    wait();
                } catch (InterruptedException e) 
                {
                    e.printStackTrace();
                }
            }

            int currentBottomLocation = getLayoutCoordinates();

            // There is some lag between open soft-keyboard function and when it really appears.
            while(currentBottomLocation == layoutBottom && started.get())
            {
                currentBottomLocation = getLayoutCoordinates();
            }

            if(started.get())
                mCallback.onSoftKeyboardShow();

            // When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
            // and at some moment equals layoutBottom.
            // That broke the previous logic, so I added this new loop to handle this.
            while(currentBottomLocation >= layoutBottom && started.get())
            {
                currentBottomLocation = getLayoutCoordinates();
            }

            // Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
            while(currentBottomLocation != layoutBottom && started.get())
            {
                                    synchronized(this)
                {
                    try 
                    {
                        wait(500);
                    } catch (InterruptedException e) 
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                currentBottomLocation = getLayoutCoordinates();
            }

            if(started.get())
                mCallback.onSoftKeyboardHide();

            // if keyboard has been opened clicking and EditText.
            if(isKeyboardShow && started.get())
                isKeyboardShow = false;

            // if an EditText is focused, remove its focus (on UI thread)
            if(started.get())
                mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
        }   
    }

    public void keyboardOpened()
    {
        synchronized(this)
        {
            notify();
        }
    }

    public void stopThread()
    {
        synchronized(this)
        {
            started.set(false);
            notify();
        }
    }

}
}

En Android Manifest, android:windowSoftInputMode="adjustResize"est nécessaire.

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use the layout root
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
}

@Override
public void onSoftKeyboardShow() {
    // Code here
}   
});

/*
Open or close the soft keyboard easily
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();

/* Prevent memory leaks:*/
@Override
public void onDestroy() {
    super.onDestroy();
    softKeyboard.unRegisterSoftKeyboardCallback();
}

PS - Complètement pris d' ici .


1

Pour le cas de adjustResizeet FragmentActivity, la solution acceptée de @Jaap ne fonctionne pas pour moi.

Voici ma solution:

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    private int contentDiff;
    private int rootHeight;
    @Override
    public void onGlobalLayout() {
        View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
        if (rootHeight != mDrawerLayout.getRootView().getHeight()) {
            rootHeight = mDrawerLayout.getRootView().getHeight();
            contentDiff = rootHeight - contentView.getHeight();
            return;
        }
        int newContentDiff = rootHeight - contentView.getHeight();
        if (contentDiff != newContentDiff) {
            if (contentDiff < newContentDiff) {
                onShowKeyboard(newContentDiff - contentDiff);
            } else {
                onHideKeyboard();
            }
            contentDiff = newContentDiff;
        }
    }
};

1

Une approche différente serait de vérifier quand l'utilisateur a arrêté de taper ...

Lorsqu'un TextEdit est en focus (l'utilisateur est / était en train de taper), vous pouvez masquer les vues (focus listener)

et utilisez un Handler + Runnable et un écouteur de changement de texte pour fermer le clavier (quelle que soit sa visibilité) et afficher les vues après un certain délai.

La principale chose à surveiller serait le délai que vous utilisez, qui dépendrait du contenu de ces TextEdits.

Handler timeoutHandler = new Handler();
Runnable typingRunnable = new Runnable() {
    public void run() {
        // current TextEdit
        View view = getCurrentFocus();

        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        // reset focus
        view.clearFocus();
        // close keyboard (whether its open or not)
        imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);

        // SET VIEWS VISIBLE
    }
};

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            // SET VIEWS GONE

            // reset handler
            timeoutHandler.removeCallbacks(typingRunnable);
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});

editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Reset Handler...
        timeoutHandler.removeCallbacks(typingRunnable);
    }

    @Override
    public void afterTextChanged(Editable s) {
        // Reset Handler Cont.
        if (editText.getText().toString().trim().length() > 0) {
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});

1

Ce code fonctionne très bien

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); //128dp
}

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

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

@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éfinissez l'écouteur du clavier en activité ou fragment:

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

        }
    });


0

Malheureusement, je n'ai pas une réputation suffisamment élevée pour commenter la réponse de Jaap van Hengstum. Mais j'ai lu quelques commentaires de personnes, ayant le problème qui contentViewTopest toujours 0et qui onShowKeyboard(...)est toujours appelé.

J'ai eu le même problème et j'ai compris le problème que j'avais. J'ai utilisé un AppCompatActivityau lieu d'un «normal» Activity. Dans ce cas, Window.ID_ANDROID_CONTENTfait référence à un ContentFrameLayoutet non au FrameLayoutavec la valeur supérieure droite. Dans mon cas, c'était bien d'utiliser le `` normal '' Activity, si vous devez utiliser un autre type d'activité (je viens de tester le AppCompatActivity, peut-être que c'est aussi un problème avec d'autres types d'activité comme le FragmentActivity), vous devez accéder au FrameLayout, qui est un ancêtre du ContentFrameLayout.


0

quand le clavier montre

rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight() 

est vrai, sinon cachez


0
private boolean isKeyboardShown = false;
private int prevContentHeight = 0;
private ViewGroup contentLayout;

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener =
        new ViewTreeObserver.OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        int contentHeight = contentLayout.getHeight();
        int rootViewHeight = contentLayout.getRootView().getHeight();

        if (contentHeight > 0) {

            if (!isKeyboardShown) {
                if (contentHeight < prevContentHeight) {
                    isKeyboardShown = true;
                    onShowKeyboard(rootViewHeight - contentHeight);
                }
            } else {
                if (contentHeight > prevContentHeight) {
                    isKeyboardShown = false;
                    onHideKeyboard();
                }
            }

            prevContentHeight = contentHeight;
        }
    }
};

J'ai un peu modifié la réponse acceptée par Jaap. Mais dans mon cas, il y a peu d'hypothèses telles que android:windowSoftInputMode=adjustResizeet le clavier n'apparaît pas au début lorsque l'application démarre. Et aussi, je suppose que l'écran en ce qui concerne correspond à la hauteur du parent.

contentHeight > 0cette vérification me permet de savoir si l'écran concerné est masqué ou affiché pour appliquer l'écoute d'événement de clavier pour cet écran spécifique. Je passe également la vue de mise en page de l'écran concernant dans attachKeyboardListeners(<your layout view here>)la onCreate()méthode de mon activité principale . Chaque fois que la hauteur de l'écran change, je l'enregistre dans une prevContentHeightvariable pour vérifier plus tard si le clavier est affiché ou masqué.

Pour moi, jusqu'à présent, cela a plutôt bien fonctionné. J'espère que cela fonctionne aussi pour les autres.


0

La réponse de "Jaap van Hengstum" fonctionne pour moi, mais il n'est pas nécessaire de définir "android: windowSoftInputMode" comme il vient de le dire!

Je l'ai réduit (il détecte maintenant simplement ce que je veux, en fait un événement lors de l'affichage et du masquage du clavier):

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
        int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            onHideKeyboard();
        } else {
            onShowKeyboard();
        }
    }
};

private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;

protected void onShowKeyboard() {}
protected void onHideKeyboard() {}

protected void attachKeyboardListeners() {
    if (keyboardListenersAttached) {
        return;
    }

    rootLayout = (ViewGroup) findViewById(R.id.CommentsActivity);
    rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

    keyboardListenersAttached = true;
}

@Override
protected void onDestroy() {
    super.onDestroy();

    if (keyboardListenersAttached) {
        rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
    }
}

et n'oubliez pas d'ajouter ceci

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_comments);
    attachKeyboardListeners();}

0

Cela fonctionnera sans qu'il soit nécessaire de modifier votre activité android:windowSoftInputMode

étape 1: étendez la classe EditText et remplacez ces deux:

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener;
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

étape 2: créez ces deux dans votre activité:

private void initKeyboard() {
    final AppEditText editText = findViewById(R.id.some_id);
    editText.setOnFocusChangeListener(new OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            setKeyboard(hasFocus);
        }
    });
    editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (event == null || event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                editText.clearFocus();
            }
            return false;
        }
    });
}

public void setKeyboard(boolean isShowing) {
    // do something
}

*** rappelez-vous que pour clearFocustravailler, vous devez rendre le parent ou le premier enfant de la hiérarchie parent focalisable.

    setFocusableInTouchMode(true);
    setFocusable(true);

0

Cela ne fonctionne pas comme souhaité ...

... ont vu de nombreux calculs de taille utiliser pour vérifier ...

Je voulais savoir s'il était ouvert ou non et j'ai trouvé isAcceptingText()

donc cela ne répond vraiment pas à la question car il ne traite pas de l'ouverture ou de la fermeture plutôt que de l'ouverture ou de la fermeture, donc c'est du code associé qui peut aider les autres dans divers scénarios ...

dans une activité

    if (((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
    }

dans un fragment

    if (((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");

    }

0

vérifiez avec le code ci-dessous:

CODE XML:

<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorParent"
    style="@style/parentLayoutPaddingStyle"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  .................


</android.support.constraint.ConstraintLayout>

CODE JAVA:

//Global Variable
android.support.constraint.ConstraintLayout activityRootView;
boolean isKeyboardShowing = false;
private  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
android.support.constraint.ConstraintLayout.LayoutParams layoutParams;




 //onCreate or onViewAttached
    activityRootView = view.findViewById(R.id.coordinatorParent);
        onGlobalLayoutListener = onGlobalLayoutListener();
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);


  //outside oncreate
  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener() {
        return new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(r);
                int screenHeight = activityRootView.getRootView().getHeight();
                int keypadHeight = screenHeight - r.bottom;

                if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
                    if (!isKeyboardShowing) {  // keyboard is opened
                        isKeyboardShowing = true;
                        onKeyboardVisibilityChanged(true);
                   }
                }
                else {
                    if (isKeyboardShowing) {   // keyboard is closed
                        isKeyboardShowing = false;
                        onKeyboardVisibilityChanged(false);
                    }
                }
            }//ends here
        };

    }


    void onKeyboardVisibilityChanged(boolean value) {
        layoutParams = (android.support.constraint.ConstraintLayout.LayoutParams)topImg.getLayoutParams();

        if(value){
           int length = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, getResources().getDisplayMetrics());
            layoutParams.height= length;
            layoutParams.width = length;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }else{
            int length1 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 175, getResources().getDisplayMetrics());
            layoutParams.height= length1;
            layoutParams.width = length1;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }
    }


    @Override
    public void onDetach() {
        super.onDetach();
        if(onGlobalLayoutListener != null) {
            activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
        }
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.