如何做好横屏全屏化交互体验以及其中遇到的坑

前言

最近公司刚起了一个直播项目,我被抽过去参与,负责其中的直播页,在写直播页的过程中,尤其是横屏状态下,用户的交互出现了很大的问题。
问题是:当用户点击EditText控件时,输入法键盘挡住了部分EditText控件。

在解决的过程中,翻阅的大量的文章:

Android 5.0及以上如何在Translucent模式下防止键盘挡住EditText

Android 5.0 如何实现将布局的内容延伸到状态栏实?

关于透明状态栏的使用以及与软键盘冲突的解决办法

android 动态控制状态栏显示和隐藏的方法实例

动态显示和隐藏状态栏

StatusBarUtil

JKeyboardPanelSwitch

虽然,翻阅了这么多文章,但是然并卵,问题并没有得到解决。最后还是在谷歌官方的例子中找到启发。

这篇文章会带你了解的东西

1 让你知道,你到底需要哪种全屏模式。
2 解决全屏模式下(添加透明状态栏情况下),EditText被挡住的问题。
如何做好横屏全屏化交互体验以及其中遇到的坑_第1张图片

先解决问题(不涉及沉浸式状态栏)

对于视频类项目来说,横屏代表着用户的参与度更高,那么也相应的需要更好的交互体验,一般来说横屏状态下,存在两个View:一个是用于视频播放的View,另一个是用于控制视频状态的控制器View。想要做到完善的交互,对于布局文件来说,还是有一定的格式要求的,请看:

<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" >

    <!-- 用于全屏化的View,你可以替代成VideoView, SurfaceView,TextureView等 -->
    <TextView  android:id="@+id/fullscreen_content" android:layout_width="match_parent" android:layout_height="200dp" android:background="#ff00ff" android:gravity="center" android:keepScreenOn="true" android:text="@string/dummy_content" android:textColor="#33b5e5" android:textSize="50sp" android:textStyle="bold" />

    <!-- 这里是控制器的布局。对于FrameLayout来说,让其使用 android:fitsSystemWindows=true的属性,可以适应系统UI,以保证App的View不被系统UI遮挡。 -->
    <FrameLayout  android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true">

        <LinearLayout  android:id="@+id/fullscreen_content_controls" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="bottom" android:orientation="vertical">

            <EditText  android:layout_width="match_parent" android:layout_height="wrap_content" android:imeOptions="actionDone|flagNoExtractUi" />
        </LinearLayout>
    </FrameLayout>
</FrameLayout>

剩下的就需要代码处理了,既然需要横屏全屏化,你往往会添加如下或者与之类似的代码,以达到全屏化的目的:

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

但是,千万不要这么做。这个全屏化和我们理想中的全屏化是有差异的,具体差异稍后再说。

为了达到全屏化的目的,我们会监听屏幕旋转事件,当进入横屏模式时, 重新修改需要全屏化View的布局参数(VideoView等),让其宽高等于屏幕的宽高,然后在处理真正的用于全屏化的逻辑。

  @Override
   public void onConfigurationChanged(Configuration newConfig) {
       super.onConfigurationChanged(newConfig);
       if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
           hideSystemBar();
           showSystemBarDelay();
           setTranslucentState();
           float height = DensityUtil.getWidthInPx(this);
           float width = DensityUtil.getHeightInPx(this);
           video_player.getLayoutParams().height = (int) height;
           video_player.getLayoutParams().width = (int) width;
       } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
           showPortraitStateSystemBar();
           clearTranslucentState();
           float width = DensityUtil.getWidthInPx(this);
           float height = getResources().getDimension(R.dimen.super_video_default_height);
           video_player.getLayoutParams().height = (int) height;
           video_player.getLayoutParams().width = (int) width;
       }
   }

其中最核心的代码为:

public void hideSystemBar() {
    int systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
        systemUiVisibility |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    }
    video_player.setSystemUiVisibility(systemUiVisibility);

    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) video_player.getLayoutParams();
    layoutParams.topMargin = 0;
    video_player.setLayoutParams(layoutParams);
}

public void showSystemBar() {
    video_player.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}

只要你按照这种方式处理完,那么横屏状态下EditText控件被遮挡的问题也就随之解决了,即使你加上了透明状态栏想关的代码,EditText也会正常显示。

有兴趣的朋友,可以去在AS上新建一个工程,选择FullScreen的模板,然后自己研究研究。

那么,为什么会这样呢?

首先,我们注意到,在两个关键方法中,都是对被全屏化的View调用了其setSystemUiVisibility(int visibility)方法,通过改变参数,来达到隐藏和显示状态栏的目的。

具体看看这个方法的说明:
public void setSystemUiVisibility(int visibility)

  1. 改变状态栏或者屏幕其他系统UI的可见性。
  2. 允许你App的内容放置在系统操作栏(状态栏)之下,通过平滑的过度效果来隐藏和显示系统UI。
  3. 使用这个方法让用户集中更多的注意力在你应用的内容上。
  4. 典型的使用到这个方法的场景是:杂志阅读器/视频播放器

总结来说:如果你做的是视频播放器,使用这个方法来达到全屏化的目的,比单纯调用getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);这块代码来达到全屏化,得到的交互效果会更加流畅和丝滑。

接下来,我们具体看看这些参数到底什么意思。

在查找资料的过程中,翻看到这篇文章:android 动态控制状态栏显示和隐藏的方法实例

其中博主提到,它使用的方法1没有实现效果的原因在于。调用setSystemUiVisibility()的View不对,并不是随便一个View来调用都能达到全屏化的效果的,必须是被全屏化的那个View来调用才行。

接下来,我们就挨个看看这些常量都是什么意思。

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

请求系统UI进入一个“低调”的模式。在游戏,阅读器,视频播放器等需要“沉浸式”体验的应用中,系统UI常常会让用户分心。在这种模式下,状态栏和导航栏会变暗。

View.SYSTEM_UI_FLAG_FULLSCREEN

请求进入全屏模式,但同时仍然允许用户与系统UI进行交互,这与WindowManager.LayoutParams.FLAG_FULLSCREEN具有相同的视觉效果,一些非关键画面UI(如状态栏)将被隐藏。

但是,WindowManager.LayoutParams.FLAG_FULLSCREEN更强调让用户一直处于一个连续的状态,比如玩游戏。而这个标记则更强调一种沉浸状态,用户可以迅速的退出这种状态。

举个例子,我们看小说的时候,很多时间会专心与小说本身,但是当用户想看一下时间时,点击屏幕,我们可以迅速友好显示系统UI和一些操作控制UI。

View.SYSTEM_UI_FLAG_LAYOUT_STABLE

简单来说,就是防止隐藏和显示系统UI时,控件发生抖动。

如果加上这个设置,无论怎么显示和隐藏系统UI,我们都回到得到一个平滑的过渡效果。

假如你设置了WindowManager.FLAG_FULLSCREEN标记而不是View.SYSTEM_UI_FLAG_FULLSCREE,那么你将得到一直处于隐藏状态的状态栏。注意,改变或清除WindowManager.FLAG_FULLSCREEN不会得到一个平滑的过渡效果。

View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY

其效果是增强显示和隐藏系统UI时的交互体验。

如果不设置的话,那么任何与系统UI的交互,都会导致退出全屏状态。如果设置的话,那么系统UI会短暂的覆盖应用的内容,而且还有一定程度的透明度,并且短暂时间后自动隐藏,并不会退出全屏化。

看一下效果图会更明显一些。

这是全屏化状态
如何做好横屏全屏化交互体验以及其中遇到的坑_第2张图片

这是设置后的效果。
如何做好横屏全屏化交互体验以及其中遇到的坑_第3张图片

这是未设置的效果,与系统UI交互直接退出全屏状态。
如何做好横屏全屏化交互体验以及其中遇到的坑_第4张图片

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

如果窗口有被设置为SYSTEM_UI_FLAG_FULLSCREEN的需求,那么请设置这两个标记,这样可以避免切换系统UI显示与隐藏时,AppUI被覆盖。

如果不设置就会出现下面的情况。
如何做好横屏全屏化交互体验以及其中遇到的坑_第5张图片

View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

隐藏导航栏

以上都是被全屏化的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不会被输入法遮挡,都是由于执行了这个方法。

你可能感兴趣的:(android,输入法,布局,控件,体验)