View
画面を表示する機能
https://github.com/facebook/react-native/tree/master/Libraries/Components/View
Reading
基本的にES5かつReact0.14以前のコードスタイルですね。
const View = React.createClass({
mixins: [NativeMethodsMixin],
viewConfig: {
uiViewClassName: 'RCTView',
validAttributes: ReactNativeViewAttributes.RCTView
},
statics: {
...statics,
},
...,
});
property定義。
次に一番重要であろうpropTypesを見ていきます。
propTypes: {
accessible: PropTypes.bool,
accessibilityLabel: PropTypes.string,
accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentType),
/*
* const AccessibilityComponentType = [
* 'none',
* 'button',
* 'radiobutton_checked',
* 'radiobutton_unchecked',
* ];
*/
accessibilityLiveRegion: PropTypes.oneOf([
'none',
'polite',
'assertive',
]),
// @platform android
importantForAccessibility: PropTypes.oneOf([
'auto',
'yes',
'no',
'no-hide-descendants',
]),
/*
* - `'none'` - The element has no traits.
* - `'button'` - The element should be treated as a button.
* - `'link'` - The element should be treated as a link.
* - `'header'` - The element is a header that divides content into sections.
* - `'search'` - The element should be treated as a search field.
* - `'image'` - The element should be treated as an image.
* - `'selected'` - The element is selected.
* - `'plays'` - The element plays sound.
* - `'key'` - The element should be treated like a keyboard key.
* - `'text'` - The element should be treated as text.
* - `'summary'` - The element provides app summary information.
* - `'disabled'` - The element is disabled.
* - `'frequentUpdates'` - The element frequently changes its value.
* - `'startsMedia'` - The element starts a media session.
* - `'adjustable'` - The element allows adjustment over a range of values.
* - `'allowsDirectInteraction'` - The element allows direct touch interaction for VoiceOver users.
* - `'pageTurn'` - Informs VoiceOver that it should scroll to the next page when it finishes reading the contents of the element.
*
* @platform ios
*/
accessibilityTraits: PropTypes.oneOfType([
PropTypes.oneOf(AccessibilityTraits),
PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)),
]),
onAccessibilityTap: PropTypes.func,
onMagicTap: PropTypes.func,
testID: PropTypes.string,
/*
* touch interactions
* func: (nativeEvent) => {}
*
* - `nativeEvent`
* - `changedTouches` - Array of all touch events that have changed since the last event.
* - `identifier` - The ID of the touch.
* - `locationX` - The X position of the touch, relative to the element.
* - `locationY` - The Y position of the touch, relative to the element.
* - `pageX` - The X position of the touch, relative to the root element.
* - `pageY` - The Y position of the touch, relative to the root element.
* - `target` - The node id of the element receiving the touch event.
* - `timestamp` - A time identifier for the touch, useful for velocity calculation.
* - `touches` - Array of all current touches on the screen.
*/
// The View is now responding for touch events
// reponderがactiveになる時に発火
onResponderGrant: PropTypes.func,
// The user is moving their finger.
// ユーザの指が動いた時に発火
onResponderMove: PropTypes.func,
// Another responder is already active and will not release it to that `View` asking to be the responder.
// 他のresponderがactiveで、responderがreleaseされずにこのresponderが発火できない時に発火
onResponderReject: PropTypes.func,
// Fired at the end of the touch.
// responderがreleaseされる時に発火
onResponderRelease: PropTypes.func,
// The responder has been taken from the `View`
// responderが終了された時に発火
onResponderTerminate: PropTypes.func,
// Some other `View` wants to become responder and is asking this `View` to release its responder.
// Returning `true` allows its release.
// 他のViewがresponderを扱いたいというリクエストを受信した時に発火
// trueを返せばresponderをrelease
onResponderTerminationRequest: PropTypes.func,
// Does this view want to become responder on the start of a touch?
// touch開始時にresponderになるかどうか、trueかfalseを返す必要あり
onStartShouldSetResponder: PropTypes.func,
// If a parent `View` wants to prevent a child `View` from becoming responder on a touch start,
// it should have this handler which returns `true`.
// childrenのViewがtouch開始時にresponderを扱うことを防ぐ場合はtrueを返す
onStartShouldSetResponderCapture: PropTypes.func,
// Does this view want to "claim" touch responsiveness?
// touchに主張した時はtrueを返す
onMoveShouldSetResponder: PropTypes.func,
// If a parent `View` wants to prevent a child `View` from becoming responder on a move,
// it should have this handler which returns `true`.
// childrenのViewがmove時にresponderを扱うことを防ぐ場合はtrueを返す
onMoveShouldSetResponderCapture: PropTypes.func,
/**
* This defines how far a touch event can start away from the view.
* Typical interface guidelines recommend touch targets that are at least
* 30 - 40 points/density-independent pixels.
*
* For example, if a touchable view has a height of 20 the touchable height can be extended to
* 40 with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`
*
* touchできる範囲を指定する
*/
hitSlop: EdgeInsetsPropType,
/**
* Invoked on mount and layout changes with:
*
* `{nativeEvent: { layout: {x, y, width, height}}}`
*
* This event is fired immediately once the layout has been calculated, but
* the new layout may not yet be reflected on the screen at the time the
* event is received, especially if a layout animation is in progress.
*
* layoutが変化した時に一度呼ばれる
*/
onLayout: PropTypes.func,
// touch eventのtagetになるかを指定する
pointerEvents: PropTypes.oneOf([
'box-none',
'none',
'box-only',
'auto',
]),
style: stylePropType,
// subviewをたくさん持っている場合に不必要に表示しないようにする?
removeClippedSubviews: PropTypes.bool,
// Whether this `View` should render itself (and all of its children) into a
// single hardware texture on the GPU.
// @platform android
renderToHardwareTextureAndroid: PropTypes.bool,
// Whether this `View` should be rendered as a bitmap before compositing.
// @platform ios
shouldRasterizeIOS: PropTypes.bool,
// childrenを描画するためだけの場合に表示を最適化する
// @platform android
collapsable: PropTypes.bool,
// @platform android
needsOffscreenAlphaCompositing: PropTypes.bool,
},
主にtouch eventを制御するpropsですね。
最後にrenderを見てみます。
render: function() {
// WARNING: This method will not be used in production mode as in that mode we
// replace wrapper component View with generated native wrapper RCTView. Avoid
// adding functionality this component that you'd want to be available in both
// dev and prod modes.
return <RCTView {...this.props} />;
},
何やらproductionではこのView自体がrenderingされるわけではないらしい・・・?
もう少し後の処理を見てみます。
const RCTView = requireNativeComponent('RCTView', View, {
nativeOnly: {
nativeBackgroundAndroid: true,
}
});
let ViewToExport = RCTView;
if (__DEV__) {
ViewToExport = View;
} else {
Object.assign(RCTView, statics);
}
module.exports = ViewToExport;
ここですね。__DEV__=true
の場合にView Componentがexportされて、productionではRCTViewがexportされているようです。
productionを見ないと意味がないので、RCTViewを生成しているrequireNativeComponent
を見ていきます。
function requireNativeComponent(
viewName: string,
componentInterface?: ?ComponentInterface,
extraConfig?: ?{nativeOnly?: Object},
): Function {
var viewConfig = UIManager[viewName];
if (!viewConfig || !viewConfig.NativeProps) {
warning(false, 'Native component for "%s" does not exist', viewName);
return UnimplementedView;
}
var nativeProps = {
...UIManager.RCTView.NativeProps,
...viewConfig.NativeProps,
};
viewConfig.uiViewClassName = viewName;
viewConfig.validAttributes = {};
viewConfig.propTypes = componentInterface && componentInterface.propTypes;
for (var key in nativeProps) {
var useAttribute = false;
var attribute = {};
var differ = TypeToDifferMap[nativeProps[key]];
if (differ) {
attribute.diff = differ;
useAttribute = true;
}
var processor = TypeToProcessorMap[nativeProps[key]];
if (processor) {
attribute.process = processor;
useAttribute = true;
}
viewConfig.validAttributes[key] = useAttribute ? attribute : true;
}
viewConfig.validAttributes.style = ReactNativeStyleAttributes;
return createReactNativeComponentClass(viewConfig);
}
createReactNativeComponentClass
を返しているので見てみます。
https://github.com/facebook/react/blob/master/src/renderers/native/createReactNativeComponentClass.js
(なぜかReact本体の方にある・・・)
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
propTypes?: Object,
}
var createReactNativeComponentClass = function(
viewConfig: ReactNativeBaseComponentViewConfig
): ReactClass<any> {
var Constructor = function(element) {
this._currentElement = element;
this._topLevelWrapper = null;
this._hostParent = null;
this._hostContainerInfo = null;
this._rootNodeID = null;
this._renderedChildren = null;
};
Constructor.displayName = viewConfig.uiViewClassName;
Constructor.viewConfig = viewConfig;
Constructor.propTypes = viewConfig.propTypes;
Constructor.prototype = new ReactNativeBaseComponent(viewConfig);
Constructor.prototype.constructor = Constructor;
return ((Constructor: any): ReactClass<any>);
};
ReactClassのprototype chainしたConstructorが返されるようですね。
prototypeのReactNativeBaseComponent
を見ていきましょう。
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
}
var ReactNativeBaseComponent = function(
viewConfig: ReactNativeBaseComponentViewConfig
) {
this.viewConfig = viewConfig;
};
Object.assign(
ReactNativeBaseComponent.prototype,
ReactMultiChild.Mixin,
ReactNativeBaseComponent.Mixin,
NativeMethodsMixin
);
色々なmethodがmixinされているようです。
これでproductionでは、ReactNativeBaseComponent
の各methodとViewのpropsを持つReact.Componentが出来上がることがわかりました。
次回は何かしらのtouch eventの仕組みを見ていこうと思います。