# UTS component development

需HBuilderX 3.6.18 及之后版本

app平台目前支持nvue/uvue,暂不支持vue

UTS component is a branch of UTS plugin. The UTS plug-in provides the extension of the native API, and the UTS component provides the development mode of the native UI component.

A component is an independent, reusable UI unit that is used to individually encapsulate and undertake certain code logic.

The difference between components and plug-ins is that the former provides UI encapsulation in the form of tags; the latter only provides APIs. Although APIs may involve UI, they are still APIs and cannot be referenced in page templates in the form of tags.

For example, <video> is a component; uni.showModal or uni.chooseVideo has UI, but it belongs to API.

Components are generally suitable for encapsulating non-full-screen scenarios, that is, embedding an area in the page. If the UI you need to encapsulate is a full-screen interface, then there is no need to use components. It is easier to develop native pages through UTS. UTS development native page example

Of course, UTS components are multi-terminal. As a uni_modules, a UTS component can support app-Android, app-iOS, web, and various MiniApp components at the same time.

This article focuses on how to package a native UI as a UTS component on app-android and app-ios for users to call as components in the page template.

# Preconditions

Before continuing to read the documentation, developers need to understand the following prerequisites:

# Introduction to UTS components

# Why use UTS to develop components

UTS components, that is: using UTS language to develop components on the uni platform.

The advantage of the UTS component is that it inherits the cross-platform characteristics of UTS, the unified UTS syntax, and the different original products of each terminal.

On the Android platform, it will be compiled and rendered as an Android native View instance, and the same is true for IOS or other terminal platforms.

uts component uni native component Vue component
Development language uts java/object-c js/ts
Component carrier App platform is the system native View object System native View object WebView internal label

In order to unify the development specifications and lower the threshold of use, UTS native components adopt the syntax of Vue components, but will be slightly customized according to the actual situation.

That is, you can use the uts language to write a UTS component just like writing a Vue component.

# UTS component structure analysis

# UTS component directory structure

	
┌─common                          // 可跨端公用的uts代码。推荐,不强制
├─static                          // 静态资源
├─utssdk
│	├─app-android                 //Android平台目录
│	│	├─assets                  //Android原生assets资源目录,可选
│	│	├─libs                    //Android原生库目录,可选
│	│	├─res                     //Android原生res资源目录,可选
│	│	├─AndroidManifest.xml     //Android原生应用清单文件,可选
│	│	├─config.json             //Android原生配置文件
│	│	├─index.uts               //Android原生插件能力实现,可选
|	|	└─index.vue               //Android原生组件能力实现,必选
│	├─app-ios                     //iOS平台目录
│	│	├─Frameworks              //iOS原生依赖的第三方 framework 依赖库存放目录,可选
│	│	├─Resources               //iOS原生所依赖的资源文件存放目录,可选
│	│	├─info.plist              //iOS原生所需要添加到主 info.plist 文件中的配置文件,可选
│	│	├─UTS.entitlements        //iOS原生所需要添加到主工程 .entitlements 文件中的配置文件,可选
│	│	├─config.json             //iOS原生配置文件
│	│	├─index.uts               //iOS原生插件能力实现,可选
|	|	└─index.vue               //iOS原生组件能力实现,必选
│	├─web                         //web平台目录
│	│	└─index.uts
│	├─mp-alipay                   // 支付宝小程序平台,可选
│	├─mp-baidu                    // 百度小程序平台,可选
│	├─mp-jd                       // 京东小程序平台(仅限vue2),可选
│	├─mp-kuaishou                 // 快手小程序平台,可选
│	├─mp-lark                     // 飞书小程序平台,可选
│	├─mp-qq                       // QQ小程序平台,可选
│	├─mp-toutiao                  // 抖音小程序平台,可选
│	├─mp-weixin                   // 微信小程序平台,可选
│	├─mp-xhs                      // 小红书小程序平台(仅限vue2),可选
│	└─index.uts                   // 跨平台插件能力实现,可选
└─package.json                    // 插件清单文件

As shown above, the directory structure of UTS components is basically the same as that of UTS plugins

The only difference is that there are two UTS component entry files:

  • Mandatory index.vue component entry

  • optional index.uts function capability entry

If the user has some component-independent capabilities that need to be exposed while developing the component, it can be implemented in index.uts

In most cases, we only need to develop one index.vue. If there are multiple components, we can create multiple xxx.vue files

About how to write index.vue source code, we will introduce it in the next chapter

# index.vue source code structure

The following is a complete example of component source code index.vue:

Notice

  • Currently the UTS component only supports the optional API of export default {}, and the combined API of vue3 is not yet supported.

Android

iOS


<template>
	<view >

	</view>
</template>
<script lang="uts">
	import TextUtils from 'android.text.TextUtils'
	import Button from 'android.widget.Button'
	import LinearLayout from 'android.widget.LinearLayout'
	import View from 'android.view.View'

	class ButtonClickListsner extends View.OnClickListener {
		constructor() {
			super()
		}
		override onClick(v ? : View) {
			console.log(v)
		}
	}

	//原生提供以下属性或方法的实现
	export default {
		/**
		 * 组件名称,也就是开发者使用的标签
		 */
		name: "uts-hello-view",
		/**
		 * 组件涉及的事件声明,只有声明过的事件,才能被正常发送
		 */
		emits: ['buttonClick'],
		/**
		 * 属性声明,组件的使用者会传递这些属性值到组件
		 */
		props: {
			/**
			 * 字符串类型 属性:buttonText  需要设置默认值
			 */
			"buttonText": {
				type: String,
				default: "点击触发"
			}
		},
		/**
		 * 组件内部变量声明
		 */
		data() {
			return {}
		},
		/**
		 * 属性变化监听器实现
		 */
		watch: {
			"buttonText": {
				/**
				 * 这里监听属性变化,并进行组件内部更新
				 */
				handler(newButtonText: string) {
					if (this.$el != null) {
						let button = this.$el!.findViewWithTag("centerButton") as Button
						if (!TextUtils.isEmpty(newButtonText)) {
							button.setText(newButtonText)
						}
					}
				},
				immediate: false //创建时是否通过此方法更新属性,默认值为false
			},
		},
		/**
		 * 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
		 * ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
		 */
		expose: ['doSth'],
		methods: {
			/**
			 * 对外公开的组件方法
			 */
			doSth(paramA: string) {
				// 这是组件的自定义方法
				console.log("paramA",paramA)
			},
			/**
			 * 内部使用的组件方法
			 */
			privateMethod() {

			}
		},

		/**
		 * 组件被创建,组件第一个生命周期,
		 * 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
		 * [可选实现]
		 */
		created() {

		},
		/**
		 * 对应平台的view载体即将被创建,对应前端beforeMount
		 * [可选实现]
		 */
		NVBeforeLoad() {

		},
		/**
		 * 创建原生View,必须定义返回值类型
		 * 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
		 * (Android需要明确知道View类型,需特殊校验)
		 * todo 补充IOS平台限制
	  * [必须实现]
		 */
		NVLoad(): LinearLayout {
			//必须实现
			let contentLayout = new LinearLayout(this.$androidContext!)
			let button = new Button(this.$androidContext!)
			button.setText("点击触发");
			button.setTag("centerButton");
			contentLayout.addView(button, new LinearLayout.LayoutParams(500, 500));
			button.setOnClickListener(new ButtonClickListsner())
			return contentLayout
		},

		/**
		 * 原生View已创建
		 * [可选实现]
		 */
		NVLoaded() {

		},
		/**
		 * 原生View布局完成
		 * [可选实现]
		 */
		NVLayouted() {

		},
		/**
		 * 原生View将释放
		 * [可选实现]
		 */
		NVBeforeUnload() {},
		/**
		 * 原生View已释放,这里可以做释放View之后的操作
		 * [可选实现]
		 */
		NVUnloaded() {

		},
		/**
		 * 组件销毁
		 * [可选实现]
		 */
		unmounted() {},
		/**
		 * 自定组件布局尺寸
		 * [可选实现]
		 */
		NVMeasure(size: UTSSize): UTSSize {
			size.width = 120.0.toFloat()
			size.height = 800.0.toFloat()
			return size
		}
	}
</script>
<style>

</style>


index.vue elements can be divided into the following categories:

  • configuration:

    name:组件的使用标签,可以省略,若省略则默认为组件名称

    emits: The name of the message event allowed by the component. If there is no component message, no configuration is required

  • 属性:

    props:需要由组件的使用者提供,比如一个Image组件,会需要一个path属性作为图像路径来源

    watch:属性的监听实现,用来监听属性数据更新。

  • 数据:

    data: The internal data definition of the component, which is used for internal logic processing of the component and is not exposed to the outside world

  • method:

    methods:组件方法定义,可以通过与expose组合使用,区分对内方法和对外方法

    expose: Used in conjunction with the methods field to distinguish between internal and external methods of the component

  • life cycle:

    组件需要重点处理 内存创建/销毁,View载体创建/销毁 过程中的资源管理,具体参考生命周期章节

  • 内置对象:

    For the convenience of component developers, UTS components have built-in variables and functions, please refer to the built-in objects and functions chapter for details

# 生命周期

Component developers need to focus on the life cycle of components in order to initialize and recycle resources

function name description recommended behavior optional
created The component is created in memory Developers can perform some initialization logic that needs to be executed at the earliest Optional
NVBeforeLoad The component corresponds to the view carrier of the platform, which is about to be created Developers can perform some logic that needs to be initialized before the View is created here Optional
NVLoad The component view carrier is being created and implemented Developers need to focus on implementing this function, declaring the process of creating native components, and the final generated native component type Must be implemented
NVLayouted The layout of the view carrier of the corresponding platform of the component has been completed Logic that needs to be executed after the view layout is completed Optional
NVBeforeUnload The view carrier is about to be unloaded Before the View is unloaded, the logic of reclaiming resources is required Optional
NVUnloaded The view carrier has been unloaded After the View is unloaded, the logic of reclaiming resources is required Optional
unmounted component is destroyed in memory resource recovery logic optional

In addition to the above life cycles, the following optional cycle functions exist:

  • NVMeasure

NVMeasure is used to tell the typesetting system, the width and height required by the component itself, and the specific calling time is determined by the typesetting system.

In general, the width and height of a component should be determined by the typesetting engine of the terminal system, and component developers do not need to implement this function.

但是部分场景下,组件开发者需要自己维护宽高,则需要开发者重写此函数

  • NVUpdateStyles

需要HBuilder X 3.96版本

NVUpdateStyles 用来监听组件的外部style 变化,通常用来写响应外部的css样式变化从而动态更新组件内部状态场景

我们可以在组件内部这样实现:

NVUpdateStyles(styles: Map<String, any>){
	console.log("NVUpdateStyles",styles)
}

注意:只有非容器组件生效,容器组件不应该重写此函数

[vue3 life cycle is not currently supported](https://uniapp.dcloud.net.cn/tutorial/vue3-api.html#%E9%80%89%E9%A1%B9-%E7%94%9F%E5 %91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90)

# Built-in Objects and Functions

For the convenience of component developers, the following objects are built into the UTS component:

Variable Name Type Introduction Platform Restrictions Methods & Properties
$el 对象 当前View实例对象 全部平台 开发者在NVLoad返回的对象类型
$androidContext 对象 当前组件上下文(可为空) 仅android android平台对应Context对象
$emit("event",Any) function send registered events all platforms $emit(event name-required, event parameters are optional)

# Generic Events

For UTS components, in addition to customizing component events through the $emit/emits function, UTS components also have the following built-in general events:

事件名称 简介
click Component click event response
longpress Component long press event response

General event, the user of the component does not need to implement it, use it directly

<uts-hello-view buttonClick="自定义事件处理函数" click="通用点击事件处理函数" longpress="通用长按事件处理函数"/>

# Example of a simple View

This chapter takes a minimalist component development as an example to introduce the UTS component development process

# Create plugin

在HBuilder X 中选中Uni-App项目下 uni_modules目录,右键选择新建uni_modules插件

创建界面

This is the directory structure after creation

Directory structure

# Write the logic

Open index.vue and type the following component source code:

Android

iOS


<template>
	<view >

	</view>
</template>
<script lang="uts">
	import TextUtils from 'android.text.TextUtils'
	import Button from 'android.widget.Button'
	import LinearLayout from 'android.widget.LinearLayout'
	import View from 'android.view.View'

	class ButtonClickListsner extends View.OnClickListener {
		constructor() {
			super()
		}
		override onClick(v ? : View) {
			console.log(v)
		}
	}

	//原生提供以下属性或方法的实现  
	export default {
		/**
		 * 组件名称,也就是开发者使用的标签
		 */
		name: "uts-hello-view",
		/**
		 * 组件涉及的事件声明,只有声明过的事件,才能被正常发送
		 */
		emits: ['buttonClick'],
		/**
		 * 属性声明,组件的使用者会传递这些属性值到组件
		 */
		props: {
			/**
			 * 字符串类型 属性:buttonText  需要设置默认值
			 */
			"buttonText": {
				type: String,
				default: "点击触发"
			}
		},
		/**
		 * 组件内部变量声明
		 */
		data() {
			return {}
		},
		/**
		 * 属性变化监听器实现
		 */
		watch: {
			"buttonText": {
				/**
				 * 这里监听属性变化,并进行组件内部更新
				 */
				handler(newButtonText: string) {
					if (this.$el != null) {
						let button = this.$el!.findViewWithTag("centerButton") as Button
						if (!TextUtils.isEmpty(newButtonText)) {
							button.setText(newButtonText)
						}
					}
				},
				immediate: false //创建时是否通过此方法更新属性,默认值为false  
			},
		},
		/**
		 * 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
		 * ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
		 */
		expose: ['doSth'],
		methods: {
			/**
			 * 对外公开的组件方法
			 */
			doSth(paramA: string) {
				// 这是组件的自定义方法
				console.log("paramA",paramA)
			},
			/**
			 * 内部使用的组件方法
			 */
			privateMethod() {

			}
		},

		/**
		 * 组件被创建,组件第一个生命周期,
		 * 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
		 * [可选实现]
		 */
		created() {

		},
		/**
		 * 对应平台的view载体即将被创建,对应前端beforeMount  
		 * [可选实现]
		 */
		NVBeforeLoad() {

		},
		/**
		 * 创建原生View,必须定义返回值类型
		 * 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
		 * (Android需要明确知道View类型,需特殊校验) 
		 * todo 补充IOS平台限制
	  * [必须实现]
		 */
		NVLoad(): LinearLayout {
			//必须实现  
			let contentLayout = new LinearLayout(this.$androidContext)
			let button = new Button(this.$androidContext)
			button.setText("点击触发");
			button.setTag("centerButton");
			contentLayout.addView(button, new LinearLayout.LayoutParams(500, 500));
			button.setOnClickListener(new ButtonClickListsner())
			return contentLayout
		}

		
	}
</script>
<style>
	
</style>

上面的代码,我们自定义了一个 名为 "uts-hello-view" 的UTS 组件,该组件对外提供了一个包含按钮的简单UI实现,并且对外暴露了一个名为 buttonText字符串属性,用来构建按钮上的文案

Next, we introduce how to use it in the uni-app project

# Using components

Note: UTS components are registered globally by default, no need for manual configuration by users

We create a new helloView.nvue page in the uni-app project,

Use the uts-hello-view tag directly, and define the text content of buttonText to see the effect.

点击组件内置按钮,可以在控制台看到组件内部实现的日志输出

点击调用组件的方法按钮,可以看到组件内置方法被调用

<template>
	<div>
		<uts-hello-view ref="helloView" buttonText="点击按钮内容" style="width:375px;height: 375px;background-color: aqua;"></uts-hello-view>
    	<button @tap="callComponentMethod">调用组件的方法</button>
	</div>
	
</template>

<script>
  	export default {
      	data() {
			return {
				
			}
		},
		methods: {
			// 调用组件内的方法
			callComponentMethod: function() {
				// nvue 页面调用方法
				this.$refs["helloView"].doSth("param doSth");
				// (this.$refs["helloView"] as UtsHelloViewElement).doSth('param doSth');
			},
		}
      
  }
</script>

<style>
</style>

# Running and testing

In the current example, no third-party dependencies are involved, and the standard base can be used to run directly

# Contains examples of third-party SDKs

This chapter takes the lottie animation component as an example to introduce the development process of the UTS component including the third-party SDK

# Create plugin

在HBuilder X 中选中Uni-App项目下 uni_modules目录,右键选择新建uni_modules插件

创建界面

This is the directory structure after creation

Directory structure

# Android platform introduces dependencies

Open ~/uni_modules/uts-animation-view/utssdk/app-android/config.json

Type the following code:

"dependencies": [
	"com.airbnb.android:lottie:3.4.0"
]

UTS components are recommended to be integrated using remote dependencies. If you need to add SDK in the form of AAR, you can add it to

~/uni_modules/uts-animation-view/utssdk/app-android/libs directory

The configuration principle of dependencies is consistent with that of the UTS plug-in [UTS plug-in dependency description](https://uniapp.dcloud.net.cn/plugin/uts-for-android.html#_3-4-%E5%A2%9E%E5% 8A%A0libs%E4%BE%9D%E8%B5%96%E8%B5%84%E6%BA%90)

# iOS platform imports dependent libraries

The iOS platform needs to put the three-party dependency library in app-ios/Frameworks under the component directory

# Write the logic

Open index.vue and type the following component source code:

Android

iOS

<template>
    <view>

    </view>
</template>
<script lang="uts">
    import Animator from 'android.animation.Animator'
    import TextUtils from 'android.text.TextUtils'
    import View from 'android.view.View'
    import LottieAnimationView from 'com.airbnb.lottie.LottieAnimationView'
    import LottieDrawable from 'com.airbnb.lottie.LottieDrawable'
	import FileInputStream from 'java.io.FileInputStream'
	import { UTSAndroid } from "io.dcloud.uts";

    class CustomAnimListener extends Animator.AnimatorListener {

        comp: UTSComponent < LottieAnimationView >
            constructor(com: UTSComponent < LottieAnimationView > ) {
                super();
                this.comp = com
            }

        override onAnimationStart(animation: Animator | null) {}

        override onAnimationEnd(animation: Animator | null, isReverse: Boolean) {
            this.comp.$emit("bindended")
        }

        override onAnimationEnd(animation: Animator | null) {}

        override onAnimationCancel(animation: Animator | null) {}

        override onAnimationRepeat(animation: Animator | null) {}
    }

    //原生提供以下属性或方法的实现
    export default {
        name: "uts-animation-view",
        /**
         * 当播放到末尾时触发 ended 事件(自然播放结束会触发回调,循环播放结束及手动停止动画不会触发)
         */
        emits: ['bindended'],
        props: {
            /**
             * 动画资源地址,目前只支持绝对路径
             */
            "path": {
                type: String,
                default: ""
            },
            /**
             * 动画是否循环播放
             */
            "autoplay": {
                type: Boolean,
                default: false
            },
            /**
             * 动画是否自动播放
             */
            "loop": {
                type: Boolean,
                default: false
            },
            /**
             * 是否隐藏动画
             */
            "hidden": {
                type: Boolean,
                default: false
            },
            /**
             * 动画操作,可取值 play、pause、stop
             */
            "action": {
                type: String,
                default: "stop"
            }

        },
        data() {
            return {

            }
        },
        watch: {
            "path": {
                handler(newPath: string) {


					if(this.$el != null){
						let lottieAnimationView = this.$el!
						if (!TextUtils.isEmpty(newPath)) {


						    if (newPath.startsWith("http://") || newPath.startsWith("https://")) {
						        lottieAnimationView.setAnimationFromUrl(newPath)
						    } else {
						        // The default is static
								var realJsonPath = UTSAndroid.getResourcePath(newPath)
						        lottieAnimationView.setAnimation(new FileInputStream(realJsonPath),newPath)
						    }
						}
						if (this.autoplay) {
						    lottieAnimationView.playAnimation()
						}
					}
                },
                immediate: false //创建时是否通过此方法更新属性,默认值为false
            },
            "loop": {
                handler(newLoop: Boolean) {
					if(this.$el != null){
						if (newLoop) {
						    this.$el!.repeatCount = Int.MAX_VALUE
						} else {
						    // Set it to 1 time if it does not loop
						    this.$el!.repeatCount = 0
						}

						if (this.autoplay) {
						    this.$el!.playAnimation()
						}
					}

                },
                immediate: false //创建时是否通过此方法更新属性,默认值为false
            },

            "autoplay": {
                handler(newValue: boolean) {
					if(this.$el != null){
						if (newValue) {
						    this.$el!.playAnimation()
						}
					}

                },
                immediate: false //创建时是否通过此方法更新属性,默认值为false
            },

            "action": {
                handler(newAction: string) {

                    if (newAction == "play" || newAction == "pause" || newAction == "stop") {

						if(this.$el != null){
							if (this.action == "play") {
							    this.$el!.playAnimation()
							} else if (this.action == "pause") {
							    this.$el!.pauseAnimation()
							} else if (this.action == "stop") {
							    this.$el!.cancelAnimation()
							    this.$el!.clearAnimation()
							}
						}


                    } else {
                        // Illegal input parameters, no matter
                    }
                },
                immediate: false //创建时是否通过此方法更新属性,默认值为false
            },

            "hidden": {
                handler(newValue: boolean) {
					if(this.$el != null){
						if (newValue) {
						    this.$el!.visibility = View.GONE
						} else {
						    this.$el!.visibility = View.VISIBLE
						}
					}
                },
                immediate: false //创建时是否通过此方法更新属性,默认值为false
            },

        },
        methods: {
            setRepeatMode(repeat: string) {
				if(this.$el != null){
					if ("RESTART" == repeat) {
					    this.$el!.repeatMode = LottieDrawable.RESTART
					} else if ("REVERSE" == repeat) {
					    this.$el!.repeatMode = LottieDrawable.RESTART
					}
				}
            },
            privateMethod() { //如何定义不对外暴露的API? 暂不支持,需在export外写
            }
        },
        created() { //创建组件,替换created

        },
        NVBeforeLoad() { //组件将要创建,对应前端beforeMount
            //可选实现,这里可以提前做一些操作
        },
        NVLoad(): LottieAnimationView { //创建原生View,必须定义返回值类型(Android需要明确知道View类型,需特殊校验)
            //必须实现
            let lottieAnimationView = new LottieAnimationView($androidContext)
            return lottieAnimationView
        },

        NVLoaded() { //原生View已创建
			//Optional implementation, follow-up operations can be done here
			if(this.$el != null){
				this.$el!.repeatMode = LottieDrawable.RESTART;
				this.$el!.visibility = View.GONE
				this.$el!.repeatCount = 0
				this.$el!.addAnimatorListener(new CustomAnimListener(this))
			}

        },
        NVLayouted() { //原生View布局完成
            //可选实现,这里可以做布局后续操作
        },
        NVBeforeUnload() { //原生View将释放
            //可选实现,这里可以做释放View之前的操作
        },
        NVUnloaded() { //原生View已释放
            //可选实现,这里可以做释放View之后的操作
        },
        unmounted() { //组件销毁
            //可选实现
        }
    }
</script>
<style>

</style>

In the above code, we have implemented a UTS component that supports lottie animation playback, and the tag name is uts-animation-view

The following properties and methods are provided externally

Attribute Type Default Value Description
path string lottie resource path, supports local address and network address under http protocol
loop boolean false Whether the animation is played in a loop
autoplay boolean true Whether the animation is played automatically
action string play Animation operation, possible values are play, pause, stop
hidden boolean true Whether to hide the animation
bindended event triggers the ended event when playback reaches the end
setRepeatMode function Set animation repeat mode, RESTART: restart playback, REVERSE, reverse playback

# 使用uts-animation-view组件

Create a new lottie/index.nvue page in the uni-app project

引用自定义 uts-animation-view 组件,并编写测试用例

<template>
    <div>
        <button @tap="changeUrl">播放本地动画资源</button>
		<button @tap="changeServerUrl">播放远程动画资源</button>

        <button @tap="changeAutoPlay">测试AutoPlay</button>
        <button @tap="changeLoop">测试Loop</button>
        <button @tap="changeAction(1)">测试action play</button>
        <button @tap="changeAction(2)">测试action pause</button>
        <button @tap="changeAction(3)">测试action stop</button>
        <uts-animation-view ref="animView" :path="animUrl" :autoplay="autoplay" :loop="loop" :action="action"
            :hidden="hidden" @bindended="testAnimEnd" @click="lottieClickTest" @longpress="lottieLongpressTest"
            :style="{width:widthNum+'rpx',height:heightNum+'px',background:yanse}">
        </uts-animation-view>

    </div>
</template>

<script>

    export default {
        data() {
            return {
                hidden: false,
                autoplay: false,
                action: "play",
                loop: false,
                yanse: "red",
                widthNum: 750,
                heightNum: 200,
                comShow: true,
                animUrl: "/static/anim_a.json"
            }
        },

        methods: {

            changeAutoPlay: function() {
                this.autoplay = !this.autoplay
            },
            changeUrl: function() {
                if (this.animUrl == "/static/anim_a.json") {
                    this.animUrl = "/static/anim_b.json"
                } else {
                    this.animUrl = "/static/anim_a.json"
                }
            },

			changeServerUrl: function() {
                this.animUrl = "https://b.bdstatic.com/miniapp/images/lottie_example_one.json"
            },
            changeAction: function(type) {
                if (type == 1) {
                    this.action = "play"
                } else if (type == 2) {
                    this.action = "pause"
                } else if (type == 3) {
                    this.action = "stop"
                }
            },
            changeLoop: function() {
                this.loop = !this.loop
            },
            testAnimEnd: function(res) {
                console.log("testAnimEnd");
            },

            changeRepeat: function(res) {
                let repeatConfig = {
                    count: 3,
                    mode: "restart"
                }
                this.$refs["animView"].updateRepeatConfig(repeatConfig, function(res) {
                    console.log(res);
                });

            },
            lottieClickTest: function(res) {
                console.log("lottieClickTest");
                console.log(res);
            },
            lottieLongpressTest: function(res) {
                console.log("lottieClickTest");
                console.log(res);
            },
        }
    }
</script>

Above, we have completed the integration and use of the uts-animation-view component

# Running and testing

In the current example, because additional third-party dependencies are configured, a custom base is required to use

# UTS development container components

# 简介

组件一般有两种场景,第一种是: 单标签组件

<uts-view style="xxxx"/>

The uts-hello-view or uts-animation-view we introduced above are all of this type

The second is used as a container:

<uts-view >
	<text> 文字子组件</text>
	<image src="https://xxx">
<uts-view >

# 声明

As a container component, UTS components follow exactly the same specifications as ordinary View components.

唯一的区别在于 当组件布局中包含 <solt> 标签时,编译器会自动将其转换为容器组件

Android

iOS

<template>
	<view>
		<solt/>
	</view>
</template>
<script lang="uts">

	import LinearLayout from 'android.widget.LinearLayout'

	//原生提供以下属性或方法的实现
	export default {
		name: "uts-hello-container",

		NVLoad(): LinearLayout {
			let contentLayout = new LinearLayout($androidContext)
			return contentLayout
		}

	}
</script>
<style>

</style>

As above, we can arrive at the simplest UTS container component

# 使用容器组件

The use of UTS container components is consistent with common front-end container components such as Vue. The only thing to note is that currently UTS container components do not yet support named slots.

The following is an example of using a container component

<template>
	<uts-hello-container>
		<text>UTS contianer组件</text>
		<button text="点击按钮内容" style="width:375px;height: 375px;background-color: aqua;"></button>
	</uts-hello-container>
</template>

<script>
</script>

<style>
</style>

# 应用程序生命周期函数监听

UTS 组件同样支持对应用程序生命周期函数的监听,方式和 UTS 插件中的完全一致。具体使用方法详见

# Quick experience

开发者可以使用Hello UTS 快速体验UTS 组件开发

Lottie动画示例,对应的源码实现:~/uni_modules/uts-animation-view

uts-animation-view动画示例,对应的源码实现:~/uni_modules/uts-animation-view

# 常见问题

# 1 使用者需要指定 组件宽高

<uts-hello-view buttonText="点击按钮内容" style="width:375px;height: 375px;background-color: aqua;"></uts-hello-view>

如果不通过style 指定组件宽高,会导致组件不显示

# 2 UTS-Android 插件涉及的像素单位说明: rpx,逻辑像素px,物理像素px

单位 说明 使用场景
逻辑像素px 逻辑像素 对应到 android中的 dp 组件使用者 在页面css中使用 举例:width:480px
物理像素px 真实的设备像素,与dp 直接的换算关系是 dp * 屏幕密度 = 物理像素 px android 原生api 传入和返回的单位均为物理像素,比如 设置layoutParam 参数,获取View宽高等
rpx 屏幕宽度固定750单位前提下的逻辑像素 组件使用者 在页面css中使用 举例:width:750rpx,可以使用UTSAndroid.rpx2px() 函数进行rpx 和 逻辑像素 px的转换

为了让组件使用者的有更好的体验,UTS 插件 应该以 逻辑像素 px 作为标准的像素单位:

  • 1 内置的UTS插件/组件,涉及对外交互时会自动进行单位转换,抹平相关差异。比如 swiper等组件监听滑动距离等场景

  • 2 建议 插件开发者的插件在设计对外像素单位时也进行单位的转换,以逻辑像素px 作为输出结果