關(guān)于沉浸式
大家有沒有覺得我這個標(biāo)題有點欠妥.網(wǎng)上很多人都叫這種適配叫沉浸式適配.實際上就是對StatusBar 和 navigationBar 的存在進(jìn)行隱藏或者是修改顏色(透明).
我的理解
沉浸式應(yīng)該是說將虛擬按鍵和狀態(tài)欄隱藏掉 達(dá)到全屏的效果.讓用戶達(dá)到一種置身于整個屏幕中的感覺扫腺。
先了解一下讀完這篇文章可以學(xué)會什么東西
屏幕延伸狀態(tài)欄(API19以上)
屏幕延伸導(dǎo)航欄(虛擬按鍵 API19以上)
獲取StatusBar高度
獲取NavigationBar高度
設(shè)置StatusBar顏色(API19以上)
設(shè)置NavigationBar顏色(API21以上)
其實以前一直都有用這類的工具類,但都是別人寫下來的,這次整理是因為發(fā)現(xiàn)項目對全面屏的虛擬按鍵不是很適配才整理了一下的课蔬。
先給大家上一張原始的效果圖
屏幕延伸狀態(tài)欄(API19以上)
JcUtilScreen.extendStatusBar(this);
整體布局下移
JcUtilScreen.moveViewDown(binding.textview, JcUtilScreen.getStatusBarHeight(this));
屏幕延伸導(dǎo)航欄(虛擬按鍵 API19以上)
JcUtilScreen.extendNavigationBar(this);
設(shè)置NavigationBar顏色(API21以上)
JcUtilScreen.setNavigationBarColor(this, Color.parseColor("#cc7f7f"));
設(shè)置StatusBar顏色(API19以上)
JcUtilScreen.setStatusBarColor(this, Color.parseColor("#cc7f7f"));
package com.hjc.screenadaptation;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by 賣火柴的小女孩 - Jc on 2018/7/31.
*/
public class JcUtilScreen {
public static Context mContext;
/**
* 屏幕密度
*/
public static float mDensity;
/**
* 屏幕寬度
*/
public static float mScreenWidth;
/**
* 屏幕高度
*/
public static float mScreenHeight;
public static void init(Context context) {
mContext = context; //通過構(gòu)造方法來傳入上下文,這個上下文是Aplication的冀泻,在Aplication創(chuàng)建的時候凛驮,創(chuàng)建了
initScreenSize();//獲取屏幕尺寸的方法
}
/**
* 屏幕的尺寸
*/
private static void initScreenSize() {
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();//上下文裆站,通過資源文件獲取到getDisplayMetrics方法
mDensity = dm.density; //獲取到屏幕的密度
mScreenHeight = dm.heightPixels;//獲取到屏幕的高度
mScreenWidth = dm.widthPixels;//獲取到屏幕的寬度
}
//延伸Layout至StatusBar - StatusBar透明
public static void extendStatusBar(Activity activity) {
//4.4及以上可以使用沉浸式狀態(tài)欄
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
//延伸布局至NavigationBar - NavigationBar透明 - 不可單獨使用
public static void extendNavigationBar(Activity activity) {
//4.4及以上可以使用沉浸式狀態(tài)欄
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
//這里有個坑 單單 當(dāng)Windows設(shè)置FLAG_TRANSLUCENT_NAVIGATION
//屏幕的顯示內(nèi)容區(qū)域就會延伸到狀態(tài)欄上,但狀態(tài)欄區(qū)域依舊被狀態(tài)欄掩蓋 -- 顏色是主題顏色 colorPrimary
//解決方案1:布局文件 android:layout_marginTop="" getStatusBarHeight()
//解決方案2:extendStatusBar() + 解決方案1
}
//獲取StatusBar的高度
public static int getStatusBarHeight(Context context) {
try {
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("status_bar_height");
int x = Integer.parseInt(field.get(obj).toString());
return context.getResources().getDimensionPixelSize(x);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
//檢查設(shè)備是否具有NavigationBar - (全面屏返回 Ture)
public static boolean checkDeviceHasNavigationBar(Activity activity) {
boolean hasNavigationBar = false;
Resources rs = activity.getResources();
int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
hasNavigationBar = rs.getBoolean(id);
}
try {
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) {
}
return hasNavigationBar;
}
public static int getNavigationBar(Context context) {
return getScreenHeight(context) - getScreenShowHeight(context);
}
//屏幕的全高
public static int getScreenHeight(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;
}
//屏幕顯示內(nèi)容的高
public static int getScreenShowHeight(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.heightPixels;
}
public static void moveViewDown(View view) {
//sdk 4.4: KITKAT
int distance = getStatusBarHeight(view.getContext());
// 父控件
ViewParent parent = view.getParent();
if (parent instanceof LinearLayout) {
// 通過marginTop屬性讓控件往下偏移一段距離
// 相當(dāng)于:android:layout_marginTop="24dp"
((LinearLayout.LayoutParams) view.getLayoutParams()).topMargin = distance;
} else if (parent instanceof RelativeLayout) {
((RelativeLayout.LayoutParams) view.getLayoutParams()).topMargin = distance;
} else if (parent instanceof FrameLayout) {
((FrameLayout.LayoutParams) view.getLayoutParams()).topMargin = distance;
}
return;
}
public static void moveViewDown(View view, int downHeight) {
//sdk 4.4: KITKAT
// 父控件
ViewParent parent = view.getParent();
if (parent instanceof LinearLayout) {
// 通過marginTop屬性讓控件往下偏移一段距離
// 相當(dāng)于:android:layout_marginTop="24dp"
((LinearLayout.LayoutParams) view.getLayoutParams()).topMargin = downHeight;
} else if (parent instanceof RelativeLayout) {
((RelativeLayout.LayoutParams) view.getLayoutParams()).topMargin = downHeight;
} else if (parent instanceof FrameLayout) {
((FrameLayout.LayoutParams) view.getLayoutParams()).topMargin = downHeight;
}
return;
}
//-- Color 設(shè)置
//設(shè)置 NavigationBar 顏色 5.0 一下機(jī)型不支持
public static void setNavigationBarColor(Activity activity, int navigationBarColor) {
//API21 以上才能設(shè)置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
activity.getWindow().setNavigationBarColor(navigationBarColor);
}
public static void setStatusBarColor(Activity activity, int statusBarColor) {
//設(shè)置的extendStatusBar將無效
// [5.0 以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setStatusBarColor5_0(activity, statusBarColor);
}
// [4.4 - 5.0}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setStatusBarColor4_4_5_0(activity, statusBarColor);
}
}
private static void setStatusBarColor5_0(Activity activity, int statusBarColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
//取消狀態(tài)欄透明
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//添加Flag把狀態(tài)欄設(shè)為可繪制模式
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//設(shè)置狀態(tài)欄顏色
window.setStatusBarColor(statusBarColor);
//設(shè)置系統(tǒng)狀態(tài)欄處于可見狀態(tài)
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
//讓view不根據(jù)系統(tǒng)窗口來調(diào)整自己的布局
ViewGroup mContentView = (ViewGroup) window.findViewById(Window.ID_ANDROID_CONTENT);
View mChildView = mContentView.getChildAt(0);
if (mChildView != null) {
ViewCompat.setFitsSystemWindows(mChildView, false);
ViewCompat.requestApplyInsets(mChildView);
}
}
}
private static View mStatusBarView;
private static void setStatusBarColor4_4_5_0(Activity activity, int statusBarColor) {
//4.4 - 5.0 不支持直接設(shè)置statusBarHeight
//思路:全屏 - 添加一個View置頂 高為getStatusBarHeight
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//獲取狀態(tài)欄的高度
int statusBarHeight = getStatusBarHeight(activity);
//將頂部空間的top padding設(shè)置為和狀態(tài)欄一樣的高度,以此達(dá)到預(yù)期的效果
ViewGroup root = (ViewGroup) activity.findViewById(android.R.id.content);
if (mStatusBarView == null) {
mStatusBarView = new View(activity);
mStatusBarView.setBackgroundColor(statusBarColor);
} else {
// 先解除父子控件關(guān)系黔夭,否則重復(fù)把一個控件多次
// 添加到其它父控件中會出錯
ViewParent parent = mStatusBarView.getParent();
if (parent != null) {
ViewGroup viewGroup = (ViewGroup) parent;
if (viewGroup != null)
viewGroup.removeView(mStatusBarView);
}
}
ViewGroup.LayoutParams param = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
statusBarHeight);
root.addView(mStatusBarView, param);
return;
}
//-- 單位 轉(zhuǎn)換
/**
* dp轉(zhuǎn)px
*
* @param context
* @param dpVal
* @return
*/
public static int dp2px(Context context, float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics());
}
/**
* sp轉(zhuǎn)px
*
* @param context
* @param spVal
* @return
*/
public static int sp2px(Context context, float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.getResources().getDisplayMetrics());
}
/**
* px轉(zhuǎn)dp
*
* @param context
* @param pxVal
* @return
*/
public static float px2dp(Context context, float pxVal) {
final float scale = context.getResources().getDisplayMetrics().density;
return (pxVal / scale);
}
/**
* px轉(zhuǎn)sp
*
* @param context
* @param pxVal
* @return
*/
public static float px2sp(Context context, float pxVal) {
return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
}
}
至于我寫這個工具類的時候并沒有使用到全屏(標(biāo)題欄懸浮)的效果,所以我就沒有寫這個方法上去了宏胯。
public static void fullScreen(Activity activity) {
activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
這個方法呢.跟extendStatusBar差不多。extendStatusBar的狀態(tài)欄任然顯示只不過是透明的而已本姥。
做視頻播放的時候可以考慮使用一下這個fullScreen肩袍。禁止不動狀態(tài)欄自動隱藏 , 觸摸屏幕 狀態(tài)欄顯示幾秒鐘鳖擒。
關(guān)于全面屏的NavigationBar
全面屏都沒有Home鍵 (實際觸摸按鍵),所以理論上都支持NavigationBar的,我自己的小米8是支持的闷营。當(dāng)你打開全面屏模式的時候,去checkDeviceHasNavigationBar() ,他是返回True回來的皮仁。
關(guān)于適配通過 Settings.Secure.getInt() 去拿到各大廠商不一樣的設(shè)置值,判斷用戶手機(jī)是否開啟了虛擬按鍵功能剃根。
我個人覺得適配狀態(tài)欄和導(dǎo)航欄,這個類已經(jīng)夠用了.有補(bǔ)充的小伙伴.歡迎提出.代碼有Bug,歡迎找我.