/**
* Copyright (c) 2015-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.uimanager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
import android.view.View;
import android.view.ViewGroup;
/**
* 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();
}
@Override
public Class<? extends LayoutShadowNode> getShadowNodeClass() {
return LayoutShadowNode.class;
}
@Override
public void updateExtraData(T root, Object extraData) {
}
public void addView(T parent, View child, int index) {
parent.addView(child, index);
reorderChildrenByZIndex(parent);
}
/**
* Convenience method for batching a set of addView calls
* Note that this adds the views to the beginning of the ViewGroup
*
* @param parent the parent ViewGroup
* @param views the set of views to add
*/
public void addViews(T parent, List<View> views) {
for (int i = 0, size = views.size(); i < size; i++) {
addView(parent, views.get(i), i);
}
}
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) {
return parent.getChildCount();
}
public View getChildAt(T parent, int index) {
return parent.getChildAt(index);
}
public void removeViewAt(T parent, int index) {
parent.removeViewAt(index);
}
public void removeView(T parent, View view) {
for (int i = 0; i < getChildCount(parent); i++) {
if (getChildAt(parent, i) == view) {
removeViewAt(parent, i);
break;
}
}
}
public void removeAllViews(T parent) {
for (int i = getChildCount(parent) - 1; i >= 0; i--) {
removeViewAt(parent, i);
}
}
/**
* Returns whether this View type needs to handle laying out its own children instead of
* deferring to the standard css-layout algorithm.
* Returns true for the layout to *not* be automatically invoked. Instead onLayout will be
* invoked as normal and it is the View instance's responsibility to properly call layout on its
* children.
* Returns false for the default behavior of automatically laying out children without going
* through the ViewGroup's onLayout method. In that case, onLayout for this View type must *not*
* call layout on its children.
*/
public boolean needsCustomLayoutForChildren() {
return false;
}
/**
* Returns whether or not this View type should promote its grandchildren as Views. This is an
* optimization for Scrollable containers when using Nodes, where instead of having one ViewGroup
* containing a large number of draw commands (and thus being more expensive in the case of
* an invalidate or re-draw), we split them up into several draw commands.
*/
public boolean shouldPromoteGrandchildren() {
return false;
}
}