1 关键字

Launcher,拖动应用,图标闪烁

2 问题描述

开发板型号:RK3568

系统版本:Openharmony 3.2.13.5

问题现象:拖动桌面应用后,桌面其他应用图标闪烁

测试步骤:

  1. 下载每日构建的 rk3568镜像 烧录设备

  2. 正常解锁进入桌面,拖动桌面上其中一个应用,拖动到目标位置后放开,发现桌面其他应用会闪烁一下。

3 问题原因

在拖动图标后,桌面应用的列表数据进行了替换更新,导致整个桌面页面进行了重新渲染,应用图标重新加载导致闪烁。

3.1 正常机制

  • 在拖动应用放置后,拖动的应用放置到目标位置,其他应用图标正常显示无闪烁。

3.2 异常机制

  • 拖动完成后其他应用图标发生闪烁现象。

4 解决方案

修改桌面数据的绑定方式,在拖动图标后,只更新数据变化了的应用数据,做到桌面局部更新,避免其他应用图标重新渲染导致闪烁。

拉取 Launcher应用OpenHarmony-3.2-Release分支的代码:

git clone -b OpenHarmony-3.2-Release https://gitee.com/openharmony/applications_launcher.git

1.修改feature/pagedesktop/src/main/ets/default/common/components/GridSwiper.ets文件:

修改一:添加appListInfo的数据变化监听事件及响应方法,新增appGridInds管理桌面。

img

@StorageLink('appListInfo') @Watch('updateAppListInfo') appListInfo: {
@State appGridInds: Array<any> =  [];
updateAppListInfo(){
    if(this.appListInfo.appGridInfo.length > this.appGridInds.length){
        while (this.appListInfo.appGridInfo.length > this.appGridInds.length){
            this.appGridInds.push(this.appGridInds.length);
        }
    } else if(this.appListInfo.appGridInfo.length < this.appGridInds.length){
        while (this.appListInfo.appGridInfo.length < this.appGridInds.length){
            this.appGridInds.pop();
        }
    }
}

修改二:使用新的appGridInds数据进行页面加载

img

ForEach(this.appGridInds, (item) => {
# 注意,这一行有两个位置需要修改
swiperPageIndex: item,

2.修改feature/pagedesktop/src/main/ets/default/common/components/SwiperPage.ets文件:

修改一:修改mAppListInfo状态,添加对appListInfo数据的监听,添加swiperPageIndex属性

img

@StorageLink('appListInfo') @Watch('updateAppListInfo') appListInfo: {
    appGridInfo: [[]]
} = { appGridInfo: [[]] };
@State mAppListInfo: Array<any> = [];
private mAppListInfoStr: string = "[]";
private swiperPageIndex;

修改二:添加appListInfo数据更新响应的方法

img

updateAppListInfo(){
    let appListInfo:any = this.appListInfo.appGridInfo[this.swiperPageIndex];
    let len = this.mAppListInfo.length;
    if(appListInfo.length > this.mAppListInfo.length){
        while (appListInfo.length > this.mAppListInfo.length){
            this.mAppListInfo.push(appListInfo[this.mAppListInfo.length]);
        }
    } else if(appListInfo.length < this.mAppListInfo.length){
        len = appListInfo.length;
        while (appListInfo.length < this.mAppListInfo.length){
            this.mAppListInfo.pop();
        }
    }
    let mAppListInfo = JSON.parse(this.mAppListInfoStr);
    while (len > 0){
        let data = appListInfo[len-1];
        let mdata = mAppListInfo[len-1];
        let shouldUpdate = false;
        for(let key in data){
            if(data[key] != mdata[key] && JSON.stringify(data[key]) != JSON.stringify(mdata[key])){
                shouldUpdate = true;
            }
        }
        if(shouldUpdate){
            this.mAppListInfo[len-1] = {...data};
        };
        len--;
    }
    this.mAppListInfoStr = JSON.stringify(this.mAppListInfo);
}

修改完成后重新编译打包,替换Launcher应用即可。

5 定位过程

  1. 最开始猜测是桌面图标在重新加载时,未做图片数据缓存而是去重新拿图片渲染导致闪烁,排查后发现图片数据在第一次加载完成后就已经做了缓存,并且图片渲染是优先拿缓存数据。

img

  1. 然后注意到所有桌面和所有应用是统一管理的, 使用this.appListInfo.appGridInfo数据统一渲染:

img

添加对this.appListInfo.appGridInfo数据的监听:

img

打印日志后发现每次拖动后,数据都会发生变化,并且桌面图标闪烁

img

由此产生了不使用this.appListInfo.appGridInfo来进行桌面渲染,创建新的数据单独管理桌面页数并进行标号,在单个桌面中,根据桌面标号在this.appListInfo.appGridInfo中拿到对应桌面的应用列表来渲染桌面,在应用被拖动时,对比数据对桌面应用数据进行单独更新。详细参考[解决方案](# 4 解决方案)

6 知识分享

在开发比较复杂的页面时,要避免使用一个大的数据对象来进行整个页面结构树的渲染,封装的组件在与父组件无直关联操作时,其数据应选择由组件本身管理,父组件只关注与父组件层级的业务逻辑,比如此例中,子组件中应用数据的变化导致父组件的数据更新所有子组件进行了重新渲染导致了页面闪烁,要避免此情况只需在应用拖动时,只改变当前拖动的应用数据,其他部分数据不做更新即可。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐