Come verificare la visibilità della tastiera del software in Android?


516

Devo fare una cosa molto semplice: scoprire se viene mostrata la tastiera del software. È possibile su Android?


9
Sebbene la risposta di Reuben Scratton sia buona, sembra rotta su un tablet. Ho sostituito il controllo diff> 128 con diff> screenHeight / 3.
Kingston,

2
La risposta di Reuben Scratton è stata buona, ma ho richiesto l'adeguamento di KaChi per usarlo effettivamente.
Cullan,

1
Perché Google non fa funzionare un metodo integrato standard per tutte le app per tastiera?
Frutta

4
Mi rincuora ancora che questo non funzioni un sistema ...
Longi,

1
È assolutamente fuori di testa che questa API manca ancora 10 anni dopo . Molto contento di essermi allontanato da Android.
Reuben Scratton,

Risposte:


674

NUOVA RISPOSTA aggiunta il 25 gennaio 2012

Da quando ho scritto la risposta di seguito, qualcuno mi ha fatto capire l'esistenza di ViewTreeObserver e dei suoi amici, API che si nascondono nell'SDK dalla versione 1.

Invece di richiedere un tipo di layout personalizzato, una soluzione molto più semplice consiste nel fornire alla vista radice della tua attività un ID noto, ad esempio @+id/activityRoot, agganciare un GlobalLayoutListener nel ViewTreeObserver e da lì calcolare la differenza di dimensione tra la radice della vista dell'attività e la dimensione della finestra:

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

Utilizzando un'utilità come:

public static float dpToPx(Context context, float valueInDp) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}

Facile!

Nota: l'applicazione deve impostare questo flag in Android Manifest, android:windowSoftInputMode="adjustResize"altrimenti la soluzione precedente non funzionerà.

RISPOSTA ORIGINALE

Sì, è possibile, ma è molto più difficile di quanto dovrebbe essere.

Se devo occuparmi di quando appare e scompare la tastiera (cosa abbastanza spesso), ciò che faccio è personalizzare la mia classe di layout di livello superiore in una che prevale onMeasure(). La logica di base è che se il layout si trova riempiendo significativamente meno dell'area totale della finestra, probabilmente sta mostrando una tastiera soft.

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

    }

Quindi nella tua classe Activity ...

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
    }

    ...
}

62
Questo non ha funzionato per me fino a quando ho capito che devi impostare il seguente attributo sulla tua attività: android: windowSoftInputMode = "AdjustResize"
ajh158

9
Sembra fare il trucco. Inoltre, se non conosci l'ID della vista radice, ecco come puoi ottenere la vista:((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
Orafo

8
Se provi questo utilizzando la vista radice effettiva ( android.R.id.content) sarai in grado di dire con più sicurezza che Systeml'entità anziché l'applicazione è l'entità che cambia altezza. Sarebbe molto più sicuro per il team Android darci una pausa e farci sapere almeno le cose di base sull'input di SoftKeyboard.
Graeme,

14
Attenzione che heightDiffincluderà sempre l'altezza della barra delle azioni. Nella nuova risposta che è stata ignorata dal test se quell'altezza è maggiore di qualche costante, ma 100 pixel non sono sufficienti per i dispositivi xxhdpi come il Nexus 4. Valuta di convertire quel valore in DP se vuoi davvero usare questo hacky work- in giro.
Paul Lammertsma,

8
Avviso: non funziona con WindowManager.LayoutParams.FLAG_FULLSCREEN e con un tema a schermo intero.
VAV,

303

Quindi speriamo che questo aiuti qualcuno.

La nuova risposta che Reuben Scratton ha dato è ottima e davvero efficiente, ma funziona davvero solo se imposti windowSoftInputMode su AdjustResize. Se lo imposti su AdjustPan, non è ancora possibile rilevare se la tastiera è visibile o meno utilizzando il suo frammento di codice. Per ovviare a questo, ho apportato questa piccola modifica al codice sopra.

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 > 0.25*activityRootView.getRootView().getHeight()) { // if more than 25% of the screen, its probably a keyboard...
        ... do something here
    }
 }
}); 

1
Questo è quello che ha funzionato per me. Stavo tentando di rilevare lo stato della tastiera da un'abitudine TwoDScrollerViewsimile a stackoverflow.com/a/5224088/530513 anche se con lo zoom anche. Il bambino non era un ImageViewlayout semplice ma personalizzato (si estende RelativeLayout) ma non era in grado di rilevare la tastiera utilizzando la soluzione consigliata nonostante l'impostazione android:windowSoftInputMode="adjustResize". Grazie!
David O'Meara,

1
Grazie, grazie, grazie! AdjustResize non è fattibile per la mia app e la tua soluzione ha funzionato perfettamente.
dwemthy,

1
Funziona con ActionBare ActionBarSherlock. Molte grazie! A proposito, c'è un metodo r.height():)
Dmitry Zaytsev il

1
Allegherò una taglia qui entro 23 ore per contrassegnare questa risposta in qualche modo.
Dmitry Zaytsev il

9
heightDiff > root.getRootView().getHeight() / 4è un buon valore per lavorare con dispositivi ad alta risoluzione. 100px è troppo corto. in Nexus 5 con risoluzione 1080x1920, 1920 - (996-75)>? 100 = 999 1920 - (1776-75)>? 100 = 219 // tastiera in alto nella galassia s2 con risoluzione 480x800, 800 - (800-38)>? 100 = 38 800 - (410-38)>? 100 = 428 // la tastiera è attiva, quindi il numero magico 100px non è abbastanza buono.
Flask_KR il

55

È stato per sempre in termini di computer, ma questa domanda è ancora incredibilmente rilevante!

Quindi ho preso le risposte sopra e le ho unite e raffinate un po '...

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

Per me va bene :)

NOTA: Se noti che DefaultKeyboardDP non si adatta al tuo dispositivo gioca con il valore e pubblica un commento affinché tutti possano sapere quale dovrebbe essere il valore ... alla fine otterremo il valore corretto per adattarsi a tutti i dispositivi!

Per maggiori dettagli, controlla l'implementazione su Cyborg


2
+1 Grazie mille !! Stavo provando le altre risposte, ma non funzionano. Poi ho trovato il tuo e funziona come un fascino. Codice fantastico! : D
Kevin van Mierlo,

questo funziona solo se aggiungi: android: windowSoftInputMode = "stateHidden | AdjustPan" o android: windowSoftInputMode = "stateHidden | AdjustResize" Grazie !!!!
Lena Bru,

Sei sicuro? Se i server di memoria, ho ottenuto gli eventi correttamente anche quando Android: windowSoftInputMode aveva il suo valore predefinito ... l'unica cosa che non ha funzionato è il comportamento dello schermo, quindi l'ho ridotto manualmente ...
TacB0sS

2
una piccola correzione: int finale privato EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP? 48: 0);
binaryKarmic il

2
Eccellente!! Indipendentemente da "windowSoftInputMode" è impostato su "AdjustPan" / "AdjustResize" / "AdjustPan | stateHidden" / "AdjustResize | stateHidden", o anche senza questa opzione, funziona sempre! Testato su XiaoMi 8.
Zhou Hongbo,

52

Ci scusiamo per la risposta tardiva, ma avevo creato una piccola classe di supporto per gestire gli eventi di apertura / chiusura con notifica agli ascoltatori e altre cose utili, potrebbe essere qualcuno potrebbe trovarlo 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();
            }
        }
    }
}

Esempio di utilizzo:

final SoftKeyboardStateWatcher softKeyboardStateWatcher 
    = new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);

// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks

2
La classe non è piccola ma l'implementazione è sicuramente :). Grazie proverò questo :)
Atul O Holic

1
Ping me se hai problemi :) L'ho usato con successo in 2 progetti
Artem Zinnatullin

Dopo aver provato molti esempi sopra e aver riscontrato problemi minori, questo è stato quello che ha funzionato meglio per me su molti dispositivi diversi (incluso xxhdpi). Inoltre è nella sua bella classe riutilizzabile. L'ho convertito per essere utilizzato in mono droide.
kheit,

Alcune tastiere, in alcuni casi, hanno in cima una riga aggiuntiva di tasti personalizzati (ad esempio, parole previste). Apparentemente non fanno parte della tastiera stessa, poiché l'uso di getLastKeyboardHeightInPx()non include l'altezza di quella riga. Conosci un modo per tenerne conto?
ygesher,

Funziona solo se sei pronto a compromettere la modifica dell'altezza del layout quando appare la tastiera. destra?
M. Usman Khan,

34

Alcuni miglioramenti per evitare di rilevare erroneamente la visibilità della tastiera software su dispositivi ad alta densità:

  1. La soglia della differenza di altezza deve essere definita come 128 dp , non 128 pixel .
    Fare riferimento al documento di progettazione di Google su Metriche e griglia , 48 dp è una dimensione comoda per l'oggetto touch e 32 dp è minimo per i pulsanti. La tastiera virtuale generica dovrebbe includere 4 file di pulsanti chiave, quindi l'altezza minima della tastiera dovrebbe essere: 32 dp * 4 = 128 dp , ciò significa che la dimensione della soglia dovrebbe essere trasferita in pixel moltiplicando la densità del dispositivo. Per i dispositivi xxxhdpi (densità 4), la soglia di altezza della tastiera software dovrebbe essere 128 * 4 = 512 pixel.

  2. Differenza di altezza tra la vista radice e la sua area visibile:
    altezza della vista radice - altezza della barra di stato - altezza della cornice visibile = fondo della vista radice - fondo della cornice visibile, poiché l'altezza della barra di stato è uguale alla parte superiore della cornice visibile della vista radice.

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

4
Questo merita di essere la risposta accettata. Ignorare la densità mi ha dato risultati molto diversi su dispositivi con diversi fattori di forma ma dimensioni dei pixel simili. Grazie!
Ginger McMurray,

2
Grazie ... questa è una condizione migliore!
TacB0sS,

1
Eccellente. Il metodo isKeyboardShown () è ciò di cui abbiamo bisogno. Grazie
Danylo Volokh,

Funziona anche nel 2020. Risposta perfetta Ho provato tutto il codice ma su questo funziona perfettamente.
Mitesh Jain,

8

Ho usato un po 'di tempo per capirlo ... Ho eseguito alcune CastExceptions, ma ho capito che puoi sostituire LinearLayout in layout.xml con il nome della classe.

Come questo:

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

In questo modo non si verificano problemi di trasmissione.

... e se non vuoi farlo su ogni pagina, ti consiglio di usare "MasterPage in Android". Vedi il link qui: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx


Fai attenzione a incollarlo nel tuo XML se non hai lo stesso nome pacchetto / classe. Eclipse decide solo di congelare e devi chiuderlo. Un prodotto così professionale. / s
Spencer Ruport,

5
@SpencerRuport, ecco perché è gratuito.
Cody,

@DoctorOreo - scarica IntelliJ. È gratuito e non fa schifo.
Segna il

@Mark - pochi mesi dopo aver pubblicato questo, ho provato IntelliJ. È molto meglio, IMO, di Eclipse. Tutti i loro prodotti (per la maggior parte) penso siano eccellenti. Ne ho anche comprato alcuni.
Cody,

Ci scusiamo per aver rianimato un thread di commento così vecchio. Sono contento che tu lo stia usando e ti piaccia. Adoro usare IntelliJ, AppCode per iOS e PyCharm per Python. Saluti!
Mark


5

L'idea è che, se è necessario nascondere la tastiera e controllare contemporaneamente lo stato di input morbido, utilizzare la seguente soluzione:

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

Questo metodo restituisce true se la tastiera è stata mostrata prima di nascondersi.


Questo è quello che funziona senza usare l'altezza e tutto ... Grazie ... mi
hai

4

Ho scoperto che una combinazione del metodo di @ Reuben_Scratton con il metodo di @ Yogesh sembra funzionare meglio. Combinando i loro metodi si otterrebbe qualcosa del genere:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
       // ... do something here
    }
  }
});

sempre Configuration.KEYBOARDHIDDEN_NO.
fantouch,

4

Puoi osservare la pelle delle softkeyboard usando decorView dell'attività.

public final class SoftKeyboardUtil {
    public static final String TAG = "SoftKeyboardUtil";
    public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
        final View decorView = activity.getWindow().getDecorView();
        decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect rect = new Rect();
                decorView.getWindowVisibleDisplayFrame(rect);
                int displayHight = rect.bottom - rect.top;
                int hight = decorView.getHeight();
                boolean hide = (double)displayHight / hight > 0.8 ;
                if(Log.isLoggable(TAG, Log.DEBUG)){
                    Log.d(TAG ,"DecorView display hight = "+displayHight);
                    Log.d(TAG ,"DecorView hight = "+ hight);
                    Log.d(TAG, "softkeyboard visible = " + !hide);
                }

                listener.onSoftKeyBoardVisible(!hide);

            }
        });
    }



    public interface OnSoftKeyBoardHideListener{
        void onSoftKeyBoardVisible(boolean visible);
    }
}

4

Invece di assumere la differenza con la codifica, ho fatto qualcosa del genere, dato che non avevo opzioni di menu nella mia applicazione.

final View root= findViewById(R.id.myrootview); 
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
    public void onGlobalLayout() {
        int heightDiff = root.getRootView().getHeight() - root.getHeight();

        Rect rectgle= new Rect();
        Window window= getWindow();
        window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
        int contentViewTop=                     
          window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            //Soft KeyBoard Hidden
        }else{
            //Soft KeyBoard Shown
        }
     }
});

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

4

Esiste anche una soluzione con inserti di sistema, ma funziona solo con API >= 21( Android L). Di 'che hai BottomNavigationView, che è figlio di LinearLayoute devi nasconderlo quando viene mostrata la tastiera:

> LinearLayout
  > ContentView
  > BottomNavigationView

Tutto quello che devi fare è estendere LinearLayoutin questo modo:

public class KeyboardAwareLinearLayout extends LinearLayout {
    public KeyboardAwareLinearLayout(Context context) {
        super(context);
    }

    public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardAwareLinearLayout(Context context,
                                     @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public KeyboardAwareLinearLayout(Context context, AttributeSet attrs,
                                     int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        int childCount = getChildCount();
        for (int index = 0; index < childCount; index++) {
            View view = getChildAt(index);
            if (view instanceof BottomNavigationView) {
                int bottom = insets.getSystemWindowInsetBottom();
                if (bottom >= ViewUtils.dpToPx(200)) {
                    // keyboard is shown
                    view.setVisibility(GONE);
                } else {
                    // keyboard is hidden
                    view.setVisibility(VISIBLE);
                }
            }
        }
        return insets;
    }
}

L'idea è che quando viene mostrata la tastiera, gli inserti di sistema vengono modificati con un .bottomvalore piuttosto elevato.


4

C'è un metodo nascosto che può aiutare per questo InputMethodManager.getInputMethodWindowVisibleHeight,. Ma non so perché sia ​​nascosto.

import android.content.Context
import android.os.Handler
import android.view.inputmethod.InputMethodManager

class SoftKeyboardStateWatcher(private val ctx: Context) {
  companion object {
    private const val DELAY = 10L
  }

  private val handler = Handler()
  private var isSoftKeyboardOpened: Boolean = false

  private val height: Int
    get() {
      val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
      val method = imm.javaClass.getMethod("getInputMethodWindowVisibleHeight")
      method.isAccessible = true
      return method.invoke(imm) as Int
    }

  private val task: Runnable by lazy {
    Runnable {
      start()
      if (!isSoftKeyboardOpened && height > 0) {
        isSoftKeyboardOpened = true
        notifyOnSoftKeyboardOpened(height)
      } else if (isSoftKeyboardOpened && height == 0) {
        isSoftKeyboardOpened = false
        notifyOnSoftKeyboardClosed()
      }
    }
  }

  var listener: SoftKeyboardStateListener? = null

  interface SoftKeyboardStateListener {
    fun onSoftKeyboardOpened(keyboardHeightInPx: Int)
    fun onSoftKeyboardClosed()
  }

  fun start() {
    handler.postDelayed(task, DELAY)
  }

  fun stop() {
    handler.postDelayed({
      if (!isSoftKeyboardOpened) handler.removeCallbacks(task)
    }, DELAY * 10)
  }

  private fun notifyOnSoftKeyboardOpened(keyboardHeightInPx: Int) {
    listener?.onSoftKeyboardOpened(keyboardHeightInPx)
  }

  private fun notifyOnSoftKeyboardClosed() {
    listener?.onSoftKeyboardClosed()
  }
}

Se qualcuno ne avesse bisogno - funziona anche in Xamarin, il nome del metodo è esattamente lo stesso e deve essere accessibile allo stesso modo - tramite la proprietà Class su InputMethodManager.
Konstantin Severy,

Fai attenzione usando questo, è API non supportata (è nascosto per un motivo) e per cominciare non funziona su KitKat.
Daniele Ricci,

3

Nessuna di queste soluzioni funzionerà per Lollipop così com'è. In Lollipop activityRootView.getRootView().getHeight()include l'altezza della barra dei pulsanti, mentre la misurazione della vista no. Ho adattato la soluzione migliore / più semplice sopra per lavorare con Lollipop.

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    Rect r = new Rect();
    //r will be populated with the coordinates of your view that area still visible.
    activityRootView.getWindowVisibleDisplayFrame(r);

    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    Resources res = getResources();
    // The status bar is 25dp, use 50dp for assurance
    float maxDiff =
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, res.getDisplayMetrics());

    //Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      float buttonBarHeight =
          TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, res.getDisplayMetrics());
      maxDiff += buttonBarHeight;
    }
    if (heightDiff > maxDiff) { // if more than 100 pixels, its probably a keyboard...
      ...do something here
    }
  }
});

Perché una soluzione simile da stackoverflow.com/a/18992807/2914140 non ha funzionato per te e in cosa differisce la tua soluzione?
CoolMind

3

Ho appena riscontrato un bug durante l'utilizzo della maggior parte delle soluzioni sopra che suggeriscono di aggiungere un numero fisso.

S4 ha un dpi elevato che ha portato l'altezza della barra di navigazione a 100 px, quindi la mia app pensa che la tastiera sia sempre aperta.

Quindi, con tutti i nuovi telefoni ad alta risoluzione rilasciati credo che usare un valore hard coded non sia una buona idea a lungo termine.

Un approccio migliore che ho trovato dopo alcuni test su vari schermi e dispositivi era l'uso della percentuale. Scopri la differenza tra decorView e il contenuto dell'app e successivamente controlla qual è la percentuale di tale differenza. Dalle statistiche che ho ottenuto, la maggior parte della barra di navigazione (indipendentemente dalle dimensioni, risoluzione ecc.) Occuperà tra il 3% e il 5% dello schermo. Dove come se la tastiera fosse aperta, si impiegava tra il 47% e il 55% dello schermo.

In conclusione, la mia soluzione è stata quella di verificare se il diff è superiore al 10%, quindi presumo che sia una tastiera aperta.


3

Ho usato una leggera variante della risposta di Reuban, che si è rivelata più utile in determinate circostanze, specialmente con dispositivi ad alta risoluzione.

final View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 3) {
                    //Make changes for Keyboard not visible
                } else {
                    //Make changes for keyboard visible
                }
            }
        });

che cos'è questo R.id.activityRoot
ranjith

2
invece di creare e utilizzare R.id.activityRoot, puoi semplicemente usare android.R.id.contentqual è esattamente ciò di cui hai bisogno.
Marcin Orlowski,

3

È stato per sempre in termini di computer, ma questa domanda è ancora incredibilmente rilevante! Quindi ho preso le risposte sopra e le ho unite e raffinate un po '...

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

public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
    final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        private boolean wasOpened;

    private final Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            activityRootView.getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isOpen = heightDiff > 100;
            if (isOpen == wasOpened) {
                logDebug("Ignoring global layout change...");
                return;
            }

            wasOpened = isOpen;
            listener.onVisibilityChanged(isOpen);
        }
    });
}

Per me funziona.


3

Prova questo:

final View activityRootView = getWindow().getDecorView().getRootView();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        Rect r = new Rect();
        //r will be populated with the coordinates of your view that area still visible.
        activityRootView.getWindowVisibleDisplayFrame(r);

        int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
             // ... do something here ... \\
        }
    }
});

2

La mia risposta è sostanzialmente la stessa della risposta di Kachi, ma l'ho racchiusa in una bella classe di supporto per ripulire il modo in cui viene utilizzata in tutta la mia app.

import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

/**
 * Detects Keyboard Status changes and fires events only once for each change
 */
public class KeyboardStatusDetector {
    KeyboardVisibilityListener visibilityListener;

    boolean keyboardVisible = false;

    public void registerFragment(Fragment f) {
        registerView(f.getView());
    }

    public void registerActivity(Activity a) {
        registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
    }

    public KeyboardStatusDetector registerView(final View v) {
        v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                v.getWindowVisibleDisplayFrame(r);

                int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
                    /** Check this variable to debounce layout events */
                    if(!keyboardVisible) {
                        keyboardVisible = true;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
                    }
                } else {
                    if(keyboardVisible) {
                        keyboardVisible = false;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
                    }
                }
            }
        });

        return this;
    }

    public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
        visibilityListener = listener;
        return this;
    }

    public static interface KeyboardVisibilityListener {
        public void onVisibilityChanged(boolean keyboardVisible);
    }
}

Puoi usarlo per rilevare i cambiamenti della tastiera ovunque nell'app in questo modo:

    new KeyboardStatusDetector()
            .registerFragment(fragment)  //register to a fragment 
            .registerActivity(activity)  //or register to an activity
            .registerView(view)          //or register to a view
            .setVisibilityListener(new KeyboardVisibilityListener() {
                @Override
                public void onVisibilityChanged(boolean keyboardVisible) {
                    if(keyboardVisible) {
                       //Do stuff for keyboard visible
                    }else {
                       //Do stuff for keyboard hidden
                    }
                }
            });

Nota: utilizzare solo una delle chiamate "registra". Funzionano tutti allo stesso modo e sono lì solo per comodità


2

puoi provare questo, funziona benissimo per me:

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

if (imm.isAcceptingText()) {
    //Software Keyboard was shown..
} else {
    //Software Keyboard was not shown..
}

1
Non usare questo, ritorna vero se la tastiera era nascosta usando back ma la vista è focalizzata, almeno su Marshmallow
Maragues

2
risponde sempre che è visibile
jose920405,

2

Ho avuto difficoltà a mantenere lo stato della tastiera quando ho cambiato l'orientamento dei frammenti all'interno di un viewpager. Non sono sicuro del perché, ma sembra solo essere traballante e si comporta in modo diverso da un'attività standard.

Per mantenere lo stato della tastiera in questo caso, devi prima aggiungerlo android:windowSoftInputMode = "stateUnchanged"al tuo AndroidManifest.xml. Si può notare, tuttavia, che questo non risolve effettivamente l'intero problema: la tastiera non si apriva per me se era stata precedentemente aperta prima del cambio di orientamento. In tutti gli altri casi, il comportamento sembrava corretto.

Quindi, dobbiamo implementare una delle soluzioni menzionate qui. Il più pulito che ho trovato è stato quello di George Maisuradze - usa il callback booleano da hideSoftInputFromWindow:

InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);

Ho memorizzato questo valore nel onSaveInstanceStatemetodo del mio frammento e l'ho recuperato onCreate. Quindi, ho forzatamente mostrato la tastiera onCreateViewse aveva un valore di true(ritorna vero se la tastiera è visibile prima di nasconderla effettivamente prima della distruzione del frammento).


1

Ecco la mia soluzione e funziona. Invece di cercare le dimensioni dei pixel, controlla che l'altezza della visualizzazione del contenuto sia cambiata o meno:

// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private int oldHeight;

        @Override
        public void onGlobalLayout() {
            int newHeight = commentsContent.getMeasuredHeight();
            if (newHeight < oldHeight) {
                // Check for the keyboard showing in case the height difference
                // is a result of orientation change
                if (isSoftKeyboardShowing(CommentsActivity.this)) {
                    // Keyboard is showing so scroll to the latest comment
                    scrollToLatestComment();
                }
            }
            oldHeight = newHeight;
        }

    });


public static boolean isSoftKeyboardShowing(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    return inputMethodManager.isActive();
}

inputMethodManager.isActive () restituisce sempre true per me, indipendentemente dal fatto che la tastiera sia
attiva

1

Non creare alcun codice fisso. Il modo migliore è che devi ridimensionare le tue viste mentre ti concentri su EditText con KeyBord Show. Puoi fare questo aggiungendo la proprietà di ridimensionamento sull'attività nel file manifest usando il codice seguente.

android:windowSoftInputMode="adjustResize"


1

C'è un metodo diretto per scoprirlo. E non richiede modifiche al layout.
Quindi funziona anche in modalità fullscreen immersiva.

Il trucco è che si tenta di nascondere o mostrare la tastiera software e acquisire il risultato di quel tentativo.
Nessun panico, questo non mostra o nasconde davvero la tastiera. Chiediamo solo lo stato.

Per rimanere aggiornato, è possibile semplicemente ripetere l'operazione, ad esempio ogni 200 millisecondi, utilizzando un gestore.

Puoi trovare un'implementazione qui: https://stackoverflow.com/a/27567074/2525452


1

penso che questo metodo ti aiuterà a scoprire se il keybord è visibile o no.

 public Boolean isSoftKeyBoardVisible(){
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

    if (imm.isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
        return true;
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
        return false;
    }

}

risponde sempre che è visibile
jose920405,

0

Nuova risposta di Reuben Scratton (calcola HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight(); ) non funzionerà se si imposta la modalità della barra di stato traslucida.

se usi la barra di stato traslucida, activityRootView.getHeight() non cambierà mai il tempo la tastiera virtuale è visibile. restituirà sempre l'altezza dell'attività e la barra di stato.

Ad esempio, Nexus 4, Android 5.0.1, impostato android:windowTranslucentStatussu true, restituirà 1184 per sempre, anche se l'immagine è stata aperta. Se hai impostatoandroid:windowTranslucentStatus su false, restituirà Height correttamente, se ime invisibile, restituirà 1134 (non includere la barra di stato) 。Chiudi l'ime, restituirà forse 5xx (dipende dall'altezza dell'ime)

Non so che tempo è un bug, ho provato su 4.4.4 e 5.0.1, il risultato è lo stesso.

Quindi, fino ad ora, la seconda risposta più concordata, la soluzione di Kachi sarà il modo più sicuro per calcolare l'altezza dell'ime. Ecco una copia:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new        OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);

int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
    ... do something here
    }
 }
}); 

0

Un metodo che non richiede un LayoutListener

Nel mio caso, vorrei salvare lo stato della tastiera prima di sostituire il mio frammento. Chiamo il metodo hideSoftInputFromWindow daonSaveInstanceState , che chiude la tastiera e mi restituisce se la tastiera era visibile o meno.

Questo metodo è semplice ma può cambiare lo stato della tastiera.


0

So che questo è un vecchio post, ma penso che questo sia l'approccio più semplice che conosco e il mio dispositivo di test è Nexus 5. Non l'ho provato su altri dispositivi. Spero che altri condividano il loro approccio se scoprono che il mio codice non è buono :)

public static boolean isKeyboardShown(Context context, View view) {
        if (context == null || view == null) {
            return false;
        }
        InputMethodManager imm = (InputMethodManager) context
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 
}

imm.hideSoftInputFromWindow restituisce valori booleani.

Grazie,


0
if (keyopen())
{
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);            
}

La funzione sopra è quella che uso per verificare se una tastiera è visibile. Se lo è, allora lo chiudo.

Di seguito mostra i due metodi richiesti.

Innanzitutto, definire l'altezza della finestra praticabile in onCreate.

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

//  add to onCreate method
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    sheight= rectgle.bottom;
//

} 

Quindi, aggiungi un metodo booleano che ottiene l'altezza della finestra in quell'istanza. Se non corrisponde all'originale (supponendo che non lo si stia modificando lungo la strada ...), la tastiera è aperta.

public boolean keyopen()
{
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    int curheight= rectgle.bottom;

    if (curheight!=sheight)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Frotz!


0

So quanto puoi determinare se la tastiera è nascosta o meno.

public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public int getNavigationBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public boolean isKeyboardHidden() {
    int delta = mRootView.getRootView().getHeight() - mRootView.getHeight() - getNavigationBarHeight() - getStatusBarHeight()
            - getSupportActionBar().getHeight();
    return delta <= 0;
}

Questo funziona per tablet. Quando la barra di navigazione viene visualizzata in orizzontale.

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.