Android监听键盘状态获取键盘高度的实现方法

前言

Android暂时还没有提供一个合适的API来获取/监听键盘的状态和高度 , 而我们又经常会有这个需求.

最近我的一个项目中,在ugc页面需要在键盘顶部,紧贴着键盘显示一个文字提示,当键盘消失时就隐藏.
因此,我需要监听软键盘的打开/关闭 , 以及获取它的高度.

ViewTreeObserver

A view tree observer is used to register listeners that can be notified of global changes in the view tree. Such global events include, but are not limited to, layout of the whole tree, beginning of the drawing pass, touch mode change…

Android框架提供了一个ViewTreeObserver类,它是一个View视图树的观察者类。ViewTreeObserver类中定义了一系列的公共接口(public interface)。当一个View attach到一个窗口上时就会创建一个ViewTreeObserver对象,这样当一个View的视图树发生改变时,就会调用该对象的某个方法,将事件通知给每个注册的监听者。

OnGlobalLayoutListener是ViewTreeObserver中定义的众多接口中的一个,它用来监听一个视图树中全局布局的改变或者视图树中的某个视图的可视状态的改变。当软键盘由隐藏变为显示,或由显示变为隐藏时,都会调用当前布局中所有存在的View中的ViewTreeObserver对象的dispatchOnGlobalLayout()方法,此方法中会遍历所有已注册的OnGlobalLayoutListener,执行相应的回调方法,将全局布局改变的消息通知给每个注册的监听者。

view.getViewTreeObserver().addOnGlobalLayoutListener(listener);

getWindowVisibleDisplayFrame

Retrieve the overall visible display size in which the window this view is attached to has been positioned in.

getWindowVisibleDisplayFrame()会返回窗口的可见区域高度,通过和屏幕高度相减,就可以得到软键盘的高度了。

完整示例代码

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

可以看到, 我建了一个自己的一个Listener , 通过这个listener实现我们想要的监听 , 然后在这里处理一些逻辑问题.

主要代码还是在onGlobalLayout中:

首先通过activityRootView.getWindowVisibleDisplayFrame(r)检索此视图所附加的窗口所在的整个可见显示大小 ,然后减去,已显示的视图的高度 ,(r.bottom - r.top)就是显示的view的下坐标和上坐标,差即为高度.

至此,我们得到了剩余的高度 . 这个高度可能就是键盘高度了, 为什么说可能呢?因为还么有考虑到顶部的状态栏和底部的虚拟导航栏. 当然也可能不是键盘.

然后我们根据这个高度和之前已知的键盘状态来判断是否为键盘.
并回调给监听者.

使用

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,
        0,
        0,
        keyboardHeightInPx
          - ScreenUtils.getStatusHeight(UGCEditActivity.this)
          - ScreenUtils.getBottomStatusHeight(UGCEditActivity.this));
     }

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

Scrollview是整个页面的根布局, 我通过监听它来实现对整个布局的监听.

mError1000tv就是我一开始提到的要紧贴键盘顶部显示的一个textview了.

我通过LayoutParams给它设置边距 , 只设置了底部边距 , 值为返回的"键盘高度"- 顶部状态栏高度-虚拟导航栏的高度. 得到真实的键盘高度.

在onSoftKeyboardOpened和onSoftKeyboardClosed这两个回调中, 处理自己的逻辑就好了.

然后放上我这边屏幕工具类ScreenUtils的代码, 需要的可以复制下来

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, 0, 0, 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, 0, statusBarHeight, width, 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;
 }
}

全文完.

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • Android项目类似淘宝 电商 搜索功能,监听软键盘搜索事件,延迟自动搜索,以及时间排序的搜索历史记录的实现

    最近跳槽去新公司,接受的第一个任务是在 一个电商模块的搜索功能以及搜索历史记录的实现. 需求和淘宝等电商的功能大体差不多,最上面一个搜索框,下面显示搜索历史记录.在EditText里输入要搜索的关键字后,按软键盘的搜索按键/延迟xxxxms后自动搜索.然后将搜索的内容展示给用户/提示用户没有搜到相关信息. 历史记录是按时间排序的,最新的在前面,输入以前搜索过的关键字,例如牛仔裤(本来是第二条),会更新这条记录的时间,下次再看,牛仔裤的排列就在第一位了.并且有清除历史记录的功能. 整理需求,大致需

  • Android中监听软键盘显示状态实现代码

    /**监听软键盘状态 * @param activity * @param listener */ public static void addOnSoftKeyBoardVisibleListener(Activity activity, final OnSoftKeyBoardVisibleListener listener) { final View decorView = activity.getWindow().getDecorView(); decorView.getViewTree

  • Android监听软键盘弹出与隐藏的两种方法

    需求: 现在有一个需求是点击一行文本框,弹出一个之前隐藏的输入框,输入完成后按返回键或者其他的东西隐藏键盘和输入框,将输入框的内容填充到文本框中. 实现: 拿到这个需求的第一反应就是写一个监听来监听键盘的显示和隐藏来控制输入框的显示和隐藏,控制文本框中的内容. 所以我做了如下操作: 指定android:windowSoftInputMode="adjustResize|stateAlwaysHidden"这个的做法是为了让键盘弹出时改变布局. 让Activity实现Layoutchan

  • Android 监听软键盘状态的实例详解

    Android 监听软键盘状态的实例详解 近日遇到要检测软键盘是否显示或隐藏的问题,搜了一下网上,最后找到一个很简单的,记录一下. activityRoot是activity的根view,就是xml里面的第一个view,给它设置一个id. final View activityRootView = findViewById(R.id.activityRoot); activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(ne

  • Android App实现监听软键盘按键的三种方式

    前言: 我们在Android手机上面有时候会遇到监听手机软键盘按键的时候,例如:我们在浏览器输入url完毕后可以点击软键盘右下角的"Go"按键加载url页面:在点击搜索框的时候,点击右下角的search符号键可以进行搜索:或者在全部数据输入完毕后,点击右下角的"done"就马上进行下一步操作. 效果图: function 1: 重写Activity的dispatchKeyEvent(KeyEvent event)方法,在其中监听KeyEventKey.KEYCODE

  • android监听软键盘的弹出与隐藏的示例代码

    情境:布局文件中有ScrollView,ScrollView中有个EditView,布局底部有一个控件(见下面布局代码),程序一启动EditView就获取焦点,弹出软键盘,将这个底部的控件也顶上去了,感觉不太好,所以我就想监听下软键盘弹出,此时去隐藏底部控件,软键盘隐藏时则显示底部控件. 初始:       <?xml version="1.0" encoding="utf-8"?> <LinearLayout android:id="@

  • Android监听键盘状态获取键盘高度的实现方法

    前言 Android暂时还没有提供一个合适的API来获取/监听键盘的状态和高度 , 而我们又经常会有这个需求. 最近我的一个项目中,在ugc页面需要在键盘顶部,紧贴着键盘显示一个文字提示,当键盘消失时就隐藏. 因此,我需要监听软键盘的打开/关闭 , 以及获取它的高度. ViewTreeObserver A view tree observer is used to register listeners that can be notified of global changes in the v

  • Android 判断网络状态对音频静音的实现方法

    在实际应用中,我们不希望在教室网络,打开游戏就显示较大的声音,进而影响上课质量.因此,就需要让app变得智能,让app可以根据使用者当前网络状态,自动进行静音等操作. 本次内容分为两部分:1. 识别网络环境 2. 实现app自动静音. 自动静音 /** * 实现静音功能 */ private void silentSwitchOn() { AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVI

  • Android基于Sensor感应器获取重力感应加速度的方法

    本文实例讲述了Android基于Sensor感应器获取重力感应加速度的方法.分享给大家供大家参考,具体如下: FETC项目指导老师提出了新的需求,想要在游戏地图中表现出用户用户当期移动的方向,再用GPS的话显然很不靠谱,所以想到了android强大的感应器... 很多移动设备都内置了感应器,android通过Sensor和SensorManager类抽象了这些感应器,通过这些类可以使用android设备的传感器 一 介绍Sensor类 SDK只有一句介绍"Class representing a

  • Android 7.0开发获取存储设备信息的方法

    本文实例讲述了 Android 7.0开发获取存储设备信息的方法.分享给大家供大家参考,具体如下: Android 7.0开发相较之前有不少改进,具体可参考前面的文章Android7.0版本影响开发的改进分析,这里简单总结一下Android 7.0针对存储设备的简单操作方法. MountPoint 我们通过MountPoint来描述android设备信息 private static class MountPoint { String mDescription; String mPath; bo

  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)

    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的 1.我的手机中power_profile.xml的内容: HTC t328w 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?><device name="Android">    <item name="none"&

  • Android使用Sensor感应器获取用户移动方向(指南针原理)

    本文实例讲述了Android使用Sensor感应器获取用户移动方向的方法.分享给大家供大家参考,具体如下: 今天继续给大家分享一下第二个重要的感应器,其实获取方向本应该很简单的事情,在前面文章中看到有个TYPE_ORIENTATION 关键字,说明可以直接获取设备的移动方向,但是最新版的SDK加上了这么一句话"TYPE_ORIENTATION   This constant is deprecated. use SensorManager.getOrientation() instead. &q

  • 详解Android中获取软键盘状态和软键盘高度

    详解Android中获取软键盘状态和软键盘高度 应用场景 在Android应用中有时会需要获取软键盘的状态(即软键盘是显示还是隐藏)和软键盘的高度.这里列举了一些可能的应用场景. 场景一 当软键盘显示时,按下返回键应当是收起软键盘,而不是回退到上一个界面,但部分机型在返回键处理上有bug,按下返回键后,虽然软键盘会自动收起,但不会消费返回事件,导致Activity还会收到这次返回事件,执行回退操作,这时就需要判断,如果软键盘刚刚由显示变为隐藏状态,就不执行回退操作. 场景二 当软键盘弹出后,会将

  • Android获取view高度的三种方式

    本文为大家分享了Android获取view高度的方法,供大家参考,具体内容如下 getMeasuredHeight()与getHeight的区别 实际上在当屏幕可以包裹内容的时候,他们的值相等, 只有当view超出屏幕后,才能看出他们的区别: getMeasuredHeight()是实际View的大小,与屏幕无关, 而getHeight的大小此时则是屏幕的大小. 当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的大小 具体方法 我们知道在oncr

  • Android 6.0上sdcard和U盘路径获取和区分方法

    Android6.0上会根据卡的种类和卡的挂载状态来动态生成挂载路径,所以之前写固定路径的办法不可用,最后通过网上查找和分析android源码,通过反射获取到路径,并且进行了正确区分,代码如下: /** * 6.0获取外置sdcard和U盘路径,并区分 * @param mContext * @param keyword SD = "内部存储"; EXT = "SD卡"; USB = "U盘" * @return */ public static

  • Android中获取状态栏高度的两种方法分享

    前言 最近在做一个关于FAB的功能的时候需要获取状态栏的高度,在网上查了很多种方法,下面是选出的比较合理的两个方法.主要参考stackoverflow的这篇问答:http://stackoverflow.com/questions/3407256/height-of-status-bar-in-android 方法一: private double getStatusBarHeight(Context context){ double statusBarHeight = Math.ceil(25

随机推荐