AI摘要

本文介绍Fusion App2.0在本地交互和兼容性方面的提升,阐述其在混合开发中优势,如降低成本、无需服务器部署。还详细讲解加载本地H5、核心监听判断、模拟用户点击、隐藏网页滑动条、禁用浏览页长按菜单、本地H5更改网页字体等功能实现方法。

此内容由AI生成,仅用于文章内容的总结

前言

值得一提的是,Fusion App2.0相较于1.13版本,对于本地交互方面以及兼容性有质的飞跃,旧版本的交互还需要取一个网页加载过渡,这意味着我们无法将H5加载页面跳转放在本地,虽然H5布局可以放在本地,但是超链接点击会有闪烁,所以需要新建很多个相同的h5加载动画,来监听跳转。

好在新版本解决了这一问题,这让混合开发的成本大大降低,不需要服务器部署,完全本地也可以实现丝滑的交互。并且不需要去适配网页夜间模式,内置了网页夜间渲染。

下面我将详细讲解,由浅入深带你快速上手,并且提供了优化的思路与方法。

一、如何加载本地H5?

local uiManager=this.uiManager
--第1个浏览页加载本地网页,并且浏览页添加一个,地址填about:empty
activity.uiManager.currentPage.webView.loadUrl("file://"..activity.getLuaDir().."/index.html")--加载APP内部网页
--如果只有一个页面,这样就够了,如果有多个浏览页都加载本地网页看下面

--第2个浏览页加载本地网页
activity.uiManager.getFragment(1).webView.loadUrl("file://"..activity.getLuaDir().."/youxi/index2.html")
--第3个浏览页加载本地网页
activity.uiManager.getFragment(2).webView.loadUrl("file://"..activity.getLuaDir().."/guanyu/index3.html")
--第4个浏览页加载本地网页
activity.uiManager.getFragment(3).webView.loadUrl("file://"..activity.getLuaDir().."/start/index4.html")
--以此类推即可,不过一般四个就够了,需要开启离屏预加载

补充一个新方式:

local html = [[

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        .highlight-text {
            color: #C0341D;
            background-color: #FBE5E1;
            padding: 4px;
            border-radius: 4px;
        }
    </style>
    <title>高亮文本示例</title>
</head>
<body>
    <p><br>这是一个示例文本,<code class="highlight-text">高亮的文字在这里</code></p>
</body>
</html>

]]

local webView = LuaWebView(this)
webView.getSettings().setLoadWithOverviewMode(true)
webView.getSettings().setUseWideViewPort(true)

local FrameLayout = luajava.bindClass "android.widget.FrameLayout"
local Gravity = luajava.bindClass "android.view.Gravity"
local lp = FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT, FrameLayout.LayoutParams.FILL_PARENT)
activity.addContentView(webView,lp)
--add是添加,set是覆盖
webView.loadDataWithBaseURL(nil, html, nil, nil,nil)

这个方式可以实现不需要引入html文件,将html,css甚至js全部封装进lua进行解析。

二、核心监听判断

往往来讲,我们既然用H5,基本都是当布局使用的,所以,点击H5布局能实现与原生的交互,这是最核心的问题,我的思路就是通过网页监听事件,拦截H5的超链接,实现跳转子页面以及与其他原生功能交互。

--核心网页判断
import "net.fusionapp.core.ui.fragment.WebInterface"
import "androidx.viewpager.widget.ViewPager$OnPageChangeListener"
local uiManager=this.uiManager
local viewPager=uiManager.viewPager
local pagerAdapter=uiManager.pagerAdapter
local pagerCount=pagerAdapter.getCount()
function webInterface()
  for i = 0,pagerCount-1,1 do
    local fragment=uiManager.getFragment(i)
    if fragment then
      fragment.setWebInterface(WebInterface{onPageFinished=function(view,url)
          --页面加载结束事件,这个后退不能去掉,一直带着就可以
          网页后退()
        end,
        onPageStarted=function(view,url,favicon)
          --页面开始加载事件,思路就是你点击后可以拦截,直接进入你想要的子页面
          --也可以进行其他事件,不一定是进入子页面,自由发挥即可
          if url:find("这里填写你的h5布局元素的超链接") then
            网页后退()
            进入子页面("start")

            return false
          end

          if url:find("这里填写超链接") then
            网页后退()
            进入子页面("login")
            return false
          end
        end,
        onReceivedTitle=function(view,title)
          --获取到网页标题时加载的事件
        end,
        onLoadResource=function(view,url)
          --页面资源加载监听
          --可通过该方法获取网页上的资源
        end,
        onUrlLoad=function(view,url)

          --即将开始加载事件,url参数是即将加载的url
          --该函数返回一个布尔值
          --返回true则拦截本次加载
          return false
        end,
        onReceivedSslError=function(view, sslErrorHandler, sslError)
          --ssl证书错误处理事件
          --需自行处理,请返回true拦截原事件
          return false
        end
      })
    end
  end
end
webInterface()
viewPager.setOnPageChangeListener(OnPageChangeListener{
  onPageSelected=function(n)
    webInterface()
  end
})


Fa2默认的监听事件可以删掉,这里是监听所有浏览页的,不论你用了几个本地H5浏览页,都可以在上边这些代码中监听,网页后退()缺一不可。通过上述方法我们已经初步实现了网页点击与子页面的交互功能。

三、模拟用户点击

上面讲了H5去定向到原生,也就是说我们已经实现了以H5当布局时,点击布局元素实现原生的功能。

那么原生如何定向到H5呢?在一些引入外部网站时可能会用到,假设我们引入了一个我们的博客网站,但是我想让顶栏用原生框架,那么我们可以删除掉网站的原有头部顶栏元素(或者利用JS屏蔽元素)这里屏蔽要屏蔽最大的父元素。然后我们找到网站的侧滑栏按钮元素,用JS的模拟点击该元素。

可通过 document.querySelector 获取网页元素,再调用 click 方法来模拟点击。在原生Lua布局框架中调用网页,可将JavaScript代码注入到网页中执行。假设侧滑栏元素的CSS选择器为 .sidebar-toggle 

// 获取要点击的元素
var element = document.querySelector('.sidebar-toggle');
if (element) {
    // 模拟点击事件
    element.click();
} else {
    console.log('未找到对应的元素');
}


再举个简单的例子,比如APP我们设置了一个悬浮球,让他实现返回网页顶部。Fa中文函数模块配置了返回顶部功能,直接使用即可。

--悬浮按钮点击事件
function onFloatingActionButtonClick(v)
  --TODO:onFloatingActionButtonClick
  返回网页顶部()
end

--[[这里的Js应该是这个:
// 页面加载完成后执行滚动到顶部操作
window.onload = function() {
    window.scrollTo(0, 0);
};
]]


不过这个中文函数自带的返回顶部是直接跳到顶部,并不是平滑动画返回顶部,要实现平滑返回,可以这样写JS:

window.onload = function() {
    const duration = 500; // 滚动动画持续时间,单位毫秒
    const start = window.pageYOffset;
    const startTime = performance.now();

    function easeInOutQuad(t) {
        return t < 0.5? 2 * t * t : -1 + (4 - 2 * t) * t;
    }

    function animateScroll() {
        const currentTime = performance.now();
        const timeElapsed = currentTime - startTime;
        const scrollAmount = start * easeInOutQuad(timeElapsed / duration);

        window.scrollBy(0, -scrollAmount);

        if (timeElapsed < duration) {
            requestAnimationFrame(animateScroll);
        } else {
            window.scrollTo(0, 0);
        }
    }

    requestAnimationFrame(animateScroll);
};


总之,就是利用JS模拟用户点击,点击html中指定元素
并将这个加载JS代码写进你的原生点击事件即可。

四、隐藏网页滑动条

我们还可以把本地网页的上下滑动条给隐藏起来,把下面CSS代码放入本地html文件的head标签内即可

<style>
html,
body {
  overflow-y: hidden;
}
html {
  scroll-behavior: smooth;
}
</style>


当然,这个功能使用也会有一些网站无法生效,网页可能有其他CSS规则也在设置 overflow-y 属性,且具有更高的优先级,比如内联样式或者使用了 !important 声明的样式,就会覆盖你设置的 overflow-y: hidden 
一些框架或库也可能会动态地给 html 或 body 元素添加样式,影响滚动条的显示和页面的滑动。
还有就是如果网页内容的高度超出了视口高度,且没有设置合适的容器来限制内容的显示区域,可能会导致滚动条无法隐藏。因为浏览器为了让用户能够查看全部内容,会强制显示滚动条。

五、禁用浏览页的长按菜单

我们可以禁止本地浏览页的长按功能,无法复制和出现菜单,从而实现伪原生,你可以用lua,也可以用js实现。先来说lua代码:

--第一个浏览页禁止长按
local fragment=uiManager.currentFragment
fragment.setMenuInterface(luajava.bindClass"net.fusionapp.core.ui.fragment.WebViewMenuSupport".Interface{
  --显示浏览器菜单时,会经过这个方法
  --你可以通过这个方法自定义浏览器菜单
  --或者禁用浏览器菜单事件
  onMenuItemCreate=function(list)
    --添加一项
    list.add("白衣")
    --删除一项
    --list.remove("复制链接")
    --如果删除下面的return语句,则浏览器没有长按菜单
    -- return list
  end,
  
})

--第二个浏览页禁止长按
local fragment=uiManager.getFragment(1)
fragment.setMenuInterface(luajava.bindClass"net.fusionapp.core.ui.fragment.WebViewMenuSupport".Interface{
  onMenuItemCreate=function(list)
    --添加一项
    list.add("白衣")
    --删除一项
    --list.remove("复制链接")
    --如果删除下面的return语句,则浏览器没有长按菜单
    -- return list
  end,
})


另一个方法:
使用CSS禁止长按选中文本:在HTML文件的 <head> 标签内的 <style> 标签中添加如下代码:

* {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}


使用JavaScript禁止长按弹出菜单

document.addEventListener('contextmenu', function (e) {
    e.preventDefault();
}, false);

document.addEventListener('touchstart', function (e) {
    e.preventDefault();
}, { passive: false });

六、原生遮罩动画

你或许可以加入一个短暂遮罩动画函数,实现丝滑载入,配合离屏预加载谁知道你是H5?布局好看就完事了

七、本地H5更改网页字体

更改网页字体的目的是如果你的APP设置了自定义字体,那么你的网页尽可能的也需要使用相应的字体。字体不需要外链,直接读取APP内本地的字体文件即可,如果你的H5页面比较多,如果按照传统路径会增加大量的空间和重复字体文件,通过相对路径,实现所有本地网页加载同一个目录下的文件。

路径对照表

路径写法 含义说明
test.html 此页面在当前页面所在目录下
./test.html 此页面在当前页面所在目录下,与直接写文件名含义相同
/test.html 此页面在网站根目录下
../test.html 此页面在当前页面的上一级目录下
../../test.html 此页面在当前页面的上一级的上一级目录下(即上两级目录下),每增加一级上级目录,就增加一个../
../web/test.html 此页面在当前页面上一级目录的web子目录下

下面css代码放进html即可

@font-face {
    font-family: 'ziti';
    src: url('../../font.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

body {
    font-family: 'ziti', sans-serif;
}


这个实例的路径说明一下,字体font.ttf放在了工程根目录,这个css代码对应的html路径在子页面目录,即需要往上两级去加载文件,也就是../../

八、去除加载进度条

--去除加载进度条
activity.uiManager.getFragment(0).webView.parent.removeViewAt(2)
--去头部留白--document.body.style.paddingTop=0--此处用的是JS

九、浏览页自定义布局三种方法

第一种

function setContentView(h)
  local args={h}
  local list=table.clone(args)
  table.foreach(list,function(k,v)
    list[k]=luajava.instanceof(v,View)and v or loadlayout(v)--加载布局
    local webView=this.uiManager.getFragment(k-1).webView--获取浏览器控件( k-1 是网页的页面,可以自己改,注意不能没有浏览页)
    webView.setVisibility(8)--隐藏浏览器控件
    webView.parent.addView(list[k])--设置布局
  end)
end

layout={
  LinearLayout,
  layout_width=-1,
  layout_height=-1,
  gravity="center",
  {
    Button,
  },
}

setContentView(layout)--调用方法

第二种

local homepagelayout=--这里写布局,有几个页面就需要添加几个空白浏览页(about:blank)
activity.uiManager.getFragment(0).view.removeAllViews().addView(loadlayout(homepagelayout))
--lua布局对应fa自带浏览页 0就是第一个浏览页(需要开启全部离屏预加载)

第三种

local layout=--这里写布局,有几个页面就需要添加几个空白浏览页(about:none)
--上面的layout和下面括号里的layout对应,添加多个请有相应的修改。
this.uiManager.getFragment(0).view.addView(loadlayout(layout))
--lua布局对应fa自带浏览页 0就是第一个浏览页

补充这个自定义布局是为了应对有多个浏览页时,例如第一个浏览页用原生布局,第二个浏览页用HTML布局等等