Devo fare una cosa molto semplice: scoprire se viene mostrata la tastiera del software. È possibile su Android?
Devo fare una cosa molto semplice: scoprire se viene mostrata la tastiera del software. È possibile su Android?
Risposte:
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
}
...
}
((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
android.R.id.content
) sarai in grado di dire con più sicurezza che System
l'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.
heightDiff
includerà 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.
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
}
}
});
TwoDScrollerView
simile a stackoverflow.com/a/5224088/530513 anche se con lo zoom anche. Il bambino non era un ImageView
layout 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!
ActionBar
e ActionBarSherlock
. Molte grazie! A proposito, c'è un metodo r.height()
:)
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.
È 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
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
getLastKeyboardHeightInPx()
non include l'altezza di quella riga. Conosci un modo per tenerne conto?
Alcuni miglioramenti per evitare di rilevare erroneamente la visibilità della tastiera software su dispositivi ad alta densità:
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.
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;
}
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
Il controllo dell'altezza degli elementi non è affidabile perché alcune tastiere come WifiKeyboard hanno un'altezza zero.
Invece, puoi usare il risultato di callback di showSoftInput () e hideSoftInput () per verificare lo stato della tastiera. Dettagli completi e esempio di codice a
https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android
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.
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
}
}
});
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);
}
}
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
}
}
});
Esiste anche una soluzione con inserti di sistema, ma funziona solo con API >= 21
( Android L
). Di 'che hai BottomNavigationView
, che è figlio di LinearLayout
e devi nasconderlo quando viene mostrata la tastiera:
> LinearLayout
> ContentView
> BottomNavigationView
Tutto quello che devi fare è estendere LinearLayout
in 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 .bottom
valore piuttosto elevato.
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()
}
}
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
}
}
});
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.
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
}
}
});
R.id.activityRoot
, puoi semplicemente usare android.R.id.content
qual è esattamente ciò di cui hai bisogno.
È 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.
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 ... \\
}
}
});
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à
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..
}
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 onSaveInstanceState
metodo del mio frammento e l'ho recuperato onCreate
. Quindi, ho forzatamente mostrato la tastiera onCreateView
se aveva un valore di true
(ritorna vero se la tastiera è visibile prima di nasconderla effettivamente prima della distruzione del frammento).
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();
}
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"
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
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;
}
}
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:windowTranslucentStatus
su 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
}
}
});
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.
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,
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!
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.