Android应用:StatusBar状态栏、NavigationBar虚拟按键栏、ActionBar标题栏、Window屏幕内容区域等的宽高

一、屏幕中各种栏目以及屏幕的尺寸

        当我们需要计算屏幕中一些元素的高度时,或许需要先获取到屏幕或者各种栏目的高度,下面这个类包含了Status bar状态栏,Navigation bar虚拟按键栏,Action bar标题栏, Window屏幕内容等的宽高的计算,可以带来极大的方便。

        因为我在代码中做了比较详尽的注释,在这里不再多阐述,以下是代码:

/**
 * 这个类描述了当前设备的配置中system bar的尺寸(StatusBar状态栏,NavigationBar虚拟按键栏,ActionBar标题栏)、
 * 屏幕宽高以及一些相关的特征。
 */
public static class SystemBarConfig {

    private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
    private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height";
    private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape";
    private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width";
    private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";

    private final int mStatusBarHeight;
    private final int mActionBarHeight;
    private final boolean mHasNavigationBar;
    private final int mNavigationBarHeight;
    private final int mNavigationBarWidth;
    private final int mContentHeight;
    private final int mContentWidth;
    private final boolean mInPortrait;
    private final float mSmallestWidthDp;

    private SystemBarConfig(Activity activity) {
        Resources res = activity.getResources();
        mInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
        mSmallestWidthDp = getSmallestWidthDp(activity);
        mStatusBarHeight = getInternalDimensionSize(res, STATUS_BAR_HEIGHT_RES_NAME);
        mActionBarHeight = getActionBarHeight(activity);
        mNavigationBarHeight = getNavigationBarHeight(activity);
        mNavigationBarWidth = getNavigationBarWidth(activity);
        mContentHeight = getContentHeight(activity);
        mContentWidth = getContentWidth(activity);
        mHasNavigationBar = (mNavigationBarHeight > 0);
               
    }
    
    // 安卓系统允许修改系统的属性来控制navigation bar的显示和隐藏,此方法用来判断是否有修改过相关属性。
    // (修改系统文件,在build.prop最后加入qemu.hw.mainkeys=1即可隐藏navigation bar)
    // 相关属性模拟器中有使用。
    // 当返回值等于"1"表示隐藏navigation bar,等于"0"表示显示navigation bar。
    @TargetApi(19)
    private String getNavBarOverride() {
    	String isNavBarOverride = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            try {
                Class c = Class.forName("android.os.SystemProperties");
                Method m = c.getDeclaredMethod("get", String.class);
                m.setAccessible(true);
                isNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys");
            } catch (Throwable e) {
                isNavBarOverride = null;
            }
        }
        return isNavBarOverride;
    }

    //通过此方法获取action bar的高度
    @TargetApi(14)
    private int getActionBarHeight(Context context) {
        int result = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            TypedValue tv = new TypedValue();
            context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
            result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics());
        }
        return result;
    }
    
    //通过此方法获取navigation bar的高度
    @TargetApi(14)
    private int getNavigationBarHeight(Context context) {
        Resources res = context.getResources();
        int result = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            if (hasNavBar(context)) {
                String key;
                if (mInPortrait) {
                    key = NAV_BAR_HEIGHT_RES_NAME;
                } else {
                    key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME;
                }
                return getInternalDimensionSize(res, key);
            }
        }
        return result;
    }

    //通过此方法获取navigation bar的宽度
    @TargetApi(14)
    private int getNavigationBarWidth(Context context) {
        Resources res = context.getResources();
        int result = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            if (hasNavBar(context)) {
                return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME);
            }
        }
        return result;
    }

    //通过此方法判断是否存在navigation bar
    @TargetApi(14)
    private boolean hasNavBar(Context context) {
        Resources res = context.getResources();
        int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android");
        if (resourceId != 0) {
            boolean hasNav = res.getBoolean(resourceId);
            // 查看是否有通过系统属性来控制navigation bar。
            if ("1".equals(getNavBarOverride())) {
                hasNav = false;
            } else if ("0".equals(getNavBarOverride())) {
                hasNav = true;
            }
            return hasNav;
        } else { 
        	//可通过此方法来查看设备是否存在物理按键(menu,back,home键)。
            return !ViewConfiguration.get(context).hasPermanentMenuKey();
        }
    }
    
    //通过此方法获取资源对应的像素值
    private int getInternalDimensionSize(Resources res, String key) {
        int result = 0;
        int resourceId = res.getIdentifier(key, "dimen", "android");
        if (resourceId > 0) {
            result = res.getDimensionPixelSize(resourceId);
        }
        return result;
    }

    //通过此方法获取最小一边的dp值,再通过这个dp值大小来判断设备的navigation bar是显示在底部还是右侧
    @TargetApi(17)
    private float getSmallestWidthDp(Activity activity) {
        DisplayMetrics metrics = new DisplayMetrics();
        float widthDp;
        float heightDp;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        	//API 17之后使用,获取的像素宽高包含虚拟键所占空间,在API 17之前通过反射获取,
        	//获取的屏幕高度包含status bar和navigation bar
            activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
            widthDp = metrics.widthPixels / metrics.density;
            heightDp = metrics.heightPixels / metrics.density;
        } else {
        	//获取的屏幕高度包含status bar,但不包含navigation bar
            activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
            widthDp = metrics.widthPixels / metrics.density;
            heightDp = (metrics.heightPixels + getNavigationBarWidth(activity))/ metrics.density;
        }
        return Math.min(widthDp, heightDp);
    }
    
    //通过此方法获取屏幕高度(不含status bar 和 navigation bar的高度)
    private int getContentHeight(Activity activity) {
    	DisplayMetrics metrics = new DisplayMetrics();
    	activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
        return metrics.heightPixels - getStatusBarHeight();
    }
    
    //通过此方法获取屏幕的宽度(不含navigation bar的宽度)
    private int getContentWidth(Activity activity) {
    	DisplayMetrics metrics = new DisplayMetrics();
    	activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    	return metrics.widthPixels;
    }

    /**
     * 判断navigation bar 是显示在底部还是显示在右侧
     *
     * @return true表示在底部,false表示在右侧
     */
    public boolean isNavigationAtBottom() {
        return (mSmallestWidthDp >= 600 || mInPortrait);
    }

    /**
     * 获取status bar状态栏高度
     *
     * @return 状态栏高度的像素值
     */
    public int getStatusBarHeight() {
        return mStatusBarHeight;
    }

    /**
     * 获取action bar的高度
     *
     * @return action bar高度的像素值
     */
    public int getActionBarHeight() {
        return mActionBarHeight;
    }

    /**
     * 判断此设备是否有navigation bar虚拟按键栏
     *
     * @return true表示有,false表示无
     */
    public boolean hasNavigtionBar() {
        return mHasNavigationBar;
    }

    /**
     * 获取navigation bar虚拟按键栏的高度
     *
     * @return 返回navigation bar虚拟按键栏的高度的像素值,如果设备没有navigation bar虚拟按键栏则返回0
     */
    public int getNavigationBarHeight() {
        return mNavigationBarHeight;
    }

    /**
     * 获取navigation bar虚拟按键栏的宽度(当navigation bar虚拟按键栏垂直显示在右侧时使用)
     *
     * @return 返回navigation bar虚拟按键栏的宽度的像素值,如果设备没有navigation bar虚拟按键栏则返回0
     */
    public int getNavigationBarWidth() {
        return mNavigationBarWidth;
    }
    
    /**
     * 获取屏幕高度(不含status bar 和 navigation bar的高度)
     * 
     * @return 返回屏幕高度的像素值(不含status bar 和 navigation bar的高度)
     */
    public int getContentHeight() {
    	return mContentHeight;
    }
    
    /**
     * 获取屏幕宽度(不含navigation bar的宽度)
     * 
     * @return 返回屏幕宽度的像素值(不含navigation bar的宽度)
     */
    public int getContentWidth() {
    	return mContentWidth;
    }

}

二、控制Navigation Bar的显示和隐藏

        在Android4.4.2(KITKAT<Build.VERSION_CODES.KITKAT>)之前,只能设置:           
        1)View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        其缺点是当Touch Screen时,Navigation bar将显示出来。

        从Android4.4.2起,可以设置:
        1)View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        2)View.SYSTEM_UI_FLAG_IMMERSIVE
        同时设置以上两个参数,即使Touch Screen时,Navigation bar也不会显示出来。

        实现代码

private static Handler sHandler;

protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

	sHandler = new Handler();

	sHandler.post(mHideRunnable); // hide the navigation bar

	final View decorView = getWindow().getDecorView();
	decorView
			.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
				@Override
				public void onSystemUiVisibilityChange(int visibility) {
					sHandler.post(mHideRunnable); // hide the navigation bar
				}
			});
}

Runnable mHideRunnable = new Runnable() {
	@Override
	public void run() {
		int flags;
		int curApiVersion = android.os.Build.VERSION.SDK_INT;
		// This work only for android 4.4+
		if (curApiVersion >= Build.VERSION_CODES.KITKAT) {
			// This work only for android 4.4+
			// hide navigation bar permanently in android activity
			// touch the screen, the navigation bar will not show
			flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
					| View.SYSTEM_UI_FLAG_IMMERSIVE
					| View.SYSTEM_UI_FLAG_FULLSCREEN;

		} else {
			// touch the screen, the navigation bar will show
			flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
		}

		// must be executed in main thread :)
		getWindow().getDecorView().setSystemUiVisibility(flags);
	}
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要取消 Android 底部导航的半透明/毛玻璃效果,可以在 styles.xml 文件中的 AppTheme 样式中添加以下属性: ```xml <item name="android:navigationBarColor">@android:color/transparent</item> <item name="android:windowTranslucentNavigation">false</item> ``` 其中,`android:navigationBarColor` 属性设置为 `@android:color/transparent` 表示将导航的颜色设置为透明,`android:windowTranslucentNavigation` 属性设置为 `false` 表示取消导航的半透明效果。 完整的 AppTheme 样式如下: ```xml <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- 取消导航的半透明效果 --> <item name="android:navigationBarColor">@android:color/transparent</item> <item name="android:windowTranslucentNavigation">false</item> <!-- 其他属性 --> <!-- ... --> </style> ``` 如果想要实现 Android 底部导航毛玻璃效果,可以使用 Android 系统提供的 `BlurDrawable` 类来实现。具体实现步骤可以参考以下代码: ```java // 创建一个 BlurDrawable 对象 BlurDrawable blurDrawable = new BlurDrawable(); // 设置模糊半径和颜色 blurDrawable.setBlurRadius(10); blurDrawable.setColor(Color.parseColor("#66000000")); // 将 BlurDrawable 对象设置为导航的背景 getWindow().setNavigationBarColor(Color.TRANSPARENT); getWindow().setNavigationBarDividerColor(Color.TRANSPARENT); getWindow().setNavigationBarColor(blurDrawable.getColor()); getWindow().setNavigationBarDividerColor(blurDrawable.getColor()); ``` 其中,`BlurDrawable` 对象的 `setBlurRadius` 方法用于设置模糊半径,`setColor` 方法用于设置毛玻璃颜色。最后将 `BlurDrawable` 对象设置为导航的背景即可。 需要注意的是,`BlurDrawable` 类是 Android 11 引入的新类,如果你的应用最低支持 Android 版本不是 11,需要在 build.gradle 文件中添加以下依赖: ```groovy implementation 'androidx.appcompat:appcompat:1.4.0' implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.window:window:1.0.0' ``` 并且在代码中导入以下类: ```java import androidx.appcompat.graphics.drawable.BlurDrawable; import androidx.window.WindowManager; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值