weex折叠屏适配方案

前序


将介绍 Weex 适配不同尺寸屏幕的方法以及横竖屏动态切换时如何自适应。

Weex 如何将前端样式值转换为系统坐标值

以 iOS 为例,在应用启动时,Weex 获取当前屏幕宽度作为全局默认值。在 iOS 系统上该宽度为实际像素/屏幕比例后的 UIKit 宽度。比如 iPhone6 为 375。

@implementation WXCoreBridge

+ (void)install
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        WeexCore::WXCoreEnvironment* env = WeexCore::WXCoreEnvironment::getInstance();
        env->SetPlatform(OS_iOS);
        env->AddOption("scale", "1");
        
        CGSize screenSize = [UIScreen mainScreen].bounds.size;
        env->SetDeviceWidth(std::to_string(screenSize.width));
        env->SetDeviceHeight(std::to_string(screenSize.height));
        ...
        ...
}

创建的每个 WXSDKInstance,其默认的 viewPortWidth 为 750px。

// The default screen width which helps us to calculate the real size or scale in different devices.
static const CGFloat WXDefaultScreenWidth = 750.0;

当指定 CSS 样式值为 “375px” 时,Weex 在接收到该样式后,自动根据当前屏幕宽度和当前 Instance 的 viewPortWidth 计算出在 iOS 系统上,对应的 UIKit 坐标值为:

dimension(UIKit) = dimensionPx(CSS) / viewPortWidth(instance) * globalScreenWidth
代入后: dimension(UIKit) = 375 / 750 * 375 = 187.5

之后 Weex 排版引擎使用 187.5 来排版,并最终将排版后的结果设置给 iOS UIView。之后没有坐标转换过程了。

一、查看weex官方提供meta模块API


meta模块API:setViewport

setViewport(options)

  • @options
    • width: number,具体数值或 “device-width” 和 “device-height” 宏
    • height: number,具体数据或 “device-width” 和 “device-height” 宏
    • deviceWidth: number,设置渲染容器宽度
    • roundOffDeviation: bool, 0.20.0+ & Android Only,表示layout引擎在布局时会忽略小数点导致的误差;若发现组件拼接处有缝隙,可以将 roundOffDeviation 设置为false,此时layout引擎将自动填补小数点误差,默认值为 true。
    • reserveCssStyles:bool,true (设置页面保留原始 CSS 样式值,0.25.0+)

Weex 容器默认的宽度 (viewport) 是 750px,通过 setViewport 方法可以改变页面的显示宽度,仅对当前页面生效。

在weex工程实例化之前设置该API内参数deviceWidth

meta.setViewport({
	width: 750,//UI设计图的渲染标准默认750px
	deviceWidth: [number],//设置数字类型参数
	roundOffDeviation: false,
	reserveCssStyles: true
});

(1)需要注意的是:只有在页面渲染开始之前设置 viewport 才会生效。 也就是说,setViewport 方法只能在入口文件中使用,而且要在 new Vue(…) 之前调用;如果是在组件中使用,就只有在渲染到该组件的时候才会执行相应的代码,此时页面已经处于渲染过程中,设置 viewport 将不会再生效。
(2)宽度和高度的单位默认是 px,暂不支持其他单位。

1、通过原生(安卓)在切换操作时的状态,在weex工程的入口文件entry.js内引入模块meta及安卓自定义模块screen-metrics(ios没有)。

/**
* entry.js(入口文件)
*/
const meta = weex.requireModule("meta");
const storage = weex.requireModule("storage");
const screenMstrice = weex.requireModule("screen-metrics");

    let eWidth
	if(screenMstrice){
        eWidth = screenMstrice.getScreenRealWidth();
    }else{
        eWidth = weex.config.env.deviceWidth
    }
    meta.setViewport({
        width: 750,
        deviceWidth: eWidth,
        roundOffDeviation: false,
        reserveCssStyles: true
    });

	//由于是回调函数形式,且new Vue()需要在设置生效后执行,因此,将该文件的其余处理都放置在此回调内执行
	...
	new Vue(Vue.util.extend({ el: "#root", router, store }, App));
	...

2、保存当前tab选中状态,在处理tab点击事件文件内处理

/*示例tabbar.vue*/

const context = weex.requireModule("context")
const storage = weex.requireModule("storage")
const screenMstrice = weex.requireModule('screen-metrics')

/*下边为两个相关处理函数*/

/*处理选中状态的函数*/
initState () {
	var baseURL = weex.config.bundleUrl.replace(/\/weex\/index.weex.js\/?.*/, '')
	this.tabLength = this.tabItems.length;
	for (let i = 0; i < this.tabLength; i++) {
		this.tabItems[i].src = baseURL + this.tabItems[i].src
		if (this.tabItems[i].visibility == "visible") {
			this.selectedIndex = i;
		}
	}
	/*在将新的选中tab页的index送去下一函数处理前,判断是否折叠操作,取值成功且为yes便使用缓存内的最后一次页面index*/
	storage.getItem("IsTabPage", (event) => {
		if (event.result == 'success' && event.data == 'yes') {
			this.selectedIndex = context.sessionGetString('tabSelIndex')
			this.select(this.selectedIndex);
		} else {
			this.select(this.selectedIndex);
		}
	})
}

/*tabbar底部按钮点击事件*/
tabItemOnClick(e){
    /*切换tab页同时主动存储当前屏幕渲染页面宽度*/
	storage.getItem("CurrentScreenWidth", (event) => {
      // vue实例化前设置viewport,判断该值是否存在就说明是否发生了折叠屏事件
      if (event.result != "success") {
          storage.setItem('CurrentScreenWidth', screenMstrice.getScreenRealWidth(), event => {})
      }
    });
	/*存储每次点击后的选中index*/
	context.sessionSetString('tabSelIndex', e.index)
}

1、上述提到weex自身模块storage使用字段CurrentScreenWidthIsTabPage是安卓sdk设定好的。
2、模块screen-metrics是安卓原生自定义模块。
3、该方案仅供安卓使用。
4、若ios报错没有screen-metrics模块,可自行注册。或者对应的方法可判断ios环境不执行。