# 设置加载页 UI
# 1. 效果展示
FinClip 小程序 SDK支持对加载页、错误页进行个性化配置。其中:
- 加载页是指:当启动小程序时,如果小程序未下载到设备,小程序容器会启动加载页提示用户等待,待小程序安装到设备上,加载页关闭并跳转至小程序。用户首次打开小程序、或小程序版本更新后,加载页停留时间会相对较长
- 错误页是指:小程序加载失败、或产生其他未知错误时,展示给用户的页面
具体UI效果可参考下图:
# 2. 覆盖范围
该设置由App实现,可配置加载页、错误页的视图,一经设置,App内的全部小程序、小游戏、H5应用均将按照本效果实现。
# 3. iOS 设置方法
# 3.1 实现自定义加载页
对于 iOS 小程序,SDK 支持开发者自定义加载页内容,您可按照以下步骤进行配置:
1.在继承自FATBaseLoadingView
的子类中修改加载页的样式。
2.45.7版本之前的代码示例如下:
@interface LoadingView : FATBaseLoadingView @end @implementation LoadingView - (instancetype)initWithFrame:(CGRect)frame { if ([super initWithFrame:frame]) { self.loadingView.padding = 20; self.loadingView.dotView.backgroundColor = [UIColor blackColor]; self.loadingView.animation.duration = 5; self.titleLabel.textColor = [UIColor redColor]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; // 如果要改logo,必须在这里修改。 // 修改小程序logo应该是从管理后台上传新的小程序图标,因为更多面板、关于页面也会展示小程序logo,只改这里只是loding页面生效 self.loadingView.iconImageView.image = [UIImage imageNamed:@"mini_logo"]; } @end
已复制代码
注意
因为小程序在加载过程中可能会更新加载页的背景色、小程序标题、小程序图标和各控件的frame,所以对这些属性的修改需要放在layoutSubviews内。 小程序的标题和图标建议通过管理后台中自定义菜单功能修改。
2.45.7及以后的版本,由于小程序支持设置主题模式,自定义加载页内容有点区别,代码示例如下:
@interface LoadingView : FATBaseLoadingView @end @implementation LoadingView - (instancetype)initWithFrame:(CGRect)frame { if ([super initWithFrame:frame]) { self.loadingView.padding = 20; self.loadingView.dotView.backgroundColor = [UIColor blackColor]; self.loadingView.animation.duration = 5; self.titleLabel.textColor = [UIColor redColor]; } return self; } - (void)updateThemeStyle:(BOOL)isDarkTheme { [super updateThemeStyle:isDarkTheme]; // 如果要改logo,必须在这里修改。 // 修改小程序logo应该是从管理后台上传新的小程序图标,因为更多面板、关于页面也会展示小程序logo,只改这里只是loding页面生效 self.loadingView.iconImageView.image = [UIImage imageNamed:@"mini_logo"]; } @end
已复制代码
2.将FATConfig
对象的baseLoadingViewClass
属性设置自定义加载页的类名。
FATConfig *config = [FATConfig configWithStoreConfigs:storeConfigs]; config.baseLoadingViewClass = @"LoadingView"; [[FATClient sharedClient] initWithConfig:config error:nil];
已复制代码
# 3.2 实现自定义加载失败页
实现方式与自定义加载页类似,具体步骤如下:
1.在继承自FATBaseLoadFailedView
的子类中修改加载失败页的样式。
@interface LoadFailedView : FATBaseLoadFailedView @end @implementation LoadFailedView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.errorLabel.textColor = [UIColor redColor]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat bottom = self.fat_height; self.errorImageView.fat_bottom = bottom - 110; self.errorLabel.fat_top = self.errorImageView.fat_bottom + 30; self.detailLabel.fat_top = self.errorLabel.fat_bottom + 15; } @end
已复制代码
2.将FATConfig
对象的baseLoadingViewClass
属性设置自定义加载失败页的类名。
FATConfig *config = [FATConfig configWithStoreConfigs:storeConfigs]; config.baseLoadFailedViewClass = @"LoadFailedView"; [[FATClient sharedClient] initWithConfig:config error:nil];
已复制代码
# 4. Android 设置方法
SDK提供了抽象类IFinAppletLoadingPage
,开发者可继承该类,实现自己的加载页视图和失败页视图,并在初始化SDK时配置实现类。
# 4.1 IFinAppletLoadingPage 抽象方法说明
/** * 小程序加载中的布局ID */ abstract fun getLoadingLayoutRes(): Int /** * 小程序加载失败的布局ID */ abstract fun getFailureLayoutRes(): Int /** * 小程序加载失败回调 * * @param msg 错误信息 */ @Deprecated("自2.36.3版本之后不再回调,请使用 onLoadingFailure(title: String, msg: String)") abstract fun onLoadingFailure(msg: String) /** * 小程序加载失败回调 * * @param title 错误标题 * @param msg 错误信息 */ abstract fun onLoadingFailure(title: String, msg: String) /** * 更新小程序相关信息,参数为小程序基本信息,会多次回调, * 初次回调时可能为空(如小程序初次启动,基本信息还未下载), * 需开发者自行进行判空。 * * @param appTitle app名字 * @param appAvatarUrl app图标链接 */ abstract fun onUpdate(appTitle: String, appAvatarUrl: String)
已复制代码
该类提供两个布局实例:
failureLayout
对应 getFailureLayoutRes 方法返回的错误页布局文件layout
loadingLayout
对应 getLoadingLayoutRes 方法返回的加载页布局文件layout
# 4.2 自定义加载页完整实现示例
注意
由于SDK内部使用了反射技术将实现类进行实例化,因此加载页实现类必须包含仅有Context
作为参数的构造函数。
示例如下
CustomLoadingPage:
class CustomLoadingPage constructor(context: Context) : IFinAppletLoadingPage(context) { override fun getFailureLayoutRes(): Int { // 错误页布局资源文件 return R.layout.loading_failure_layout } override fun getLoadingLayoutRes(): Int { // 加载页布局资源文件 return R.layout.loading_layout } override fun onLoadingFailure(msg: String) { // 该方法已废弃,留空即可,请使用下面的 onLoadingFailure(title: String, msg: String) } override fun onLoadingFailure(title: String, msg: String) { // 错误页错误消息正文 failureLayout.findViewById<TextView>(R.id.tvLoadingFailedTitle).text = title failureLayout.findViewById<TextView>(R.id.tvLoadingFailedMsg).text = msg } override fun onUpdate(appTitle: String, appAvatarUrl: String) { // 加载页小程序名字 loadingLayout.findViewById<TextView>(R.id.tvTitle).text = appTitle // 加载页小程序图标 ImageLoader.get(context).load(appAvatarUrl, object : DrawableCallback { override fun onLoadFailure() { } override fun onLoadSuccess(r: Drawable) { val ivAvatar = loadingLayout.findViewById<RoundedImageView>(R.id.ivAvatar) // 不要直接使用Handler post设置图标,应该先判断是否主线程,如果是主线程则直接设置图标, // 避免主线程更新时因Handler post导致图标显示时机变慢。 if (Looper.myLooper() == Looper.getMainLooper()) { ivAvatar.setImageDrawable(r) } else { handler?.post { ivAvatar.setImageDrawable(r) } } } }) } }
已复制代码
loading_failure_layout:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/fin_color_bg_pure_auto" android:orientation="vertical"> <TextView android:id="@+id/tvAppName" android:layout_width="wrap_content" android:layout_height="@dimen/fin_applet_navbar_height" android:layout_alignParentTop="@+id/rlLoadingFailed" android:layout_centerHorizontal="true" android:layout_marginTop="30dp" android:gravity="center" android:textColor="@color/fin_color_text_auto" android:textSize="18sp" android:textStyle="bold" android:visibility="gone" tools:text="小程序名称" tools:visibility="visible" /> <RelativeLayout android:id="@+id/rlLoadingFailed" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="125dp"> <ImageView android:id="@+id/ivLoadingFailed" android:layout_width="60dp" android:layout_height="60dp" android:layout_centerHorizontal="true" android:src="@drawable/fin_applet_loading_error" tools:ignore="ContentDescription" /> <TextView android:id="@+id/tvLoadingFailedTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/ivLoadingFailed" android:layout_centerHorizontal="true" android:layout_marginTop="25dp" android:gravity="center" android:text="@string/fin_applet_loading_failed_tip_without_message" android:textColor="@color/fin_color_text_auto" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/tvLoadingFailed" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tvLoadingFailedTitle" android:layout_centerHorizontal="true" android:layout_marginTop="15dp" android:gravity="center" android:textColor="@color/fin_color_secondary_text_auto" android:textSize="15sp" tools:text="错误提示" /> </RelativeLayout> <!-- 注意 fin_btn_fail_reload_margin_bottom 横竖屏的时候距离会不一样。 --> <TextView android:id="@+id/btnFailReload" android:layout_width="wrap_content" android:layout_height="40dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="@dimen/fin_btn_fail_reload_margin_bottom" android:background="@drawable/fin_applet_btn_reload_blue" android:gravity="center" android:minWidth="200dp" android:text="@string/fin_applet_load_again" android:textColor="@android:color/white" android:textSize="15sp" /> </RelativeLayout>
已复制代码
loading_layout:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/fin_color_bg_pure_auto" android:orientation="vertical"> <RelativeLayout android:id="@+id/rlLoading" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true"> <FrameLayout android:id="@+id/flAvatar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true"> <com.finogeeks.lib.applet.modules.appletloadinglayout.FinAppletLoadingSurfaceView android:id="@+id/finAppletLoadingView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <com.finogeeks.lib.applet.externallib.makeramen.roundedimageview.RoundedImageView android:id="@+id/ivAvatar" android:layout_width="52dp" android:layout_height="52dp" android:layout_gravity="center" android:src="@color/color_ebeced" app:fin_applet_riv_corner_radius="6dp" /> </FrameLayout> <TextView android:id="@+id/tvTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/flAvatar" android:layout_centerHorizontal="true" android:ellipsize="end" android:gravity="center" android:maxLines="2" android:textAlignment="center" android:textColor="@color/fin_color_text_auto" android:textSize="18sp" android:textStyle="bold" /> </RelativeLayout> <ImageView android:id="@+id/ivTechSupport" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="45dp" android:src="@drawable/fin_applet_tech_support" android:visibility="gone" tools:visibility="visible" /> </RelativeLayout>
已复制代码
# 4.3 在SDK初始化时配置加载页
val uiConfig = FinAppConfig.UIConfig() // 配置自定义加载页 uiConfig.setLoadingLayoutCls(CustomLoadingPage::class.java) val config = FinAppConfig.Builder() // 其它配置项省略 .setUiConfig(uiConfig) .build() FinAppClient.init(application, config, object : FinCallback<Any?> { override fun onSuccess(result: Any?) { } override fun onError(code: Int, error: String) { } override fun onProgress(status: Int, error: String) { } })
已复制代码
# 5. Harmony 设置方法
# 5.1 实现自定义加载页
在 SDK 初始化前添加代理方法,具体参数可以参考CustomLayoutHandler
示例代码:
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk' class CustomLayoutHandler extends IFinProxyHandlerItem.CustomLayoutHandler { // 如果小程序启动依赖某些异步数据才渲染,比如请求等,这时候小程序可能还是处于无数据的状态,可以通过延迟调用 resolve 的方式来延缓 loading 页销毁时机 onLoadingLayoutReady(): Promise<void> { return new Promise((resolve) => { setTimeout(() => { resolve() }, 200) }) } getCustomLoadingLayout(): (() => void) | void { return CustomLoadingLayout } } FinAppProxyHandlerManager.customLayoutHandler = new CustomLayoutHandler() @Builder function CustomLoadingLayout() { CustomViewComponent() } // 该组件会渲染在整个 loading 页面 @Component struct CustomViewComponent { @Consume layoutParams: IFinAppProxy.ICustomLoadingLayoutParams build() { Column() { Image(this.layoutParams.logo) Text(this.layoutParams.name) } } }
已复制代码
# 5.2 实现自定义加载失败页
在 SDK 初始化前添加代理方法,具体参数可以参考CustomLayoutHandler
示例代码:
import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk' class CustomLayoutHandler extends IFinProxyHandlerItem.CustomLayoutHandler { // 如果小程序启动依赖某些异步数据才渲染,比如请求等,这时候小程序可能还是处于无数据的状态,可以通过延迟调用 resolve 的方式来延缓 loading 页销毁时机 onLoadingLayoutReady(): Promise<void> { return new Promise((resolve) => { setTimeout(() => { resolve() }, 200) }) } getLoadFailedLayout(): ((appId: string, apiServer: string, title: string, label: string) => void) | void { return LoadFailedLayout } } FinAppProxyHandlerManager.customLayoutHandler = new CustomLayoutHandler() // 这里会传入小程序的页面的信息 @Builder function LoadFailedLayout(appId: string, apiServer: string, title: string, label: string) { CustomViewComponent({ appId, apiServer, title, label }) } @Component struct CustomViewComponent { @Prop appId: string @Prop apiServer: string @Prop title: string @Prop label: string build() { Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center, direction: FlexDirection.Column }) { Column() { Text('appletName: ' + this.title) Text('label: ' + this.label) Text('apiServer: ' + this.apiServer) Text('appId: ' + this.appId) } } .backgroundColor(Color.Green) } }
已复制代码