Tastiera morbida apri e chiudi ascoltatore in un'attività su Android


136

Ho un Activitydove ci sono 5 EditTexts. Quando l'utente fa clic sul primo EditText, la tastiera virtuale si apre per inserire un valore in essa. Voglio impostare un'altra Viewvisibilità su Gonequando si apre la tastiera software e anche quando l'utente fa clic sulla prima EditTexte anche quando la tastiera software si chiude dalla stessa EditTextpremendo il pulsante Indietro. Quindi voglio impostare Viewla visibilità di alcuni altri su visibile.

C'è qualche ascoltatore o callback o qualche hack per quando la tastiera virtuale si apre da un clic sul primo EditTextin Android?


1
No. Non ci sono tali ascoltatori. Ci sono hack per ottenere ciò che stai cercando di fare. Ecco un possibile approccio: come inviare un evento puntatore in Android .
Vikram,

@Vikram Non sto cercandotrying to detect the virtual keyboard height in Android.
N Sharma,

Lo so. Se passi attraverso il codice, vedrai come viene determinata l'altezza. Viene inviato un evento puntatore -> due casi => 1. se la tastiera è aperta => & se il puntatore Xe la Yposizione cadono sopra / sopra la tastiera => SecurityException=> decrementa Ye riprova => fino a quando non viene generata alcuna eccezione => il Yvalore corrente è l'altezza della tastiera. 2. se la tastiera non è aperta => no SecurityException.
Vikram,

Come si applica al tuo scenario? Invia un evento puntatore a diciamo 2/3 dell'altezza dello schermo. Se SecurityExceptionviene lanciato a = = la tastiera è aperta. Altrimenti, la tastiera è chiusa.
Vikram,

@Vikram Lo voglio solo per primo, EditTextnon per altro EditText. Come posso distinguerlo?
N Sharma,

Risposte:


91

Funziona solo quando la android:windowSoftInputModetua attività è impostata su adjustResizemanifest. Puoi usare un listener di layout per vedere se il layout di root della tua attività è ridimensionato dalla tastiera.

Uso qualcosa come la seguente classe base per le mie attività:

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

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

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

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

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

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

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

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

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

        keyboardListenersAttached = true;
    }

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

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

La seguente attività di esempio usa questa opzione per nascondere una vista quando viene mostrata la tastiera e mostrarla di nuovo quando la tastiera è nascosta.

Il layout XML:

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

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

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

        <!-- omitted for brevity -->

    </ScrollView>

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

        <!-- omitted for brevity -->

    </LinearLayout>

</LinearLayout>

E l'attività:

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

        attachKeyboardListeners();
    }

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

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

4
+1 Sì Questa è la soluzione perfetta per il mio problema.
N Sharma,

18
Ciao, hai usato getTop () su Window.ID_ANDROID_CONTENT. Get top non funziona per me. è sempre 0 qui, funziona come dovrebbe invece usare getHeight ().
Daniele Segato,

1
da dove vieni rootLayout = (ViewGroup) findViewById(R.id.rootLayout);?
CommonSenseCode

1
Non funziona per me per qualche motivo, chiama sempre ShowKeyboard o lo apro o lo chiudo. Sto usando findViewById (android.R.id.content), forse è questo il problema?
McSullivan D'Ander,

2
@tsig la tua soluzione +100 dipende dallo schermo specifico. Errore su tablet e telefoni hdpi. Ho usato la correzione come dieci percento dell'altezza del dispositivo. Ciò significa che se l'altezza della vista è inferiore a screenHeight - 10% la tastiera è aperta. altrimenti la tastiera è chiusa. Ecco il mio contentViewTop in onGlobalLayout: contentViewTop = (getWindow (). GetDecorView (). GetBottom () / 10)
ilker

94

Pezzo di torta con il fantastico libreria KeyboardVisibilityEvent

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

Crediti per Yasuhiro SHIMIZU


Questo non funzionerà perché le tastiere non hanno altezze statiche e l'altezza in questa libreria è impostata su 100 dpi.
milosmns,

@milosmns viene utilizzata un'altezza soglia di 100dp per il rilevamento della tastiera. Non si ipotizza l'altezza effettiva della tastiera
Nino van Hooff,

11
È ancora codificato. Multi finestra? Samsung vista divisa? Immagine in modalità immagine? Inoltre c'è una tastiera minima a una riga che cadrà sotto 100dp. Non ci sono proiettili d'argento qui ...
milosmns

1
Dato che non ci sono problemi per questo problema, questo sembra essere il più semplice da implementare e basta tornare al codice su cui si vuole effettivamente lavorare :)
Machine Tribe

1
Questa è di gran lunga la risposta migliore, totalmente affidabile su qualsiasi dispositivo
Pelanes,

70

Come ha sottolineato Vikram nei commenti, rilevare se la tastiera virtuale è mostrata o è scomparsa è possibile solo con alcuni brutti hack.

Forse è sufficiente impostare un listener di focus sull'edittext :

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

28
supponiamo che io faccia clic sul edittext, quindi setOnFocusChangeListenerverrà chiamato l'ascoltatore, quindi premo indietro, quindi chiude la tastiera e non ho fatto clic su altre viste, ora faccio di nuovo clic sullo stesso edittext che ha già lo stato attivo, quindi cosa accadrà?
N Sharma,

3
@Williams non ne sono del tutto sicuro, ma sospetto che onFocusChange()non verrà chiamato.
CommonGuy,

1
questa non è la mia domanda Ti preghiamo di leggere di nuovo la mia domanda: ho Attività in cui ci sono 5 EditText. Quando l'utente fa clic sul primo EditText, si apre la softkey per inserire un valore. Voglio impostare la visibilità dell'altro Visualizza su Andato quando la tastiera virtuale si apre quando l'utente fa clic sul primo EditText e quando la tastiera software si chiude dallo stesso EditText sul retro, quindi voglio impostare la visibilità dell'altra vista su visibile. C'è qualche ascoltatore o callback o qualche hack quando la tastiera virtuale è aperta al clic sul primo EditText in Android?
N Sharma,

4
I ragazzi non guardano questa risposta perché sta dicendo qualcosa di diverso anche se io non capisco.
N Sharma,

2
Ad ogni modo, non funziona per me ... Quando la tastiera software è nascosta, non c'è stato alcun cambio di focus sull'EditText ... Quindi non posso ricevere notifiche da questo Listener.
Licat Julius,

50

Per attività:

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

                activityRootView.getWindowVisibleDisplayFrame(r);

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

Per frammento:

    view = inflater.inflate(R.layout.live_chat_fragment, null);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                //r will be populated with the coordinates of your view that area still visible.
                view.getWindowVisibleDisplayFrame(r);

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

                }
            }
        });

3
L'ho usato per l'attività, ma invece di confrontare con la vista ho confrontato con le dimensioni dello schermo. funziona alla grande
Roee,

meglio sarebbe confrontare heightDiff con un'altezza in dp anziché in pixel. Può variare in modo significativo.
Leo Droidcoder il

Questo ha bisogno android:windowSoftInputMode="adjustResize"dell'in manifest?
LiuWenbin_NO.

android: windowSoftInputMode = "AdjustResize" android: configChanges = "orientamento | tastiera | tastiera
nascosta

Funziona per me, tuttavia, ho una domanda. Costa un sacco di risorse?
Licat Julius,

32

La risposta di Jaap non funzionerà per AppCompatActivity. Prendi invece l'altezza della barra di stato, della barra di navigazione ecc. E confronta con le dimensioni della finestra della tua app.

Così:

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

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

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

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

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

Sembra funzionare abbastanza bene con una sola eccezione: interruzioni in modalità schermo diviso. Altrimenti è fantastico.
MCLLC,

14

Puoi provarlo:

private void initKeyBoardListener() {
    // Минимальное значение клавиатуры. 
    // Threshold for minimal keyboard height.
    final int MIN_KEYBOARD_HEIGHT_PX = 150;
    // Окно верхнего уровня view. 
    // Top-level window decor view.
    final View decorView = getWindow().getDecorView();
    // Регистрируем глобальный слушатель. Register global layout listener.
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        // Видимый прямоугольник внутри окна. 
        // Retrieve visible rectangle inside window.
        private final Rect windowVisibleDisplayFrame = new Rect();
        private int lastVisibleDecorViewHeight;

        @Override
        public void onGlobalLayout() {
            decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
            final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();

            if (lastVisibleDecorViewHeight != 0) {
                if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
                    Log.d("Pasha", "SHOW");
                } else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
                    Log.d("Pasha", "HIDE");
                }
            }
            // Сохраняем текущую высоту view до следующего вызова.
            // Save current decor view height for the next call.
            lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
    });
}

Spasibo, Dolbik! :)
AlexS il

4

Puoi usare la mia funzione di estensione Rx (Kotlin).

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

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

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

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

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

    val visibleDisplayFrame = Rect()

    val viewTreeObserver = window.decorView.viewTreeObserver

    val onDrawListener = ViewTreeObserver.OnDrawListener {

        window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)

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

        val keyboardOpen = keyboardHeight >= approximateKeyboardHeight

        val hasChanged = isKeyboardOpen xor keyboardOpen

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

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

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

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

Esempio:

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

Non funziona per me. Non riesco a trovare metodi dip()egetScreenHeight()
Marcin Kunert,

@MarcinKunert è solo una funzione di estensione che ti aiuta a convertire i pixel in dp e ad ottenere l'altezza dello schermo. Se vuoi, posso darti un esempio di tali funzioni
Vlad,

GenericLifecycleObserver è obsoleto? qualche soluzione?
Zainal Fahrudin,

4

Il codice seguente funziona per me,

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

2

Se puoi, prova a estendere EditText e sovrascrivere il metodo 'onKeyPreIme'.

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

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

Come puoi estenderlo:

  1. Implementa l'ascolto onFocus e dichiara 'onKeyboardShown'
  2. dichiarare 'onKeyboardHidden'

Penso che il ricalcolo dell'altezza dello schermo non abbia successo al 100% come menzionato prima. Per essere chiari, l'override di "onKeyPreIme" non è chiamato su metodi "nascondi tastiera programmaticamente", MA se lo fai ovunque, dovresti fare lì la logica "onKeyboardHidden" e non creare soluzioni complete.


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

supponiamo che io faccia clic sul edittext, quindi setOnFocusChangeListenerverrà chiamato l'ascoltatore, quindi premo indietro, quindi chiude la tastiera e non ho fatto clic su altre viste, ora faccio di nuovo clic sullo stesso edittext che ha già lo stato attivo, quindi cosa accadrà?
N Sharma,

questa non è la mia domanda Ti preghiamo di leggere di nuovo la mia domanda: ho Attività in cui ci sono 5 EditText. Quando l'utente fa clic sul primo EditText, si apre la softkey per inserire un valore. Voglio impostare un'altra visibilità della vista su Fine quando la tastiera virtuale si apre quando l'utente fa clic sul primo EditText e quando la tastiera software si chiude dallo stesso EditText sul retro, quindi voglio impostare la visibilità dell'altra vista su visibile. C'è qualche ascoltatore o callback o qualche hack quando la tastiera virtuale è aperta al clic sul primo EditText in Android?
N Sharma,

1
Quando si preme indietro, si chiude la tastiera che l' onfocusascoltatore del tempo non chiama mai, è che non sto guardando ciò che stai suggerendo
N Sharma

1

Usa questa classe,

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

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

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

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

private View tempView; // reference to a focused EditText

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            int currentBottomLocation = getLayoutCoordinates();

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

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

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

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

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

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

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

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

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

}
}

In Android Manifest, android:windowSoftInputMode="adjustResize"è necessario.

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

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

@Override
public void onSoftKeyboardHide()  {
    // Code here
}

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

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

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

PS - Completamente preso da qui .


1

Nel caso di adjustResizee FragmentActivity la soluzione accettata da @Jaap non funziona per me.

Ecco la mia soluzione:

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

1

Un approccio diverso sarebbe quello di verificare quando l'utente ha smesso di digitare ...

Quando un oggetto TextEdit è attivo (l'utente sta digitando / stava scrivendo) è possibile nascondere le visualizzazioni (ascoltatore attivo)

e usa un Handler + Runnable e un listener di cambio testo per chiudere la tastiera (indipendentemente dalla sua visibilità) e mostrare le visualizzazioni dopo qualche ritardo.

La cosa principale da cercare sarebbe il ritardo che utilizzi, che dipenderà dal contenuto di questi TextEdits.

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

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

        // SET VIEWS VISIBLE
    }
};

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

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

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

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

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

1

Questo codice funziona benissimo

usa questa classe per la vista di root:

public class KeyboardConstraintLayout extends ConstraintLayout {

private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;

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

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

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

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (!isInEditMode()) {
        Activity activity = (Activity) getContext();
        @SuppressLint("DrawAllocation")
        Rect rect = new Rect();
        getWindowVisibleDisplayFrame(rect);

        int statusBarHeight = rect.top;
        int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;

        if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
            if (keyboardHeight > minKeyboardHeight) {
                if (!isShow) {
                    isShow = true;
                    keyboardListener.onKeyboardVisibility(true);
                }
            }else {
                if (isShow) {
                    isShow = false;
                    keyboardListener.onKeyboardVisibility(false);
                }
            }
        }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

public boolean isShowKeyboard() {
    return isShow;
}

public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
    this.targetEditText = targetEditText;
    this.keyboardListener = keyboardListener;
}

public interface KeyboardListener {
    void onKeyboardVisibility (boolean isVisible);
}

}

e imposta il listener da tastiera in attività o frammento:

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

        }
    });


0

Purtroppo non ho una reputazione sufficientemente elevata per commentare la risposta di Jaap van Hengstum. Ma ho letto alcuni commenti di persone, avendo il problema che contentViewTopè sempre 0e quelloonShowKeyboard(...) viene sempre chiamato.

Ho avuto lo stesso problema e ho capito il problema che avevo. Ho usato un AppCompatActivityinvece di un "normale" Activity. In questo caso si Window.ID_ANDROID_CONTENTriferisce a an ContentFrameLayoute non a FrameLayoutcon il valore superiore corretto. Nel mio caso andava bene usare il 'normale' Activity, se devi usare un altro tipo di attività (ho appena testato il AppCompatActivity, forse è anche un problema con altri tipi di attività come il FragmentActivity), devi accedere al FrameLayout, che è un antenato del ContentFrameLayout.


0

quando la tastiera mostra

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

è vero, altrimenti nascondi


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

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

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

        if (contentHeight > 0) {

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

            prevContentHeight = contentHeight;
        }
    }
};

Ho modificato un po 'la risposta accettata dallo Jaap. Ma nel mio caso, ci sono alcuni presupposti come android:windowSoftInputMode=adjustResizee la tastiera non viene visualizzata all'inizio all'avvio dell'app. Inoltre, presumo che lo schermo in relazione corrisponda all'altezza del genitore.

contentHeight > 0questo controllo mi consente di sapere se la schermata relativa è nascosta o mostrata per applicare l'ascolto di eventi da tastiera per questa schermata specifica. Inoltre passo la vista del layout della schermata relativa nel metodo attachKeyboardListeners(<your layout view here>)della mia attività principale onCreate(). Ogni volta che cambia l'altezza della schermata relativa, la salvoprevContentHeight variabile per controllare in seguito se la tastiera è mostrata o nascosta.

Per me, finora ha funzionato abbastanza bene. Spero che funzioni anche per gli altri.


0

La risposta di "Jaap van Hengstum" funziona per me, ma non è necessario impostare "android: windowSoftInputMode" come ha appena detto!

L'ho reso più piccolo (ora rileva solo ciò che voglio, in realtà un evento su mostrare e nascondere la tastiera):

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

private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;

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

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

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

    keyboardListenersAttached = true;
}

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

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

e non dimenticare di aggiungere questo

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

0

Funzionerà senza alcuna necessità di cambiare la tua attività android:windowSoftInputMode

passaggio 1: estendi la classe EditText e sovrascrivi questi due:

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

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

passaggio 2: crea questi due nella tua attività:

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

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

*** Ricorda che per rendere il clearFocuslavoro devi rendere focalizzabile il genitore o il primo figlio nella gerarchia dei genitori.

    setFocusableInTouchMode(true);
    setFocusable(true);

0

Questo non funziona come desiderato ...

... ho visto molti usare i calcoli delle dimensioni per controllare ...

Volevo determinare se fosse aperto o meno e l' ho trovato isAcceptingText()

quindi questo in realtà non risponde alla domanda in quanto non affronta l'apertura o la chiusura piuttosto come se fosse aperto o chiuso, quindi è un codice correlato che può aiutare gli altri in vari scenari ...

in un'attività

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

in un frammento

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

    }

0

verificare con il codice seguente:

CODICE XML:

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

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


</android.support.constraint.ConstraintLayout>

CODICE JAVA:

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




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


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

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

    }


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

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


    @Override
    public void onDetach() {
        super.onDetach();
        if(onGlobalLayoutListener != null) {
            activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
        }
    }
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.