Android监听键盘状态获取键盘高度的实现方法
作者:__卓原 发布时间:2023-12-02 16:44:17
前言
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;
}
}
全文完.
来源:https://blog.csdn.net/u011272795/article/details/103694804
猜你喜欢
- 用Visual Studio等IDE写C#的Hello World非常简单,但脱离了IDE你能不能打印出Hello World呢?这不是说工
- 本文实例讲述了android电源信息查看方法。分享给大家供大家参考。具体如下:1. PowerTestActivity:import and
- 本文实例讲述了c#用for语句输出一个三角形的方法。分享给大家供大家参考。具体分析如下:这是一道面试题,要求是这样的:只使用一个for循环输
- 什么是命名查询? Hibernate允许在映射文件中定义字符串形式的查询语句,这种查询方式成为命名查询 使用命名查询有什么好处? 由于使用H
- 目录1、Stream API2、ParallelStreams执行原理3、ParallelStreams注意事项前言:并行编程势不可挡,Ja
- 应朋友们反馈的Android基础薄弱的问题,决定出一套Android基础教程,帮助大家复习,巩固Android基础,今天要讲的是Androi
- using System;using System.Collections;using System.Xml;namespace Jb51.
- 一直写过数组全排列的算法,当时接触的是使用回溯的方法,这样可以保证生成的全排列一定是按照字典序的,但是今天在做leetcode上的一道题时,
- @JsonInclude(JsonInclude.Include.NON_NULL)不起作用记录一下使用@JsonInclude(JsonI
- 简介官方API文档Scaffold的of方法说明有说明调用Scaffold.of方法是在Scallfold的子组件的Build方法中,也就是
- SpringBoot @ComponentScan的使用SpringBoot的启动类中有一个@ComponentScan,之前项目由于这个注
- 前言在工作中,比如要实现一个功能,前端传什么参数,后端的controller层中怎么接收参数 ,封装成了什么实体对象,有些参数是在URL上使
- 如下所示:package cn.jdk.foreach;import java.util.HashMap;import java.util.
- springboot静态资源加载顺序优先级看springboot源码里面springboot静态资源加载规则我们经常会使用springboo
- 思路:先获得当前季度的开始和结束日期,在当前日期的基础上往前推3个月即上个季度的开始和结束日期/** * @param fla
- 前言本篇文章主要讲述的是SpringBoot整合Mybatis、Druid和PageHelper 并实现多数据源和分页。其中SpringBo
- 本文实例为大家分享了Springboot POI导出Excel的具体代码,供大家参考,具体内容如下需求:页面根据查询条件导出(浏览器)由于本
- 释放公平锁(基于JDK1.7.0_40)1. unlock()unlock()在ReentrantLock.java中实现的,源码如下:pu
- 本文实例讲述了Java简单验证身份证功能。分享给大家供大家参考,具体如下:package org.cxy.csdn.example;impo
- 一、项目简述功能包括(管理员和游客角色):1:用户及主要操作功能 游客可以浏览网站的主页,但是需要注册为会员后部分小 说才能对网络小说进免费