/** * 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; } }