SAPUI5自定义控件聚合更新时计算viewBox的最优实现方案问询
你提到的在constructor中绑定聚合绑定的change事件的方案存在适用场景限制:它只能监听到通过绑定路径动态加载聚合内容的变更场景,如果你手动调用addElement、removeElement等API修改elements聚合,这个事件不会触发,因此不是最优方案。
推荐的最优实现方案
SAPUI5的ManagedObject(所有控件的基类)提供了覆盖全场景的聚合变更监听能力,你可以按照以下步骤实现:
- 首先在聚合定义中添加
invalidate: false配置,避免聚合变更时自动触发控件重绘,减少不必要的渲染开销 - 在
init生命周期方法中监听聚合的全局变更事件,无论手动修改还是绑定变更都会触发该事件 - 在事件回调中计算
_sViewBox,计算完成后手动触发控件重绘即可 - 如果你的
viewBox计算依赖elements聚合内部控件的属性,还需要在聚合变更时给新增的元素绑定属性变更监听,元素移除时解绑避免内存泄漏
修改后的代码示例
sap.ui.define([ "sap/ui/core/Control" ], function(Control) { "use strict"; return Control.extend("my.control.SvgVisualizer", { metadata: { properties: { width: { type: "sap.ui.core.CSSSize", defaultValue: "100%" }, height: { type: "sap.ui.core.CSSSize", defaultValue: "100%" } }, aggregations: { elements: { type: "my.control.Element", multiple: true, singularName: "element", invalidate: false // 聚合变更不自动触发重绘 } } }, init: function() { // 监听elements聚合的所有变更事件 this.attachAggregationChange("elements", this._onElementsChange.bind(this)); // 存储元素的属性变更监听函数,方便后续解绑 this._mElementListeners = new Map(); }, _onElementsChange: function(oEvent) { const oParams = oEvent.getParameters(); // 处理新增的元素 if (oParams.added) { oParams.added.forEach(oElement => { const fnListener = this._onElementPropChange.bind(this); oElement.attachEvent("propertyChanged", fnListener); this._mElementListeners.set(oElement, fnListener); }); } // 处理移除的元素,解绑监听避免内存泄漏 if (oParams.removed) { oParams.removed.forEach(oElement => { const fnListener = this._mElementListeners.get(oElement); if (fnListener) { oElement.detachEvent("propertyChanged", fnListener); this._mElementListeners.delete(oElement); } }); } // 重新计算viewBox this._calculateViewBox(); // 手动触发重绘 this.invalidate(); }, _onElementPropChange: function() { // 元素属性变更时重新计算viewBox this._calculateViewBox(); this.invalidate(); }, _calculateViewBox: function() { // 你的viewBox计算逻辑,结果赋值给this._sViewBox const aElements = this.getElements(); // 此处省略计算逻辑... this._sViewBox = ""; // 替换为实际计算结果 }, renderer: { apiVersion: 2, render: function(oRm, oControl) { oRm.openStart("svg", oControl.getId()) .attr("viewBox", oControl._sViewBox) .attr("width", oControl.getWidth()) .attr("height", oControl.getHeight()) .openEnd(); // 渲染elements聚合的内容 oControl.getElements().forEach(oElement => oRm.renderControl(oElement)); oRm.close("svg"); } }, exit: function() { // 控件销毁时清空所有监听避免内存泄漏 if (this._mElementListeners) { this._mElementListeners.forEach((fnListener, oElement) => { oElement.detachEvent("propertyChanged", fnListener); }); this._mElementListeners.clear(); delete this._mElementListeners; } } }); });
补充说明
如果你不需要监听聚合内元素的属性变更,只需要在聚合的元素增删时重新计算,可以省略元素属性监听的相关逻辑,代码会更简洁。这种实现方式完全满足你“仅在必要时计算viewBox、避免renderer中重复计算”的需求,性能开销更低,覆盖场景也更全。
内容的提问来源于stack exchange,提问作者Philipp Kolbe
相关产品推荐
相关产品推荐

