From dd2320d2ebbd958a855a2b268b8e7cb09d5a2924 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Fri, 8 Dec 2023 16:24:06 +0800 Subject: [PATCH 1/5] fix(ios): add protection to prevent crashes caused by negative sizes --- .../waterfalllist/NativeRenderWaterfallView.mm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm index a2c69447ccb..1d8a7f19f87 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm +++ b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm @@ -420,11 +420,17 @@ - (void)addCellViewToCollectionViewCell:(UICollectionViewCell *)cell atIndexPath } #pragma mark - NativeRenderCollectionViewDelegateWaterfallLayout + - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout - sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - HippyShadowView *renderObjectView = [_dataSource cellForIndexPath:indexPath]; - return renderObjectView.frame.size; + sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + HippyShadowView *shadowView = [_dataSource cellForIndexPath:indexPath]; + CGSize itemSize = shadowView.frame.size; + if (itemSize.width < .0 || itemSize.height < .0) { + HippyLogError(@"Negative item size for %@ at %@ of %@", shadowView, indexPath, self); + return CGSizeZero; + } + return shadowView.frame.size; } - (NSInteger)collectionView:(UICollectionView *)collectionView From 5b94924fb1887d9ca6bbe78281cb200c1db8bb89 Mon Sep 17 00:00:00 2001 From: ilikethese Date: Fri, 15 Dec 2023 10:59:52 +0800 Subject: [PATCH 2/5] fix(dom): convert layout result nan to 0 --- dom/src/dom/dom_node.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dom/src/dom/dom_node.cc b/dom/src/dom/dom_node.cc index 96c1a33d09f..b1cebb9be71 100644 --- a/dom/src/dom/dom_node.cc +++ b/dom/src/dom/dom_node.cc @@ -326,10 +326,10 @@ void DomNode::TransferLayoutOutputsRecursive(std::vectorGetLeft()) || not_equal(layout_.top, layout_node_->GetTop()) || not_equal(layout_.width, layout_node_->GetWidth()) || not_equal(layout_.height, layout_node_->GetHeight()); - layout_.left = layout_node_->GetLeft(); - layout_.top = layout_node_->GetTop(); - layout_.width = layout_node_->GetWidth(); - layout_.height = layout_node_->GetHeight(); + layout_.left = std::isnan(layout_node_->GetLeft()) ? 0 : layout_node_->GetLeft(); + layout_.top = std::isnan(layout_node_->GetTop()) ? 0 : layout_node_->GetTop(); + layout_.width = std::isnan(layout_node_->GetWidth()) ? 0 : layout_node_->GetWidth(); + layout_.height = std::isnan(layout_node_->GetHeight()) ? 0 : layout_node_->GetHeight(); layout_.marginLeft = layout_node_->GetMargin(Edge::EdgeLeft); layout_.marginTop = layout_node_->GetMargin(Edge::EdgeTop); layout_.marginRight = layout_node_->GetMargin(Edge::EdgeRight); From 637d5e9d9fbc470d12370a3e4020dc05d29798b1 Mon Sep 17 00:00:00 2001 From: ilikethese Date: Thu, 14 Dec 2023 17:47:57 +0800 Subject: [PATCH 3/5] fix(dom): fix dom diff algorithm --- dom/include/dom/root_node.h | 36 +++++++--- dom/src/dom/root_node.cc | 132 +++++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 64 deletions(-) diff --git a/dom/include/dom/root_node.h b/dom/include/dom/root_node.h index a711fc8e578..1b329f13fe9 100644 --- a/dom/include/dom/root_node.h +++ b/dom/include/dom/root_node.h @@ -22,13 +22,37 @@ #include +#include "dom/diff_utils.h" #include "dom/dom_node.h" -#include "footstone/task_runner.h" #include "footstone/persistent_object_map.h" +#include "footstone/task_runner.h" namespace hippy { inline namespace dom { +class RootNode; + +/** + * In HippyVue/HippyReact, updating node styles can be intricate. + * This class is specifically designed to compute the differences when updating DOM node styles. + */ +class DomNodeStyleDiffer { + public: + DomNodeStyleDiffer() = default; + ~DomNodeStyleDiffer() = default; + + bool Calculate(const std::shared_ptr& root_node, const std::shared_ptr& dom_info, + hippy::dom::DiffValue& style_diff, hippy::dom::DiffValue& ext_style_diff); + void Reset() { + node_ext_style_map_.clear(); + node_style_map_.clear(); + } + + private: + std::unordered_map>> node_style_map_; + std::unordered_map>> node_ext_style_map_; +}; + class RootNode : public DomNode { public: using TaskRunner = footstone::runner::TaskRunner; @@ -71,7 +95,6 @@ class RootNode : public DomNode { void Traverse(const std::function&)>& on_traverse); void AddInterceptor(const std::shared_ptr& interceptor); - static footstone::utils::PersistentObjectMap>& PersistentMap() { return persistent_map_; } @@ -80,16 +103,12 @@ class RootNode : public DomNode { static void MarkLayoutNodeDirty(const std::vector>& nodes); struct DomOperation { - enum class Op { - kOpCreate, kOpUpdate, kOpDelete, kOpMove - } op; + enum class Op { kOpCreate, kOpUpdate, kOpDelete, kOpMove } op; std::vector> nodes; }; struct EventOperation { - enum class Op { - kOpAdd, kOpRemove - } op; + enum class Op { kOpAdd, kOpRemove } op; uint32_t id; std::string name; }; @@ -107,6 +126,7 @@ class RootNode : public DomNode { std::weak_ptr dom_manager_; std::vector> interceptors_; std::shared_ptr animation_manager_; + std::unique_ptr style_differ_; static footstone::utils::PersistentObjectMap> persistent_map_; }; diff --git a/dom/src/dom/root_node.cc b/dom/src/dom/root_node.cc index 06459ad6f4e..0225647656c 100644 --- a/dom/src/dom/root_node.cc +++ b/dom/src/dom/root_node.cc @@ -23,7 +23,6 @@ #include #include "dom/animation/animation_manager.h" -#include "dom/diff_utils.h" #include "dom/render_manager.h" #include "footstone/deserializer.h" #include "footstone/hippy_value.h" @@ -45,10 +44,67 @@ using Task = footstone::Task; footstone::utils::PersistentObjectMap> RootNode::persistent_map_; +// In Hippy Vue, there are some special cases where there are multiple update instructions for the same node. This can +// cause issues with the diff algorithm and lead to incorrect results. +// Example: +// +// Dom Node: +// |------|--------------------------------------| +// | id | style: {text: "a", color: "red"} | +// | 1 | diff: {} | +// |------|--------------------------------------| +// +// Previous update algorithm: +// |------|-----------------------| update instructions: |------|-----------------------| update instructions: |------|-------------------------------------| +// | id | style: {text: "a"} | { text: "b"} | id | style: {text: "b"} | { text: "b", fontsize: 12} | id | style: {text: "b", fontsize: 12} | +// | 1 | diff: {} | -------------------> | 1 | diff: {text: "b"} | --------------------------> | 1 | diff: {fontsize: "b"} | +// |------|-----------------------| |------|-----------------------| |------|-------------------------------------| +// In the previous diff algorithm, the differences were generated by comparing the DOM styles and update instructions. +// However, in Hippy Vue, two update instructions might be generated within the same batch. This can lead to incorrect diff results. +// The diff should be {text: "b", fontsize: 12}, but the previous diff algorithm cacluate {fontsize: "b"} +// +// To address this issue, the new update algorithm is as follows: +// 1. When a node's style needs to be updated for the first time, we save the current style. +// 2. Subsequent update differences are generated by comparing the saved styles with the update instructions. +// 3. At the end of the batch, we clear the saved styles. +bool DomNodeStyleDiffer::Calculate(const std::shared_ptr& root_node, + const std::shared_ptr& dom_info, hippy::dom::DiffValue& style_diff, + hippy::dom::DiffValue& ext_style_diff) { + if (!root_node) return false; + if (dom_info == nullptr || dom_info->dom_node == nullptr) return false; + + auto dom_node = root_node->GetNode(dom_info->dom_node->GetId()); + if (dom_node == nullptr) return false; + uint32_t dom_id = dom_node->GetId(); + + // 保存 batch 最早的 style 和 ext_style, 该批次中的所有的 diff 都由这个 style 比较产生 + if (node_style_map_.find(dom_id) == node_style_map_.end()) { + std::unordered_map> style; + std::unordered_map> ext_style; + auto dom_style = dom_node->GetStyleMap(); + for (const auto& pair : *dom_style) { + style[pair.first] = std::make_shared(*pair.second); + } + node_style_map_.insert({dom_id, style}); + auto dom_ext_style = dom_node->GetExtStyle(); + for (const auto& pair : *dom_ext_style) { + ext_style[pair.first] = std::make_shared(*pair.second); + } + node_ext_style_map_.insert({dom_id, ext_style}); + } + + auto base_style = node_style_map_.at(dom_id); + auto base_ext_style = node_ext_style_map_.at(dom_id); + style_diff = DiffUtils::DiffProps(base_style, *dom_info->dom_node->GetStyleMap(), false); + ext_style_diff = DiffUtils::DiffProps(base_ext_style, *dom_info->dom_node->GetExtStyle(), false); + return true; +} + RootNode::RootNode(uint32_t id) : DomNode(id, 0, 0, "", "", nullptr, nullptr, {}) { SetRenderInfo({id, 0, 0}); animation_manager_ = std::make_shared(); interceptors_.push_back(animation_manager_); + style_differ_ = std::make_unique(); } RootNode::RootNode() : RootNode(0) {} @@ -101,55 +157,21 @@ void RootNode::UpdateDomNodes(std::vector>&& nodes) { interceptor->OnDomNodeUpdate(nodes); } - // In Hippy Vue, there are some special cases where there are multiple update instructions for the same node. This can - // cause issues with the diff algorithm and lead to incorrect results. - // Example: - // - // Dom Node Style: - // |------|----------------| - // | id | style | - // | 1 | text : {} | - // | 2 | some style | - // - // Update instructions: - // |------|-------------------------------|----------------|------------------------| - // | id | style | operation | diff style result | - // | 1 | text : { "color": "blue" } | compare | { "color": "blue" } | - // | 2 | some style | | | - // | 1 | text : { "color": "red" } | compare | { "color": "red" } | - // | 1 | text : { "color": "red" } | compare | { } | - // In last diff algroithm the diff_style = {} - // - // To Solve this case we should use the last update instruction to generate the diff style. - // Update instructions: - // |------|-------------------------------|----------------|------------------------| - // | id | style | operation | diff style result | - // | 1 | text : { "color": "blue" } | skip | { } | - // | 2 | some style | | | - // | 1 | text : { "color": "red" } | skip | { } | - // | 1 | text : { "color": "red" } | compare | { "color": "red" } | - // In new diff algroithm the diff_style = { "color": "red" } - std::unordered_map> skipped_instructions; - for (const auto& node_info : nodes) { - auto id = node_info->dom_node->GetId(); - skipped_instructions[id] = node_info; - } - std::vector> nodes_to_update; - for (const auto& [id, node_info] : skipped_instructions) { - std::shared_ptr dom_node = GetNode(node_info->dom_node->GetId()); + for (const auto& node : nodes) { + std::shared_ptr dom_node = GetNode(node->dom_node->GetId()); if (dom_node == nullptr) { continue; } - auto skip_style_diff = false; - if (node_info->diff_info != nullptr) { - skip_style_diff = node_info->diff_info->skip_style_diff; + + hippy::dom::DiffValue style_diff, ext_style_diff; + if (!style_differ_->Calculate(std::static_pointer_cast(shared_from_this()), node, style_diff, + ext_style_diff)) { + continue; } - // diff props - auto style_diff_value = DiffUtils::DiffProps(*dom_node->GetStyleMap(), *node_info->dom_node->GetStyleMap(), skip_style_diff); - auto ext_diff_value = DiffUtils::DiffProps(*dom_node->GetExtStyle(), *node_info->dom_node->GetExtStyle(), false); - auto style_update = std::get<0>(style_diff_value); - auto ext_update = std::get<0>(ext_diff_value); + + auto style_update = std::get<0>(style_diff); + auto ext_update = std::get<0>(ext_style_diff); std::shared_ptr diff_value = std::make_shared(); if (!style_update->empty()) { diff_value->insert(style_update->begin(), style_update->end()); @@ -157,14 +179,12 @@ void RootNode::UpdateDomNodes(std::vector>&& nodes) { if (!ext_update->empty()) { diff_value->insert(ext_update->begin(), ext_update->end()); } - if (!skip_style_diff) { - dom_node->SetStyleMap(node_info->dom_node->GetStyleMap()); - } - dom_node->SetExtStyleMap(node_info->dom_node->GetExtStyle()); + dom_node->SetStyleMap(node->dom_node->GetStyleMap()); + dom_node->SetExtStyleMap(node->dom_node->GetExtStyle()); dom_node->SetDiffStyle(diff_value); - auto style_delete = std::get<1>(style_diff_value); - auto ext_delete = std::get<1>(ext_diff_value); + auto style_delete = std::get<1>(style_diff); + auto ext_delete = std::get<1>(ext_style_diff); std::shared_ptr> delete_value = std::make_shared>(); if (!style_delete->empty()) { delete_value->insert(delete_value->end(), style_delete->begin(), style_delete->end()); @@ -174,8 +194,6 @@ void RootNode::UpdateDomNodes(std::vector>&& nodes) { delete_value->insert(delete_value->end(), ext_delete->begin(), ext_delete->end()); } dom_node->SetDeleteProps(delete_value); - node_info->dom_node->SetDiffStyle(diff_value); - node_info->dom_node->SetDeleteProps(delete_value); if (!style_update->empty() || !style_delete->empty()) { dom_node->UpdateLayoutStyleInfo(*style_update, *style_delete); } @@ -278,9 +296,11 @@ void RootNode::CallFunction(uint32_t id, const std::string& name, const DomArgum } void RootNode::SyncWithRenderManager(const std::shared_ptr& render_manager) { - TDF_PERF_DO_STMT_AND_LOG(unsigned long domCnt = dom_operations_.size(); , "RootNode::SyncWithRenderManager"); + TDF_PERF_DO_STMT_AND_LOG(unsigned long domCnt = dom_operations_.size();, "RootNode::SyncWithRenderManager"); + if (style_differ_ != nullptr) style_differ_->Reset(); FlushDomOperations(render_manager); - TDF_PERF_DO_STMT_AND_LOG(unsigned long evCnt = event_operations_.size(); , "RootNode::FlushDomOperations Done, dom op count:%lld", domCnt); + TDF_PERF_DO_STMT_AND_LOG(unsigned long evCnt = event_operations_.size(); + , "RootNode::FlushDomOperations Done, dom op count:%lld", domCnt); FlushEventOperations(render_manager); TDF_PERF_LOG("RootNode::FlushEventOperations Done, event op count:%d", evCnt); DoAndFlushLayout(render_manager); @@ -455,7 +475,7 @@ void RootNode::FlushDomOperations(const std::shared_ptr& render_m } void RootNode::MarkLayoutNodeDirty(const std::vector>& nodes) { - for (const auto& node: nodes) { + for (const auto& node : nodes) { if (node && node->GetLayoutNode() && !node->GetLayoutNode()->HasParentEngineNode()) { auto parent = node->GetParent(); while (parent) { From 9a41ae9aed1eeccd31c5fe5ffa986b54f38d7b58 Mon Sep 17 00:00:00 2001 From: ilikethese Date: Fri, 15 Dec 2023 16:25:33 +0800 Subject: [PATCH 4/5] fix(dom): layout width and height result great than 0 --- dom/src/dom/dom_node.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dom/src/dom/dom_node.cc b/dom/src/dom/dom_node.cc index b1cebb9be71..861c617d3f2 100644 --- a/dom/src/dom/dom_node.cc +++ b/dom/src/dom/dom_node.cc @@ -328,8 +328,8 @@ void DomNode::TransferLayoutOutputsRecursive(std::vectorGetLeft()) ? 0 : layout_node_->GetLeft(); layout_.top = std::isnan(layout_node_->GetTop()) ? 0 : layout_node_->GetTop(); - layout_.width = std::isnan(layout_node_->GetWidth()) ? 0 : layout_node_->GetWidth(); - layout_.height = std::isnan(layout_node_->GetHeight()) ? 0 : layout_node_->GetHeight(); + layout_.width = std::isnan(layout_node_->GetWidth()) ? 0 : std::max(layout_node_->GetWidth(), .0); + layout_.height = std::isnan(layout_node_->GetHeight()) ? 0 : std::max(layout_node_->GetHeight(), .0); layout_.marginLeft = layout_node_->GetMargin(Edge::EdgeLeft); layout_.marginTop = layout_node_->GetMargin(Edge::EdgeTop); layout_.marginRight = layout_node_->GetMargin(Edge::EdgeRight); From 795aff416738bd75784544aed76b2022322996b9 Mon Sep 17 00:00:00 2001 From: iPel Date: Thu, 14 Dec 2023 18:03:26 +0800 Subject: [PATCH 5/5] fix(android): fix nested list issues - nested list not show - updating layout will scroll to wrong position - nested list item can not be deleted, then cause Exception --- .../widget/HippyLinearLayoutManager.java | 5 +-- .../mtt/hippy/uimanager/RenderManager.java | 3 +- .../hippylist/HippyRecyclerListAdapter.java | 13 ++----- .../HippyRecyclerViewController.java | 12 ++++++ .../renderer/node/ListItemRenderNode.java | 1 + .../com/tencent/renderer/node/RenderNode.java | 38 ++++++++++++++++--- 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyLinearLayoutManager.java b/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyLinearLayoutManager.java index 085bf5dd61a..d1c58c554af 100644 --- a/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyLinearLayoutManager.java +++ b/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyLinearLayoutManager.java @@ -127,11 +127,10 @@ int getSizeUntilPosition(int position) { if (size == null) { size = getItemSizeFromAdapter(i); } - if (size != null && size != INVALID_SIZE) { - totalSize += size; - } else { + if (size == INVALID_SIZE) { return INVALID_SIZE; } + totalSize += size; } return totalSize; } diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/RenderManager.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/RenderManager.java index c82d88444c6..85676097fd3 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/RenderManager.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/RenderManager.java @@ -153,8 +153,7 @@ public void createNode(int rootId, int id, int pid, int index, "appendVirtualChild: rootNode=" + rootNode + " parentNode=" + parentNode); return; } - RenderNode node = mControllerManager.createRenderNode(rootId, id, props, className, - isLazy || parentNode.checkNodeFlag(FLAG_LAZY_LOAD)); + RenderNode node = mControllerManager.createRenderNode(rootId, id, props, className, isLazy); if (node == null) { LogUtils.w(TAG, "createNode: node == null"); return; diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerListAdapter.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerListAdapter.java index 8cde8d1ccc8..973fc728349 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerListAdapter.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerListAdapter.java @@ -306,11 +306,7 @@ public int getRenderNodeTotalHeight() { } public int getItemHeight(int position) { - Integer itemHeight = getRenderNodeHeight(position); - if (itemHeight != null) { - return itemHeight; - } - return 0; + return getRenderNodeHeight(position); } public int getRenderNodeHeight(int position) { @@ -336,11 +332,7 @@ public int getRenderNodeHeight(int position) { } public int getItemWidth(int position) { - Integer renderNodeWidth = getRenderNodeWidth(position); - if (renderNodeWidth != null) { - return renderNodeWidth; - } - return 0; + return getRenderNodeWidth(position); } public int getRenderNodeWidth(int position) { @@ -406,6 +398,7 @@ public void getItemLayoutParams(int position, LayoutParams lp) { return; } lp.height = getItemHeight(position); + lp.width = getItemWidth(position); } /*package*/ boolean hasHeader() { diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java index 5005a3dfecb..d922c379be4 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java @@ -35,6 +35,7 @@ import com.tencent.mtt.hippy.common.HippyMap; import com.tencent.mtt.hippy.dom.node.NodeProps; import com.tencent.mtt.hippy.uimanager.ControllerManager; +import com.tencent.mtt.hippy.uimanager.ControllerRegistry; import com.tencent.mtt.hippy.uimanager.HippyViewController; import com.tencent.renderer.NativeRenderContext; import com.tencent.renderer.node.ListViewRenderNode; @@ -144,6 +145,17 @@ public RenderNode createRenderNode(int rootId, int id, @Nullable Map) view).getRecyclerView().dispatchLayout(); + } + } + @HippyControllerProps(name = "horizontal", defaultType = HippyControllerProps.BOOLEAN) public void setHorizontalEnable(final HRW viewWrapper, boolean flag) { LayoutManager layoutManager = viewWrapper.getRecyclerView().getLayoutManager(); diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/node/ListItemRenderNode.java b/renderer/native/android/src/main/java/com/tencent/renderer/node/ListItemRenderNode.java index 690943d35d8..a2d6dbf94a9 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/node/ListItemRenderNode.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/node/ListItemRenderNode.java @@ -171,6 +171,7 @@ public void updateLayout(int x, int y, int w, int h) { mLeft = x; mTop = y; View view = mControllerManager.findView(mRootId, mId); + mX = view != null ? view.getLeft() : 0; mY = view != null ? view.getTop() : 0; if (getParent() != null) { RenderManager renderManager = mControllerManager.getRenderManager(); diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/node/RenderNode.java b/renderer/native/android/src/main/java/com/tencent/renderer/node/RenderNode.java index 34ccdeab4a0..fb136787b72 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/node/RenderNode.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/node/RenderNode.java @@ -91,6 +91,10 @@ public class RenderNode { * Mark should update children drawing order. */ public static final int FLAG_UPDATE_DRAWING_ORDER = 0x00000100; + /** + * Mark node has lazy parent node, which means there is an ancestor node has flag {@link #FLAG_LAZY_LOAD}. + */ + public static final int FLAG_PARENT_LAZY_LOAD = 0x00000200; private int mNodeFlags = 0; private PoolType mPoolInUse = PoolType.NONE; protected int mX; @@ -280,6 +284,7 @@ public void addChild(@NonNull RenderNode node, int index) { index = (index < 0) ? 0 : Math.min(index, mChildren.size()); mChildren.add(index, node); node.mParent = this; + node.onParentLazyChanged(isLazyLoad()); // If has set z index in the child nodes, the rendering order needs to be rearranged // after adding nodes if (mDrawingOrder != null) { @@ -288,15 +293,38 @@ public void addChild(@NonNull RenderNode node, int index) { } public void setLazy(boolean isLazy) { + if (isLazy == checkNodeFlag(FLAG_LAZY_LOAD)) { + return; + } + boolean oldLazy = isLazyLoad(); if (isLazy) { setNodeFlag(FLAG_LAZY_LOAD); } else { resetNodeFlag(FLAG_LAZY_LOAD); } - for (int i = 0; i < getChildCount(); i++) { - RenderNode child = getChildAt(i); - if (child != null) { - child.setLazy(isLazy); + notifyLazyChanged(oldLazy, isLazyLoad()); + } + + public void onParentLazyChanged(boolean isLazy) { + if (isLazy == checkNodeFlag(FLAG_PARENT_LAZY_LOAD)) { + return; + } + boolean oldLazy = isLazyLoad(); + if (isLazy) { + setNodeFlag(FLAG_PARENT_LAZY_LOAD); + } else { + resetNodeFlag(FLAG_PARENT_LAZY_LOAD); + } + notifyLazyChanged(oldLazy, isLazyLoad()); + } + + private void notifyLazyChanged(boolean oldLazy, boolean newLazy) { + if (oldLazy != newLazy) { + for (int i = 0; i < getChildCount(); i++) { + RenderNode child = getChildAt(i); + if (child != null) { + child.onParentLazyChanged(newLazy); + } } } } @@ -659,7 +687,7 @@ public boolean isDeleted() { } public boolean isLazyLoad() { - return checkNodeFlag(FLAG_LAZY_LOAD); + return checkNodeFlag(FLAG_LAZY_LOAD) || checkNodeFlag(FLAG_PARENT_LAZY_LOAD); } public boolean checkRegisteredEvent(@NonNull String eventName) {