动态布局下加载loading效果实现

动态布局下加载loading效果实现

HarmonyOS
2024-06-11 20:40:40
浏览
收藏 0
回答 1
回答 1
按赞同
/
按时间
踮脚在树梢上

应用市场需要实现加载loading效果,又因为应用市场是动态布局实现页面布局渲染的,所以加载loading需要包括2种:一是进入首页或页面跳转间的loading。二是应用内底部tab切换的loading。

原理

1. 进入首页或页面跳转间的loading:

2. 应用内底部tab切换的loading:

具体实现

(一) 进入首页或页面跳转间的loading。

1. 封装一个页面直接跳转加载的loading组件LocalStateCard。

@Component 
export struct LocalStateCard { 
  @State state: string = StatefulEvents.Send.Action.LOADING; 
  @Prop pageId: string; 
  @State payload: payloadProd = undefined; 
  private context: Context = getContext(this); 
  private callback; 
  
  aboutToAppear() { 
    this.registerEvent(); //emit注册监听事件,监听动态布局接口响应结果 
  } 
  
  aboutToDisappear() { 
    emitter.off(this.context, EmitEventIdStr.NOTICE_NETWORK_REQUEST_RESULT, this.callback); 
  } 
  
  build() { 
   LoadingProgress() 
      .color($r('sys.color.ohos_id_color_progress')) 
      .width(50) 
      .height(50) 
      .visibility(this.state === StatefulEvents.Send.Action.LOADING ? Visibility.Visible : Visibility.None) 
      .focusable(false) // 避免loading动画抢占焦点,导致loading动画结束搜索框才能获焦 
  } 
  
  private handleMessage() { 
    try { 
      if (!this.payload || this.payload?.pageId !== this.pageId) return; 
      this.state = this.payload.method; 
    } catch (err) {} 
  } 
  
  private registerEvent(){ 
    this.callback = (eventData) => { 
      this.payload = eventData.data; 
      this.handleMessage(); 
    }; 
    emitter.on(this.context, EmitEventIdStr.NOTICE_NETWORK_REQUEST_RESULT, this.callback); 
  } 
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

2. 在首页或者页面上添加LocalStateCard组件,如MainPage.ets中:即首页渲染LocalStateCard本地加载loading。

@Entry 
@Component 
struct PagePage { 
  private pageInstanceManager: PageInstanceManager | null = null; 
  @State pageId: string = 'c|xxxxxxx'; 
  
  aboutToAppear() { 
    this.pageInstanceManager = FLayoutHelper.getInstance().createPageInstanceManager(this.pageId); 
  } 
  
  build() { 
    Column() { 
      LocalStateCard({pageId: this.pageId}); 
      if (this.pageInstanceManager) { 
        Frame({ pageInstanceManager: this.pageInstanceManager }); 
      } 
    } 
    .width('100%') 
    .height('100%') 
    .backgroundColor($r('sys.color.ohos_id_color_sub_background')) 
  } 
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

3. 拦截动态布局接口响应,通过import emitter from '@ohos.events.emitter'向LocalStateCard通知请求结果。

LocalStateCard组件广播请求结果

export class PageLoadServiceImpl implements FLPageLoadService.Provider { 
    private currentEntryPageId: string = ""; 
    private isNetwork: boolean = true; 
  
    request(pageRequest: PageRequest, cardData?: FLCardData): Promise<JSONObject> { 
        return new Promise<JSONObject>((resolve,reject) => { 
            this.currentEntryPageId = pageRequest.getPageId(); 
            let retryTime = 0; 
                       const pageId: string = pageRequest.getPageId(); 
               const extra = pageRequest.getExtra(); 
               const pageSize: number = extra?.pageSize || 25; 
               let requestBean = new PageDetailRequestBean(1, pageSize, pageId); 
            new PageDetailRequest().request(requestBean).then((pageData) => {      
                resolve(pageData); 
            }).catch((err)=>{ 
                this.emitSuccess(this.currentEntryPageId, StatefulEvents.Send.Action.NONETWORK);  // 向LocalStateCard组件广播请求结果 
                reject(new FLException(err, ResponseErrorCode.ERROR, 'cardListRequest.cardListRequest request error')); 
            });;       
        }); 
    } 
  
  
    parse(pageParser: FLPageParser, data: JSONObject): Promise<FLPageBundle> { 
        return new Promise<FLPageBundle>((resolve, reject) => { 
            const pageId=data?.router?.entry; 
            let rtnCode = data?.rtnCode; 
            if (pageId) { 
                this.emitSuccess(this.currentEntryPageId, StatefulEvents.Send.Action.SUCCESS); 
                resolve(pageParser.parsePage(data.pages, pageId));           
            } else { 
                this.emitSuccess(this.currentEntryPageId, StatefulEvents.Send.Action.NONETWORK); // 向LocalStateCard组件广播请求结果 
                reject(new FLException(ResponseErrorCode.ERROR_NO_NETWORK, ResponseErrorCode.ERROR_NO_NETWORK, 'parse error ')); 
            } 
        }); 
    } 
    private emitSuccess(pageId: string, method: string) { 
        var eventData: EventData = { 
            data:{ method, pageId} 
        }; 
        const context: UIAbilityContext = getContext(); 
        emitter.emit(context, EmitEventIdStr.NOTICE_NETWORK_REQUEST_RESULT, eventData); 
    } 
} 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.

(二) 应用内底部tab切换的loading。

1. 封装一个应用内底部tab切换loading的原子卡组StatefullCard,并在动态布局中统一注册engine.registerCard(StatefullCardBuilder)engine.registerCard(StatefullCardBuilder)):

@Component 
export struct StatefullCard { 
  cardData: FLCardData; 
  private subscriberId: number = Subscriber.INVALID_ID; 
  @State statefulVisibility: Visibility = Visibility.Visible; 
  @State state: string = StatefulEvents.Send.Action.LOADING; 
  
  aboutToAppear() { 
    this.subscribe() 
  } 
  aboutToDisappear() { 
    this.unsubscribe() 
  } 
  
  private subscribe(): void { //注册监听,动态布局(id=stateful)加载成功的事件 
    this.subscriberId = EventQueue.sharedInstance.subscribe(this.subscriberId, this.cardData, function (msg:any){ 
      this.state = msg?.payload?.method; 
    }.bind(this)) 
  } 
  
  private unsubscribe(): void { 
    if (this.subscriberId != Subscriber.INVALID_ID) { 
        EventQueue.sharedInstance.unsubscribe(this.subscriberId); 
    } 
    this.subscriberId = Subscriber.INVALID_ID; 
  } 
  
  build() { 
    LoadingProgress() 
      .color($r('sys.color.ohos_id_color_progress')) 
      .width(50) 
      .height(50) 
      .visibility(this.state === StatefulEvents.Send.Action.LOADING ? Visibility.Visible : Visibility.None) 
      .focusable(false) // 避免loading动画抢占焦点,导致loading动画结束搜索框才能获焦 
  } 
  
  @Builder 
  static cardBuilder(options: BuildOptions<FLCardData>) { 
    StatefullCard({cardData: options.data} ) 
  } 
} 
  
export const StatefullCardBuilder = { 
  name: "statefullcard", 
  builder: StatefullCard.cardBuilder, 
  dataType: FLCardData 
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.

2. 配置卡片模板中的loading(即id=stateful):

{ 
  "name": "com.example.xxx.appgallery.homepage", 
  "type": "page", 
  }, 
  "children": [ 
    { 
      "type": "box", 
      "style": { 
        "height": "100%" 
      }, 
      "children": [ 
        { 
          "type": "tabs", 
          "data": { 
            "defaultItem": "{{tabScope.selection}}" 
          }, 
          "style": ".tabStyle", 
          "children": [ 
            { 
              "type": "tabcontent", 
              "data": { 
                "smoothScroll": false 
              }, 
              "children": [ 
                { 
                  "type": "box", 
                  "style": { 
                    "height": "100%" 
                  }, 
                  "s-for": "{{index,item in tabScope.list}}", 
                  "children": [ 
                    { 
                      "id": "stateful", 
                      "type": "statefullcard", 
                      "if": "{{index > 0}}" 
                    }, 
                    { 
                      "type": "frame", 
                      "style": { 
                        "height": "100%" 
                      }, 
                      "data": { 
                        "pageId": "{{item.frame.pageId}}" 
                      } 
                    } 
                  ] 
                } 
              ] 
            }, 
            { 
              "type": "tabitem", 
              "style": ".tabitemStyle", 
              "children": [ 
                { 
                  "type": "tabbutton", 
                  "s-for": "{{item in tabScope.list}}", 
                  "data": { 
                    "tabName": "{{item.title}}", 
                    "icon":"{{item.icon}}" 
                  } 
                } 
              ] 
            } 
          ] 
        } 
      ] 
    } 
  ] 
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.

疑问:

1. 为什么要分进入首页或页面跳转间的loading和应用内底部tab切换的loading。

答:因为应用市场是基于动态布局2.1页面级渲染的,再首次启动进入应用市场时,还未下载页面卡片模板,此时也就存在一定的空白界面。所以用首页或页面跳转间的loading,能避免启动到动态布局下载卡片模板这段时间的空白界面。

分享
微博
QQ
微信
回复
2024-06-12 17:42:00


相关问题
HarmonyOS 效果实现方案
1083浏览 • 1回复 待解决
HarmonyOS 动画效果实现
621浏览 • 1回复 待解决
HarmonyOS clipShape 动画效果实现
167浏览 • 0回复 待解决
HarmonyOS list选中效果实现
279浏览 • 1回复 待解决
HarmonyOS 类似翻页效果实现
677浏览 • 1回复 待解决
HarmonyOS 首页轮播效果实现
306浏览 • 1回复 待解决
PopWindow的效果实现有哪些?
966浏览 • 1回复 待解决
如何通过Progress实现loading效果
645浏览 • 1回复 待解决
引导遮罩效果实现的最佳方案
1784浏览 • 1回复 待解决
栅格布局怎么实现滚动效果
1127浏览 • 0回复 待解决
是否支持模块的动态加载?如何实现
2928浏览 • 1回复 待解决
如何在HarmonyOS中实现动态加载模块?
864浏览 • 1回复 待解决