现在市面上全面屏的手机,有些机型存在悬浮球的设置。在界面设计的时候就要考虑如全面屏/虚拟键/悬浮球三种情形的关于控件适配的问题。当遇到一些视频显示及图片显示时,全面屏/虚拟控件/悬浮球状态时会对控件进行缩放。这样就使得原本按比例显示的视频和图片出现拉伸的情况出现。这时我们要设定比例的控件不受全面屏/虚拟控件的显示及隐藏/悬浮球显示与否的影响。
1、全面屏:
2、虚拟控件隐藏:
3、悬浮球显示:
一,首先讨论虚拟键隐藏的情形:
虚拟键在不隐藏时,没有任何设置的话。控件设置为充满decorView时,虚拟键会占用显示空间,这样就要隐藏虚拟键,如下设置会隐藏虚拟键且不占用显示空间。
@SuppressLint("ResourceAsColor")
public static void hideBottomUIMenu(Activity context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
context.getWindow().setNavigationBarColor(Color.parseColor("#00000000"));
}
//隐藏虚拟按键,并且全屏
if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
View v = context.getWindow().getDecorView();
v.setSystemUiVisibility(View.GONE);
} else if (Build.VERSION.SDK_INT >= 19) {
//for new api versions.
View decorView = context.getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION //导航键不占显示屏幕的空间
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //隐藏导航键(这里指时隐藏虚拟键)
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY //浸入式布局,当侧边划出导航键时自动隐藏
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setBackgroundColor(R.color.black); //设置decorView背景为黑色
decorView.setSystemUiVisibility(uiOptions);
}
}
二.接下来全面屏的设置的时候:
全面屏设置后,不会在出现虚拟键。这样控件设置为充满decorView时,就会充满整个屏幕。
三.设置显示悬浮球:
当设置悬浮球后,如没有加入如下的设置。则当悬浮球操作时虚拟键显示且占用显示空间,而当加入如下的设置后,当悬浮球操作后,虚拟键会显示且不占用显示空间之后虚拟键不会隐藏。
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION ; //导航键不占显示屏幕的空间
四.现在假设一个videoView控件(为不让显示视频或者图片不拉伸)按照1080*1920的比例值横屏方式显示,在屏幕不显示不完的区域为decorView的背景色。而videovView控件显示在整个屏幕的中央位置的需求。
- 现在构造如下的工具类:
package com.example.demo1;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.View;
import java.lang.reflect.Method;
/**
* @author wangyongyao
* @package com.example.demo1
* @date 2019-07-18 20:15
* @decribe TODO
* @project
*/
public class NavigationBarUtils {
public final static float STANDARD_SCREEN_SCALE = 1080f / 1920f;
@SuppressLint("ResourceAsColor")
public static void hideBottomUIMenu(Activity context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
context.getWindow().setNavigationBarColor(Color.parseColor("#00000000"));
}
//隐藏虚拟按键,并且全屏
if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
View v = context.getWindow().getDecorView();
v.setSystemUiVisibility(View.GONE);
} else if (Build.VERSION.SDK_INT >= 19) {
//for new api versions.
View decorView = context.getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION //导航键不占显示屏幕的空间
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //隐藏导航键(这里指时隐藏虚拟键)
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY //浸入式布局,当侧边划出导航键时自动隐藏
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setBackgroundColor(R.color.black); //设置decorView背景为黑色
decorView.setSystemUiVisibility(uiOptions);
}
}
/**
* 屏幕宽高比例与1080/1920对比
* @param dm
* @return
*/
public static boolean isHWProportion(DisplayMetrics dm) {
float scale = dm.heightPixels / (dm.widthPixels * 1.0f);
if (scale <= STANDARD_SCREEN_SCALE) {
return true;
}else {
return false;
}
}
/**
* 截取显示控件的宽高按照 1080/1920的比例
* @param dm
* @return
*/
public static float[] getRootHeightAndWidth(DisplayMetrics dm) {
float[] hw = new float[2];
if (isHWProportion(dm)) {
hw[0] = dm.heightPixels;
hw[1] = dm.heightPixels / STANDARD_SCREEN_SCALE;
}else {
hw[0] = dm.widthPixels * STANDARD_SCREEN_SCALE;
hw[1] = dm.widthPixels;
}
return hw;
}
/**
* 判断当前设备是手机还是平板,代码来自 Google I/O App for Android
* @param context
* @return 平板返回 True,手机返回 False
*/
public static boolean isPad(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
//判断是否存在NavigationBar
public static boolean hasNavigationBar(Context context) {
boolean hasNavigationBar = false;
Resources rs = context.getResources();
int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
hasNavigationBar = rs.getBoolean(id);
}
try {
//反射获取SystemProperties类,并调用它的get方法
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method m = systemPropertiesClass.getMethod("get", String.class);
String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
hasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
hasNavigationBar = true;
}
} catch (Exception e) {
e.printStackTrace();
}
return hasNavigationBar;
}
/**
* 全面屏(是否开启全面屏开关 0 关闭 1 开启)
*
* @param context
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
public 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")) {
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 {
return "navigationbar_is_min";
}
}
}
- 构造一个使得videoView按照1080*1920比例显示的方法后在虚拟键是否隐藏和Android平板上显示屏幕的中央位置:
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private void adjustFullScreenLayout(Context mContext, Activity mActivity) {
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
int cHeight = (int) NavigationBarUtils.getRootHeightAndWidth(dm)[0];
int cWidth = (int) NavigationBarUtils.getRootHeightAndWidth(dm)[1];
if (NavigationBarUtils.isPad(mActivity)) {
int height = dm.heightPixels;
int diffHeight = (int) (height - NavigationBarUtils.getRootHeightAndWidth(dm)[0]);
mLayoutParams.width = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cWidth, mContext.getResources().getDisplayMetrics()));
mLayoutParams.height = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cHeight, mContext.getResources().getDisplayMetrics()));
marginLayoutParams.setMargins(0, cHeight / 20,0,0);
} else {
if (NavigationBarUtils.isHWProportion(dm)) {
int width = dm.widthPixels;
int diffWidth = (int) (width - NavigationBarUtils.getRootHeightAndWidth(dm)[1]);
mLayoutParams.width = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cWidth, mContext.getResources().getDisplayMetrics()));
mLayoutParams.height = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cHeight, mContext.getResources().getDisplayMetrics()));
if (NavigationBarUtils.hasNavigationBar(mActivity)) {
if (NavigationBarUtils.navigationGestureEnabled(mActivity)) {
//全面屏时
marginLayoutParams.setMargins(diffWidth / 2,0,diffWidth / 2,0);
}else {
//隐藏虚拟键
marginLayoutParams.setMargins(diffWidth,0,0,0);
}
}else {
marginLayoutParams.setMargins(diffWidth / 2,0,diffWidth / 2,0);
}
}else {
int height = dm.heightPixels;
int diffHeight = (int) (height - NavigationBarUtils.getRootHeightAndWidth(dm)[0]);
mLayoutParams.width = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cWidth, mContext.getResources().getDisplayMetrics()));
mLayoutParams.height = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cHeight, mContext.getResources().getDisplayMetrics()));
// if (NavigationBarUtils.hasNavigationBar(mContext)) {
// if (NavigationBarUtils.navigationGestureEnabled(mContext)) {
// marginLayoutParams.setMargins(0,diffHeight / 2,0,diffHeight / 2);
// }else {
// marginLayoutParams.setMargins(cWidth / 26,diffHeight / 2,0,diffHeight / 2);
// }
// }else {
marginLayoutParams.setMargins(0,diffHeight / 2,0,diffHeight / 2);
// }
}
}
}
- 在view显示时隐藏虚拟控件:
@Override
protected void onResume() {
super.onResume();
NavigationBarUtils.hideBottomUIMenu(this);
}
- 最后,在设置悬浮球后,点击悬浮球后会显示虚拟键。虽在onResume()方法中设置隐藏虚拟键的操作。但最终虚拟键显示后就没有消失。经过生命周期的打印输出,操作悬浮球后回到activity中并没有走onResume()这方法。现在考虑悬浮球的是操作只是夺取了activity显示的焦点并没有让activity重走onResume(),这样就要了解activity中另一个方法onWindowFocusChanged()这个方法,其在activity失去焦点和获取焦点中调用。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
NavigationBarUtils.hideBottomUIMenu(this);
}
如下贴上videoView按照1080*1920比例显示的代码:
package com.example.demo1;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import java.util.HashSet;
import java.util.Set;
public class MainActivity extends Activity {
private RelativeLayout videoView;
private ViewGroup.LayoutParams mLayoutParams;
private ViewGroup.MarginLayoutParams marginLayoutParams = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initView() {
videoView = (RelativeLayout) findViewById(R.id.rl_full_view);
mLayoutParams = videoView.getLayoutParams();
//获取view的margin设置参数
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
marginLayoutParams = (ViewGroup.MarginLayoutParams) mLayoutParams;
} else {
//不存在时创建一个新的参数
//基于View本身原有的布局参数对象
marginLayoutParams = new ViewGroup.MarginLayoutParams(mLayoutParams);
}
}
private void initEvent() {
}
@Override
protected void onResume() {
super.onResume();
NavigationBarUtils.hideBottomUIMenu(this);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
NavigationBarUtils.hideBottomUIMenu(this);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private void adjustFullScreenLayout(Context mContext, Activity mActivity) {
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
int cHeight = (int) NavigationBarUtils.getRootHeightAndWidth(dm)[0];
int cWidth = (int) NavigationBarUtils.getRootHeightAndWidth(dm)[1];
if (NavigationBarUtils.isPad(mActivity)) {
int height = dm.heightPixels;
int diffHeight = (int) (height - NavigationBarUtils.getRootHeightAndWidth(dm)[0]);
mLayoutParams.width = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cWidth, mContext.getResources().getDisplayMetrics()));
mLayoutParams.height = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cHeight, mContext.getResources().getDisplayMetrics()));
marginLayoutParams.setMargins(0, cHeight / 20,0,0);
} else {
if (NavigationBarUtils.isHWProportion(dm)) {
int width = dm.widthPixels;
int diffWidth = (int) (width - NavigationBarUtils.getRootHeightAndWidth(dm)[1]);
mLayoutParams.width = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cWidth, mContext.getResources().getDisplayMetrics()));
mLayoutParams.height = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cHeight, mContext.getResources().getDisplayMetrics()));
if (NavigationBarUtils.hasNavigationBar(mActivity)) {
if (NavigationBarUtils.navigationGestureEnabled(mActivity)) {
//全面屏时
marginLayoutParams.setMargins(diffWidth / 2,0,diffWidth / 2,0);
}else {
//隐藏虚拟键
marginLayoutParams.setMargins(diffWidth,0,0,0);
}
}else {
marginLayoutParams.setMargins(diffWidth / 2,0,diffWidth / 2,0);
}
}else {
int height = dm.heightPixels;
int diffHeight = (int) (height - NavigationBarUtils.getRootHeightAndWidth(dm)[0]);
mLayoutParams.width = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cWidth, mContext.getResources().getDisplayMetrics()));
mLayoutParams.height = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, cHeight, mContext.getResources().getDisplayMetrics()));
// if (NavigationBarUtils.hasNavigationBar(mContext)) {
// if (NavigationBarUtils.navigationGestureEnabled(mContext)) {
// marginLayoutParams.setMargins(0,diffHeight / 2,0,diffHeight / 2);
// }else {
// marginLayoutParams.setMargins(cWidth / 26,diffHeight / 2,0,diffHeight / 2);
// }
// }else {
marginLayoutParams.setMargins(0,diffHeight / 2,0,diffHeight / 2);
// }
}
}
}
}