基本概念
属性
NativeScript 中的属性系统允许将原生平台属性暴露给 JavaScript 组件,并提供了一些内置的保证;例如
- 它只会在原生平台对象创建后才设置属性
- 它允许使用自定义值转换器定义属性
- 它会自动检测属性值是否更改
- 它可以根据需要暂停更新值
- 它可以根据需要在属性更改时自动请求布局更新
在 NativeScript 中,Property 类充当基本属性系统,是对 Object.defineProperty 函数的简单增强。此类还包含补充回调,例如 valueChange、valueConverter 和 equalityComparer。
使用属性
向 View 实例添加新属性
通过创建具有所需 选项 的新 Property
来添加新属性。
class MyButtonBase extends Button {}
export const textProperty = new Property<MyButtonBase, string>({
name: 'text',
defaultValue: '',
affectsLayout: true,
})
然后使用所需的类注册属性
textProperty.register(MyButtonBase)
要将 JS 值应用于暴露的原生视图,请在属性实例上调用 setNative
方法。
[textProperty.setNative](value: string) {
this.nativeView.text = value
}
要全面了解 NativeScript 如何使用属性系统来暴露原生 UI 组件,建议阅读名为 如何创建使用原生 iOS 和 Android 视图的 NativeScript 插件(第 1 部分)- 标签跑马灯! 的文章。本文提供了一个实用的现实世界示例,演示了开发 NativeScript 插件并使用原生 iOS 和 Android 视图的过程。
添加新 CSS 属性
要添加新 CSS 属性,请使用 CssProperty 类。它扩展了 Property 类,以接受额外的 cssName
选项。CSS 属性使用 Style 类注册。
import { CssProperty, Style } from '@nativescript-core'
export const myOpacityProperty = new CssProperty<Style, number>({
name: 'myOpacity',
cssName: 'my-opacity',
defaultValue: 1,
valueConverter: (v) => {
const x = parseFloat(v)
if (x < 0 || x > 1) {
throw new Error(`opacity accepts values in the range [0, 1]. Value: ${v}`)
}
return x
},
})
myOpacityProperty.register(Style)
随后,任何 Style 类实例都将具有 myOpacity
属性,开发人员可以在 CSS 中按如下方式设置它
.some-class {
my-opacity: 0.5;
}
可继承属性
要创建可应用于视图并由该视图的子视图继承的 CSS 属性,请实例化 InheritedCssProperty 类,并向其传递 PropertyOptions 对象。然后使用 setInheritedValue: (value: U)
方法设置继承值。可继承属性的示例包括FontSize
、FontWeight
、Color
等。
import { Color, InheritedCssProperty, Style } from '@nativescript-core'
export const selectedBackgroundColorProperty = new InheritedCssProperty<
Style,
Color
>({
name: 'selectedBackgroundColor',
cssName: 'selected-background-color',
equalityComparer: Color.equals,
valueConverter: (v) => new Color(v),
})
selectedBackgroundColorProperty.register(Style)
简写属性
要创建可以使用简写语法规则应用的 CSS 属性,请使用 ShorthandProperty 类,并向其传递 ShorthandPropertyOptions 对象。例如,您无需使用单独的规则为每边设置边距,
.title {
margin-top: 0;
margin-right: 10;
margin-bottom: 0;
margin-left: 10;
}
可以使用一条规则来设置所有边的边距。
.title {
margin: 0 10 0 10;
}
要创建简写属性,请使用 CssProperty
类以通常的方式单独定义所有属性。然后使用 ShorthandProperty
类的 getter()
方法返回简写。以下是如何实现边距简写的示例
const marginProperty = new ShorthandProperty<
Style,
string | CoreTypes.PercentLengthType
>({
name: 'margin',
cssName: 'margin',
getter: function (this: Style) {
if (
PercentLength.equals(this.marginTop, this.marginRight) &&
PercentLength.equals(this.marginTop, this.marginBottom) &&
PercentLength.equals(this.marginTop, this.marginLeft)
) {
return this.marginTop
}
return `${PercentLength.convertToString(
this.marginTop
)} ${PercentLength.convertToString(
this.marginRight
)} ${PercentLength.convertToString(
this.marginBottom
)} ${PercentLength.convertToString(this.marginLeft)}`
},
converter: convertToMargins,
})
marginProperty.register(Style)
创建可强制转换属性
要创建可强制转换属性,请使用 CoercibleProperty 类,并向其传递 CoerciblePropertyOptions 对象。
export const selectedIndexProperty = new CoercibleProperty<
SegmentedBar,
number
>({
name: 'selectedIndex',
defaultValue: -1,
valueChanged: (target, oldValue, newValue) => {
target.notify(<SelectedIndexChangedEventData>{
eventName: SegmentedBar.selectedIndexChangedEvent,
object: target,
oldIndex: oldValue,
newIndex: newValue,
})
},
// in this case the coerce value will change depending on whether the actual number of items
// is more or less than the value we want to apply for selectedIndex
coerceValue: (target, value) => {
let items = target.items
if (items) {
let max = items.length - 1
if (value < 0) {
value = 0
}
if (value > max) {
value = max
}
} else {
value = -1
}
return value
},
valueConverter: (v) => parseInt(v),
})
selectedIndexProperty.register(SegmentedBar)
随后,在为属性赋值时,请调用 coerce()
方法。
[itemsProperty.setNative](value: SegmentedBarItem[]) {
...
selectedIndexProperty.coerce(this);
}
属性系统参考
Property 类
Property 类具有以下成员。
constructor
property = new Property<MyClass, U>(propertyOptions)
enumerable
isEnumerable: boolean = property.enumerable
configurable
isConfigurable: boolean = property.configurable
writable
isWritable: boolean = property.writable
name
propertyName: string = property.name
getDefault
;[property.getDefault]()
setNative
[property.setNative](value){
}
defaultValue
defaultValue: U = property.defaultValue
nativeValueChange
property.nativeValueChange(owner: T, value: U)
isStyleProperty
isStyleProperty: boolean = property.isStyleProperty
get()
propertyValue: U = property.get()
set()
property.set(value: U)
register()
property.register(SomeClass)
isSet()
isPropertySet: boolean = property.isSet(instance: T)
### overrideHandlers()
property.overrideHandlers(options: PropertyOptions<T, U>)
PropertyOptions 接口
name
{
name: 'propertyName'
}
defaultValue
{
defaultValue: someValue
}
affectsLayout
{
affectLayout: true
}
equalityComparer
{
equalityComparer: (x, y) => {
// compare x to y and return boolean
return true
}
}
valueChanged
{
valueChanged: (target: T, oldValue: U, newValue: U) => {}
}
valueConverter
{
valueConverter: (value: string) => {
return someValue
}
}
CssPropertyOptions 接口
cssName
{
cssName: 'my-css-property'
}
打算在 CSS 规则中使用的属性名称。
ShorthandPropertyOptions 接口
{
name: 'newProperty'
}
cssName
{
cssName: 'my-css-property'
}
converter()
{
}
getter()
{
getter: function (this: Style){
return cssValue
}
}