如何做好全屏化的交互體驗(解決EditText導致的界面上移、擋住等問題)

EditText導致的問題

在使用EditText並且將其放在屏幕底部的時候,往往會使用例如:

android:windowSoftInputMode="adjustResize|stateHidden"
  • 1
  • 1

等這樣的參數,但是它有着很大的缺點,會導致界面上移、EditText被擋住的問題一些問題。

簡單思考EditText鍵盤彈出後爲什麼導致界面上移呢?

不從代碼層次上考慮,當鍵盤彈出後,鍵盤本身佔據一定空間會致使EditText隨之上移,EditText上移也會導致同一佈局層次的其他控件發生位移狀況,這就是我們常常碰到的情況,也是非常頭痛。

那麼,如果將EditText放在一個獨立層次的控件中,並且讓其上部(因爲往往EditText都會放在底部)有一個自動控制其大小寬高的控件,是不是就會解決這個問題呢?

答案是肯定的!

將EditText放入獨立的層次佈局中,並輔以可自動收縮的控件。

類似於這樣:

 <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/send_edt"
        android:layout_width="match_parent"
        android:layout_height="55dp"
        android:layout_alignParentBottom="true"
        android:gravity="center_vertical|left"
        android:imeOptions="actionSend|flagNoExtractUi"
        android:lines="1"
        android:minHeight="38dp"
        android:padding="5dp"
        android:textColor="#222222"
        android:textColorHint="#b4b4b4"
        android:textSize="13sp"
        android:visibility="visible" />
    <!--也可以是RecyclerView或ListView,但是不能是LinearLayout或RelativeLayout等佈局-->
    <ScrollView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_above="@id/send_edt" />
</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

你可以直接將這段代碼放入到你的Demo中,就會驚人的發現EditText會如你所願的彈出。

但是問題真正結束了麼?當然沒有。

在全屏化的體驗中,我們的背景往往是一個VideoViewSurfaceView等類似的播放控件。

如果僅僅按照我們如上所示的做,你會發現VideoViewSurfaceView這樣的控件,在播放時播放內容會被自動縮小,這也是我們不可接受的。

此時,我的佈局如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0099cc"
    tools:context="com.bzh.FullscreenActivity">

    <!-- The primary full-screen view. This can be replaced with whatever view
         is needed to present your content, e.g. VideoView, SurfaceView,
         TextureView, etc. -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#CCFF00FF"
        android:padding="30dp">

        <VideoView
            android:layout_centerInParent="true"
            android:id="@+id/fullscreen_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#55ff00ff"
            />
    </RelativeLayout>

    <!-- This FrameLayout insets its children based on system windows using
         android:fitsSystemWindows. -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <EditText
                android:id="@+id/send_edt"
                android:layout_width="match_parent"
                android:layout_height="55dp"
                android:layout_alignParentBottom="true"
                android:gravity="center_vertical|left"
                android:imeOptions="actionSend|flagNoExtractUi"
                android:lines="1"
                android:minHeight="38dp"
                android:padding="5dp"
                android:textColor="#222222"
                android:textColorHint="#b4b4b4"
                android:textSize="13sp"
                android:visibility="visible" />
            <!--也可以是RecyclerView或ListView,但是不能是LinearLayout或RelativeLayout等佈局-->
            <ScrollView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_above="@id/send_edt" />
        </RelativeLayout>
    </FrameLayout>
</FrameLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

那麼如何解決這個問題? 我在Google的全屏化Demo中找到了靈感。

藉助View.setSystemUiVisibility()方法來達到真正的全屏化目的

很多同學查閱Android如何全屏化的時候,往往都會得到如下的結果:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
  • 1
  • 1

使用Window的flag來達到目的,但是千萬不要這麼作,如果你的全屏化狀態帶有和用戶交互的行爲,你就應該選擇另一種全屏化代碼:

setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
  • 1
  • 1

具體詳細的例子可以看Google的官方例子:

AndroidStudio -> NewProject -> 項目名 -> 選擇模板(FullScreen)
  • 1
  • 1

當我們加上如上代碼時鍵盤彈出就不會導致VideoView控件播放內容被擠壓了。

但是,問題還沒有結束,你會發現EditText的輸入框不見了。

Why?這是因爲EditText並沒有隨着View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN生效後自動調整。

這個問題的解決辦法Google的FullScreen例子也給出瞭解決辦法,加入android:fitsSystemWindows.

<!-- This FrameLayout insets its children based on system windows using
     android:fitsSystemWindows. -->
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

至此,EditText所引發的問題就得到解決了。

Demo地址 [https://github.com/biezhihua/FullScreenDemo]

View.setSystemUiVisibility介紹

public void setSystemUiVisibility(int visibility)
  • 1
  • 1

達到交互式全屏化是調用了上述方法,並通過改變參數來達到隱藏和顯示狀態欄的目的,其方法簡介如下:

  1. 改變狀態欄或者屏幕其他系統UI的可見性。
  2. 允許你App的內容放置在系統操作欄(狀態欄)之下,通過平滑的過度效果來隱藏和顯示系統UI。
  3. 使用這個方法讓用戶集中更多的注意力在你應用的內容上。
  4. 典型的使用到這個方法的場景是:雜誌閱讀器/視頻播放器

總結來說,如果你做的是視頻播放器,使用這個方法來達到全屏化的目的,比單純調用下面的方法來達到全屏化,得到的交互效果會更加流暢和絲滑。

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
  • 1
  • 1

Flags參數到底是什麼意思?

接下來,我們就挨個看看這些常量都是什麼意思。

View.SYSTEM_UI_FLAG_LOW_PROFILE
View.SYSTEM_UI_FLAG_FULLSCREEN
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
View.SYSTEM_UI_FLAG_LOW_PROFILE
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

請求系統UI進入一個“低調”的模式。在遊戲,閱讀器,視頻播放器等需要“沉浸式”體驗的應用中,系統UI常常會讓用戶分心。在這種模式下,狀態欄和導航欄會變暗。

View.SYSTEM_UI_FLAG_FULLSCREEN
  • 1
  • 1

請求進入全屏模式,但同時仍然允許用戶與系統UI進行交互,這與WindowManager.LayoutParams.FLAG_FULLSCREEN具有相同的視覺效果,一些非關鍵畫面UI(如狀態欄)將被隱藏。

但是,WindowManager.LayoutParams.FLAG_FULLSCREEN更強調讓用戶一直處於一個連續的狀態,比如玩遊戲。而這個標記則更強調一種沉浸狀態,用戶可以迅速的退出這種狀態。

舉個例子,我們看小說的時候,很多時間會專心與小說本身,但是當用戶想看一下時間時,點擊屏幕,我們可以迅速友好顯示系統UI和一些操作控制UI。

View.SYSTEM_UI_FLAG_LAYOUT_STABLE
  • 1
  • 1

簡單來說,就是防止隱藏和顯示系統UI時,控件發生抖動。

如果加上這個設置,無論怎麼顯示和隱藏系統UI,我們都回到得到一個平滑的過渡效果。

假如你設置了WindowManager.FLAG_FULLSCREEN標記而不是View.SYSTEM_UI_FLAG_FULLSCREE,那麼你將得到一直處於隱藏狀態的狀態欄。

注意,改變或清除WindowManager.FLAG_FULLSCREEN不會得到一個平滑的過渡效果。

View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
  • 1
  • 1

效果是增強顯示和隱藏系統UI時的交互體驗。

如果不設置的話,那麼任何與系統UI的交互,都會導致退出全屏狀態。如果設置的話,那麼系統UI會短暫的覆蓋應用的內容,而且還有一定程度的透明度,並且短暫時間後自動隱藏,並不會退出全屏化。

看一下效果圖會更明顯一些。

這是全屏化狀態 
這裏寫圖片描述

這是設置後的效果。 
這裏寫圖片描述

這是未設置的效果,與系統UI交互直接退出全屏狀態。 
這裏寫圖片描述

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 
  • 1
  • 1
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
  • 1
  • 1

如果窗口有被設置爲SYSTEM_UI_FLAG_FULLSCREEN的需求,那麼請設置這兩個標記,這樣可以避免切換系統UI顯示與隱藏時,AppUI被覆蓋。

如果不設置就會出現下面的情況。 
這裏寫圖片描述

 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
  • 1
  • 1

隱藏導航欄

以上都是被全屏化的View所需要進行的處理。對於控制器來說,如果不在佈局中設置android:fitsSystemWindows=true屬性,那麼控制器的UI就會跑到狀態欄的下面。

這裏寫圖片描述

爲了避免以上的情況,就不得不解釋setFitsSystemWindows(boolean)fitSystemWindows(rect)方法了。

setFitsSystemWindows()方法是設置此視圖是否應該考慮系統UI,例如狀態欄。併爲其留出空間。如果參數爲true,代表會爲系統UI留出空間,那麼fitSystemWindows(rect)會被執行。

fitSystemWindows(rect)方法是,當Window內容的邊距發生改變時(進入全屏化),允許調用該方法調正我們App的UI,以適應Window的變化。

正常情況下,系統UI會入侵到AppUI的一些空間(狀態欄,輸入法,導航欄等),也就是我們App的UI不會出現在系統UI之下。

不過萬一你使用了SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION ,你的內容可能會被系統UI覆蓋。此時,你能調用這個方法,來確保你的UI不會被系統的UI覆蓋。

這個方法的簡單實現是使用padding來做到的,並且這個方法默認是不執行的。

其實,在橫屏全屏狀態下,之所以EditText不會被輸入法遮擋,都是由於執行了這個方法




Java和Android架構

Java和Android架構是一個數萬人關注的探討Java和Android開發的公衆號,分享和原創最有價值的乾貨文章,讓你成爲這方面的大牛!

我們探討android和Java開發最前沿的技術:android性能優化 ,插件化,跨平臺,動態化,加固和反破解等,也討論設計模式/軟件架構等。由羣來自BAT的工程師組成的團隊

關注即送紅包,回覆:“百度” 、“阿里”、“騰訊” 有驚喜!!!關注後可用入微信羣。羣裏都是來自百度阿里騰訊的大牛。

歡迎關注我們,一起討論技術,掃描和長按下方的二維碼可快速關注我們。搜索微信公衆號:JANiubility。

公衆號:JANiubility



發佈了26 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章