与DialogX一起实现全屏WebView对话框和沉浸式适配
与DialogX一起实现全屏WebView对话框和沉浸式适配
开发中我们往往会遇到一种需求,需要临时弹出一个 Web 层展示 H5 页面,iOS 端提供了一种原界面下沉的全屏展开 WebView 可以很方便的实现,用户也可以通过下滑操作关闭 Web 页面的浏览返回原界面。
要在 Android 平台实现类似的效果,可以尝试使用 DialogX 的 FullScreenDialog 搭配 WebView 实现沉浸式的全屏浏览器呈现,同样的,也支持滑动到 Web 页面顶端时继续向下滑动关闭对话框的操作,另外也支持沉浸式哦,跟随我一起来看看我是如何实现这样炫酷的功能吧!
首先介绍一下 DialogX
DialogX 是一款简单易用的对话框组件,相比原生对话框使用体验更佳,可自定义程度更高,扩展性更强,轻松实现各种对话框、菜单和提示效果,更有iOS、MIUI、Material You等主题扩展可选。
先来一起创建一个全屏 WebView 对话框吧!
如上图所示,是一个从屏幕下方展开的全屏对话框,它基于 DialogX 的 FullScreenDialog 实现,只需要自定义一个 WebView 布局即可,要实现滑动继承,需要自定义 WebView 并实现一个 DialogX 的接口,范例代码如下所示:
public class CustomWebView extends WebView implements ScrollController {
public CustomWebView(@NonNull Context context) {
super(context);
}
public CustomWebView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomWebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
@Deprecated
public boolean isLockScroll() {
return lockScroll;
}
boolean lockScroll;
@Override
public void lockScroll(boolean lockScroll) {
this.lockScroll = lockScroll;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (lockScroll) return false;
return super.onTouchEvent(event);
}
@Override
public int getScrollDistance() {
return getScrollY();
}
boolean canScroll = true;
public CustomWebView setCanScroll(boolean canScroll) {
this.canScroll = canScroll;
return this;
}
@Override
public boolean isCanScroll() {
return canScroll;
}
}
按照上述固定写法即可。
将 CustomWebView 添加一份布局,放上关闭按钮即可完成界面布局的搭建工作。为方便后续操作,暂时将布局命名为 layout_dialog_webview
参考布局代码如下,请注意设置 tag 为 ScrollController
以方便 FullScreenDialog 绑定滑动继承组件:
<?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="50dp"
android:orientation="horizontal">
<Space
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<TextView
android:id="@+id/btn_close"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="关闭"
android:textColor="@color/dialogxIOSBlue"
android:textSize="18dp" />
</LinearLayout>
<com.kongzue.dialogxdemo.CustomWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="ScrollController" />
</LinearLayout>
接下来编写 FullScreenDialog 的实现代码
FullScreenDialog.show(new OnBindView<FullScreenDialog>(R.layout.layout_dialog_webview) {
private TextView btnClose;
private CustomWebView webView;
@Override
public void onBind(FullScreenDialog dialog, View v) {
btnClose = v.findViewById(R.id.btn_close);
webView = v.findViewById(R.id.webView);
//Set WebView Settings...
webView.loadUrl("https://www.example.com");
}
});
即可启动一个全屏展示的 WebView 对话框并加载一个页面。
那么该如何实现沉浸式适配呢?FullScreenDialog 默认情况下已经做好了一些沉浸式工作,例如对话框的内容一定会在沉浸式的“安全区”范围内,对话框顶部只会上移到手机状态栏以下,而对话框内容底部也会在导航栏以上,但这距离我们想要的页面内容“下沉”到导航栏以下的需求不符,此时只需要一个开关即可实现:
.setBottomNonSafetyAreaBySelf(true)
此设置是 FullScreenDialog 的设置,如果没有请更新至最新测试版本的 DialogX,开启后内容布局将被允许下沉到底部导航栏以后显示,此时 Web 页面将能够沉浸的显示在导航栏后了,那么接下来的问题就是如何为页面内容设置一个 paddingBottom,使其内容不分可以在用户可操作的屏幕安全区内,沉浸,但不影响正常使用。
设置页面内容到安全范围内
常见的方案大致是对内容直接设置一个导航栏高度的 paddingBottom,使其背景不受隐藏但内容在安全范围内,
我之前 有一篇文章 有说过沉浸式的基本逻辑在于正确的对安全区和非安全区的处理,原则即:将背景下沉,将操作区域和主要内容区域放在安全区内:
要实现这点,我们需要获取底部导航栏的高度,再对 WebView 的页面内容进行 paddingBottom 的设置,前者其实 DialogX 已经帮你处理好了,只需要一句代码即可获取:
int bottomUnsafeAreaHeight = dialog.getDialogImpl().boxRoot.getUnsafePlace().bottom;
但实际上,不建议这样粗暴的获取,因为非安全区位置可能因为横竖屏切换,以及不分设备上可以隐藏导航栏导致高度发生变化,更建议的方案是动态回调的方式获取,这样更加安全可靠:
dialog.getDialogImpl().boxRoot.setOnSafeInsetsChangeListener(new OnSafeInsetsChangeListener() {
@Override
public void onChange(Rect unsafeRect) {
int bottomUnsafeAreaHeight = unsafeRect.bottom;
}
});
bottomUnsafeAreaHeight 的值就是底部非安全区的高度啦,单位是像素。
但此时你又会遇到一个严重问题,那就是 WebView 不吃 setPadding
这套,对 WebView 设置 padding 是无效的,我们需要使页面内容 paddingBottom = bottomUnsafeAreaHeight 的距离以保证当 WebView 滑动到底部后为导航栏空出一段距离。
那么此时就得用到 WebView 的 Client,在其中 onPageFinished
方法设置一段 js 来为页面的 body 设置一段底部 padding 来达成需要的效果,不废话直接上代码:
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
if (unsafeRect != null) {
webView.loadUrl("javascript:document.body.style.paddingBottom="" + px2dip(bottomUnsafeAreaHeight) + "px"; void 0");
}
super.onPageFinished(view, url);
}
}
其中,px2dip
是一个像素对 dp 的转换工具,因为适配移动端的 H5 在 WebView 呈现时默认会适应屏幕的像素密度,因此对于网页内容中的 px 实际上等同于 dp 的值,因此此处需要将底部非安全区的高度转换为 dp 当做 px 设置给页面。
上述代码为页面的 body 添加了 paddingBottom 的设置,但这需要 H5 页面支持,如果你需要显示的 H5 页面不生效,请根据实际需要检查内部元素适当的调整上述 js 以适配实际需求。
至此,基于 DialogX 的 FullScreenDialog 实现全屏 WebView 和沉浸式适配工作也就基本完成了,让我们在 App 全部沉浸式的道路上更进一步吧!
转载自:https://juejin.cn/post/7219312510588125244