public void updateLayout(int parentTag, int tag, int x, int y, int width, int height) {
    UiThreadUtil.assertOnUiThread();

    View viewToUpdate = mTagsToViews.get(tag);
    if (viewToUpdate == null) {
      throw new IllegalViewOperationException(
          "Trying to update view with tag " + tag + " which " + "doesn't exist");
    }

    // Even though we have exact dimensions, we still call measure because some platform views (e.g.
    // Switch) assume that method will always be called before onLayout and onDraw. They use it to
    // calculate and cache information used in the draw pass. For most views, onMeasure can be
    // stubbed out to only call setMeasuredDimensions. For ViewGroups, onLayout should be stubbed
    // out to not recursively call layout on its children: React Native already handles doing that.
    //
    // Also, note measure and layout need to be called *after* all View properties have been updated
    // because of caching and calculation that may occur in onMeasure and onLayout. Layout
    // operations should also follow the native view hierarchy and go top to bottom for consistency
    // with standard layout passes (some views may depend on this).

    viewToUpdate.measure(
        View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
        View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));

    // Check if the parent of the view has to layout the view, or the child has to lay itself out.
    if (!mRootTags.get(parentTag)) {
      ViewManager parentViewManager = mTagsToViewManagers.get(parentTag);
      ViewGroupManager parentViewGroupManager;
      if (parentViewManager instanceof ViewGroupManager) {
        parentViewGroupManager = (ViewGroupManager) parentViewManager;
      } else {
        throw new IllegalViewOperationException(
            "Trying to use view with tag "
                + tag
                + " as a parent, but its Manager doesn't extends ViewGroupManager");
      }
      if (parentViewGroupManager != null
          && !parentViewGroupManager.needsCustomLayoutForChildren()) {
        viewToUpdate.layout(x, y, x + width, y + height);
      }
    } else {
      viewToUpdate.layout(x, y, x + width, y + height);
    }
  }
Exemplo n.º 2
0
 private void assertNodeDoesNotNeedCustomLayoutForChildren(ReactShadowNode node) {
   ViewManager viewManager = Assertions.assertNotNull(mViewManagers.get(node.getViewClass()));
   ViewGroupManager viewGroupManager;
   if (viewManager instanceof ViewGroupManager) {
     viewGroupManager = (ViewGroupManager) viewManager;
   } else {
     throw new IllegalViewOperationException(
         "Trying to use view "
             + node.getViewClass()
             + " as a parent, but its Manager doesn't extends ViewGroupManager");
   }
   if (viewGroupManager != null && viewGroupManager.needsCustomLayoutForChildren()) {
     throw new IllegalViewOperationException(
         "Trying to measure a view using measureLayout/measureLayoutRelativeToParent relative to"
             + " an ancestor that requires custom layout for it's children ("
             + node.getViewClass()
             + "). Use measure instead.");
   }
 }
 /** Releases all references to given native View. */
 private void dropView(View view) {
   UiThreadUtil.assertOnUiThread();
   if (!mRootTags.get(view.getId())) {
     // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance}
     Assertions.assertNotNull(mTagsToViewManagers.get(view.getId()))
         .onDropViewInstance((ThemedReactContext) view.getContext(), view);
   }
   ViewManager viewManager = mTagsToViewManagers.get(view.getId());
   if (view instanceof ViewGroup && viewManager instanceof ViewGroupManager) {
     ViewGroup viewGroup = (ViewGroup) view;
     ViewGroupManager viewGroupManager = (ViewGroupManager) viewManager;
     for (int i = viewGroupManager.getChildCount(viewGroup) - 1; i >= 0; i--) {
       View child = viewGroupManager.getChildAt(viewGroup, i);
       if (mTagsToViews.get(child.getId()) != null) {
         dropView(child);
       }
     }
     viewGroupManager.removeAllViews(viewGroup);
   }
   mTagsToViews.remove(view.getId());
   mTagsToViewManagers.remove(view.getId());
 }
  /**
   * @param tag react tag of the node we want to manage
   * @param indicesToRemove ordered (asc) list of indicies at which view should be removed
   * @param viewsToAdd ordered (asc based on mIndex property) list of tag-index pairs that represent
   *     a view which should be added at the specified index
   * @param tagsToDelete list of tags corresponding to views that should be removed
   */
  public void manageChildren(
      int tag,
      @Nullable int[] indicesToRemove,
      @Nullable ViewAtIndex[] viewsToAdd,
      @Nullable int[] tagsToDelete) {
    ViewGroup viewToManage = (ViewGroup) mTagsToViews.get(tag);
    ViewGroupManager viewManager = (ViewGroupManager) mTagsToViewManagers.get(tag);
    if (viewManager == null) {
      throw new IllegalViewOperationException("ViewManager for tag " + tag + " could not be found");
    }
    if (viewToManage == null) {
      throw new IllegalViewOperationException(
          "Trying to manageChildren view with tag "
              + tag
              + " which doesn't exist\n detail: "
              + constructManageChildrenErrorMessage(
                  viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete));
    }

    int lastIndexToRemove = viewManager.getChildCount(viewToManage);
    if (indicesToRemove != null) {
      for (int i = indicesToRemove.length - 1; i >= 0; i--) {
        int indexToRemove = indicesToRemove[i];
        if (indexToRemove < 0) {
          throw new IllegalViewOperationException(
              "Trying to remove a negative view index:"
                  + indexToRemove
                  + " view tag: "
                  + tag
                  + "\n detail: "
                  + constructManageChildrenErrorMessage(
                      viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete));
        }
        if (indexToRemove >= viewManager.getChildCount(viewToManage)) {
          throw new IllegalViewOperationException(
              "Trying to remove a view index above child "
                  + "count "
                  + indexToRemove
                  + " view tag: "
                  + tag
                  + "\n detail: "
                  + constructManageChildrenErrorMessage(
                      viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete));
        }
        if (indexToRemove >= lastIndexToRemove) {
          throw new IllegalViewOperationException(
              "Trying to remove an out of order view index:"
                  + indexToRemove
                  + " view tag: "
                  + tag
                  + "\n detail: "
                  + constructManageChildrenErrorMessage(
                      viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete));
        }
        viewManager.removeViewAt(viewToManage, indicesToRemove[i]);
        lastIndexToRemove = indexToRemove;
      }
    }

    if (viewsToAdd != null) {
      for (int i = 0; i < viewsToAdd.length; i++) {
        ViewAtIndex viewAtIndex = viewsToAdd[i];
        View viewToAdd = mTagsToViews.get(viewAtIndex.mTag);
        if (viewToAdd == null) {
          throw new IllegalViewOperationException(
              "Trying to add unknown view tag: "
                  + viewAtIndex.mTag
                  + "\n detail: "
                  + constructManageChildrenErrorMessage(
                      viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete));
        }
        viewManager.addView(viewToManage, viewToAdd, viewAtIndex.mIndex);
      }
    }

    if (tagsToDelete != null) {
      for (int i = 0; i < tagsToDelete.length; i++) {
        int tagToDelete = tagsToDelete[i];
        View viewToDestroy = mTagsToViews.get(tagToDelete);
        if (viewToDestroy == null) {
          throw new IllegalViewOperationException(
              "Trying to destroy unknown view tag: "
                  + tagToDelete
                  + "\n detail: "
                  + constructManageChildrenErrorMessage(
                      viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete));
        }
        dropView(viewToDestroy);
      }
    }
  }
  private static String constructManageChildrenErrorMessage(
      ViewGroup viewToManage,
      ViewGroupManager viewManager,
      @Nullable int[] indicesToRemove,
      @Nullable ViewAtIndex[] viewsToAdd,
      @Nullable int[] tagsToDelete) {
    StringBuilder stringBuilder = new StringBuilder();

    stringBuilder.append("View tag:" + viewToManage.getId() + "\n");
    stringBuilder.append("  children(" + viewManager.getChildCount(viewToManage) + "): [\n");
    for (int index = 0; index < viewManager.getChildCount(viewToManage); index += 16) {
      for (int innerOffset = 0;
          ((index + innerOffset) < viewManager.getChildCount(viewToManage)) && innerOffset < 16;
          innerOffset++) {
        stringBuilder.append(
            viewManager.getChildAt(viewToManage, index + innerOffset).getId() + ",");
      }
      stringBuilder.append("\n");
    }
    stringBuilder.append(" ],\n");
    if (indicesToRemove != null) {
      stringBuilder.append("  indicesToRemove(" + indicesToRemove.length + "): [\n");
      for (int index = 0; index < indicesToRemove.length; index += 16) {
        for (int innerOffset = 0;
            ((index + innerOffset) < indicesToRemove.length) && innerOffset < 16;
            innerOffset++) {
          stringBuilder.append(indicesToRemove[index + innerOffset] + ",");
        }
        stringBuilder.append("\n");
      }
      stringBuilder.append(" ],\n");
    }
    if (viewsToAdd != null) {
      stringBuilder.append("  viewsToAdd(" + viewsToAdd.length + "): [\n");
      for (int index = 0; index < viewsToAdd.length; index += 16) {
        for (int innerOffset = 0;
            ((index + innerOffset) < viewsToAdd.length) && innerOffset < 16;
            innerOffset++) {
          stringBuilder.append(
              "["
                  + viewsToAdd[index + innerOffset].mIndex
                  + ","
                  + viewsToAdd[index + innerOffset].mTag
                  + "],");
        }
        stringBuilder.append("\n");
      }
      stringBuilder.append(" ],\n");
    }
    if (tagsToDelete != null) {
      stringBuilder.append("  tagsToDelete(" + tagsToDelete.length + "): [\n");
      for (int index = 0; index < tagsToDelete.length; index += 16) {
        for (int innerOffset = 0;
            ((index + innerOffset) < tagsToDelete.length) && innerOffset < 16;
            innerOffset++) {
          stringBuilder.append(tagsToDelete[index + innerOffset] + ",");
        }
        stringBuilder.append("\n");
      }
      stringBuilder.append(" ]\n");
    }

    return stringBuilder.toString();
  }