Implementation method of Android monitoring keyboard state and obtaining keyboard height

preface

Android has not yet provided an appropriate API to get / monitor the status and height of the keyboard, and we often have this requirement

In a recent project, on the UGC page, I need to display a text prompt on the top of the keyboard, close to the keyboard, and hide it when the keyboard disappears. Therefore, I need to monitor the opening / closing of the soft keyboard and obtain its height

ViewTreeObserver

The Android framework provides a viewtreeobserver class, which is an observer class of the view tree. A series of public interfaces are defined in the viewtreeobserver class. When a view attaches to a window, a viewtreeobserver object will be created. In this way, when the view tree of a view changes, a method of the object will be called to notify each registered listener of the event.

Ongloballayoutlistener is one of many interfaces defined in viewtreeobserver. It is used to listen for changes in the global layout of a view tree or changes in the visual state of a view in the view tree. When the soft keyboard changes from hidden to displayed or from displayed to hidden, the dispatchongloballayout() method of the viewtreeobserver object in all existing views in the current layout will be called. In this method, all registered ongloballayoutlisteners will be traversed, the corresponding callback method will be executed, and the message of global layout change will be notified to each registered listener.

view.getViewTreeObserver().addOnGlobalLayoutListener(listener);

getWindowVisibleDisplayFrame

Getwindowvisibledisplayframe() will return the height of the visible area of the window. You can get the height of the soft keyboard by subtracting it from the screen height.

Complete sample code

package com.cari.cari.promo.diskon.util;

import android.content.Context;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewTreeObserver;

import java.util.LinkedList;
import java.util.List;

public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {

 public interface SoftKeyboardStateListener {
  void onSoftKeyboardOpened(int keyboardHeightInPx);

  void onSoftKeyboardClosed();
 }

 private final List<SoftKeyboardStateListener> listeners = new LinkedList<>();
 private final View activityRootView;
 private int lastSoftKeyboardHeightInPx;
 private boolean isSoftKeyboardOpened;
 private Context mContext;

 //使用时用这个构造方法
 public SoftKeyboardStateWatcher(View activityRootView,Context context) {

  this(activityRootView,false);
  this.mContext = context;
 }

 private SoftKeyboardStateWatcher(View activityRootView) {
  this(activityRootView,false);
 }

 private SoftKeyboardStateWatcher(View activityRootView,boolean isSoftKeyboardOpened) {
  this.activityRootView = activityRootView;
  this.isSoftKeyboardOpened = isSoftKeyboardOpened;
  activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
 }

 @Override
 public void onGlobalLayout() {
  final Rect r = new Rect();
  activityRootView.getWindowVisibleDisplayFrame(r);

  final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
  if (!isSoftKeyboardOpened && heightDiff > dpToPx(mContext,200)) {
   isSoftKeyboardOpened = true;
   notifyOnSoftKeyboardOpened(heightDiff);
  } else if (isSoftKeyboardOpened && heightDiff < dpToPx(mContext,200)) {
   isSoftKeyboardOpened = false;
   notifyOnSoftKeyboardClosed();
  }
 }

 public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
  this.isSoftKeyboardOpened = isSoftKeyboardOpened;
 }

 public boolean isSoftKeyboardOpened() {
  return isSoftKeyboardOpened;
 }

 /**
  * Default value is zero {@code 0}.
  *
  * @return last saved keyboard height in px
  */
 public int getLastSoftKeyboardHeightInPx() {
  return lastSoftKeyboardHeightInPx;
 }

 public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
  listeners.add(listener);
 }

 public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
  listeners.remove(listener);
 }

 /**
  * @param keyboardHeightInPx 可能是包含状态栏的高度和底部虚拟按键的高度
  */
 private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
  this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;

  for (SoftKeyboardStateListener listener : listeners) {
   if (listener != null) {
    listener.onSoftKeyboardOpened(keyboardHeightInPx);
   }
  }
 }

 private void notifyOnSoftKeyboardClosed() {
  for (SoftKeyboardStateListener listener : listeners) {
   if (listener != null) {
    listener.onSoftKeyboardClosed();
   }
  }
 }

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

As you can see, I built a listener of my own, realized the listening we want through this listener, and then handled some logic problems here

The main code is still in ongloballayout:

First, retrieve the entire visible display size of the window attached to this view through activityrootview. Getwindowvisibledisplayframe (R), and then subtract the height of the displayed view, (r.bottom - r.top) is the lower and upper coordinates of the displayed view, and the difference is the height

So far, we have the remaining height. This height may be the keyboard height. Why is it possible? Because the status bar at the top and the virtual navigation bar at the bottom are not considered. Of course, it may not be a keyboard

Then we judge whether it is a keyboard according to this height and the previously known keyboard state, and call back to the listener

use

ScrollView scrollView = findViewById(R.id.ugc_scrollview);
  final SoftKeyboardStateWatcher watcher = new SoftKeyboardStateWatcher(scrollView,this);
  watcher.addSoftKeyboardStateListener(
    new SoftKeyboardStateWatcher.softKeyboardStateListener() {

     @Override
     public void onSoftKeyboardOpened(int keyboardHeightInPx) {
      ConstraintLayout.LayoutParams layoutParamsVideo = (ConstraintLayout.LayoutParams) mError1000tv.getLayoutParams();

      layoutParamsVideo.setMargins(0,keyboardHeightInPx
          - ScreenUtils.getStatusHeight(UGCEditActivity.this)
          - ScreenUtils.getBottomStatusHeight(UGCEditActivity.this));
     }

     @Override
     public void onSoftKeyboardClosed() {
      mError1000tv.setVisibility(View.GONE);
     }
    }
  );

Scrollview is the root layout of the whole page. I monitor the whole layout by listening to it

Merror1000tv is a textview that I mentioned at the beginning to be displayed close to the top of the keyboard

I set the margin for it through layoutparams, only the bottom margin. The value is the returned "keyboard height" - the height of the top status bar - the height of the virtual navigation bar. Get the real keyboard height

In the two callbacks onsoftkeyboardopened and onsoftkeyboardclosed, it's good to handle your own logic

Then put the code of my screen tool class screenutils, and copy what you need

ScreenUtils

package com.cari.promo.diskon.util;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;

import java.lang.reflect.Method;

public class ScreenUtils {
 private ScreenUtils() {
  /* cannot be instantiated */
  throw new UnsupportedOperationException("cannot be instantiated");
 }

 /**
  * 获得屏幕高度
  *
  * @param context
  * @return
  */
 public static int getScreenWidth(Context context) {
  WindowManager wm = (WindowManager) context
    .getSystemService(Context.WINDOW_SERVICE);
  DisplayMetrics outMetrics = new DisplayMetrics();
  wm.getDefaultDisplay().getMetrics(outMetrics);
  return outMetrics.widthPixels;
 }

 /**
  * 获得屏幕宽度
  *
  * @param context
  * @return
  */
 public static int getScreenHeight(Context context) {
  WindowManager wm = (WindowManager) context
    .getSystemService(Context.WINDOW_SERVICE);
  DisplayMetrics outMetrics = new DisplayMetrics();
  wm.getDefaultDisplay().getMetrics(outMetrics);
  return outMetrics.heightPixels;
 }

 /**
  * 获得状态栏的高度
  *
  * @param context
  * @return
  */
 public static int getStatusHeight(Context context) {

  int statusHeight = -1;
  try {
   Class<?> clazz = Class.forName("com.android.internal.R$dimen");
   Object object = clazz.newInstance();
   int height = Integer.parseInt(clazz.getField("status_bar_height")
     .get(object).toString());
   statusHeight = context.getResources().getDimensionPixelSize(height);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return statusHeight;
 }

 /**
  * 获取当前屏幕截图,包含状态栏
  *
  * @param activity
  * @return
  */
 public static Bitmap snapShotWithStatusBar(Activity activity) {
  View view = activity.getWindow().getDecorView();
  view.setDrawingCacheEnabled(true);
  view.buildDrawingCache();
  Bitmap bmp = view.getDrawingCache();
  int width = getScreenWidth(activity);
  int height = getScreenHeight(activity);
  Bitmap bp = null;
  bp = Bitmap.createBitmap(bmp,width,height);
  view.destroyDrawingCache();
  return bp;

 }

 /**
  * 获取当前屏幕截图,不包含状态栏
  *
  * @param activity
  * @return
  */
 public static Bitmap snapShotWithoutStatusBar(Activity activity) {
  View view = activity.getWindow().getDecorView();
  view.setDrawingCacheEnabled(true);
  view.buildDrawingCache();
  Bitmap bmp = view.getDrawingCache();
  Rect frame = new Rect();
  activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
  int statusBarHeight = frame.top;

  int width = getScreenWidth(activity);
  int height = getScreenHeight(activity);
  Bitmap bp = null;
  bp = Bitmap.createBitmap(bmp,statusBarHeight,height
    - statusBarHeight);
  view.destroyDrawingCache();
  return bp;
 }

 /**
  * 获取 虚拟按键的高度
  *
  * @param context 上下文
  * @return 虚拟键高度
  */
 public static int getBottomStatusHeight(Context context) {
  int totalHeight = getAbsoluteHeight(context);

  int contentHeight = getScreenHeight(context);

  return totalHeight - contentHeight;
 }

 /**
  * 获取屏幕原始尺寸高度,包括虚拟功能键高度
  *
  * @param context 上下文
  * @return The absolute height of the available display size in pixels.
  */
 private static int getAbsoluteHeight(Context context) {
  int absoluteHeight = 0;
  WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  Display display = null;
  if (windowManager != null) {
   display = windowManager.getDefaultDisplay();
  }
  DisplayMetrics displayMetrics = new DisplayMetrics();
  @SuppressWarnings("rawtypes")
  Class c;
  try {
   c = Class.forName("android.view.Display");
   @SuppressWarnings("unchecked")
   Method method = c.getmethod("getRealMetrics",DisplayMetrics.class);
   method.invoke(display,displayMetrics);
   absoluteHeight = displayMetrics.heightPixels;
  } catch (Exception e) {
   e.printStackTrace();
  }
  return absoluteHeight;
 }
}

The full text ends

summary

The above is the whole content of this article. I hope the content of this article has a certain reference value for your study or work. Thank you for your support.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>