Miękka klawiatura otwiera i zamyka słuchacza w działaniu w systemie Android


136

Mam Activitygdzie jest 5 EditTexts. Gdy użytkownik kliknie pierwszą EditText, klawiatura programowa otwiera się, aby wprowadzić do niej jakąś wartość. Chcę ustawić Viewwidoczność innych osób , Gonekiedy miękka klawiatura otwiera się, a także kiedy użytkownik klika pierwszą, EditTexta także kiedy miękka klawiatura zamyka się z tego samego EditTextprzycisku Wstecz. Następnie chcę ustawić Viewwidoczność innych osób jako widoczną.

Czy jest jakiś słuchacz, oddzwonienie lub jakikolwiek hack, gdy miękka klawiatura otwiera się po kliknięciu pierwszej EditTextw Androidzie?


1
Nie. Nie ma takich słuchaczy. Tam hacki do osiągnięcia tego, co próbujesz. Oto możliwe podejście: Jak wysłać zdarzenie wskaźnika w systemie Android .
Vikram,

@Vikram Nie szukamtrying to detect the virtual keyboard height in Android.
N Sharma

Wiem. Jeśli przejrzysz kod, zobaczysz, jak określana jest wysokość. Wysyłane jest zdarzenie wskaźnika -> dwa przypadki => 1. jeśli klawiatura jest otwarta => & jeśli wskaźnik Xi Ypołożenie wskaźnika przypadają na klawiaturę / nad nią => SecurityException=> zmniejsz Yi spróbuj ponownie => dopóki nie zostanie zgłoszony żaden wyjątek => aktualna Ywartość to wysokość klawiatury. 2. jeśli klawiatura nie jest otwarta => nie SecurityException.
Vikram,

Jak to się ma do twojego scenariusza? Wyślij zdarzenie wskaźnika na, powiedzmy, 2/3 wysokości ekranu. Jeśli SecurityExceptionzostanie rzucony => klawiatura jest otwarta. W przeciwnym razie klawiatura jest zamknięta.
Vikram,

@Vikram Chcę tylko tego, a EditTextnie innego EditText. Jak mogę to odróżnić?
N Sharma,

Odpowiedzi:


91

Działa to tylko wtedy, gdy android:windowSoftInputModeTwoja aktywność jest ustawiona adjustResizew manifeście. Możesz użyć nasłuchiwania układu, aby sprawdzić, czy rozmiar układu głównego działania jest zmieniany za pomocą klawiatury.

Do moich działań używam czegoś podobnego do następującej klasy bazowej:

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

Poniższe przykładowe działanie wykorzystuje to, aby ukryć widok, gdy klawiatura jest pokazana i pokazać go ponownie, gdy klawiatura jest ukryta.

Układ 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>

I aktywność:

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 Tak. To idealne rozwiązanie mojego problemu.
N Sharma

18
Cześć, użyłeś getTop () w Window.ID_ANDROID_CONTENT. Get top nie działa dla mnie. zawsze jest tutaj 0, działa tak, jakby zamiast tego powinno używać metody getHeight ().
Daniele Segato

1
gdzie można dostać rootLayout = (ViewGroup) findViewById(R.id.rootLayout);od?
CommonSenseCode

1
Z jakiegoś powodu nie działa dla mnie, zawsze wywołuje onShowKeyboard albo otwieram go, albo zamykam. Używam findViewById (android.R.id.content), może w tym tkwi problem?
McSullivan D'Ander

2
@tsig Twoje rozwiązanie +100 zależy od konkretnego ekranu. Niepowodzenie na tabletach i telefonach HDpi. Zastosowałem korektę jako dziesięć procent wysokości urządzenia. Oznacza to, że jeśli wysokość widoku jest niższa niż screenHeight - 10%, klawiatura jest otwarta. w przeciwnym razie klawiatura jest zamknięta. Oto moja contentViewTop w onGlobalLayout: contentViewTop = (getWindow (). GetDecorView (). GetBottom () / 10)
ilker

94

To bułka z masłem dzięki niesamowitej bibliotece KeyboardVisibilityEvent

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

Kredyty dla Yasuhiro SHIMIZU


To nie zadziała, ponieważ klawiatury nie mają statycznych wysokości, a wysokość w tej bibliotece jest ustawiona na 100 dp.
milosmns

@milosmns do wykrywania klawiatury używana jest wysokość progu 100 dp. Nie ma żadnych założeń co do faktycznej wysokości klawiatury
Nino van Hooff

11
Nadal jest zakodowany. Wiele okien? Podzielony pogląd Samsunga? Obraz w trybie obrazu? Istnieje również minimalna klawiatura jednorzędowa, która spadnie poniżej 100 dpi. Nie ma tu srebrnej kuli ...
milosmns

1
Ponieważ nie ma żadnego haczyka na ten problem, wydaje się, że jest to najłatwiejsze do zaimplementowania i po prostu wróć do kodu, nad którym chcesz popracować :)
Machine Tribe

1
To zdecydowanie najlepsza odpowiedź, całkowicie niezawodna na każdym urządzeniu
Pelanes,

69

Jak zauważył Vikram w komentarzach, wykrycie, czy klawiatura programowa jest wyświetlana, czy zniknęła, jest możliwe tylko w przypadku niektórych brzydkich hacków.

Może wystarczy ustawić fokus słuchacza na edittext :

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

27
przypuśćmy, że kliknę na edittext, a następnie setOnFocusChangeListenerzostanie wywołany słuchacz, a następnie naciśnę, a następnie zamknie klawiaturę i ale nie kliknę innych widoków, teraz ponownie klikam ten sam edytorxt, który już jest aktywny, to co się stanie?
N Sharma

3
@Williams Nie jestem do końca pewien, ale podejrzewam, że onFocusChange()to nie zostanie wywołane.
CommonGuy

1
to nie jest moje pytanie. Przeczytaj ponownie moje pytanie - mam Aktywność, w której jest 5 EdytujTekst. Kiedy użytkownik kliknie najpierw EditText, a następnie klawiatura programowa otworzy się, aby wprowadzić do niej jakąś wartość. Chcę ustawić inną widoczność widoku na Gone, gdy klawiatura programowa jest otwarta, gdy użytkownik kliknie pierwszy tekst EditText i gdy klawiatura programowa zamknie się z tego samego EditText po naciśnięciu wstecznego, chcę ustawić inną widoczność widoku jako widoczną. Czy jest jakiś słuchacz, oddzwonienie lub jakikolwiek hack, gdy miękka klawiatura jest otwarta po kliknięciu pierwszego EditText w Androidzie?
N Sharma

4
Faceci nie szukają tej odpowiedzi, ponieważ mówi coś innego, nawet ja nie rozumiem.
N Sharma

2
W każdym razie to nie działa dla mnie ... Kiedy klawiatura programowa jest ukryta, nie nastąpiła żadna zmiana fokusu w EditText ... Więc nie mogę otrzymać powiadomienia od tego Listenera.
Licat Julius

50

Do aktywności:

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

Dla fragmentu:

    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
Użyłem go do aktywności, ale zamiast porównywać z widokiem i w porównaniu z rozmiarem ekranu. działa świetnie
Roee

lepiej byłoby porównać heightDiff z wysokością w dp, a nie w pikselach. Może się znacznie różnić.
Leo Droidcoder

Czy to wymaga android:windowSoftInputMode="adjustResize"manifestu?
LiuWenbin_NO.

android: windowSoftInputMode = "adjustResize" android: configChanges = "orientacja | klawiatura | klawiaturaHidden"
M Singh Karnawat

U mnie to działa, ale mam pytanie. Czy kosztuje dużo zasobów?
Licat Julius

32

Odpowiedź Jaapa nie zadziała w przypadku AppCompatActivity. Zamiast tego uzyskaj wysokość paska stanu i paska nawigacji itp. I porównaj z rozmiarem okna aplikacji.

Tak jak to:

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

Wydaje się działać całkiem nieźle z jednym wyjątkiem: przerwy w trybie podzielonego ekranu. W przeciwnym razie jest świetnie.
MCLLC

14

Możesz tego spróbować:

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

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

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

Spasibo, Dolbik! :)
AlexS,

4

Możesz użyć mojej funkcji rozszerzenia 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()
}

Przykład:

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

Nie działa na mnie. Nie mogę znaleźć metod dip()igetScreenHeight()
Marcin Kunert

@MarcinKunert to tylko funkcja rozszerzenia, która pomaga konwertować piksele na dp i uzyskać wysokość ekranu. Jeśli chcesz, mogę podać przykład takich funkcji
Vlad

GenericLifecycleObserver jest przestarzały? jakieś rozwiązanie?
Zainal Fahrudin

4

Poniższy kod działa dla mnie,

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

Jeśli możesz, spróbuj rozszerzyć EditText i zastąpić metodę „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);
}

Jak możesz to przedłużyć:

  1. Zaimplementuj nasłuchiwanie onFocus i zadeklaruj „onKeyboardShown”
  2. zadeklaruj „onKeyboardHidden”

Myślę, że ponowne obliczenie wysokości ekranu nie jest w 100% skuteczne, jak wspomniano wcześniej. Dla jasności, nadpisywanie „onKeyPreIme” nie jest wywoływane w metodach „ukrywania programowo miękkiej klawiatury”, ALE jeśli robisz to gdziekolwiek, powinieneś zastosować tam logikę „onKeyboardHidden” i nie tworzyć kompleksowych rozwiązań.


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

przypuśćmy, że kliknę na edittext, a następnie setOnFocusChangeListenerzostanie wywołany słuchacz, a następnie naciśnę, a następnie zamknie klawiaturę i ale nie kliknę innych widoków, teraz ponownie klikam ten sam edittext, który już jest aktywny, co się stanie?
N Sharma

to nie jest moje pytanie. Przeczytaj ponownie moje pytanie - mam Aktywność, w której jest 5 EdytujTekst. Kiedy użytkownik kliknie najpierw EditText, a następnie klawiatura programowa otworzy się, aby wprowadzić do niej jakąś wartość. Chcę ustawić inną widoczność widoku na Gone, gdy klawiatura programowa jest otwarta, gdy użytkownik kliknie pierwszy tekst EditText i gdy klawiatura programowa zamknie się z tego samego EditText po naciśnięciu wstecznego, chcę ustawić inną widoczność widoku jako widoczną. Czy jest jakiś słuchacz, oddzwonienie lub jakikolwiek hack, gdy miękka klawiatura jest otwarta po kliknięciu pierwszego EditText w Androidzie?
N Sharma

1
Po naciśnięciu wtedy to odrzucenie klawiatury, która razem onfocussłuchacz nigdy nazywają to jest ja nie patrząc którym sugeruje
N Sharma

1

Użyj tej klasy,

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

}
}

W Android Manifest, android:windowSoftInputMode="adjustResize"jest konieczne.

/*
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 - Całkowicie wzięte stąd .


1

W przypadku adjustResizei FragmentActivity zaakceptowane rozwiązanie @Jaap nie działa dla mnie.

Oto moje rozwiązanie:

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

Innym podejściem byłoby sprawdzenie, kiedy użytkownik przestał pisać ...

Kiedy TextEdit jest aktywny (użytkownik pisze / pisał), można ukryć widoki (fokus nasłuchujący)

i użyj Handler + Runnable i nasłuchiwania zmian tekstu, aby zamknąć klawiaturę (niezależnie od jej widoczności) i wyświetlić widoki z pewnym opóźnieniem.

Najważniejszą rzeczą, na którą należy zwrócić uwagę, jest opóźnienie, którego używasz, które zależy od zawartości tych 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

Ten kod działa świetnie

użyj tej klasy jako widoku głównego:

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

}

i ustaw nasłuchiwanie klawiatury na aktywność lub fragment:

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

        }
    });


0

Niestety nie mam wystarczająco dobrej reputacji, aby komentować odpowiedź Jaapa van Hengstuma. Ale przeczytałem kilka komentarzy ludzi, którzy mają problem, który contentViewTopjest zawsze 0i onShowKeyboard(...)zawsze się nazywa.

Miałem ten sam problem i zorientowałem się, jaki mam problem. Użyłem AppCompatActivityzamiast „normalnego” Activity. W tym przypadku Window.ID_ANDROID_CONTENTodnosi się do an, ContentFrameLayouta nie do FrameLayoutz prawą górną wartością. W moim przypadku to było w porządku do korzystania z „normalną” Activity, jeśli trzeba użyć innej działalności typu (ja tylko testowałem AppCompatActivity, może to również problem z innymi acitivy typów, takich jak wybrany FragmentActivity), trzeba uzyskać dostęp do FrameLayout, który jest przodek ContentFrameLayout.


0

kiedy klawiatura pokazuje

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

jest prawdą, inaczej ukryj


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

Zmodyfikowałem nieco zaakceptowaną odpowiedź Jaapa. Ale w moim przypadku założeń jest kilka takich jak android:windowSoftInputMode=adjustResizei klawiatura nie pojawia się na początku po uruchomieniu aplikacji. Zakładam również, że ekran pod względem odpowiada wzrostowi rodzica.

contentHeight > 0to sprawdzenie pozwala mi wiedzieć, czy odpowiedni ekran jest ukryty lub pokazany, aby zastosować nasłuchiwanie zdarzeń klawiatury dla tego konkretnego ekranu. Przekazuję również widok układu odpowiedniego ekranu w metodzie attachKeyboardListeners(<your layout view here>)mojej głównej działalności onCreate(). Za każdym razem, gdy zmienia się wysokość danego ekranu, zapisuję go jako prevContentHeightzmienną, aby później sprawdzić, czy klawiatura jest pokazana lub ukryta.

Dla mnie jak dotąd działało to całkiem nieźle. Mam nadzieję, że u innych też działa.


0

Odpowiedź "Jaapa van Hengstuma" działa dla mnie, ale nie ma potrzeby ustawiania "android: windowSoftInputMode", jak właśnie powiedział!

Zmniejszyłem to (teraz po prostu wykrywa to, czego chcę, a właściwie zdarzenie dotyczące pokazywania i ukrywania klawiatury):

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

i po prostu nie zapomnij o tym dodać

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

0

To zadziała bez konieczności zmiany Twojej aktywności android:windowSoftInputMode

krok 1: rozszerz klasę EditText i nadpisz te dwa:

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

krok 2: utwórz te dwa w swoim działaniu:

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
}

*** pamiętaj, że aby clearFocuspraca była możliwa, musisz sprawić, by rodzic lub pierwsze dziecko w hierarchii nadrzędnej podlegało koncentracji.

    setFocusableInTouchMode(true);
    setFocusable(true);

0

To nie działa zgodnie z oczekiwaniami ...

... widziałem wiele obliczeń rozmiaru użytkowego do sprawdzenia ...

Chciałem ustalić, czy było otwarte, czy nie i znalazłem isAcceptingText()

więc to naprawdę nie odpowiada na pytanie, ponieważ nie dotyczy otwierania lub zamykania, a raczej jest otwarte lub zamknięte, więc jest to powiązany kod, który może pomóc innym w różnych scenariuszach ...

w działaniu

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

we fragmencie

    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

sprawdź poniższy kod:

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

KOD 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);
        }
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.