刨根问底-论Android“沉浸式”

lybm9884 8年前
   <p>网上谈论“沉浸式”的文章多的不可胜数,有人把 <strong>“沉浸式”</strong> 叫做 <strong>“沉浸式状态栏”</strong> ,还有人称作为 <strong>“透明状态栏”</strong> 、 <strong>“变色状态栏”</strong> 等。</p>    <p>这里我们先给出 <strong>“沉浸式”</strong> 的直观感受,引用“知乎”网友的答案</p>    <p>使用带虚拟键的手机才能明显感觉到沉浸式所带来的变化:状态栏、导航栏隐藏。</p>    <p>而对于使用实体按键的手机的用户来说, <strong>“沉浸式”</strong> 所带来的变化仅仅是状态栏隐藏,事实上,状态栏隐藏在之前也很常见,各种国产应用启动时都会隐藏状态栏。</p>    <p>把上面的文字翻译成图片的话,标准的 <strong>“沉浸式”</strong> 大致是这个效果。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/553f67bfcb7d355dda9891c0e2bdebbf.gif"></p>    <p style="text-align:center">标准沉浸式.gif</p>    <p>App默认是全屏的,用户可以从顶部或者底部“滑出”状态栏和导航栏,一段时间后状态栏和导航栏会自动消失。</p>    <p>嗯,没错,这个才是标准的 <strong>“沉浸式”</strong> 。</p>    <p>有了以上的定义,下面这两种常见的效果,只能说改变了状态栏的“样式”(Translucent Bar倒是比较贴近)。但是,他们不是 <strong>“沉浸式”</strong> 。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/02adb4b1f24f9bc3e163ae5cee2060fc.png"></p>    <p style="text-align:center">非沉浸式.png</p>    <p>接下来,将进入本篇文章的主题,我们会从两个方面去 <strong>“论沉浸式”</strong> 。</p>    <ul>     <li>4.4之前,谷歌是如何提供类似 <strong>“沉浸式”</strong> 的体验的</li>     <li>如何以最简单的方式实现 <strong>“状态栏”</strong> 与App <strong>“合二为一”</strong> 的效果(真个真的不叫 <strong>沉浸式</strong> 啦~)</li>    </ul>    <p><strong>setSystemUiVisibility</strong></p>    <p>4.0之后(3.0也支持,这里暂时只讨论手机平台),官方提供了这个方法,可以改变系统UI的可见性,使用方法如下:</p>    <pre>  <code class="language-xml">int flag = View.SYSTEM_UI_FLAG_FULLSCREEN;  getWindow().getDecorView().setSystemUiVisibility(flag);</code></pre>    <p>多个值可使用“|”操作符,比如:</p>    <pre>  <code class="language-xml">int flag = View.SYSTEM_UI_FLAG_FULLSCREEN              | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;  getWindow().getDecorView().setSystemUiVisibility(flag);</code></pre>    <p>系统提供了很多类似View.SYSTEM_UI_FLAG_xxx的常量,具体有下面几种:</p>    <ul>     <li> <p><strong>SYSTEM_UI_FLAG_FULLSCREEN(4.1+):</strong>隐藏状态栏,手指在屏幕顶部往下拖动,状态栏会再次出现且不会消失,另外activity界面会重新调整大小,直观感觉就是activity高度有个变小的过程。</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/be1c996a08ddc46d39dd2f28c4fcb674.gif"></p>    <p style="text-align:center">SYSTEM_UI_FLAG_FULLSCREEN.gif</p>    <ul>     <li> <p><strong>SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN(4.1+):配合SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN</strong>一起使用,效果使得状态栏出现的时候不会挤压activity高度,状态栏会覆盖在activity之上。</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b3f0851c505a16cdfd07609c3d98fa2a.gif"></p>    <p style="text-align:center">SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN.gif</p>    <ul>     <li> <p><strong>SYSTEM_UI_FLAG_HIDE_NAVIGATION(4.0+)</strong></p> <p>:会使得虚拟导航栏隐藏,但同样用户可以从屏幕下边缘“拖出”且不会再次消失,同时activity界面会被挤压。</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/aaf4c01e851bbbfeb36b27162105a1f6.gif"></p>    <p style="text-align:center">SYSTEM_UI_FLAG_HIDE_NAVIGATION.gif</p>    <ul>     <li> <p><strong>SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION(4.1+):配合 SYSTEM_UI_FLAG_HIDE_NAVIGATION</strong> 一起使用,效果使得导航栏出现的时候不会挤压activity高度,导航栏会覆盖在activity之上。</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5c44aaf879448bf2f2ac5c2f813711bb.gif"></p>    <p style="text-align:center">SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.gif</p>    <ul>     <li> <p><strong>SYSTEM_UI_FLAG_LAYOUT_STABLE(4.1+):</strong>使用以上四个属性,可以达到activity占据屏幕所有空间,同时状态栏和导航栏可以悬浮在activity之上的效果。但是此时activity的内容也会(比如顶部和底部各有一个TextView)状态栏和导航栏之下,当状态栏和导航栏出现的时候,看起来会这样:</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1924f56913f72aed1c76b22e15b801c5.gif"></p>    <p style="text-align:center">1.gif</p>    <p>显然,文字被遮盖了我们是不能接受的,此时我们需要另外一个属性, android:fitsSystemWindows=“true” ,这个属性表示系统UI(状态栏、导航栏)可见的时候,会给我们的布局加上padding(paddingTop、paddingBottom)属性,这样内容就不会被盖住了。我们在activity的根布局加上这个属性,效果如下</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/df39c1fd970c015f92854b964864397c.gif"></p>    <p style="text-align:center">fitsSystemWindows.gif</p>    <p>我们发现,当状态栏和导航栏出现的时候,内容会被挤压一下,这个体验也不是很理想,那么怎么解决这个问题呢?</p>    <p>其实,我们的内容本就不该占据状态栏和导航栏的位置(游戏一般才会全屏,普通app的状态栏一般都是透明而不是让内容去占据这个空间,后面会介绍怎么实现状态栏“透明效果”),我们需要加上SYSTEM_UI_FLAG_LAYOUT_STABLE这个属性来解决这个问题</p>    <p>,效果如下</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/bfb0377705af395c04e0951278d6186e.gif"></p>    <p style="text-align:center">SYSTEM_UI_FLAG_LAYOUT_STABLE.gif</p>    <p>以上都是4.1(除了SYSTEM_UI_FLAG_HIDE_NAVIGATION</p>    <p>)的属性,观察之后我们发现,不管是那种属性,状态栏和导航栏总是会“遮挡”activity,为了解决这个问题,4.4引入了 <strong>“全屏沉浸模式”</strong> 这个概念。</p>    <ul>     <li> <p>SYSTEM_UI_FLAG_IMMERSIVE(4.4+):这个属性是用来实现 <strong>“沉浸式”</strong> 效果的,官方称作 <strong>“Immersive full-screen mode”</strong> 。</p> <pre>  <code class="language-xml">To provide your app with a layout that fills the entire screen, the new SYSTEM_UI_FLAG_IMMERSIVE    flag for setSystemUiVisibility()    (when combined with SYSTEM_UI_FLAG_HIDE_NAVIGATION    ) enables a new immersive full-screen mode.</code></pre> <p> </p> </li>    </ul>    <p>大意是SYSTEM_UI_FLAG_IMMERSIVE和 SYSTEM_UI_FLAG_HIDE_NAVIGATION一起使用,会带来一个全新的 <strong>“全屏沉浸模式”</strong> 。我们使用这两个属性看下效果~</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d834b453bb94300db0756c88fd2543cd.gif"></p>    <p style="text-align:center">immersive1.gif</p>    <p>纳尼?显然和官方描述的占据整个屏幕有点不符合,状态栏一直占据着空间(不知道为何官方这么描述~)。我们再加一个属性SYSTEM_UI_FLAG_FULLSCREEN,我们再来看下效果</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/517852eaa57f285c78df08ffa5d166e9.gif"></p>    <p style="text-align:center">immersive2.gif</p>    <p>挤压的效果如果你不满意,加上<strong>SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION</strong>,这会让状态栏和导航栏“悬浮”在activity之上,这个前面提到过,不做更多解释,效果如下</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/46b423d679cf85b0271ca3645eaa4084.gif"></p>    <p style="text-align:center">immersive3.gif</p>    <ul>     <li> <p><strong>SYSTEM_UI_FLAG_IMMERSIVE_STICKY:</strong>和SYSTEM_UI_FLAG_IMMERSIVE相似,它被称作“粘性”的沉浸模式,这个模式会在状态栏和导航栏显示一段时间后,自动隐藏(你可以点击一下屏幕,立即隐藏)。同时需要重点说明的是,这种模式下,状态栏和导航栏出现的时候是“半透明”状态,效果如下</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/553f67bfcb7d355dda9891c0e2bdebbf.gif"></p>    <p style="text-align:center">immersive4.gif</p>    <p>我觉得官方要表达的 <strong>“Immersive full-screen mode”</strong> ,应该是这个意思才对吧?</p>    <p>慎用 <strong>“沉浸式”</strong> ,除非你真的需要这样做。比如做一款游戏或者绘图应用就很合适。</p>    <p>“Translucent Bar”</p>    <p>网上所谓的 <strong>“沉浸式状态栏”</strong> ,这个概念官方从未提及过。我看了就大多数文章,最终表达的效果基本是这样</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ff2c667071be8ff18539bcc0fb0f9a2d.png"></p>    <p style="text-align:center">预览.png</p>    <p>为了对比,贴出了不同Android版本的效果(某Q也是如此),其实就是为了让状态栏看上起和我们应用的标题栏(当然,也可能是和整个背景)“合二为一”,不至于和之前一样,看到的是“黑乎乎”的状态栏,网上也有很多第三方库来实现这样的效果,比如: <a href="/misc/goto?guid=4958988897022679385" rel="nofollow,noindex">SystemBarTint</a> 。</p>    <p>其实这个效果4.4以上实现很简单,大概需要用到以下两个属性:</p>    <ul>     <li> <p><strong>windowTranslucentNavigation:</strong>application的主题加上这个属性,表示状态栏半透明,另外,会使得状态栏会悬浮在activity之上:</p> <pre>  <code class="language-xml"><style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">                                      <item name="android:windowTranslucentStatus">true</item>  </style></code></pre> </li>    </ul>    <p>为了不遮挡activity内容,需要配合另外一个属性</p>    <ul>     <li> <p><strong>android:fitsSystemWindows:</strong>使用这个属性的View,系统会在View顶部添加padding(大小为状态栏高度):</p> <pre>  <code class="language-xml"><?xml version="1.0" encoding="utf-8"?>  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:orientation="vertical">      <LinearLayout          android:layout_width="match_parent"          android:layout_height="wrap_content"          android:fitsSystemWindows="true"          android:background="@color/colorPrimary">          <!--嵌套的原因是因为LinearLayout会被加上paddingTop,导致TextView无法居中-->          <RelativeLayout              android:layout_width="match_parent"              android:layout_height="50dp">              <TextView                  android:layout_width="wrap_content"                  android:layout_height="wrap_content"                  android:layout_centerInParent="true"                  android:textColor="@android:color/white"                  android:textSize="20sp"                  android:text="这是标题"/>          </RelativeLayout>      </LinearLayout>  </LinearLayout></code></pre> </li>    </ul>    <p>有些应用没有标题,希望将背景和标题融为一体,比如顶部是“轮播”栏:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0a840c084cd3880ffe808218f896c6c0.png"></p>    <p style="text-align:center">预览2.png</p>    <p>其实也很简单,布局稍微调整下:</p>    <pre>  <code class="language-xml"><?xml version="1.0" encoding="utf-8"?>  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:orientation="vertical">      <ImageView          android:layout_width="match_parent"          android:layout_height="150dp"          android:fitsSystemWindows="true"          android:scaleType="centerCrop"          android:src="@drawable/bg"/>      <TextView          android:layout_width="match_parent"          android:layout_height="match_parent"          android:gravity="center"          android:text="内容区域"/>  </LinearLayout></code></pre>    <p>总之一个原则是,你希望哪块区域扩展到状态栏,就给它加上</p>    <pre>  <code class="language-xml">android:fitsSystemWindows="true"</code></pre>    <p>另外,别忘了,主题要设置成状态栏半透明</p>    <pre>  <code class="language-xml"><item name="android:windowTranslucentStatus">true</item></code></pre>    <p>我们注意到, <strong>状态栏</strong> 在4.4-5.0之间的效果是全透明,5.0+是半透明,颜色的差别QQ等App也没有特意去做处理,我也觉得没必要,你觉得呢?</p>    <p>好了,希望本文能帮助你真正理解什么是 <strong>“沉浸式”</strong> 。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/38c2239dd0d4</p>    <p> </p>