这可能是我最短的一篇博客了哈 哈 哈~ 魂淡,填完坑后,就不是最短的一篇博客了。。。。。。
图片来源 http://www.pcpop.com/view/1/1157/1157137_1.shtml?r=09092235
从网上盗的一张Nexus 6P运行程序的截图,是不是看起来还是不错,但是有个问题就是底部的 Navigation Bar ,尤其如果是在游戏界面,这个东西还是一直存在,导致游戏不能全屏,太影响观感了。手游基本上都是全屏的,这个不能忍,必须干掉。
犹记得4.0刚来的时候,这个黑条就出现了,一直霸占着屏幕底部,那时无能无力,后来估计Google也意识到这个黑条过于嚣张了,该是治治的时候了,于是乎就给出了解决方案,咱们可以隐藏这个黑条了。
解决方法如下,在onCreate里或者游戏程序开始时调用一下下面的这个方法就行了。
private void hideSystemUI(){
View decorView = getWindow().getDecorView();
int uiOptions = 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_STICKY;
decorView.setSystemUiVisibility(uiOptions);
}
在程序开始的时候调用这个方法,然后就会看到程序界面是全屏的,此时 Navigation Bar和Status Bar都是消失不可见的,如果此时想退出游戏,只需从屏幕顶部或者底部向屏幕里轻滑一下,轻滑后Navigation Bar和Status Bar就会以半透明状态出现,然后等待一会就会自动消失,这样就满足了我们的需求了。这段程序在Nexus6P上测试过,没有问题。
如果想知道这个方法的原理以及里面参数的作用,请去查看博客最后的参考文章。
方法局限:
SYSTEM_UI_FLAG_IMMERSIVE_STICKY
上面这个参数是从Android 4.4 (API Level 19)开始加上的,然后结果就是4.4之前的系统还未测试过,不过可以去参考下面参考文章里的最后一篇文章,加一个监听器,监听 sysui的显示状态,然后再做显示或者隐藏处理。
1.游戏以全屏状态显示,没有Navigation Bar和Status Bar。
2.要使用Android系统的返回键时,从顶部或者底部向屏幕内滑动一下,就可以看到Navigation Bar和Status Bar,你可以点击返回按钮退出游戏,也可以看时间。
3.滑出Navigation后,过一会儿Navigation Bar会自动隐藏,这样就可以继续全屏玩游戏了,不影响游戏观感。
参考文章:
http://developer.android.com/intl/zh-cn/training/system-ui/status.html
http://developer.android.com/intl/zh-cn/training/system-ui/navigation.html
http://developer.android.com/intl/zh-cn/training/system-ui/immersive.html
http://developer.android.com/training/system-ui/visibility.html
以上文章需要翻墙才可以看,如果翻墙有问题的话也可以去你本机android sdk目录中的Docs中找到,文章所在目录是 docs/training/system-ui/
---------------------------------------------------------------------------------------------------填坑分割线-------------------------------------------------------------------------------------------------------------
功能实现了,但是有bug,下面是填坑内容;
坑1---------运行程序状态,点击Home键回到主页,然后再返回程序,黑条会出现,而且不会消失。
出现这个的原因是,程序从后台再回到前台时,Activity onRestart了,而我们的hideSystemUI是写在onCreate里的,onRestart时不会调用onCreate,所以Activity重新开启后,这个系统黑条又出现了。
解决方案:将 hideSystemUI写到 onResume 方法里,因为程序第一次启动以及从后台返回到前台时都会调用这个方法,这样就可以再次隐藏系统黑条了。
坑2-----------出现此坑需要满足的条件
1.Moto X2机子,因为目前真机只有Nexus6和MotoX2,但是Nexus6不会出现此坑,所以先让MotoX2背这个锅,也或许你们不会遇到。
2.设置手机屏幕锁定方式为滑动。
3.程序运行状态,并且划出了半透明的黑条。
4.快速按关机键锁屏接着开屏,要快速 ->_->
好,按上面步骤完成后,此时上划开屏,你会发现黑条又出现了,onResume 那个方式失效了。
出现这个情况的原因应该是黑条出现的时机晚于 onResume的运行。不过Nexus6不会出现这种情况,略坑。然后Nexus6和MotoX2两个机子还有一个区别就是系统版本,Nexus6是6.0,而MotoX2的是5.1,不知道是不是和系统版本有关。
解决方案:因为Activity的创建虽然会经过onResume,onStart等等这些阶段,但是Activity最终创建初始化结束,呈现到手机界面上,却不是在这几个阶段中,最终阶段可以在 onWindowFocusChanged 这个方法中判断,即在窗口得到焦点后,可以表明Activity整个初始化完毕了。关于这个方法详情可以参考这个文章http://blog.csdn.net/pi9nc/article/details/9237031。
所以可以在Activity里实现这个方法,然后将hideSystemUI方法写到里面。记得方法里要调用父类的onWindowFocusChanged,即
super.onWindowFocusChanged,否则会出现黑屏bug。
坑3-----------当程序中有 EditText组件或者其他可以呼出虚拟键盘的组件,当呼出虚拟键盘时黑条也会跟着一起出现,但是当你输入完文字,点击完成或者返回键隐藏虚拟键盘后,你就会发现黑条却坚挺的留在那里,系统的status bar也会存在,坑爹呀!!!
坑3填坑过程,之所以坑3排在最后是因为这个真的很坑,然后为了填坑,我先是研究了如何判断虚拟键盘的显示和隐藏,然后又研究了虚拟键盘按键事件。关于如何判断虚拟键盘的显示和隐藏,网上找了许久也没有满意的答案,因为这个虚拟键盘有两种显示状态,一种是在竖屏状态,一种是在横屏状态,横屏状态默认是全屏显示的,网上那个计算layout尺寸的过于麻烦且不适用横屏情况。
然后关于检测按键事件这个,如果你的程序是横屏模式,默认显示的虚拟键盘是全屏显示的话,就可以在Activity里实现onKeyUp方法,然后通过判断点击完成按键事件,进而调用hideSystemUI方法。因为横屏全屏键盘,会存在一个完成按钮(如下图示例),点击这个按钮后虚拟键盘就会自动隐藏,这个按键对应的 keyCode == KeyEvent.KEYCODE_ENTER,在onKeyUp方法里判断是这个点击的话,就说明输入结束,要隐藏虚拟按键了。
但是竖屏时的虚拟键盘一般没有这个完成按钮,就无法判断用户何时隐藏虚拟键盘了,当然你可以自定义你的虚拟键盘的Enter按键,也可以实现,只是针对这个问题还是不通用。
于是乎祭出大招了,设置Listener,只要检测到这个黑条出现就调用hideSystemUI方法。
具体代码如下:
private void setVisibilityListener(){
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener(){
@Override
public void onSystemUiVisibilityChange(int visibility) {
if (visibility == 0){
hideSystemUI();
}
}
});
}
就是设置 OnSystemUiVisibilityChangeListener ,在其 onSystemUiVisibilityChange 中可以获取 SystemUI的显示状态,当 visibility 的值为0时表示SystemUI是显示状态,此时就调用hideSystemUI方法隐藏黑条。P.S.因为正常滑动显示的半透明状态Navigation bar时,这个监听器是不做监听的,所以不必担心正常滑出状态。
坑4--出现虚拟键盘并且旋转屏幕的情况。开始是横屏状态,呼出的虚拟键盘,然后切换到竖屏状态,此时在点击Navigation bar上的返回键,可以隐藏虚拟键盘,但是黑条不会消失。
解决方案:
1.在mainfest中,Activity 属性定义中,写上这个
android:configChanges="orientation|keyboardHidden|screenSize"有啥作用呢,就是屏幕旋转的时候,会进
onConfigurationChanged
方法,而不会进onCreate方法,具体可以参考这篇文章 http://www.cnblogs.com/lijunamneg/archive/2013/03/26/2982461.html
然后比较坑的就是 keyboardHidden属性,测试了很长时间,一直以为定义了这个后,虚拟键盘改变状态后就会调用 onConfigurationChanged方法,然而却没有,看了Google官方文档,以及网上所有的解释,都没人说清这个值的作用,个人猜测是和实体键盘有关的,和虚拟键盘无关。
当屏幕旋转后就会进上面的方法。
------------------------------------------------------------------------结局分割线------------------------------------------------------------------------------------
可能这不是结局,应该还有坑。。。。。。。
综上,为了解决上面的三个坑,需要写三个填坑的方法
1.onResume --------------------------------------------------- Home 按键坑
2.onWindowFocusChanged ------------------------------- 锁屏开屏坑
3.OnSystemUiVisibilityChangeListener ----------------- 虚拟键盘坑
4.onConfigurationChanged -------------------------------- 虚拟键盘加旋转屏幕坑
最终的源码如下:
package com.example.lovex.decorviewtest;
import android.content.res.Configuration;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private final String TAG = "DecorView";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setVisibilityListener();
}
/**
* 设置System UI显示状态监听
* 当 visibility 为 0 时,表示Navigation Bar是显示状态,此时进行隐藏
* 此方法用于解决虚拟键盘呼出后的问题
*/
private void setVisibilityListener(){
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener(){
@Override
public void onSystemUiVisibilityChange(int visibility) {
if (visibility == 0){
hideSystemUI();
}
}
});
}
/**
* 隐藏系统UI,包括顶部Status Bar和底部 Navigation Bar
*/
private void hideSystemUI(){
View decorView = getWindow().getDecorView();
if (decorView != null){
int uiOptions = 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_STICKY;
decorView.setSystemUiVisibility(uiOptions);
}
}
/**
* 锁屏开屏返回获取窗口焦点后进行隐藏
* 此方法用于解决锁屏开屏后的问题
* @param hasFocus
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus){
hideSystemUI();
}
}
/**
* 横竖屏切换时会进入此方法
* 用来解决弹出虚拟键盘后旋转屏幕后出现的问题
* @param newConfig
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
hideSystemUI();
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
/**
* 程序第一次启动以及从后台返回都会调用这个方法
* 此方法用于解决点击Home键再返回程序的问题
*/
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
hideSystemUI();
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
/**
* 这个适用于横屏全屏键盘
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP){
hideSystemUI();
}
return super.onKeyUp(keyCode, event);
}
}