问题:直播评论时,需要部分布局随软键盘移动,直播画面等不动
解决方案:监听软键盘显示隐藏,代码设置控件移动
使用第三方框架进行软件盘监听:com.github.yoyoyaobin:PreventKeyboardBlockUtil
但是这个框架不对导航栏进行处理,需要在上层重新封装。
import android.app.Activity;
import android.content.res.Configuration;
import android.view.View;
import com.blankj.utilcode.util.KeyboardUtils;
import com.hyb.library.KeyboardHeightObserver;
import com.hyb.library.KeyboardHeightProvider;
/**
* @author chenpin
*/
public class KeyboardManager {
private int keyBoardHeight = 0;
/**用于在UI线程启动监听*/
private View mBtnView;
private Activity mActivity;
private KeyboardListener mKeyboardListener;
private KeyboardHeightProvider keyboardHeightProvider;
/**记录界面打开时,导航栏是否显示*/
private boolean firstNavigationShow;
/**记录小米手机全面屏进来时,会赋值软键盘高度为-130*/
private boolean firstKeyboardHeightIsNegative;
public KeyboardManager(Activity activity,View view){
mActivity = activity;
if(keyboardHeightProvider != null){
keyboardHeightProvider.recycle();
keyboardHeightProvider = null;
}
this.mBtnView = view;
keyboardHeightProvider = new KeyboardHeightProvider(activity);
firstNavigationShow = NavigationManager.hasNavigationBar(activity);
}
public void setmKeyboardListener(KeyboardListener mKeyboardListener) {
this.mKeyboardListener = mKeyboardListener;
}
public void register(){
keyboardHeightProvider.setKeyboardHeightObserver(new KeyboardHeightObserver() {
@Override
public void onKeyboardHeightChanged(int height, int orientation) {
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return;
}
//如果为负数,那么是第一次进来赋值的,保存下来状态(小米)
if(!firstKeyboardHeightIsNegative){
firstKeyboardHeightIsNegative = height < 0;
}
/*是否有导航栏*/
boolean hasNavigationBar = NavigationManager.hasNavigationBar(mActivity);
/*导航栏高度*/
int navigationBarHeight = NavigationManager.getNavigationBarHeight(mActivity);
if (keyBoardHeight == height) {
return;
} else {
int diffHeight = keyBoardHeight - height;
//如果仅仅是对导航栏操作,不做处理(系统会自行填充 华为)
if(Math.abs(diffHeight) == navigationBarHeight
|| height == navigationBarHeight){
keyBoardHeight = height;
return;
}
keyBoardHeight = height;
}
if (keyBoardHeight <= 0) {//键盘收起
if(mKeyboardListener != null){
mKeyboardListener.onChange(false,0);
}
} else {//键盘打开
if(mKeyboardListener != null){
if(firstNavigationShow && !hasNavigationBar){
//如果进来时导航栏是打开状态,而现在是关闭状态
mKeyboardListener.onChange(true
,keyBoardHeight+navigationBarHeight);
}else if(!firstNavigationShow && hasNavigationBar){
//如果进来时导航栏是关闭状态,而现在是打开状态
mKeyboardListener.onChange(true
,keyBoardHeight-navigationBarHeight);
}else{
//小米手机适配
if(firstKeyboardHeightIsNegative){
mKeyboardListener.onChange(true
,keyBoardHeight + navigationBarHeight);
}else{
mKeyboardListener.onChange(true
,keyBoardHeight);
}
}
}
}
}
});
//只能在主线程中开始监听
mBtnView.post(new Runnable() {
@Override
public void run() {
keyboardHeightProvider.start();
}
});
}
public void unRegister() {
keyBoardHeight = 0;
if(keyboardHeightProvider != null){
keyboardHeightProvider.setKeyboardHeightObserver(null);
keyboardHeightProvider.recycle();
keyboardHeightProvider.close();
keyboardHeightProvider = null;
}
}
public interface KeyboardListener{
/**
* 软键盘监听
* @param keyboardHeight 高度
* @param mShow 是否展示
* */
void onChange(boolean mShow,int keyboardHeight);
}
}
这个是主要代码,还有一个获取导航栏状态的类:
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import java.lang.reflect.Method;
/**
* @author chenpin
*/
public class NavigationManager {
/**
* @param context
* @return 返回true表示显示虚拟导航键、false表示隐藏虚拟导航键
*/
public static boolean hasNavigationBar(Context context) {
//navigationGestureEnabled()从设置中取不到值的话,返回false,因此也不会影响在其他手机上的判断
return deviceHasNavigationBar() && !navigationGestureEnabled(context);
}
/**
* 获取主流手机设置中的"navigation_gesture_on"值,判断当前系统是使用导航键还是手势导航操作
* @param context app Context
* @return
* false 表示使用的是虚拟导航键(NavigationBar),
* true 表示使用的是手势, 默认是false
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static boolean navigationGestureEnabled(Context context) {
int val = Settings.Global.getInt(context.getContentResolver(), getDeviceInfo(), 0);
return val != 0;
}
/**
* 获取设备信息(目前支持几大主流的全面屏手机,亲测华为、小米、oppo、魅族、vivo、三星都可以)
* @return
*/
private static String getDeviceInfo() {
String brand = Build.BRAND;
if(TextUtils.isEmpty(brand)) {
return "navigationbar_is_min";
}
if (brand.equalsIgnoreCase("HUAWEI")||"HONOR".equals(brand)) {
return "navigationbar_is_min";
} else if (brand.equalsIgnoreCase("XIAOMI")) {
return "force_fsg_nav_bar";
} else if (brand.equalsIgnoreCase("VIVO")) {
return "navigation_gesture_on";
} else if (brand.equalsIgnoreCase("OPPO")) {
return "navigation_gesture_on";
} else if(brand.equalsIgnoreCase("samsung")){
return "navigationbar_hide_bar_enabled";
}else {
return "navigationbar_is_min";
}
}
/**
* 判断设备是否存在NavigationBar
* @return true 存在, false 不存在
*/
@SuppressLint("PrivateApi")
private static boolean deviceHasNavigationBar() {
boolean haveNav = false;
try {
//1.通过WindowManagerGlobal获取windowManagerService
// 反射方法:IWindowManager windowManagerService = WindowManagerGlobal.getWindowManagerService();
Class<?> windowManagerGlobalClass = Class.forName("android.view.WindowManagerGlobal");
Method getWmServiceMethod = windowManagerGlobalClass.getDeclaredMethod("getWindowManagerService");
getWmServiceMethod.setAccessible(true);
//getWindowManagerService是静态方法,所以invoke null
Object iWindowManager = getWmServiceMethod.invoke(null);
//2.获取windowMangerService的hasNavigationBar方法返回值
// 反射方法:haveNav = windowManagerService.hasNavigationBar();
Class<?> iWindowManagerClass = iWindowManager.getClass();
Method hasNavBarMethod = iWindowManagerClass.getDeclaredMethod("hasNavigationBar");
hasNavBarMethod.setAccessible(true);
haveNav = (Boolean) hasNavBarMethod.invoke(iWindowManager);
} catch (Exception e) {
e.printStackTrace();
}
return haveNav;
}
/**
* 获取导航栏高度
* @param context
* @return
*/
public static int getNavigationBarHeight(Context context) {
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height","dimen", "android");
int height = resources.getDimensionPixelSize(resourceId);
return height;
}
}
使用第三方框架是创建popWindow进行监听,所以需要在onResume之后进行注册,框架上也有提示的:
@Override protected void onResume() { super.onResume(); mKeyboardManager.register(); } @Override protected void onPause() { super.onPause(); mKeyboardManager.unRegister(); }
当然,还有回调下监听,不贴了。
这个方法,只在华为和小米手机的全面屏上测试过,其他手机还需要进一步验证,具体的实现方案差不多就这样了。