Add zIndex support

Summary:
Adds zIndex support :)

**Test Plan**

Tested the following components by adding two of each, overlapping them, and setting a high zIndex on the first of the two:

ActivityIndicator
Image
MapView
Picker
ScrollView
Slider
Switch
Text
TextInput
View
WebView

Tested on Android 4.1 and iOS 8.4. Also tested updating zIndexes on Views in my own app.

<img width="359" alt="ios activityindicator" src="https://cloud.githubusercontent.com/assets/4349082/15633473/88f842cc-257b-11e6-8539-c41c0b179f80.png">
<img width="330" alt="android activityindicator" src="https://cloud.githubusercontent.com/assets/4349082/15633475/88f95784-257b-11e6-80c0-2bf3ed836503.png">
<img width="357" alt="ios image" src="https://cloud.githubusercontent.com/assets/4349082/15633474/88f93d80-257b-11e6-9e54-4ff8e4d25f71.png">
<img width="340" alt="android image" src="https://cloud.githubusercontent.com/assets/4349082/15633478/88fd2788-257b-11e6-8c80-29078e65e808.png">
<img width="342" alt="android picker" src="ht
Closes https://github.com/facebook/react-native/pull/7825

Differential Revision: D3469374

Pulled By: lexs

fbshipit-source-id: b2b74b71d968ebf73ecfd457ace3f35f8f7c7658
This commit is contained in:
Tucker Connelly 2016-06-29 07:38:30 -07:00 committed by Facebook Github Bot 3
parent 3085b35e63
commit 3d3b067f6f
6 changed files with 72 additions and 2 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 153 KiB

View File

@ -93,7 +93,7 @@ var ZIndexExample = React.createClass({
flipped: false
};
},
render() {
const indices = this.state.flipped ? [-1, 0, 1, 2] : [2, 1, 0, -1];
return (
@ -128,7 +128,7 @@ var ZIndexExample = React.createClass({
</TouchableWithoutFeedback>
);
},
_handlePress() {
this.setState({flipped: !this.state.flipped});
}

View File

@ -5,6 +5,7 @@ package com.facebook.react.uimanager;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
@ -21,6 +22,7 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
private static final String PROP_TRANSFORM = "transform";
private static final String PROP_OPACITY = "opacity";
private static final String PROP_ELEVATION = "elevation";
private static final String PROP_Z_INDEX = "zIndex";
private static final String PROP_RENDER_TO_HARDWARE_TEXTURE = "renderToHardwareTextureAndroid";
private static final String PROP_ACCESSIBILITY_LABEL = "accessibilityLabel";
private static final String PROP_ACCESSIBILITY_COMPONENT_TYPE = "accessibilityComponentType";
@ -70,6 +72,12 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
// Do nothing on API < 21
}
@ReactProp(name = PROP_Z_INDEX)
public void setZIndex(T view, float zIndex) {
int integerZIndex = Math.round(zIndex);
ViewGroupManager.setViewZIndex(view, integerZIndex);
}
@ReactProp(name = PROP_RENDER_TO_HARDWARE_TEXTURE)
public void setRenderToHardwareTexture(T view, boolean useHWTexture) {
view.setLayerType(useHWTexture ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null);

View File

@ -12,12 +12,20 @@ package com.facebook.react.uimanager;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.WeakHashMap;
/**
* Class providing children management API for view managers of classes extending ViewGroup.
*/
public abstract class ViewGroupManager <T extends ViewGroup>
extends BaseViewManager<T, LayoutShadowNode> {
public static WeakHashMap<View, Integer> mZIndexHash = new WeakHashMap<>();
@Override
public LayoutShadowNode createShadowNodeInstance() {
return new LayoutShadowNode();
@ -34,6 +42,59 @@ public abstract class ViewGroupManager <T extends ViewGroup>
public void addView(T parent, View child, int index) {
parent.addView(child, index);
reorderChildrenByZIndex(parent);
}
public static void setViewZIndex(View view, int zIndex) {
mZIndexHash.put(view, zIndex);
// zIndex prop gets set BEFORE the view is added, so parent may be null.
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
reorderChildrenByZIndex(parent);
}
}
public static void reorderChildrenByZIndex(ViewGroup view) {
// Optimization: loop through the zIndexHash to test if there are any non-zero zIndexes
// If there aren't any, we can just return out
Collection<Integer> zIndexes = mZIndexHash.values();
boolean containsZIndexedElement = false;
for (Integer zIndex : zIndexes) {
if (zIndex != 0) {
containsZIndexedElement = true;
break;
}
}
if (!containsZIndexedElement) {
return;
}
// Add all children to a sortable ArrayList
ArrayList<View> viewsToSort = new ArrayList<>();
for (int i = 0; i < view.getChildCount(); i++) {
viewsToSort.add(view.getChildAt(i));
}
// Sort the views by zIndex
Collections.sort(viewsToSort, new Comparator<View>() {
@Override
public int compare(View view1, View view2) {
Integer view1ZIndex = mZIndexHash.get(view1);
if (view1ZIndex == null) {
view1ZIndex = 0;
}
Integer view2ZIndex = mZIndexHash.get(view2);
if (view2ZIndex == null) {
view2ZIndex = 0;
}
return view1ZIndex - view2ZIndex;
}
});
// Call .bringToFront on the sorted list of views
for (int i = 0; i < viewsToSort.size(); i++) {
viewsToSort.get(i).bringToFront();
}
view.invalidate();
}
public int getChildCount(T parent) {

View File

@ -195,6 +195,7 @@ public class ReactViewManager extends ViewGroupManager<ReactViewGroup> {
} else {
parent.addView(child, index);
}
reorderChildrenByZIndex(parent);
}
@Override