目錄介紹
- 1.相似酷狗等鎖屏頁面實現步驟
- 1.1 什麼是鎖屏聯動媒體播放器
- 1.2 如何實現鎖屏頁面
- 1.3 關於自定義鎖屏頁面左右滑動的控件
- 1.4 注意要點分析
- 1.5 具體完整代碼的案例
- 1.6 效果圖展現案例
- 2.自定義鎖屏頁的基本原理
- 2.1 基本原理
- 2.2 原理圖形展現
- 2.3 討論一些細節
- 3.鎖屏Activity配置信息說明
- 3.1 去掉系統鎖屏作法
- 3.2 權限問題
- 4.屏蔽物理或者手機返回鍵
- 4.1 爲何要這樣處理
- 4.2 如何實現,實現邏輯代碼
- 5.滑動屏幕解鎖
- 5.1 滑動解鎖原理
- 5.2 滑動控件自定義
- 6.透明欄與沉浸模式
- 6.1 透明欄與沉浸模式的概念
- 6.2 如何實現,代碼展現
- 7.用戶指紋識別,如何鎖屏頁面失效
- 8.關於其餘
- 8.1 版本更新狀況
- 8.2 參考案例
- 8.3 我的博客
0.備註
1.相似酷狗等鎖屏頁面實現步驟
1.1 什麼是鎖屏聯動媒體播放器
- 播放器除了播放了音樂以外什麼都沒作,就能夠分別在任務管理、鎖屏、負一屏控制播放器。
- 也能夠這樣通俗的解釋,這個舉例子說一個應用場景,我使用混沌大學聽音頻,而後我關閉了屏幕(屏幕滅了),當我再次打開的時候,屏幕的鎖屏頁面或者頂層頁面便會出現一層音頻播放器控制的頁面,那麼即便我不用解鎖屏幕,也照樣能夠控制音頻播放器的基本播放操做。若是你細心觀察一下,也會發現有些APP正式這樣操做的。目前我發現QQ音樂,酷狗音樂,混沌大學等是這樣的
- 如何實現,邏輯思路
- 第一步:在服務中註冊屏幕熄滅廣播
- 第二步:處理邏輯,發現屏幕熄滅就開啓鎖屏頁面,再次點亮屏幕時就能夠看到鎖屏頁面
- 第三步:點擊鎖屏頁面上的按鈕,好比上一首,下一首,播放暫停能夠與主程序同步信息。
- 第四步:滑動鎖屏頁面,鎖屏頁面被銷燬,進入程序主界面。
1.2 如何實現鎖屏頁面
- 1.2.1 註冊一個廣播接收者監聽屏幕亮了或者滅了
public class AudioBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(action!=null && action.length()>0){
switch (action){
//鎖屏時處理的邏輯
case Constant.LOCK_SCREEN:
PlayService.startCommand(context,Constant.LOCK_SCREEN);
break;
//當屏幕滅了
case Intent.ACTION_SCREEN_OFF:
PlayService.startCommand(context,Intent.ACTION_SCREEN_OFF);
break;
//當屏幕亮了
case Intent.ACTION_SCREEN_ON:
PlayService.startCommand(context,Intent.ACTION_SCREEN_ON);
break;
default:
break;
}
}
}
}
- 1.2.2 在服務中開啓和註銷鎖屏操做
- 在oncreate方法中註冊廣播接收者
final IntentFilter filter = new IntentFilter();
//鎖屏
filter.addAction(Constant.LOCK_SCREEN);
//當屏幕滅了
filter.addAction(Intent.ACTION_SCREEN_OFF);
//當屏幕亮了
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(mAudioReceiver, filter);
unregisterReceiver(mAudioReceiver);
1.3 關於自定義鎖屏頁面左右滑動的控件
public class SlitherFinishLayout extends RelativeLayout implements OnTouchListener {
/**
* SlitherFinishLayout佈局的父佈局
*/
private ViewGroup mParentView;
/**
* 處理滑動邏輯的View
*/
private View touchView;
/**
* 滑動的最小距離
*/
private int mTouchSlop;
/**
* 按下點的X座標
*/
private int downX;
/**
* 按下點的Y座標
*/
private int downY;
/**
* 臨時存儲X座標
*/
private int tempX;
/**
* 滑動類
*/
private Scroller mScroller;
/**
* SlitherFinishLayout的寬度
*/
private int viewWidth;
/**
* 記錄是否正在滑動
*/
private boolean isSlither;
private OnSlitherFinishListener onSlitherFinishListener;
private boolean isFinish;
public SlitherFinishLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlitherFinishLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mScroller = new Scroller(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
// 獲取SlitherFinishLayout所在佈局的父佈局
mParentView = (ViewGroup) this.getParent();
viewWidth = this.getWidth();
}
}
/**
* 設置OnSlitherFinishListener, 在onSlitherFinish()方法中finish Activity
* @param onSlitherFinishListener listener
*/
public void setOnSlitherFinishListener(OnSlitherFinishListener onSlitherFinishListener) {
this.onSlitherFinishListener = onSlitherFinishListener;
}
/**
* 設置Touch的View
* @param touchView
*/
public void setTouchView(View touchView) {
this.touchView = touchView;
touchView.setOnTouchListener(this);
}
public View getTouchView() {
return touchView;
}
/**
* 滾動出界面
*/
private void scrollRight() {
final int delta = (viewWidth + mParentView.getScrollX());
// 調用startScroll方法來設置一些滾動的參數,咱們在computeScroll()方法中調用scrollTo來滾動item
mScroller.startScroll(mParentView.getScrollX(), 0, -delta + 1, 0, Math.abs(delta));
postInvalidate();
}
/**
* 滾動到起始位置
*/
private void scrollOrigin() {
int delta = mParentView.getScrollX();
mScroller.startScroll(mParentView.getScrollX(), 0, -delta, 0, Math.abs(delta));
postInvalidate();
}
/**
* touch的View是不是AbsListView, 例如ListView, GridView等其子類
* @return
*/
private boolean isTouchOnAbsListView() {
return touchView instanceof AbsListView ? true : false;
}
/**
* touch的view是不是ScrollView或者其子類
* @return
*/
private boolean isTouchOnScrollView() {
return touchView instanceof ScrollView ? true : false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = tempX = (int) event.getRawX();
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getRawX();
int deltaX = tempX - moveX;
tempX = moveX;
if (Math.abs(moveX - downX) > mTouchSlop && Math.abs((int) event.getRawY() - downY) < mTouchSlop) {
isSlither = true;
// 若touchView是AbsListView,
// 則當手指滑動,取消item的點擊事件,否則咱們滑動也伴隨着item點擊事件的發生
if (isTouchOnAbsListView()) {
MotionEvent cancelEvent = MotionEvent.obtain(event);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL
| (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
v.onTouchEvent(cancelEvent);
}
}
if (moveX - downX >= 0 && isSlither) {
mParentView.scrollBy(deltaX, 0);
// 屏蔽在滑動過程當中ListView ScrollView等本身的滑動事件
if (isTouchOnScrollView() || isTouchOnAbsListView()) {
return true;
}
}
break;
case MotionEvent.ACTION_UP:
isSlither = false;
if (mParentView.getScrollX() <= -viewWidth / 2) {
isFinish = true;
scrollRight();
} else {
scrollOrigin();
isFinish = false;
}
break;
default:
break;
}
// 假如touch的view是AbsListView或者ScrollView 咱們處理完上面本身的邏輯以後
// 再交給AbsListView, ScrollView本身處理其本身的邏輯
if (isTouchOnScrollView() || isTouchOnAbsListView()) {
return v.onTouchEvent(event);
}
// 其餘的狀況直接返回true
return true;
}
@Override
public void computeScroll() {
// 調用startScroll的時候scroller.computeScrollOffset()返回true,
if (mScroller.computeScrollOffset()) {
mParentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
if (mScroller.isFinished()) {
if (onSlitherFinishListener != null && isFinish) {
onSlitherFinishListener.onSlitherFinish();
}
}
}
}
public interface OnSlitherFinishListener {
void onSlitherFinish();
}
}
public class SlideFinishLayout extends RelativeLayout {
private final String TAG = SlideFinishLayout.class.getName();
/**
* SlideFinishLayout佈局的父佈局
*/
private ViewGroup mParentView;
/**
* 滑動的最小距離
*/
private int mTouchSlop;
/**
* 按下點的X座標
*/
private int downX;
/**
* 按下點的Y座標
*/
private int downY;
/**
* 臨時存儲X座標
*/
private int tempX;
/**
* 滑動類
*/
private Scroller mScroller;
/**
* SlideFinishLayout的寬度
*/
private int viewWidth;
/**
* 記錄是否正在滑動
*/
private boolean isSlide;
private OnSlideFinishListener onSlideFinishListener;
/**
* 是否開啓左側切換事件
*/
private boolean enableLeftSlideEvent = true;
/**
* 是否開啓右側切換事件
*/
private boolean enableRightSlideEvent = true;
/**
* 按下時範圍(處於這個範圍內就啓用切換事件,目的是使當用戶從左右邊界點擊時才響應)
*/
private int size ;
/**
* 是否攔截觸摸事件
*/
private boolean isIntercept = false;
/**
* 是否可切換
*/
private boolean canSwitch;
/**
* 左側切換
*/
private boolean isSwitchFromLeft = false;
/**
* 右側側切換
*/
private boolean isSwitchFromRight = false;
public SlideFinishLayout(Context context) {
super(context);
init(context);
}
public SlideFinishLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0);
init(context);
}
public SlideFinishLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
Log.i(TAG, "設備的最小滑動距離:" + mTouchSlop);
mScroller = new Scroller(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
// 獲取SlideFinishLayout所在佈局的父佈局
mParentView = (ViewGroup) this.getParent();
viewWidth = this.getWidth();
size = viewWidth;
}
Log.i(TAG, "viewWidth=" + viewWidth);
}
public void setEnableLeftSlideEvent(boolean enableLeftSlideEvent) {
this.enableLeftSlideEvent = enableLeftSlideEvent;
}
public void setEnableRightSlideEvent(boolean enableRightSlideEvent) {
this.enableRightSlideEvent = enableRightSlideEvent;
}
/**
* 設置OnSlideFinishListener, 在onSlideFinish()方法中finish Activity
* @param onSlideFinishListener onSlideFinishListener
*/
public void setOnSlideFinishListener(OnSlideFinishListener onSlideFinishListener) {
this.onSlideFinishListener = onSlideFinishListener;
}
/**
* 是否攔截事件,若是不攔截事件,對於有滾動的控件的界面將出現問題(相沖突)
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
float downX = ev.getRawX();
Log.i(TAG, "downX =" + downX + ",viewWidth=" + viewWidth);
if(enableLeftSlideEvent && downX < size){
Log.e(TAG, "downX 在左側範圍內 ,攔截事件");
isIntercept = true;
isSwitchFromLeft = true;
isSwitchFromRight = false;
return false;
}else if(enableRightSlideEvent && downX > (viewWidth - size)){
Log.i(TAG, "downX 在右側範圍內 ,攔截事件");
isIntercept = true;
isSwitchFromRight = true;
isSwitchFromLeft = false;
return true;
}else{
Log.i(TAG, "downX 不在範圍內 ,不攔截事件");
isIntercept = false;
isSwitchFromLeft = false;
isSwitchFromRight = false;
}
return super.onInterceptTouchEvent(ev);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
//不攔截事件時 不處理
if(!isIntercept){
Log.d(TAG,"false------------");
return false;
}
Log.d(TAG,"true-----------");
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
downX = tempX = (int) event.getRawX();
downY = (int) event.getRawY();
Log.d(TAG,"downX---"+downX+"downY---"+downY);
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getRawX();
int deltaX = tempX - moveX;
tempX = moveX;
if (Math.abs(moveX - downX) > mTouchSlop && Math.abs((int) event.getRawY() - downY) < mTouchSlop) {
isSlide = true;
}
Log.e(TAG, "scroll deltaX=" + deltaX);
//左側滑動
if(enableLeftSlideEvent){
if (moveX - downX >= 0 && isSlide) {
mParentView.scrollBy(deltaX, 0);
}
}
//右側滑動
if(enableRightSlideEvent){
if (moveX - downX <= 0 && isSlide) {
mParentView.scrollBy(deltaX, 0);
}
}
Log.i(TAG + "/onTouchEvent", "mParentView.getScrollX()=" + mParentView.getScrollX());
break;
case MotionEvent.ACTION_UP:
isSlide = false;
//mParentView.getScrollX() <= -viewWidth / 2 ==>指左側滑動
//mParentView.getScrollX() >= viewWidth / 2 ==>指右側滑動
if (mParentView.getScrollX() <= -viewWidth / 2 || mParentView.getScrollX() >= viewWidth / 2) {
canSwitch = true;
if(isSwitchFromLeft){
scrollToRight();
}
if(isSwitchFromRight){
scrollToLeft();
}
} else {
scrollOrigin();
canSwitch = false;
}
break;
default:
break;
}
return true;
}
/**
* 滾動出界面至右側
*/
private void scrollToRight() {
final int delta = (viewWidth + mParentView.getScrollX());
// 調用startScroll方法來設置一些滾動的參數,咱們在computeScroll()方法中調用scrollTo來滾動item
mScroller.startScroll(mParentView.getScrollX(), 0, -delta + 1, 0, Math.abs(delta));
postInvalidate();
}
/**
* 滾動出界面至左側
*/
private void scrollToLeft() {
final int delta = (viewWidth - mParentView.getScrollX());
// 調用startScroll方法來設置一些滾動的參數,咱們在computeScroll()方法中調用scrollTo來滾動item
//此處就不可用+1,也不卡直接用delta
mScroller.startScroll(mParentView.getScrollX(), 0, delta - 1, 0, Math.abs(delta));
postInvalidate();
}
/**
* 滾動到起始位置
*/
private void scrollOrigin() {
int delta = mParentView.getScrollX();
mScroller.startScroll(mParentView.getScrollX(), 0, -delta, 0, Math.abs(delta));
postInvalidate();
}
@Override
public void computeScroll(){
// 調用startScroll的時候scroller.computeScrollOffset()返回true,
if (mScroller.computeScrollOffset()) {
mParentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
if (mScroller.isFinished()) {
if (onSlideFinishListener != null && canSwitch) {
//回調,左側切換事件
if(isSwitchFromLeft){
onSlideFinishListener.onSlideBack();
}
//右側切換事件
if(isSwitchFromRight){
onSlideFinishListener.onSlideForward();
}
}
}
}
}
public interface OnSlideFinishListener {
void onSlideBack();
void onSlideForward();
}
}
1.4 注意要點分析
<activity android:name=".ui.lock.LockTestActivity"
android:noHistory="false"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:exported="false"
android:launchMode="singleInstance"
android:theme="@style/LockScreenTheme"/>
- 1.4.2 程序在前臺時,當從鎖屏頁面finish時,會有閃屏效果
- 若是加上這句話android:launchMode="singleInstance",那麼程序在前臺時會有閃屏效果,若是在後臺時,則直接展示棧頂頁面
- 若是不加這句話
1.5 具體完整代碼的案例
2.自定義鎖屏頁的基本原理
2.1 基本原理
- Android系統實現自定義鎖屏頁的思路很簡單,即在App啓動時開啓一個service,在Service中時刻監聽系統SCREEN_OFF的廣播,當屏幕熄滅時,Service監聽到廣播,開啓一個鎖屏頁Activity在屏幕最上層顯示,該Activity建立的同時會去掉系統鎖屏(固然若是有密碼是禁不掉的)。
2.2 原理圖形展現
2.3 討論一些細節
- 2.3.1 關於啓動Activity時Intent的Flag問題
- 若是不添加FLAG_ACTIVITY_NEW_TASK的標誌位,會出現「Calling startActivity() from outside of an Activity」的運行時異常,畢竟咱們是從Service啓動的Activity。Activity要存在於activity的棧中,而Service在啓動activity時必然不存在一個activity的棧,因此要新起一個棧,並裝入啓動的activity。使用該標誌位時,也須要在AndroidManifest中聲明taskAffinity,即新task的名稱,不然鎖屏Activity實質上仍是在創建在原來App的task棧中。
- 標誌位FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,是爲了不在最近使用程序列表出現Service所啓動的Activity,但這個標誌位不是必須的,其使用依狀況而定。
- 2.3.2 動態註冊廣播接收者
IntentFilter mScreenOffFilter = new IntentFilter();
mScreenOffFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenOffReceiver, mScreenOffFilter);
3.鎖屏Activity配置信息說明
3.1 去掉系統鎖屏作法
- 在自定義鎖屏Activity的onCreate()方法裏設定如下標誌位就能徹底實現相同的功能:
//注意須要作一下判斷
if (getWindow() != null) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
// 鎖屏的activity內部也要作相應的配置,讓activity在鎖屏時也可以顯示,同時去掉系統鎖屏。
// 固然若是設置了系統鎖屏密碼,系統鎖屏是沒有辦法去掉的
// FLAG_DISMISS_KEYGUARD用於去掉系統鎖屏頁
// FLAG_SHOW_WHEN_LOCKED使Activity在鎖屏時仍然可以顯示
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE);
}
}
3.2 權限問題
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
4.屏蔽物理或者手機返回鍵
4.1 爲何要這樣處理
- 當自定義鎖屏頁最終出如今手機上時,咱們總但願它像系統鎖屏頁那樣屹立不倒,全部的按鍵都不能觸動它,只有經過劃瓶或者指紋才能解鎖,所以有必要對按鍵進行必定程度上的屏蔽。針對只有虛擬按鍵的手機,咱們能夠經過隱藏虛擬按鍵的方式部分解決這個問題,具體方法在後文會介紹。可是當用戶在鎖屏頁底部滑動,隱藏後的虛擬按鍵仍是會滑出,並且若是用戶是物理按鍵的話就必須進行屏蔽了。
4.2 如何實現,實現邏輯代碼
@Override
public void onBackPressed() {
// 不作任何事,爲了屏蔽back鍵
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
int key = event.getKeyCode();
switch (key) {
case KeyEvent.KEYCODE_BACK: {
return true;
}
case KeyEvent.KEYCODE_MENU:{
return true;
}
default:
break;
}
return super.onKeyDown(keyCode, event);
}
5.滑動屏幕解鎖
5.1 滑動解鎖原理
- 當手指在屏幕上滑動時,攔截並處理滑動事件,使鎖屏頁面隨着手指運動,當運動到達必定的閥值時,用戶手指鬆開手指,鎖屏頁自動滑動到屏幕邊界消失,若是沒有達到運動閥值,就會自動滑動到起始位置,從新覆蓋屏幕。
- 對滑動的距離與閥值進行一個比較,此處的閥值爲0.5*屏幕寬度,若是低於閥值,則移動到初始位置;若是高於閥值,以一樣的方式移出屏幕右邊界,而後將Activity幹掉
5.2 滑動控件自定義
6.透明欄與沉浸模式
6.1 透明欄與沉浸模式的概念
- 沉浸模式與透明欄是兩個不一樣的概念,因爲某些緣由,國內一些開發或產品會把這兩個概念混淆。
- 6.1.1 沉浸模式 什麼是沉浸模式?
- 從4.4開始,Android 爲 「setSystemUiVisibility()」方法提供了新的標記 「SYSTEM_UI_FLAG_IMMERSIVE」以及」SYSTEM_UI_FLAG_IMMERSIVE_STIKY」,就是咱們所談的沉浸模式,全稱爲 「Immersive Full-Screen Mode」,它可使你的app隱藏狀態欄和導航欄,實現真正意義上的全屏體驗。
- 以前 Android 也是有全屏模式的,主要經過」setSystemUiVisibility()」添加兩個Flag,即」SYSTEM_UI_FLAG_FULLSCREEN」,」SYSTEM_UI_FLAG_HIDE_NAVIGATION」(僅適用於使用導航欄的設備,即虛擬按鍵)。
- 這兩個標記都存在一些問題,例如使用第一個標記的時候,除非 App 提供暫時退出全屏模式的功能(例如部分電子書軟件中點擊一次屏幕中央位置),用戶是一直都無法看見狀態欄的。這樣,若是用戶想去看看通知中心有什麼通知,那就必須點擊一次屏幕,顯示狀態欄,而後才能調出通知中心。
- 而第二個標記的問題在於,Google 認爲導航欄對於用戶來講是十分重要的,因此只會短暫隱藏導航欄。一旦用戶作其餘操做,例如點擊一次屏幕,導航欄就會立刻被從新調出。這樣的設定對於看圖軟件,視頻軟件等等沒什麼大問題,可是對於遊戲之類用戶須要常常點擊屏幕的 App,那就幾乎是悲劇了——這也是爲何你在 Android 4.4 以前找不到什麼全屏模式會自動隱藏導航欄的應用。
6.2 如何實現,代碼展現
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
window.getDecorView().setSystemUiVisibility(
// SYSTEM_UI_FLAG_LAYOUT_STABLE保持整個View穩定,使View不會由於SystemUI的變化而作layout
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
// SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,開發者容易被其中的HIDE_NAVIGATION所迷惑,
// 其實這個Flag沒有隱藏導航欄的功能,只是控制導航欄浮在屏幕上層,不佔據屏幕布局空間;
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
// SYSTEM_UI_FLAG_HIDE_NAVIGATION,纔是可以隱藏導航欄的Flag;
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
// SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,由上面可知,也不能隱藏狀態欄,只是使狀態欄浮在屏幕上層。
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
}
- 注意的是,這段代碼除了須要加在Activity的OnCreate()方法中,也要加在重寫的onWindowFocusChanged()方法中,在窗口獲取焦點時再將Flag設置一遍,不然在部分手機上可能致使沒法達到預想的效果。通常狀況下沒有問題,最後建議仍是加上
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus && getWindow()!=null){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setSystemUiVisibility(
// SYSTEM_UI_FLAG_LAYOUT_STABLE保持整個View穩定,使View不會由於SystemUI的變化而作layout
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
// SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,開發者容易被其中的HIDE_NAVIGATION所迷惑,
// 其實這個Flag沒有隱藏導航欄的功能,只是控制導航欄浮在屏幕上層,不佔據屏幕布局空間;
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
// SYSTEM_UI_FLAG_HIDE_NAVIGATION,纔是可以隱藏導航欄的Flag;
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
// SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,由上面可知,也不能隱藏狀態欄,只是使狀態欄浮在屏幕上層。
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
}
}
}
8.關於其餘
8.1 版本更新狀況
8.2 參考案例
8.3 我的博客