软键盘监听以及对导航栏的处理

问题:直播评论时,需要部分布局随软键盘移动,直播画面等不动

解决方案:监听软键盘显示隐藏,代码设置控件移动

使用第三方框架进行软件盘监听: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();
}

当然,还有回调下监听,不贴了。

这个方法,只在华为和小米手机的全面屏上测试过,其他手机还需要进一步验证,具体的实现方案差不多就这样了。

发布了5 篇原创文章 · 获赞 2 · 访问量 2637

猜你喜欢

转载自blog.csdn.net/u010413558/article/details/105661827
今日推荐