android design包下的Snackbar第一个参数使用技巧

能看到这篇文章的,我想大部分都已经对Snackbar的基本用法很熟悉了.

对于Snackbar.make()方法中的第一个参数View ,该view必须能找到父布局才能用,也就是说,如果我们使用new View(this)的方法来实例化一个view放进去,虽然类型一样,但是运行时会直接报错,因为这个view找不到父view.

在一般的使用过程中下,我们都是在view的onClick事件中弹出Snackbar,而第一个参数传的也是当前点击的view,这样使用是没有一点问题的,并且针对不同的屏幕,比如有虚拟按键栏的屏幕等,适配都是很好的.

但是!!!

这种理想情况的使用方式一般只在demo教程中出现,实际应用中我们很少这样直接在onClick事件中直接弹出Snackbar.

举个栗子:

我们点击一个按钮去进行网络请求,根据请求的结果显示Snackbar,比如,网络连接异常情况下弹出一个Snackbar,没有更多数据时弹出一个Snackbar,用户登陆信息过期又弹出一个Snackbar等,按照上面所说,如果我们要在接口返回的结果中显示Snackbar,我们第一个参数就需要传递一个view,所以一般最容易想到的方式就是把当前点击的按钮当做参数传递到网络请求的方法中,并且哪里用到就得把这个button传递到哪里,而这样做的唯一目的就是最终显示一个Snackbar,如果项目经理说采用显示Toast方式提示信息,那我们这个参数view 直接就是一个废参数了,这样就造成了代码之间耦合度很高.

其实这种情况还算好的,毕竟还有view可以传递,再举一个例子,我们应用中一般都要注册一个receiver用来监听网络连接状态,比如当网络断开时,系统会发一个广播,我们接收到这个广播,就可以提示用户当前网络已经断开了,如果我们采用Snackbar的方式来提示用户,我们首先就得找到一个view,但是这种情况没有按钮点击的,我们找一个什么样的view才比较合理呢?随便找一个当前布局中的view或viewGroup?不可行!!!因为我们监听网络的状态变化,一般在BaseActivity中根据接口的回调做相应的处理,这样能保证在每个界面都能够监听到网络的变化,而BaseActivity中又不是一定有布局文件,所以也不一定能拿到view或viewGroup,但是就一定没法显示Snackbar了吗,当然不会,对于这种情况,我们需要一个可以在任何情况下,只要你在Activity中就能够显示的Snackbar,而显示Snackbar的关键在于我们需要找到一个通用的view,而显然这个view是存在的!!!

(卧槽~~铺垫了这么多,终于进入正题了)

我们知道一个布局的最最最最外层有一个DecorView,而这个view就是我们通用的view.

在activity中我们这样获取DecorView:getwindow().getDecorView();就得到了这个最外层的view.

一旦得到这个view,Snackbar的显示就简单的跟1一样.代码如下:

 /**
     * 展示一个SnackBar
     */
    public void showSnackBar(String message) {
        final Snackbar snackbar = Snackbar.make(getWindow().getDecorView(), message, Snackbar.LENGTH_INDEFINITE);
        snackbar.setAction("知道了", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                snackbar.dismiss();
            }
        }).show();
    }

你们以为这就完了?下面还有.

上面的那段代码如果在没有虚拟按键的屏幕上运行,效果堪称完美,但是一旦屏幕中有虚拟按键,比如华为的手机和平板,普遍都有虚拟按键,在这样的设备上显示的Snackbar会被虚拟按键布局遮挡住,导致根本看不见你展示的Snackbar,对自己代码没信心的,还以为自己代码写错了呢.但是被屏幕的虚拟按键布局挡住了该怎么解决呢?下面提供两种可行的解决方式:

方式1:只提供一个思路,代码实现很简单,这里就不贴代码了:

思路:既然有虚拟按键布局遮挡,那么我们直接获取这个虚拟按键布局的高度,然后我们自定义一个Snackbar的布局,在布局的下方留出这个虚拟按键布局的高度,在这个布局的上面我们自定义展示的Snackbar,思路很简单,实现起来也简单,稍微难一点的是如何获取虚拟按键布局的高度,下面贴出代码,用来获取虚拟按键布局的高度,剩下的事,大家自己动手吧.

 /**
     * 获取屏幕原始尺寸高度,包括虚拟功能键高度
     */
    public static int getRawScreenHeight(Context context) {
        int dpi = 0;
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display 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);
            dpi = displayMetrics.heightPixels;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dpi;
    }

    /**
     * 获取屏幕高度
     *
     * @return 返回当前屏幕高度
     */
    public static int getScreenHeight(Context context) {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(metrics);
        return metrics.heightPixels;
    }

    /**
     * 获取虚拟按键栏的高度(有虚拟按键栏时有值,没有虚拟按键栏时返回0)
     */
    public static int getBottomStatusHeight(Context context) {
        int totalHeight = getRawScreenHeight(context);

        int contentHeight = getScreenHeight(context);

        return totalHeight - contentHeight;
    }


方式二:

思路:既然有虚拟按键栏遮挡Snackbar,那么我们就在显示Snackbar的同时隐藏掉虚拟按键栏,这样屏幕底部就只有Snackbar,也就不存在遮挡问题了,但是有两点需要注意,第一点,配置隐藏虚拟按键栏的flag时要注意配置全面一点,不然会出现点击屏幕又弹出虚拟按键栏的情况,第二点,当Snackbar消失时,记得再次把虚拟按键栏显示出来,不然屏幕底部会多出一块布局,很难看.不过以上这两点都不用操心了,我已经在代码中配置好了,直接复制粘贴就能用,代码需要注意的地方都已经注释清楚了.代码如下:

 /**
     * 展示一个SnackBar
     */
    public void showSnackBar(String message) {
        //去掉虚拟按键
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //隐藏虚拟按键栏
                | View.SYSTEM_UI_FLAG_IMMERSIVE //防止点击屏幕时,隐藏虚拟按键栏又弹了出来
        );
        final Snackbar snackbar = Snackbar.make(getWindow().getDecorView(), message, Snackbar.LENGTH_INDEFINITE);
        snackbar.setAction("知道了", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                snackbar.dismiss();
                //隐藏SnackBar时记得恢复隐藏虚拟按键栏,不然屏幕底部会多出一块空白布局出来,和难看
                getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            }
        }).show();
    }

这两种方法都可以实现效果,选择哪种方式都ok.

动态图录制完有点卡顿,实际效果很流畅


  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值