现在市面上全面屏的手机,有些机型存在悬浮球的设置。在界面设计的时候就要考虑如全面屏/虚拟键/悬浮球三种情形的关于控件适配的问题。当遇到一些视频显示及图片显示时,全面屏/虚拟控件/悬浮球状态时会对控件进行缩放。这样就使得原本按比例显示的视频和图片出现拉伸的情况出现。这时我们要设定比例的控件不受全面屏/虚拟控件的显示及隐藏/悬浮球显示与否的影响。
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); // } } } } }