Native UI Components
Native UI Components
需要本机代码的项目
此页面仅适用于react-native init
使用Create React Native App 制作的或使用此类应用程序弹出的项目。有关弹出的更多信息,请参阅创建React Native App存储库的指南。
有许多原生UI小部件可以在最新的应用程序中使用 - 其中一些是该平台的一部分,另一些则作为第三方库提供,并且还可能在您自己的产品组合中使用。反应本土有几个最关键的平台组件已经包裹着,像ScrollView
和TextInput
,但不是所有的人,肯定不是你可能会自己写的前一个应用程序的。幸运的是,将这些现有组件与您的React Native应用程序无缝集成非常简单。
与原生模块指南一样,这也是一个更高级的指南,假设您对Android SDK编程有些熟悉。本指南将向您展示如何构建本地UI组件,引导您完成ImageView
核心React Native库中现有组件的子集的实现。
ImageView示例
在这个例子中,我们将逐步介绍实现需求以允许在JavaScript中使用ImageViews。
原生视图通过扩展ViewManager
或更常见地被创建和操纵SimpleViewManager
。A SimpleViewManager
在这种情况下很方便,因为它适用于背景颜色,不透明度和Flexbox布局等常见属性。
这些子类基本上是单一的 - 每个桥只创建一个实例。他们向本地销售本地视图NativeViewHierarchyManager
,然后委托它们根据需要设置和更新视图的属性。在ViewManagers
通常也对意见的代表,通过桥事件发送回JavaScript的。
自动售货视图很简单:
- 创建ViewManager子类。
- 实施该
createViewInstance
方法
- 使用
@ReactProp
(或@ReactPropGroup
)注释公开视图属性设置器
- 注册
createViewManagers
应用程序包中的管理器。
- 实现JavaScript模块
1.创建ViewManager子类
在这个例子中,我们创建了类型ReactImageManager
扩展SimpleViewManager
的视图管理器类ReactImageView
。ReactImageView
是管理器管理的对象的类型,这将是自定义本机视图。返回的名称getName
用于引用JavaScript中的原生视图类型。
...
public class ReactImageManager extends SimpleViewManager<ReactImageView> {
public static final String REACT_CLASS = "RCTImageView";
@Override
public String getName() {
return REACT_CLASS;
}
2.实施方法 createViewInstance
视图在createViewInstance
方法中创建,视图应该初始化为默认状态,任何属性都将通过后续调用来设置updateView.
@Override
public ReactImageView createViewInstance(ThemedReactContext context) {
return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), mCallerContext
}
3.使用@ReactProp(或@ReactPropGroup)注释公开视图属性设置器
要在JavaScript中反映的属性需要作为setter方法公开@ReactProp
(或@ReactPropGroup
)注释。Setter方法应该将视图更新为(当前视图类型的)作为第一个参数,将属性值更新为第二个参数。塞特应该被宣布为一种void
方法,应该是public
。发送给JS的属性类型是根据setter的value参数类型自动确定的。值以下类型目前支持:boolean
,int
,float
,double
,String
,Boolean
,Integer
,ReadableArray
,ReadableMap
。
注释@ReactProp
有一个name
类型的强制性参数String
。分配给@ReactProp
链接到setter方法的注释的名称用于引用JS端的属性。
除了从name
,@ReactProp
注释可以采取以下可选参数:defaultBoolean
,defaultInt
,defaultFloat
。这些参数应该是对应原始类型(相应的boolean
,int
,float
),并在情况下,当使设定器所引用的属性已经从组件移除提供将被传递给设置方法的值。请注意,仅当基本类型提供“默认”值时,如果setter具有某种复杂类型,null
则将在提供相应属性的情况下作为默认值提供。
对于使用批注的方法的Setter声明要求与@ReactPropGroup
for不同@ReactProp
,请参阅@ReactPropGroup
批注类文档以获取有关它的更多信息。
重要!
在ReactJS更新属性值将导致setter方法调用。请注意,我们可以更新组件的方法之一是删除之前设置的属性。在这种情况下,setter方法也会被调用以通知视图管理器该属性已经改变。在将提供这种情况下的“默认”值(基本类型“默认”可以值可以使用来指定defaultBoolean
,defaultFloat
的等参数@ReactProp
注释,对于复杂类型设定器将与设置为值被调用null
)。
@ReactProp(name = "src")
public void setSrc(ReactImageView view, @Nullable ReadableArray sources) {
view.setSource(sources
}
@ReactProp(name = "borderRadius", defaultFloat = 0f)
public void setBorderRadius(ReactImageView view, float borderRadius) {
view.setBorderRadius(borderRadius
}
@ReactProp(name = ViewProps.RESIZE_MODE)
public void setResizeMode(ReactImageView view, @Nullable String resizeMode) {
view.setScaleType(ImageResizeMode.toScaleType(resizeMode)
}
4.注册 ViewManager
最后的Java步骤是将ViewManager注册到应用程序,这发生在与Native Module类似的方式中,通过应用程序包成员函数 createViewManagers.
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new ReactImageManager()
}
5.实现JavaScript模块
最后一步是创建JavaScript模块,为新视图的用户定义Java和JavaScript之间的接口层。大部分工作都是由Java和JavaScript中的内部React代码来处理的,所有这些都是为了描述propTypes
。
// ImageView.js
import PropTypes from 'prop-types';
import { requireNativeComponent, View } from 'react-native';
var iface = {
name: 'ImageView',
propTypes: {
src: PropTypes.string,
borderRadius: PropTypes.number,
resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']),
...View.propTypes // include the default view properties
},
};
module.exports = requireNativeComponent('RCTImageView', iface
requireNativeComponent
通常需要两个参数,第一个是本地视图的名称,第二个是描述组件接口的对象。组件接口应声明友好的name
用于调试消息,并且必须声明propTypes
由本地视图反映。将propTypes
用于检测用户使用本地视图的有效性。请注意,如果您需要您的JavaScript组件不仅仅指定名称和propTypes
,就像执行自定义事件处理一样,您可以将本地组件包装在普通反应组件中。在这种情况下,要在包装组件,而不是传递iface
到requireNativeComponent
。这在MyCustomView
下面的例子中说明。
活动
所以现在我们知道如何公开本机视图组件,我们可以从JS轻松控制这些组件,但我们如何处理来自用户的事件,如捏缩放或平移?当本地事件发生时,本机代码应该向View的JavaScript表示发出一个事件,并且这两个视图与从该getId()
方法返回的值链接。
class MyCustomView extends View {
...
public void onReceiveNativeEvent() {
WritableMap event = Arguments.createMap(
event.putString("message", "MyMessage"
ReactContext reactContext = (ReactContext)getContext(
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(),
"topChange",
event
}
}
事件名称topChange
映射到onChange
JavaScript中的回调支持(映射在UIManagerModuleConstants.java
)。这个回调函数与原始事件一起被调用,我们通常在包装器组件中处理这个事件来创建一个更简单的API:
// MyCustomView.js
class MyCustomView extends React.Component {
constructor(props) {
super(props
this._onChange = this._onChange.bind(this
}
_onChange(event: Event) {
if (!this.props.onChangeMessage) {
return;
}
this.props.onChangeMessage(event.nativeEvent.message
}
render() {
return <RCTMyCustomView {...this.props} onChange={this._onChange} />;
}
}
MyCustomView.propTypes = {
/**
* Callback that is called continuously when the user is dragging the map.
*/
onChangeMessage: PropTypes.func,
...
};
var RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`, MyCustomView, {
nativeOnly: {onChange: true}
}
请注意nativeOnly
以上的使用。有时候,您需要为本地组件公开某些特殊属性,但实际上并不希望它们作为相关React组件的API的一部分。例如,为原始本机事件Switch
提供自定义onChange
处理程序,并公开onValueChange
仅使用布尔值而非原始事件(类似于onChangeMessage
上述示例中)调用的处理程序属性。既然你不希望这些本地唯一的属性成为API的一部分,你不想把它们放入propTypes
,但如果你不这样做,你会得到一个错误。解决方案只是通过nativeOnly
选项将它们发送出去。