mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-01-12 22:50:10 +08:00
Keyboard accessibility improvements (#24359)
Summary: In order to meet our accessibility requirements we need to have full support for keyboard navigation. The Touchable components works with press/tap with a finger, but doesn't respond to 'enter' when using a keyboard. Navigation works fine. This PR adds an onClick listener to touchable views that have the onPress prop defined. [Android] [Added] - Add View.OnClickListener to Touchable components when onPress is defined Pull Request resolved: https://github.com/facebook/react-native/pull/24359 Differential Revision: D14971230 Pulled By: cpojer fbshipit-source-id: ca5559ca1308ee6c338532a00dcea4d00fa57f42
This commit is contained in:
committed by
Facebook Github Bot
parent
a1250da646
commit
01bcde3ed8
@@ -184,6 +184,12 @@ const TouchableBounce = ((createReactClass({
|
||||
nativeID={this.props.nativeID}
|
||||
testID={this.props.testID}
|
||||
hitSlop={this.props.hitSlop}
|
||||
clickable={
|
||||
this.props.clickable !== false &&
|
||||
this.props.onPress !== undefined &&
|
||||
!this.props.disabled
|
||||
}
|
||||
onClick={this.touchableHandlePress}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
onResponderTerminationRequest={
|
||||
this.touchableHandleResponderTerminationRequest
|
||||
|
||||
@@ -422,6 +422,10 @@ const TouchableHighlight = ((createReactClass({
|
||||
nextFocusLeft={this.props.nextFocusLeft}
|
||||
nextFocusRight={this.props.nextFocusRight}
|
||||
nextFocusUp={this.props.nextFocusUp}
|
||||
clickable={
|
||||
this.props.clickable !== false && this.props.onPress !== undefined
|
||||
}
|
||||
onClick={this.touchableHandlePress}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
onResponderTerminationRequest={
|
||||
this.touchableHandleResponderTerminationRequest
|
||||
|
||||
@@ -325,6 +325,11 @@ const TouchableNativeFeedback = createReactClass({
|
||||
nextFocusRight: this.props.nextFocusRight,
|
||||
nextFocusUp: this.props.nextFocusUp,
|
||||
hasTVPreferredFocus: this.props.hasTVPreferredFocus,
|
||||
clickable:
|
||||
this.props.clickable !== false &&
|
||||
this.props.onPress !== undefined &&
|
||||
!this.props.disabled,
|
||||
onClick: this.touchableHandlePress,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
onResponderTerminationRequest: this
|
||||
.touchableHandleResponderTerminationRequest,
|
||||
|
||||
@@ -324,6 +324,10 @@ const TouchableOpacity = ((createReactClass({
|
||||
hasTVPreferredFocus={this.props.hasTVPreferredFocus}
|
||||
tvParallaxProperties={this.props.tvParallaxProperties}
|
||||
hitSlop={this.props.hitSlop}
|
||||
clickable={
|
||||
this.props.clickable !== false && this.props.onPress !== undefined
|
||||
}
|
||||
onClick={this.touchableHandlePress}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
onResponderTerminationRequest={
|
||||
this.touchableHandleResponderTerminationRequest
|
||||
|
||||
@@ -249,6 +249,9 @@ const TouchableWithoutFeedback = ((createReactClass({
|
||||
return (React: any).cloneElement(child, {
|
||||
...overrides,
|
||||
accessible: this.props.accessible !== false,
|
||||
clickable:
|
||||
this.props.clickable !== false && this.props.onPress !== undefined,
|
||||
onClick: this.touchableHandlePress,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
onResponderTerminationRequest: this
|
||||
.touchableHandleResponderTerminationRequest,
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
exports[`TouchableHighlight renders correctly 1`] = `
|
||||
<View
|
||||
accessible={true}
|
||||
clickable={false}
|
||||
isTVSelectable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
|
||||
@@ -304,6 +304,20 @@ type AndroidViewProps = $ReadOnly<{|
|
||||
* @platform android
|
||||
*/
|
||||
nextFocusUp?: ?number,
|
||||
|
||||
/**
|
||||
* Whether this `View` should be clickable with a non-touch click, eg. enter key on a hardware keyboard.
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
clickable?: boolean,
|
||||
|
||||
/**
|
||||
* The action to perform when this `View` is clicked on by a non-touch click, eg. enter key on a hardware keyboard.
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
onClick?: () => void,
|
||||
|}>;
|
||||
|
||||
type IOSViewProps = $ReadOnly<{|
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
package com.facebook.react.uimanager;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import com.facebook.react.R;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.AccessibilityDelegateUtil.AccessibilityRole;
|
||||
|
||||
@@ -83,6 +83,7 @@ import java.util.Map;
|
||||
.put("topLoadingStart", MapBuilder.of(rn, "onLoadingStart"))
|
||||
.put("topSelectionChange", MapBuilder.of(rn, "onSelectionChange"))
|
||||
.put("topMessage", MapBuilder.of(rn, "onMessage"))
|
||||
.put("topClick", MapBuilder.of(rn, "onClick"))
|
||||
// Scroll events are added as per task T22348735.
|
||||
// Subject for further improvement.
|
||||
.put("topScrollBeginDrag", MapBuilder.of(rn, "onScrollBeginDrag"))
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
@@ -21,10 +22,12 @@ import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.PointerEvents;
|
||||
import com.facebook.react.uimanager.Spacing;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.ViewGroupManager;
|
||||
import com.facebook.react.uimanager.ViewProps;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.uimanager.annotations.ReactPropGroup;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.yoga.YogaConstants;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -223,6 +226,28 @@ public class ReactViewManager extends ViewGroupManager<ReactViewGroup> {
|
||||
// handled in NativeViewHierarchyOptimizer
|
||||
}
|
||||
|
||||
@ReactProp(name = "clickable")
|
||||
public void setClickable(final ReactViewGroup view, boolean clickable) {
|
||||
if (clickable) {
|
||||
view.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final EventDispatcher mEventDispatcher = ((ReactContext)view.getContext()).getNativeModule(UIManagerModule.class)
|
||||
.getEventDispatcher();
|
||||
mEventDispatcher.dispatchEvent(new ViewGroupClickEvent(view.getId()));
|
||||
}});
|
||||
|
||||
// Clickable elements are focusable. On API 26, this is taken care by setClickable.
|
||||
// Explicitly calling setFocusable here for backward compatibility.
|
||||
view.setFocusable(true /*isFocusable*/);
|
||||
}
|
||||
else {
|
||||
view.setOnClickListener(null);
|
||||
view.setClickable(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.OVERFLOW)
|
||||
public void setOverflow(ReactViewGroup view, String overflow) {
|
||||
view.setOverflow(overflow);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.facebook.react.views.view;
|
||||
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
/**
|
||||
* Represents a Click on the ReactViewGroup
|
||||
*/
|
||||
public class ViewGroupClickEvent extends Event<ViewGroupClickEvent> {
|
||||
private static final String EVENT_NAME = "topClick";
|
||||
|
||||
public ViewGroupClickEvent(int viewId) {
|
||||
super(viewId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return EVENT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCoalesce() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), Arguments.createMap());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user