Bàn phím mềm mở và đóng trình nghe trong một hoạt động trong Android


136

Tôi có một Activitynơi có 5 EditTexts. Khi người dùng nhấp vào đầu tiên EditText, bàn phím mềm sẽ mở để nhập một số giá trị trong đó. Tôi muốn đặt Viewkhả năng hiển thị của một số người khác Gonekhi bàn phím mềm mở và cả khi người dùng nhấp vào lần đầu tiên EditTextvà cả khi bàn phím mềm đóng từ cùng EditTexttrên nút nhấn trở lại. Sau đó, tôi muốn đặt một số Viewkhả năng hiển thị của người khác thành hiển thị.

Có bất kỳ người nghe hoặc gọi lại hoặc bất kỳ hack cho khi bàn phím mềm mở từ một lần nhấp vào lần đầu tiên EditTexttrong Android?


1
Không. Không có người nghe như vậy. Có những hack để đạt được những gì bạn đang cố gắng. Đây là một cách tiếp cận có thể: Cách gửi sự kiện con trỏ trong Android .
Vikram

@Vikram Tôi không tìm kiếmtrying to detect the virtual keyboard height in Android.
N Sharma

Tôi biết. Nếu bạn đi qua mã, bạn sẽ thấy chiều cao được xác định. Một sự kiện con trỏ đang được gửi -> hai trường hợp => 1. nếu bàn phím đang mở => & nếu vị trí Xvà con trỏ Yrơi vào / trên bàn phím => SecurityException=> giảm Yvà thử lại => cho đến khi không có ngoại lệ nào được ném => Ygiá trị hiện tại là chiều cao bàn phím. 2. nếu bàn phím không mở => không SecurityException.
Vikram

Làm thế nào để áp dụng cho kịch bản của bạn? Gửi một sự kiện con trỏ tại giả sử 2/3 chiều cao của màn hình. Nếu a SecurityExceptionbị ném => bàn phím đang mở. Khác, bàn phím được đóng lại.
Vikram

@Vikram Tôi chỉ muốn cái này trước tiên EditTextchứ không phải cái khác EditText. Làm thế nào tôi có thể phân biệt điều này?
N Sharma

Câu trả lời:


91

Điều này chỉ hoạt động khi android:windowSoftInputModehoạt động của bạn được đặt thành adjustResizetrong bảng kê khai. Bạn có thể sử dụng trình lắng nghe bố cục để xem bố cục gốc của hoạt động có bị thay đổi kích thước bằng bàn phím không.

Tôi sử dụng một cái gì đó giống như lớp cơ sở sau cho các hoạt động của mình:

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

Hoạt động ví dụ sau sử dụng điều này để ẩn chế độ xem khi bàn phím được hiển thị và hiển thị lại khi bàn phím bị ẩn.

Bố cục 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>

Và hoạt động:

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 Yeah Đây là giải pháp hoàn hảo cho vấn đề của tôi.
N Sharma

18
Xin chào, bạn đã sử dụng getTop () trên Window.ID_ANDROID_CONTENT. Nhận đầu không làm việc cho tôi. luôn luôn là 0 ở đây, nó hoạt động giống như được sử dụng getHeight ().
Daniele Segato

1
bạn rootLayout = (ViewGroup) findViewById(R.id.rootLayout);đến từ đâu
CommonSenseCode

1
Không hoạt động đối với tôi vì một số lý do, nó luôn gọi onShowPal hoặc tôi mở hoặc đóng nó. Tôi đang sử dụng findViewById (android.R.id.content), có lẽ đó là vấn đề?
McSullivan D'Ander

2
@tsig giải pháp +100 của bạn phụ thuộc vào màn hình cụ thể. Thất bại trên máy tính bảng và điện thoại hdpi. Tôi đã sử dụng hiệu chỉnh là mười phần trăm chiều cao thiết bị. Điều đó có nghĩa là nếu chiều cao của màn hình thấp hơn màn hình - 10% bàn phím được mở. bàn phím khác được đóng lại. Đây là contentViewTop của tôi trong onGlobalLayout: contentViewTop = (getWindow (). GetDecorView (). GetBottom () / 10)
ilker

93

Miếng bánh với thư viện KeyboardVisibilityEvent tuyệt vời

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

Tín dụng cho Yasuhiro SHIMIZU


Điều này sẽ không hoạt động vì bàn phím không có chiều cao tĩnh và chiều cao trong thư viện này được đặt thành 100dp.
milosmns

@milosmns chiều cao ngưỡng 100dp được sử dụng để phát hiện bàn phím. Không có giả định nào về chiều cao bàn phím thực tế
Nino van Hooff

11
Nó vẫn còn cứng. Cửa sổ đa nhiệm? Samsung chia đôi góc nhìn? Hình ảnh ở chế độ hình ảnh? Ngoài ra, có một bàn phím một hàng tối thiểu sẽ nằm dưới 100dp. Không có viên đạn bạc nào ở đây ...
milosmns

1
Vì không có vấn đề gì cho vấn đề này, đây có vẻ là cách dễ thực hiện nhất và chỉ cần quay lại mã mà bạn thực sự muốn làm việc :)
Machine Tribe

1
Đây là câu trả lời hay nhất, hoàn toàn đáng tin cậy trên mọi thiết bị
Pelanes

69

Như Vikram đã chỉ ra trong các bình luận, việc phát hiện xem phím chức năng được hiển thị hay đã biến mất chỉ có thể với một số hack xấu xí.

Có lẽ nó là đủ để đặt một trình nghe tập trung vào edittext :

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

27
Giả sử tôi nhấp vào edittext thì nó setOnFocusChangeListenersẽ được gọi sau đó tôi nhấn lại sau đó nó đóng bàn phím & nhưng không nhấp vào các chế độ xem khác, bây giờ tôi lại nhấp vào cùng edittext đã có tiêu điểm rồi chuyện gì sẽ xảy ra?
N Sharma

3
@Williams Tôi không hoàn toàn chắc chắn, nhưng tôi nghi ngờ rằng nó onFocusChange()sẽ không được gọi.
CommonGuy

1
đây không phải là câu hỏi của tôi Vui lòng đọc lại câu hỏi của tôi - Tôi có Hoạt động trong đó có 5 EditText. Khi người dùng nhấp vào EditText đầu tiên, sau đó mở bàn phím mềm để nhập một số giá trị trong đó. Tôi muốn đặt chế độ hiển thị Chế độ xem khác thành Đã qua khi bàn phím mềm mở khi người dùng nhấp vào EditText đầu tiên và khi bàn phím mềm đóng từ cùng EditText khi nhấn lại thì tôi muốn đặt chế độ hiển thị Chế độ xem khác hiển thị. Có bất kỳ người nghe hoặc gọi lại hoặc bất kỳ hack khi bàn phím mềm mở khi nhấp vào EditText đầu tiên trong Android?
N Sharma

4
Các chàng trai đừng nhìn câu trả lời này bởi vì anh ta đang nói điều gì đó khác biệt thậm chí tôi không hiểu.
N Sharma

2
Dù sao, nó không hoạt động đối với tôi ... Khi bàn phím mềm bị ẩn, không có bất kỳ thay đổi tiêu điểm nào xảy ra trên EditText ... Vì vậy, tôi không thể nhận được thông báo từ Trình nghe này.
Licat Julius

50

Đối với hoạt động:

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

Đối với mảnh vỡ:

    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
Đã sử dụng nó cho hoạt động, nhưng thay vì so sánh với view i so với kích thước màn hình. hoạt động tuyệt vời
Roee

tốt hơn là so sánh heightDiff với chiều cao tính bằng dp thay vì pixel. Nó có thể thay đổi đáng kể.
Leo Droidcoder

Điều này có cần android:windowSoftInputMode="adjustResize"trong bảng kê khai?
LiuWenbin_NO.

android: windowSoftInputMode = "điều chỉnhResize" android: configChanges = "direction | keyboard | keyboardHidden"
M Singh Karnaw 16/03/18

Nó làm việc cho tôi, vẫn còn, tôi có một câu hỏi. Có phải chi phí rất nhiều nguồn lực?
Licat Julius

32

Câu trả lời của Jaap sẽ không hoạt động cho AppCompatActivity. Thay vào đó, hãy lấy chiều cao của Thanh trạng thái và thanh Điều hướng, v.v. và so sánh với kích thước cửa sổ của ứng dụng của bạn.

Thích như vậy:

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

Có vẻ hoạt động khá tốt với một ngoại lệ: nghỉ ở chế độ chia đôi màn hình. Nếu không thì thật tuyệt.
MCLLC

14

Bạn có thể thử nó:

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

Bạn có thể sử dụng chức năng mở rộng Rx của tôi (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()
}

Thí dụ:

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

Không làm việc cho tôi. Không thể tìm thấy phương pháp dip()getScreenHeight()
Marcin Kunert

@MarcinKunert chỉ là chức năng mở rộng giúp bạn chuyển đổi pixel thành dp và lấy chiều cao màn hình. Nếu bạn muốn, tôi có thể cho bạn ví dụ về các chức năng như vậy
Vlad

GenericLifecyclObserver bị phản đối? bất kì giải pháp nào?
Zainal Fahrudin

4

Mã dưới đây đang làm việc cho tôi,

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

Nếu bạn có thể, hãy thử mở rộng EditText và ghi đè phương thức '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);
}

Làm thế nào bạn có thể mở rộng nó:

  1. Thực hiện nghe trên Focus và khai báo 'onPalShown'
  2. tuyên bố 'onPalHidden'

Tôi nghĩ rằng việc tính toán lại chiều cao màn hình không thành công 100% như đã đề cập trước đó. Để rõ ràng, việc ghi đè 'onKeyPreIme' không được gọi là 'ẩn bàn phím mềm theo phương pháp', NHƯNG nếu bạn đang làm việc đó ở bất cứ đâu, bạn nên thực hiện logic 'onPalHidden' ở đó và không tạo ra giải pháp toàn diện.


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

Giả sử tôi nhấp vào edittext thì nó setOnFocusChangeListenersẽ được gọi sau đó tôi nhấn lại sau đó nó đóng bàn phím & nhưng không nhấp vào các chế độ xem khác, bây giờ tôi lại nhấp vào cùng edittext đã có tiêu điểm rồi chuyện gì sẽ xảy ra?
N Sharma

đây không phải là câu hỏi của tôi Vui lòng đọc lại câu hỏi của tôi - Tôi có Hoạt động trong đó có 5 EditText. Khi người dùng nhấp vào EditText đầu tiên, sau đó mở bàn phím mềm để nhập một số giá trị trong đó. Tôi muốn đặt chế độ hiển thị Chế độ xem khác thành Đã qua khi bàn phím mềm mở khi người dùng nhấp vào EditText đầu tiên và khi bàn phím mềm đóng từ cùng EditText khi nhấn lại thì tôi muốn đặt chế độ hiển thị Chế độ xem khác hiển thị. Có bất kỳ người nghe hoặc gọi lại hoặc bất kỳ hack khi bàn phím mềm mở khi nhấp vào EditText đầu tiên trong Android?
N Sharma

1
Khi bạn nhấn lại thì nó sẽ tắt bàn phím mà onfocusngười nghe thời gian không bao giờ gọi đó là tôi đang tìm kiếm mà bạn không gợi ý
N Sharma

1

Sử dụng lớp này,

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

}
}

Trong Android Manifest, android:windowSoftInputMode="adjustResize"là cần thiết.

/*
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 - Hoàn toàn được lấy từ đây .


1

Đối với trường hợp adjustResizevà giải pháp được chấp nhận của FragmentActivity từ @Jaap không phù hợp với tôi.

Đây là giải pháp của tôi:

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

Một cách tiếp cận khác sẽ là kiểm tra khi người dùng ngừng gõ ...

Khi TextEdit nằm trong tiêu điểm (người dùng đang / đang gõ), bạn có thể ẩn chế độ xem (trình nghe tập trung)

và sử dụng Handler + Runnable và trình nghe thay đổi văn bản để đóng bàn phím (bất kể tầm nhìn của nó) và hiển thị các chế độ xem sau một số độ trễ.

Điều chính cần chú ý là độ trễ bạn sử dụng, điều này phụ thuộc vào nội dung của các TextEdits này.

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

Mã này hoạt động rất tốt

sử dụng lớp này để xem 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);
}

}

và đặt trình nghe bàn phím trong hoạt động hoặc đoạn:

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

        }
    });


0

Thật không may, tôi không có uy tín đủ cao để nhận xét về câu trả lời của Jaap van Hengstum. Nhưng tôi đã đọc một vài bình luận của mọi người, có vấn đề contentViewTopluôn luôn 0và điều đó onShowKeyboard(...)luôn được gọi.

Tôi đã có cùng một vấn đề và tìm ra vấn đề tôi có. Tôi đã sử dụng AppCompatActivitythay vì "bình thường" Activity. Trong trường hợp này Window.ID_ANDROID_CONTENTđề cập đến một ContentFrameLayoutvà không phải FrameLayoutvới giá trị trên cùng bên phải. Trong trường hợp của tôi, sử dụng 'bình thường' là ổn Activity, nếu bạn phải sử dụng loại hoạt động khác (tôi mới thử nghiệm AppCompatActivity, có thể đó cũng là một vấn đề với các loại hình hoạt động khác như FragmentActivity), bạn phải truy cập vào FrameLayout, đó là một tổ tiên của ContentFrameLayout.


0

khi bàn phím hiển thị

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

là sự thật, ẩn khác


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

Tôi đã sửa đổi câu trả lời được chấp nhận của Jaap một chút. Nhưng trong trường hợp của tôi, có một vài giả định như android:windowSoftInputMode=adjustResizevà bàn phím không hiển thị ngay từ đầu khi ứng dụng khởi động. Ngoài ra, tôi cho rằng màn hình liên quan đến chiều cao của cha mẹ.

contentHeight > 0kiểm tra này cung cấp cho tôi để biết nếu màn hình liên quan bị ẩn hoặc hiển thị để áp dụng sự kiện bàn phím cho màn hình cụ thể này. Ngoài ra, tôi vượt qua chế độ xem bố cục của màn hình liên quan trong phương thức attachKeyboardListeners(<your layout view here>)hoạt động chính của mình onCreate(). Mỗi khi chiều cao của màn hình liên quan thay đổi, tôi lưu nó vào prevContentHeightbiến để kiểm tra xem bàn phím có được hiển thị hay ẩn đi không.

Đối với tôi, cho đến nay nó đã hoạt động khá tốt. Tôi hy vọng rằng nó cũng làm việc cho những người khác.


0

Câu trả lời của "Jaap van Hengstum" đang hoạt động với tôi, nhưng không cần thiết phải đặt "android: windowSoftInputMode" như ông vừa nói!

Tôi đã làm cho nó nhỏ hơn (bây giờ nó chỉ phát hiện những gì tôi muốn, thực sự là một sự kiện hiển thị và ẩn bàn phím):

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

và đừng quên thêm điều này

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

0

Điều này sẽ hoạt động mà không cần thay đổi hoạt động của bạn android:windowSoftInputMode

Bước 1: mở rộng lớp EditText và ghi đè hai:

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

Bước 2: tạo hai cái này trong hoạt động của bạn:

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
}

*** hãy nhớ để thực hiện clearFocuscông việc, bạn phải làm cho cha mẹ hoặc con đầu tiên trong hệ thống phân cấp cha mẹ có thể tập trung.

    setFocusableInTouchMode(true);
    setFocusable(true);

0

Điều này không hoạt động như mong muốn ...

... đã thấy nhiều tính toán kích thước sử dụng để kiểm tra ...

Tôi muốn xác định xem nó có mở hay không và tôi đã tìm thấy isAcceptingText()

Vì vậy, điều này thực sự không trả lời câu hỏi vì nó không giải quyết việc mở hoặc đóng hơn là mở hoặc đóng để nó là mã liên quan có thể giúp người khác trong các tình huống khác nhau ...

trong một hoạt động

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

trong một mảnh

    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

kiểm tra với đoạn mã dưới đây:

MÃ 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>

MÃ 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);
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.