Add a way to dismiss PopupMenu elements

Summary:
In native Android apps, like the YouTube app, context menus are closed when the device orientation changes.

In React Native apps instead, when having a [PopupMenu](https://developer.android.com/reference/android/widget/PopupMenu.html) open and rotating the device, the PopupMenu is not dismissed and appears in a wrong position on the screen.

This PR exposes a `dismissPopupMenu` method to allow the application to dismiss any open PopupMenu:

```(javascript)
UIManager.dismissPopupMenu()
```
Closes https://github.com/facebook/react-native/pull/15636

Differential Revision: D6837663

Pulled By: hramos

fbshipit-source-id: 7b0f4f04341129ad45c703a50897e17d93651974
This commit is contained in:
miguelsm 2018-03-16 16:59:49 -07:00 committed by Facebook Github Bot
parent 6426735e82
commit 353c070be9
5 changed files with 57 additions and 5 deletions

View File

@ -74,6 +74,7 @@ public class NativeViewHierarchyManager {
private final LayoutAnimationController mLayoutAnimator = new LayoutAnimationController();
private boolean mLayoutAnimationEnabled;
private PopupMenu mPopupMenu;
public NativeViewHierarchyManager(ViewManagerRegistry viewManagers) {
this(viewManagers, new RootViewManager());
@ -731,18 +732,27 @@ public class NativeViewHierarchyManager {
error.invoke("Can't display popup. Could not find view with tag " + reactTag);
return;
}
PopupMenu popupMenu = new PopupMenu(getReactContextForView(reactTag), anchor);
mPopupMenu = new PopupMenu(getReactContextForView(reactTag), anchor);
Menu menu = popupMenu.getMenu();
Menu menu = mPopupMenu.getMenu();
for (int i = 0; i < items.size(); i++) {
menu.add(Menu.NONE, Menu.NONE, i, items.getString(i));
}
PopupMenuCallbackHandler handler = new PopupMenuCallbackHandler(success);
popupMenu.setOnMenuItemClickListener(handler);
popupMenu.setOnDismissListener(handler);
mPopupMenu.setOnMenuItemClickListener(handler);
mPopupMenu.setOnDismissListener(handler);
popupMenu.show();
mPopupMenu.show();
}
/**
* Dismiss the last opened PopupMenu {@link PopupMenu}.
*/
public void dismissPopupMenu() {
if (mPopupMenu != null) {
mPopupMenu.dismiss();
}
}
private static class PopupMenuCallbackHandler implements PopupMenu.OnMenuItemClickListener,

View File

@ -802,6 +802,10 @@ public class UIImplementation {
mOperationsQueue.enqueueShowPopupMenu(reactTag, items, error, success);
}
public void dismissPopupMenu() {
mOperationsQueue.enqueueDismissPopupMenu();
}
public void sendAccessibilityEvent(int tag, int eventType) {
mOperationsQueue.enqueueSendAccessibilityEvent(tag, eventType);
}

View File

@ -600,6 +600,11 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
mUIImplementation.showPopupMenu(reactTag, items, error, success);
}
@ReactMethod
public void dismissPopupMenu() {
mUIImplementation.dismissPopupMenu();
}
/**
* LayoutAnimation API on Android is currently experimental. Therefore, it needs to be enabled
* explicitly in order to avoid regression in existing application written for iOS using this API.

View File

@ -285,6 +285,13 @@ public class UIViewOperationQueue {
}
}
private final class DismissPopupMenuOperation implements UIOperation {
@Override
public void execute() {
mNativeViewHierarchyManager.dismissPopupMenu();
}
}
/**
* A spec for animation operations (add/remove)
*/
@ -656,6 +663,10 @@ public class UIViewOperationQueue {
mOperations.add(new ShowPopupMenuOperation(reactTag, items, error, success));
}
public void enqueueDismissPopupMenu() {
mOperations.add(new DismissPopupMenuOperation());
}
public void enqueueCreateView(
ThemedReactContext themedContext,
int viewReactTag,

View File

@ -33,6 +33,7 @@ import javax.annotation.Nullable;
public class ReactToolbarManager extends ViewGroupManager<ReactToolbar> {
private static final String REACT_CLASS = "ToolbarAndroid";
private static final int COMMAND_DISMISS_POPUP_MENUS = 1;
@Override
public String getName() {
@ -157,6 +158,27 @@ public class ReactToolbarManager extends ViewGroupManager<ReactToolbar> {
return true;
}
@Nullable
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.of("dismissPopupMenus", COMMAND_DISMISS_POPUP_MENUS);
}
@Override
public void receiveCommand(ReactToolbar view, int commandType, @Nullable ReadableArray args) {
switch (commandType) {
case COMMAND_DISMISS_POPUP_MENUS: {
view.dismissPopupMenus();
return;
}
default:
throw new IllegalArgumentException(String.format(
"Unsupported command %d received by %s.",
commandType,
getClass().getSimpleName()));
}
}
private int[] getDefaultContentInsets(Context context) {
Resources.Theme theme = context.getTheme();
TypedArray toolbarStyle = null;