When webview loads a web page in HarmonyOS, it needs a progress bar or loading animation for user-perceived interaction, which can optimize the user experience, so today I wrote a loading animation (the effect is as follows) for students to learn, how to achieve it? First of all, we need to learn the three knowledge reserves of " CommonDialog ", " WebView " and " Animation Development Guide "
We are divided into "preparation stage", "custom CommonDialog implementation", "animation implementation", "webview implementation", "running effect" five steps to implement.
1. Preparation stage
Prepare a loading picture (picture below) in the resources \base\ media\ directory and the storage location is as follows
Loading pictures
Storage location
2. Customize the implementation of CommonDialog
2.1 Create a new xml named general_dialog.xml, and draw a picture in the xml file (the code is as follows)
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="80vp"
ohos:orientation="vertical"
ohos:alignment="center"
ohos:width="80vp">
<Image
ohos:id="$+id:loading"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:scale_mode="clip_center"
ohos:image_src="$media:loading"/>
</DirectionalLayout>
The effect diagram is as follows
2.2 Create a new GeneralDialog file, we refer to the custom CommonDialog scene example of HarmonyOS
The specific code is as follows
package com.harmony.alliance.mydemo.utils;
import java.util.Optional;
import com.harmony.alliance.mydemo.ResourceTable;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
public class GeneralDialog {
private float dim = -1f;
private CommonDialog sDialog;
private Context mContext;
private boolean mOutsideTouchClosable = false;
public GeneralDialog(Context context){
this.mContext=context;
}
public void show() {
if (sDialog != null) {
sDialog.show();
if (dim >= 0) {
changeDialogDim(sDialog, dim);
}
}
}
public void remove(){
if (sDialog != null) {
sDialog.destroy();
}
}
public void create() {
sDialog = new CommonDialog(mContext);
sDialog.setSize(ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT);
sDialog.setAlignment(LayoutAlignment.CENTER);
sDialog.setOffset(0,0);
sDialog.setTransparent(true);
sDialog.setContentCustomComponent(initDialog(sDialog));
sDialog.setAutoClosable(mOutsideTouchClosable);
}
private void changeDialogDim(CommonDialog dialog, float dim) {
Optional<WindowManager.LayoutConfig> configOpt = dialog.getWindow().getLayoutConfig();
configOpt.ifPresent(config -> {
config.dim = dim;
dialog.getWindow().setLayoutConfig(config);
});
}
public interface ClickedListener{
void onClick(GeneralDialog dialog);
}
private Component initDialog(CommonDialog sDialog) {
Component dialogLayout = LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_general_dialog, null, false);
dialogLayout.setBackground(new ShapeElement(){{
setRgbColor(RgbColor.fromArgbInt(ResourceTool.getColor(mContext, ResourceTable.Color_bg_dialog_light, 0xffffff)));
setCornerRadius(ResourceTool.getFloat(mContext, ResourceTable.Float_dialog_corner_radius, 0));
}});
Image image= (Image) dialogLayout.findComponentById(ResourceTable.Id_loading);
return dialogLayout;
}
}
2.2.3 Call the following code under the onStart method of MainAbility
GeneralDialog mGeneralDialog = new GeneralDialog(getContext());
mGeneralDialog.create();
mGeneralDialog.show();
The effect is as follows
2.3 Animation implementation
2.3.1 Animation function We can refer to the relevant knowledge points of AnimatorProperty in the HarmonyOS animation development guide . Next, we initDialog to open the animation function code of image as follows
Image image= (Image) dialogLayout.findComponentById(ResourceTable.Id_loading);
animatorProperty = new AnimatorProperty();
animatorProperty.setTarget(image);
animatorProperty
.rotate(360)
//无限循环
.setLoopedCount(AnimatorValue.INFINITE)
//反弹力效果
.setCurveType(Animator.CurveType.BOUNCE);
if(sDialog!=null){
sDialog.setDestroyedListener(new CommonDialog.DestroyedListener() {
@Override
public void onDestroy() {
if(animatorProperty.isRunning()){
animatorProperty.stop();
}
}
});
}
2.3.2 Write an open animation method in the GeneralDialog class (the code is as follows)
public void StartanimatorProperty(){
if(animatorProperty!=null&&animatorProperty.isRunning()){
animatorProperty.stop();
}
if(animatorProperty!=null&&!animatorProperty.isRunning()){
if(!animatorProperty.isRunning()){
animatorProperty.start();
}
}
}
2.3.3 Seal a tool class for displaying, playing and disappearing the loading popup (the code is as follows)
package com.harmony.alliance.mydemo.utils;
import ohos.app.Context;
public class LoadingDialogUtils {
private static GeneralDialog mGeneralDialog;
public static GeneralDialog getInstance(Context mContext){
if(mGeneralDialog==null){
mGeneralDialog=new GeneralDialog(mContext);
}
return mGeneralDialog;
}
public static void show(Context mContext){
LoadingDialogUtils.getInstance(mContext);
mGeneralDialog.create();
mGeneralDialog.show();
mGeneralDialog.StartanimatorProperty();
}
public static void dismiss(Context mContext){
LoadingDialogUtils.getInstance(mContext);
mGeneralDialog.remove();
}
}
2.3.4 The code for opening the animation is as follows
LoadingDialogUtils.show(MainAbility.this);
The closing animation code is as follows
LoadingDialogUtils.dismiss(MainAbility.this);
2.4 Implementation of webview
webview loads web pages, we can refer to the components of HarmonyOS's WebView
2.4.1 We learn to observe the setWebAgent of the Web state (the code is as follows)
webView.setWebAgent(new WebAgent() {
@Override
public void onLoadingPage(WebView webview, String url, PixelMap favicon) {
super.onLoadingPage(webview, url, favicon);
//todo 页面开始加载时自定义处理 开启动画
}
@Override
public void onPageLoaded(WebView webview, String url) {
super.onPageLoaded(webview, url);
// todo 页面加载结束后自定义处理 关闭动画
}
@Override
public void onLoadingContent(WebView webview, String url) {
super.onLoadingContent(webview, url);
// 加载资源时自定义处理
}
@Override
public void onError(WebView webview, ResourceRequest request, ResourceError error) {
super.onError(webview, request, error);
//todo 发生错误时自定义处理 关闭动画
}
});
2.4.2 We create a new java class of abilitySlice, and the new layout code is as follows
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<ohos.agp.components.webengine.WebView
ohos:id="$+id:my_webView"
ohos:height="match_parent"
ohos:width="match_parent">
</ohos.agp.components.webengine.WebView>
</DirectionalLayout>
2.4.3 The java class code of webview is as follows
package com.harmony.alliance.mydemo.slice;
import com.harmony.alliance.mydemo.ResourceTable;
import com.harmony.alliance.mydemo.utils.LoadingDialogUtils;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.webengine.*;
import ohos.media.image.PixelMap;
public class NewMyWebview extends AbilitySlice {
private WebView mMyWebview;
private static final String EXAMPLE_URL = "https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-fa-calls-pa-examples-0000000000618000";
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
setUIContent(ResourceTable.Layout_new_my_webview);
mMyWebview= (WebView) findComponentById(ResourceTable.Id_my_webView);
WebConfig webConfig = mMyWebview.getWebConfig();
webConfig.setJavaScriptPermit(true);
webConfig.setWebStoragePermit(true);
webConfig.setDataAbilityPermit(true);
webConfig.setLoadsImagesPermit(true);
webConfig.setMediaAutoReplay(true);
webConfig.setLocationPermit(true);
webConfig.setSecurityMode(WebConfig.SECURITY_SELF_ADAPTIVE);
mMyWebview.setWebAgent(new WebAgent() {
@Override
public void onLoadingPage(WebView webview, String url, PixelMap favicon) {
super.onLoadingPage(webview, url, favicon);
//todo 页面开始加载时自定义处理 开启动画
LoadingDialogUtils.show(NewMyWebview.this);
}
@Override
public void onPageLoaded(WebView webview, String url) {
super.onPageLoaded(webview, url);
// todo 页面加载结束后自定义处理 关闭动画
LoadingDialogUtils.dismiss(NewMyWebview.this);
}
@Override
public void onLoadingContent(WebView webview, String url) {
super.onLoadingContent(webview, url);
// 加载资源时自定义处理
}
@Override
public void onError(WebView webview, ResourceRequest request, ResourceError error) {
super.onError(webview, request, error);
//todo 发生错误时自定义处理 关闭动画
LoadingDialogUtils.dismiss(NewMyWebview.this);
}
});
mMyWebview.load(EXAMPLE_URL);
}
}
2.5 The operation effect is as follows
All codes are as follows
2.5.1general_dialog.xml code
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="80vp"
ohos:orientation="vertical"
ohos:alignment="center"
ohos:width="80vp">
<Image
ohos:id="$+id:loading"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:scale_mode="clip_center"
ohos:image_src="$media:loading"/>
</DirectionalLayout>
2.5.2 java class of GeneralDialog
//请根据实际工程/包名引入
package com.harmony.alliance.mydemo.utils;
import java.util.Optional;
import com.harmony.alliance.mydemo.ResourceTable;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
public class GeneralDialog {
private float dim = -1f;
private AnimatorProperty animatorProperty;
private CommonDialog sDialog;
private Context mContext;
private boolean mOutsideTouchClosable = false;
public GeneralDialog(Context context){
this.mContext=context;
}
public void show() {
if (sDialog != null) {
sDialog.show();
if (dim >= 0) {
changeDialogDim(sDialog, dim);
}
}
}
public void remove(){
if (sDialog != null) {
sDialog.destroy();
}
}
public void create() {
sDialog = new CommonDialog(mContext);
sDialog.setSize(ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT);
sDialog.setAlignment(LayoutAlignment.CENTER);
sDialog.setOffset(0,0);
sDialog.setTransparent(true);
sDialog.setContentCustomComponent(initDialog(sDialog));
sDialog.setAutoClosable(mOutsideTouchClosable);
}
public void StartanimatorProperty(){
if(animatorProperty!=null&&animatorProperty.isRunning()){
animatorProperty.stop();
}
if(animatorProperty!=null&&!animatorProperty.isRunning()){
if(!animatorProperty.isRunning()){
animatorProperty.start();
}
}
}
private void changeDialogDim(CommonDialog dialog, float dim) {
Optional<WindowManager.LayoutConfig> configOpt = dialog.getWindow().getLayoutConfig();
configOpt.ifPresent(config -> {
config.dim = dim;
dialog.getWindow().setLayoutConfig(config);
});
}
public interface ClickedListener{
void onClick(GeneralDialog dialog);
}
private Component initDialog(CommonDialog sDialog) {
Component dialogLayout = LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_general_dialog, null, false);
dialogLayout.setBackground(new ShapeElement(){{
setRgbColor(RgbColor.fromArgbInt(ResourceTool.getColor(mContext, ResourceTable.Color_bg_dialog_light, 0xffffff)));
setCornerRadius(ResourceTool.getFloat(mContext, ResourceTable.Float_dialog_corner_radius, 0));
}});
Image image= (Image) dialogLayout.findComponentById(ResourceTable.Id_loading);
animatorProperty = new AnimatorProperty();
animatorProperty.setTarget(image);
animatorProperty
.rotate(360)
//无限循环
.setLoopedCount(AnimatorValue.INFINITE)
//反弹力效果
.setCurveType(Animator.CurveType.BOUNCE);
if(sDialog!=null){
sDialog.setDestroyedListener(new CommonDialog.DestroyedListener() {
@Override
public void onDestroy() {
if(animatorProperty.isRunning()){
animatorProperty.stop();
}
}
});
}
return dialogLayout;
}
}
2.5.3 The tool class of LoadingDialogUtils is as follows
package com.harmony.alliance.mydemo.utils;
import ohos.app.Context;
public class LoadingDialogUtils {
private static GeneralDialog mGeneralDialog;
private static GeneralDialog getInstance(Context mContext){
if(mGeneralDialog==null){
mGeneralDialog=new GeneralDialog(mContext);
}
return mGeneralDialog;
}
public static void show(Context mContext){
LoadingDialogUtils.getInstance(mContext);
mGeneralDialog.create();
mGeneralDialog.show();
mGeneralDialog.StartanimatorProperty();
}
public static void dismiss(Context mContext){
LoadingDialogUtils.getInstance(mContext);
mGeneralDialog.remove();
}
}
2.5.4 The xml code of the layout of webViewAbilitySlice is as follows
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<ohos.agp.components.webengine.WebView
ohos:id="$+id:my_webView"
ohos:height="match_parent"
ohos:width="match_parent">
</ohos.agp.components.webengine.WebView>
</DirectionalLayout>
2.5.5 The class code of WebViewAbiltySlice is as follows
package com.harmony.alliance.mydemo.slice;
import com.harmony.alliance.mydemo.ResourceTable;
import com.harmony.alliance.mydemo.utils.LoadingDialogUtils;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.webengine.*;
import ohos.media.image.PixelMap;
public class NewMyWebview extends AbilitySlice {
private WebView mMyWebview;
private static final String EXAMPLE_URL = "https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-fa-calls-pa-examples-0000000000618000";
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
setUIContent(ResourceTable.Layout_new_my_webview);
mMyWebview= (WebView) findComponentById(ResourceTable.Id_my_webView);
WebConfig webConfig = mMyWebview.getWebConfig();
webConfig.setJavaScriptPermit(true);
webConfig.setWebStoragePermit(true);
webConfig.setDataAbilityPermit(true);
webConfig.setLoadsImagesPermit(true);
webConfig.setMediaAutoReplay(true);
webConfig.setLocationPermit(true);
webConfig.setSecurityMode(WebConfig.SECURITY_SELF_ADAPTIVE);
mMyWebview.setWebAgent(new WebAgent() {
@Override
public void onLoadingPage(WebView webview, String url, PixelMap favicon) {
super.onLoadingPage(webview, url, favicon);
//todo 页面开始加载时自定义处理 开启动画
LoadingDialogUtils.show(NewMyWebview.this);
}
@Override
public void onPageLoaded(WebView webview, String url) {
super.onPageLoaded(webview, url);
// todo 页面加载结束后自定义处理 关闭动画
LoadingDialogUtils.dismiss(NewMyWebview.this);
}
@Override
public void onLoadingContent(WebView webview, String url) {
super.onLoadingContent(webview, url);
// 加载资源时自定义处理
}
@Override
public void onError(WebView webview, ResourceRequest request, ResourceError error) {
super.onError(webview, request, error);
//todo 发生错误时自定义处理 关闭动画
LoadingDialogUtils.dismiss(NewMyWebview.this);
}
});
mMyWebview.load(EXAMPLE_URL);
}
}
The effect is as follows