Come acquisire l'evento "mostra / nascondi tastiera virtuale" in Android?


Risposte:


69

Nota

Questa soluzione non funzionerà per le tastiere soft e onConfigurationChangednon verrà chiamata per le tastiere soft (virtuali).


Devi gestire tu stesso le modifiche alla configurazione.

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

Campione:

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

Quindi basta modificare la visibilità di alcune visualizzazioni, aggiornare un campo e modificare il file di layout.


4
@shiami try newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO~ Chris
cimnine,

3
Funziona solo se hai registrato l'attività per ascoltare i cambiamenti di configurazione che desideri in AndroidManifest.
Raul Agrait,

65
si prega di aggiornare la risposta e dire che non funziona per la tastiera software. Ho sprecato la mia mezza giornata a provare il tuo codice. E poi ho visto questi commenti.
Shirish Herwade,

17
Questo non funziona per le tastiere "virtuali" che era la domanda originale.
Brummfondel,

18
Bene, la domanda era sulla SOFT KEYBOARD, perché la risposta accettata su una tastiera hardware? -1!
Denys Vitali,

56

Questa potrebbe non essere la soluzione più efficace. Ma questo ha funzionato per me ogni volta ... chiamo questa funzione dove mai ho bisogno di ascoltare il softKeyboard.

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;
            }
        }
    });
}

Nota: questo approccio causerà problemi se l'utente utilizza una tastiera mobile.


1
addOnGlobalLayoutListener?
coolcool1994,

7
Questo puzza di perdita di memoria. Stai aggiungendo un ascoltatore a un oggetto globale, che ti terrà stretto e non ti lascerà mai andare.
flexicious.com

9
Anche questo non funzionerà con le attività impostate con android:windowSoftInputMode="adjustPan"o adjustResizecon una finestra a schermo intero, poiché il layout non viene mai ridimensionato.
Ionoclast Brigham,

1
Funziona solo con AdjustResize. Per AdjustPan, heightDiff non cambia mai.
alexhilton,

2
perché stai confrontando un booleano?
Xerus,

37

Se vuoi gestire mostra / nascondi la finestra della tastiera IMM (virtuale) dalla tua Attività, dovrai sottoclassare il layout e sovrascrivere il metodo OnMesure (in modo da poter determinare la larghezza misurata e l'altezza misurata del layout). Dopodiché imposta il layout della sottoclasse come vista principale per la tua attività da setContentView (). Ora sarai in grado di gestire gli eventi IMM show / hide window. Se questo sembra complicato, non è così. Ecco il codice:

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>

Ora all'interno della tua attività dichiara la sottoclasse per il tuo layout (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);
    }
}

Puoi vedere dal codice che gonfiamo il layout per la nostra attività nel costruttore di sottoclassi

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

E ora basta impostare la visualizzazione del contenuto del layout della sottoclasse per la nostra attività.

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...

}

3
Devo indagare ulteriormente, ma ho i miei dubbi sul fatto che questo funzionerebbe nel mio caso per dialoghi di piccole dimensioni su un dispositivo a schermo grande per il quale le misure del layout non sarebbero influenzate dalla presenza di una tastiera.
PJL

4
Non funziona per Android: windowSoftInputMode = "AdjustPan". Volevo che il mio schermo non si restringesse dopo la visualizzazione della tastiera virtuale. Puoi per favore dire qualsiasi correzione in modo che funzioni anche per AdjustPan
Shirish Herwade il

Questo non funziona, va sempre nella parte altro qui se (altezza effettiva> proposta altezza) {// La tastiera è mostrata} altrimenti {// La tastiera è nascosta}
Aamirkhan

Puoi anche utilizzare una vista personalizzata con la stessa idea, segui un esempio gist.github.com/juliomarcos/8ca307cd7eca607c8547
Julio Rodrigues

1
Non funziona con le attività impostate con android:windowSoftInputMode="adjustPan"o adjustResizecon una finestra a schermo intero, poiché il layout non viene mai ridimensionato.
Ionoclast Brigham,

35

Ho fatto così:

Aggiungi OnKeyboardVisibilityListenerinterfaccia.

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();
  }
}

Spero che questo ti possa aiutare.


2
Grazie Hiren. Questa è la soluzione perfetta +1
Harin Kaklotar,

1
Grazie, ha lavorato per me! Se si desidera solo regolare la RecyclerView, vedi soluzione qui: stackoverflow.com/a/43204258/373106
David Papirov

1
Perfetta implementazione riutilizzabile, lavorato in Activity o Fragment, grazie
Pelanes,

1
davvero bello.
ZaoTaoBao,

@DavidPapirov, hai incollato un link a RecyclerView, ma non ne hai parlato qui.
CoolMind,

22

Sulla base del codice di Nebojsa Tomcic ho sviluppato la seguente sottoclasse RelativeLayout:

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();
        }
    }

}

Funziona abbastanza bene ... Segna, che questa soluzione funzionerà solo quando la modalità di input soft della tua attività è impostata su "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"


3
Non funziona per Android: windowSoftInputMode = "AdjustPan". Volevo che il mio schermo non si restringesse dopo la visualizzazione della tastiera virtuale. Puoi per favore dire qualsiasi correzione in modo che funzioni anche per AdjustPan
Shirish Herwade il

1
Anche questo non funzionerà con le attività impostate con android:windowSoftInputMode="adjustPan"o adjustResizecon una finestra a schermo intero, poiché il layout non viene mai ridimensionato.
Ionoclast Brigham,

si innesca parecchie volte.
zionpi,

22

Come la risposta di @ amalBit, registra un ascoltatore nel layout globale e calcola la differenza tra il fondo visibile di dectorView e il fondo proposto, se la differenza è maggiore di un valore (indovinato l'altezza dell'IME), pensiamo che l'IME sia alto:

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

la soglia di altezza 100 è l'altezza minima indovinata di IME.

Funziona sia con AdjustPan sia con AdjustResize.


2
Sto per strapparmi i capelli !! Mi hai salvato i capelli;)
Vijay Singh Chouhan il

1
È l'unica buona risposta qui, funziona perfettamente con la tastiera morbida, grazie
Z3nk

12

La soluzione di Nebojsa ha quasi funzionato per me. Quando ho cliccato all'interno di un EditText multilinea sapevo che la tastiera era visualizzata, ma quando ho iniziato a digitare all'interno del EditText, l'HighHight e la propostaHight erano sempre gli stessi, quindi non sapevo che la tastiera fosse ancora visualizzata. Ho apportato una leggera modifica per memorizzare l'altezza massima e funziona benissimo. Ecco la sottoclasse rivista:

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);
    }
}

10

Non sono sicuro se qualcuno pubblica questo. Ho trovato questa soluzione semplice da usare! . La classe SoftKeyboard è su gist.github.com . Ma mentre il popup della tastiera / nasconde il callback degli eventi, abbiamo bisogno di un gestore per fare correttamente le cose sull'interfaccia utente:

/*
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
                    ...
                }
            });

    }   
});

ecco il Git per ottenere SoftkeyBoard " gist.github.com/felHR85/… "
douarbou,

9

Risolvo questo sostituendo onKeyPreIme (int keyCode, evento KeyEvent) nel mio EditText personalizzato.

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

Come usarlo in Frammento o Attività? @Qbait
Maulik Dodia

Non funziona, può essere chiamato solo quando lascio la pagina nel mio caso.
DysaniazzZ,

questo è il metodo da EditText, vedi questa risposta: stackoverflow.com/a/5993196/2093236
Dmide

4

Ho una specie di hack per farlo. Anche se non sembra essere un modo per rilevare quando la tastiera virtuale ha mostrato o nascosto, si può infatti rilevare quando è su per essere visualizzato o nascosto impostando una OnFocusChangeListenersul EditTextche si sta ascoltando.

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
        }
    });

NOTA: Una cosa da tenere presente con questo trucco è che questo callback viene attivato immediatamente quando il EditTextguadagno o perde attenzione. Questo si attiverà proprio prima che la tastiera virtuale si mostri o si nasconda. Il modo migliore che ho trovato per fare qualcosa dopo che la tastiera si mostra o si nasconde è usare a Handlere ritardare qualcosa di ~ 400ms, in questo modo:

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);
        }
    });

1
Non funziona, altrimenti. OnFocusChangeListenerdice solo se EditTextha lo stato attivo dopo che lo stato è cambiato. Ma il IMEpuò essere nascosto quando EditTextsi concentra, come rilevare questo caso?
DysaniazzZ,


2

Ho risolto il problema con la codifica di testo indietro di una riga.

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
Non funziona per Android: windowSoftInputMode = "AdjustPan". Volevo che il mio schermo non si restringesse dopo la visualizzazione della tastiera virtuale. Puoi per favore dire qualsiasi correzione in modo che funzioni anche per AdjustPan
Shirish Herwade il

Quando la funzione nascondi / mostra, questo metodo di ascolto chiama due o tre volte. Non so quale sia esattamente il problema.
Jagveer Singh Rajput,

2

Puoi anche verificare la prima imbottitura inferiore del bambino DecorView. Verrà impostato su un valore diverso da zero quando viene visualizzata la tastiera.

@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);
}

1

Hide | Show: gli eventi per la tastiera possono essere ascoltati tramite un semplice hack in 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
                }
            }
        });

Qui activityRootView è la vista radice della tua attività.


Il mio heightDiff è 160 all'inizio e 742 con kbd, quindi ho dovuto presentare e impostare initialHeightDiff all'inizio
djdance il

0

usando viewTreeObserver per ottenere facilmente l'evento da tastiera.

layout_parent.viewTreeObserver.addOnGlobalLayoutListener {
            val r = Rect()
            layout_parent.getWindowVisibleDisplayFrame(r)
            if (layout_parent.rootView.height - (r.bottom - r.top) > 100) { // if more than 100 pixels, its probably a keyboard...
                Log.e("TAG:", "keyboard open")
            } else {
                Log.e("TAG:", "keyboard close")
            }
        }

** layout_parent è come la tua vistaedit_text.parent


-2

La risposta di Nebojsa Tomcic non mi è stata utile. Ho RelativeLayoutcon TextViewe AutoCompleteTextViewall'interno di esso. Devo scorrere TextViewverso il basso quando viene mostrata la tastiera e quando è nascosta. Per fare questo ho scavalcato il onLayoutmetodo e funziona bene per me.

public class ExtendedLayout extends RelativeLayout
{
    public ExtendedLayout(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 onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);

        if (changed)
        {
            int scrollEnd = (textView.getLineCount() - textView.getHeight() /
                textView.getLineHeight()) * textView.getLineHeight();
            textView.scrollTo(0, scrollEnd);
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.