Add a nativeID prop to allow native code to reference react managed views

Reviewed By: sahrens

Differential Revision: D4786713

fbshipit-source-id: af9cef0737c010b429d52d00181c00bd81f13f5b
This commit is contained in:
Andrew Y. Chen
2017-04-07 11:47:35 -07:00
committed by Facebook Github Bot
parent e154117f37
commit 909af08f24
17 changed files with 218 additions and 4 deletions

View File

@@ -13,13 +13,14 @@ deps = [
react_native_target("java/com/facebook/react/modules/appstate:appstate"),
react_native_target("java/com/facebook/react/modules/core:core"),
react_native_target("java/com/facebook/react/modules/datepicker:datepicker"),
react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"),
react_native_target("java/com/facebook/react/modules/share:share"),
react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"),
react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"),
react_native_target("java/com/facebook/react/modules/timepicker:timepicker"),
react_native_target("java/com/facebook/react/touch:touch"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
react_native_target("java/com/facebook/react/uimanager/util:util"),
react_native_target("java/com/facebook/react/views/picker:picker"),
react_native_target("java/com/facebook/react/views/progressbar:progressbar"),
react_native_target("java/com/facebook/react/views/scroll:scroll"),

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.tests;
import java.util.Arrays;
import java.util.List;
import android.view.View;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
/**
* Tests that the 'nativeID' property can be set on various views.
* The 'nativeID' property is used to reference react managed views from native code.
*/
public class NativeIdTestCase extends ReactAppInstrumentationTestCase {
@Override
protected String getReactApplicationKeyUnderTest() {
return "NativeIdTestApp";
}
private final List<String> viewTags = Arrays.asList(
"Image",
"Text",
"TouchableBounce",
"TouchableHighlight",
"TouchableOpacity",
"TouchableWithoutFeedback",
"TextInput",
"View");
public void testPropertyIsSetForViews() {
for (String nativeId : viewTags) {
View viewWithTag = ReactFindViewUtil.findViewByNativeId(
getActivity().getRootView(),
nativeId);
assertNotNull(
"View with nativeID " + nativeId + " was not found. Check NativeIdTestModule.js.",
viewWithTag);
}
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeIdTestModule
* @flow
*/
'use strict';
const Image = require('Image');
const React = require('React');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TextInput = require('TextInput');
const TouchableBounce = require('TouchableBounce');
const TouchableHighlight = require('TouchableHighlight');
const TouchableOpacity = require('TouchableOpacity');
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
const View = require('View');
/**
* All the views implemented on Android, each with the nativeID property set.
* We test that:
* - The app renders fine
* - The nativeID property is passed to the native views
*/
class NativeIdTestApp extends React.Component {
render() {
const uri = 'data:image/gif;base64,' +
'R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapy' +
'uvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/' +
'TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5' +
'iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97V' +
'riy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7';
return (
<View>
<Image
nativeID="Image"
source={{uri: uri}}
style={styles.base} />
<Text nativeID="Text">text</Text>
<TextInput nativeID="TextInput" value="Text input" />
<TouchableBounce nativeID="TouchableBounce">
<Text>TouchableBounce</Text>
</TouchableBounce>
<TouchableHighlight nativeID="TouchableHighlight">
<Text>TouchableHighlight</Text>
</TouchableHighlight>
<TouchableOpacity nativeID="TouchableOpacity">
<Text>TouchableOpacity</Text>
</TouchableOpacity>
<TouchableWithoutFeedback nativeID="TouchableWithoutFeedback">
<View>
<Text>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
<View nativeID="View" />
</View>
);
}
}
const styles = StyleSheet.create({
base: {
width: 150,
height: 50,
},
});
module.exports = {
NativeIdTestApp: NativeIdTestApp,
};

View File

@@ -69,6 +69,10 @@ var apps = [
appKey: 'MultitouchHandlingTestAppModule',
component: () => require('MultitouchHandlingTestAppModule')
},
{
appKey: 'NativeIdTestApp',
component: () => require('NativeIdTestModule').NativeIdTestApp
},
{
appKey: 'PickerAndroidTestApp',
component: () => require('PickerAndroidTestModule').PickerAndroidTestApp,

View File

@@ -41,6 +41,7 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
* Used to locate views in end-to-end (UI) tests.
*/
public static final String PROP_TEST_ID = "testID";
public static final String PROP_NATIVE_ID = "nativeID";
private static MatrixMathHelper.MatrixDecompositionContext sMatrixDecompositionContext =
new MatrixMathHelper.MatrixDecompositionContext();
@@ -92,6 +93,11 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
view.setTag(testId);
}
@ReactProp(name = PROP_NATIVE_ID)
public void setNativeId(T view, String nativeId) {
view.setTag(R.id.view_tag_native_id, nativeId);
}
@ReactProp(name = PROP_ACCESSIBILITY_LABEL)
public void setAccessibilityLabel(T view, String accessibilityLabel) {
view.setContentDescription(accessibilityLabel);

View File

@@ -0,0 +1,13 @@
include_defs("//ReactAndroid/DEFS")
android_library(
name = "util",
srcs = glob(["*.java"]),
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_target("res:uimanager"),
],
)

View File

@@ -0,0 +1,38 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.uimanager.util;
import javax.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.react.R;
/**
* Finds views in React Native view hierarchies
*/
public class ReactFindViewUtil {
/**
* Finds a view that is tagged with {@param nativeId} as its `nativeID` prop
*/
public static @Nullable View findViewByNativeId(View view, String nativeId) {
Object tag = view.getTag(R.id.view_tag_native_id);
if (tag instanceof String && tag.equals(nativeId)) {
return view;
}
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View v = findViewByNativeId(viewGroup.getChildAt(i), nativeId);
if (v != null) {
return v;
}
}
}
return null;
}
}

View File

@@ -2,4 +2,7 @@
<resources>
<!-- tag is used to store the testID tag -->
<item type="id" name="react_test_id"/>
<!-- tag is used to store the nativeID tag -->
<item type="id" name="view_tag_native_id"/>
</resources>