/** * Update the attributes according to the given attribute. Then creating a command object for * updating corresponding view and put the command object in the queue. * * @param ref Reference of the dom. * @param attrs the new style. This style is only a part of the full attribute set, and will be * merged into attributes * @see #updateStyle(String, JSONObject) */ void updateAttrs(String ref, final JSONObject attrs) { if (mDestroy) { return; } WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); final WXDomObject domObject = mRegistry.get(ref); if (domObject == null) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_UPDATEATTRS); } return; } domObject.updateAttr(attrs); mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { mWXRenderManager.updateAttrs(mInstanceId, domObject.getRef(), attrs); } @Override public String toString() { return "updateAttr"; } }); mDirty = true; if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_SUCCESS); } }
/** * Clear the mapping relationship between Reference and {@link WXDomObject}. The mapping info is * stored in {@link #mRegistry}. * * @param domObject */ private void clearRegistryForDom(WXDomObject domObject) { int count = domObject.childCount(); mRegistry.remove(domObject.getRef()); for (int i = count - 1; i >= 0; --i) { clearRegistryForDom(domObject.getChild(i)); } }
/** * Update styles according to the given style. Then creating a command object for updating * corresponding view and put the command object in the queue. * * @param ref Reference of the dom. * @param style the new style. This style is only a part of the full style, and will be merged * into styles * @see #updateAttrs(String, JSONObject) */ void updateStyle(String ref, JSONObject style) { if (mDestroy || style == null) { return; } WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); WXDomObject domObject = mRegistry.get(ref); if (domObject == null) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_UPDATESTYLE); } return; } Map<String, Object> animationMap = WXDataStructureUtil.newHashMapWithExpectedSize(2); animationMap.put(WXDomObject.TRANSFORM, style.remove(WXDomObject.TRANSFORM)); animationMap.put(WXDomObject.TRANSFORM_ORIGIN, style.remove(WXDomObject.TRANSFORM_ORIGIN)); animations.add(new Pair<>(ref, animationMap)); if (!style.isEmpty()) { domObject.updateStyle(style); transformStyle(domObject, false); updateStyle(domObject, style); } mDirty = true; if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_SUCCESS); } }
/** * Create a command object for removing the event listener of the corresponding {@link * WXDomObject} and put the command event in the queue. * * @param ref Reference of the dom. * @param type the type of the event, this may be a plain event defined in {@link * com.taobao.weex.common.Constants.Event} or a gesture defined in {@link com.taobao * .weex.ui.view.gesture.WXGestureType} */ void removeEvent(final String ref, final String type) { if (mDestroy) { return; } WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); WXDomObject domObject = mRegistry.get(ref); if (domObject == null) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_REMOVEEVENT); } return; } domObject.removeEvent(type); mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { mWXRenderManager.removeEvent(mInstanceId, ref, type); } @Override public String toString() { return "removeEvent"; } }); mDirty = true; if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_SUCCESS); } }
private void layoutAfter(WXDomObject dom) { if (dom == null || !dom.hasUpdate() || mDestroy) { return; } dom.layoutAfter(); int count = dom.childCount(); for (int i = 0; i < count; ++i) { layoutAfter(dom.getChild(i)); } }
/** * Batch the execution of command objects and execute all the command objects created other * places, e.g. call {@link IWXRenderTask#execute()}. First, it will rebuild the dom tree and do * pre layout staff. Then call {@link * com.taobao.weex.dom.flex.CSSNode#calculateLayout(CSSLayoutContext)} to start calculate layout. * Next, call {@link #applyUpdate(WXDomObject)} to get changed dom and creating corresponding * command object. Finally, walk through the queue, e.g. call {@link IWXRenderTask#execute()} for * every task in the queue. */ void batch() { long start0 = System.currentTimeMillis(); if (!mDirty || mDestroy) { return; } WXDomObject rootDom = mRegistry.get(WXDomObject.ROOT); if (rootDom == null) { return; } rebuildingDomTree(rootDom); layoutBefore(rootDom); long start = System.currentTimeMillis(); rootDom.calculateLayout(mLayoutContext); WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); if (instance != null) { instance.cssLayoutTime(System.currentTimeMillis() - start); } layoutAfter(rootDom); start = System.currentTimeMillis(); applyUpdate(rootDom); if (instance != null) { instance.applyUpdateTime(System.currentTimeMillis() - start); } start = System.currentTimeMillis(); updateDomObj(); if (instance != null) { instance.updateDomObjTime(System.currentTimeMillis() - start); } parseAnimation(); int count = mNormalTasks.size(); for (int i = 0; i < count && !mDestroy; ++i) { mWXRenderManager.runOnThread(mInstanceId, mNormalTasks.get(i)); } mNormalTasks.clear(); mAddDom.clear(); animations.clear(); mDirty = false; if (instance != null) { instance.batchTime(System.currentTimeMillis() - start0); } }
/** * Rebuild the component tree. The purpose of this method is moving fixed components to the root * component. This method will be called when {@link #batch()} is executed. * * @param root root dom */ void rebuildingDomTree(WXDomObject root) { if (root != null && root.getFixedStyleRefs() != null) { int size = root.getFixedStyleRefs().size(); for (int i = 0; i < size; i++) { String fixedRef = root.getFixedStyleRefs().get(i); WXDomObject wxDomObject = mRegistry.get(fixedRef); if (wxDomObject != null && wxDomObject.parent != null) { wxDomObject.parent.remove(wxDomObject); root.add(wxDomObject, -1); } } } }
/** * Creating the mapping between Reference to {@link WXDomObject} and store the mapping in {@link * #mRegistry}. Then, parse and copy style from DOM to {@link com.taobao.weex.dom.flex.CSSNode}. * Finally, DOM's children are also added to {@link com.taobao.weex.dom.flex.CSSNode#mChildren} if * added is true. The above procedure will be done recursively. * * @param dom the original DOM Object * @param isAdd true for adding children of {@link WXDomObject} {@link * com.taobao.weex.dom.flex.CSSNode#mChildren} and parsing style, false for only parsing * style. */ private void transformStyle(WXDomObject dom, boolean isAdd) { if (dom == null) { return; } if (isAdd) { dom.young(); mRegistry.put(dom.getRef(), dom); } WXStyle style = dom.getStyles(); /** merge default styles * */ Map<String, String> defaults = dom.getDefaultStyle(); if (defaults != null) { Iterator<Map.Entry<String, String>> it = defaults.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> entry = it.next(); if (!style.containsKey(entry.getKey())) { style.put(entry.getKey(), entry.getValue()); } } } if (dom.getStyles().size() > 0) { dom.applyStyleToNode(); } int count = dom.childCount(); WXDomObject child; for (int i = 0; i < count; ++i) { child = dom.getChild(i); transformStyle(child, isAdd); } }
private WXAnimationBean createAnimationBean(String ref, String animation) { try { WXAnimationBean animationBean = JSONObject.parseObject(animation, WXAnimationBean.class); if (animationBean != null && animationBean.styles != null) { WXDomObject domObject = mRegistry.get(ref); int width = (int) domObject.getLayoutWidth(); int height = (int) domObject.getLayoutHeight(); animationBean.styles.init( animationBean.styles.transformOrigin, animationBean.styles.transform, width, height); } return animationBean; } catch (RuntimeException e) { WXLogUtils.e("", e); return null; } }
/** * Update the specified component's dom and mark it as old. * * @param component the component to be updated */ private void updateDomObj(WXComponent component) { if (component == null) { return; } WXDomObject domObject = mRegistry.get(component.getRef()); if (domObject == null) { return; } domObject.old(); component.updateDom(domObject.clone()); if (component instanceof WXVContainer) { WXVContainer container = (WXVContainer) component; int count = container.childCount(); for (int i = 0; i < count; ++i) { updateDomObj(container.getChild(i)); } } }
/** * Create a command object for removing the specified {@link WXDomObject}. If the domObject is * null or its parent is null, this method returns directly. Otherwise, put the command object in * the queue. * * @param ref Reference of the dom. */ void removeDom(final String ref) { if (mDestroy) { return; } WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); WXDomObject domObject = mRegistry.get(ref); if (domObject == null) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_REMOVEELEMENT); } return; } WXDomObject parent = domObject.parent; if (parent == null) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_REMOVEELEMENT); } return; } clearRegistryForDom(domObject); parent.remove(domObject); mRegistry.remove(ref); mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { mWXRenderManager.removeComponent(mInstanceId, ref); } @Override public String toString() { return "removeDom"; } }); mDirty = true; if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_SUCCESS); } }
/** * Create a command object for moving the specific {@link WXDomObject} to a new parent. If any of * the following situation is met, * * <ul> * <li>dom to be moved is null * <li>dom's parent is null * <li>new parent is null * <li>parent is under {@link CSSNode#hasNewLayout()} * </ul> * * this method will return. Otherwise, put the command object in the queue. * * @param ref Reference of the dom to be moved. * @param parentRef Reference of the new parent DOM node * @param index the index of the dom to be inserted in the new parent. */ void moveDom(final String ref, final String parentRef, final int index) { if (mDestroy) { return; } WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); WXDomObject domObject = mRegistry.get(ref); WXDomObject parentObject = mRegistry.get(parentRef); if (domObject == null || domObject.parent == null || parentObject == null || parentObject.hasNewLayout()) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_MOVEELEMENT); } return; } if (domObject.parent.equals(parentObject)) { return; } domObject.parent.remove(domObject); parentObject.add(domObject, index); mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { mWXRenderManager.moveComponent(mInstanceId, ref, parentRef, index); } @Override public String toString() { return "moveDom"; } }); mDirty = true; if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_SUCCESS); } }
private WXAnimationBean createAnimationBean(String ref, Map<String, Object> style) { if (style != null) { try { Object transform = style.get(WXDomObject.TRANSFORM); if (transform instanceof String && !TextUtils.isEmpty((String) transform)) { String transformOrigin = (String) style.get(WXDomObject.TRANSFORM_ORIGIN); WXAnimationBean animationBean = new WXAnimationBean(); WXDomObject domObject = mRegistry.get(ref); int width = (int) domObject.getLayoutWidth(); int height = (int) domObject.getLayoutHeight(); animationBean.styles = new WXAnimationBean.Style(); animationBean.styles.init(transformOrigin, (String) transform, width, height); return animationBean; } } catch (RuntimeException e) { WXLogUtils.e("", e); return null; } } return null; }
/** * Parse the jsonObject to {@link WXDomObject} recursively * * @param map the original JSONObject * @return Dom Object corresponding to the JSONObject. */ private @Nullable WXDomObject parseInner(JSONObject map) { if (map == null || map.size() <= 0) { return null; } String type = (String) map.get(TYPE); WXDomObject domObject = WXDomObjectFactory.newInstance(type); if (domObject == null) { return null; } domObject.parseFromJson(map); Object children = map.get(CHILDREN); if (children != null && children instanceof JSONArray) { JSONArray childrenArray = (JSONArray) children; int count = childrenArray.size(); for (int i = 0; i < count; ++i) { domObject.add(parseInner(childrenArray.getJSONObject(i)), -1); } } return domObject; }
/** * Walk through the dom tree and create command object of re-calculating {@link * android.view.ViewGroup.LayoutParams} for dom that is old. * * @param dom the root dom of the walk through. */ private void applyUpdate(WXDomObject dom) { if (dom == null) { return; } if (dom.hasUpdate()) { dom.markUpdateSeen(); if (!dom.isYoung()) { final WXDomObject copy = dom.clone(); if (copy == null) { return; } mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { mWXRenderManager.setLayout(mInstanceId, copy.getRef(), copy); } @Override public String toString() { return "setLayout"; } }); if (dom.getExtra() != null) { mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { mWXRenderManager.setExtra(mInstanceId, copy.getRef(), copy.getExtra()); } @Override public String toString() { return "setExtra"; } }); } } } int count = dom.childCount(); for (int i = 0; i < count; ++i) { applyUpdate(dom.getChild(i)); } }
/** * Find fixed node and tell root dom * * @param obj */ void findFixed(WXDomObject obj) { WXDomObject rootDom = mRegistry.get(WXDomObject.ROOT); if (rootDom == null) { return; } if (obj.isFixed()) { rootDom.add2FixedDomList(obj.getRef()); } int childrenCount = obj.childCount(); if (childrenCount > 0) { for (int i = 0; i < childrenCount; i++) { findFixed(obj.getChild(i)); } } }
/** * Create command object for creating body according to the JSONObject. And put the command object * in the queue. * * @param element the jsonObject according to which to create command object. */ void createBody(JSONObject element) { if (mDestroy) { return; } WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); if (element == null) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_CREATEBODY); } return; } WXDomObject domObject = parseInner(element); if (domObject == null) { return; } Map<String, Object> style = new HashMap<>(5); if (!domObject.getStyles().containsKey(Constants.Name.FLEX_DIRECTION)) { style.put(Constants.Name.FLEX_DIRECTION, "column"); } if (!domObject.getStyles().containsKey(Constants.Name.BACKGROUND_COLOR)) { style.put(Constants.Name.BACKGROUND_COLOR, "#ffffff"); } // If there is height or width in JS, then that value will override value here. if (!domObject.getStyles().containsKey(Constants.Name.WIDTH)) { style.put( Constants.Name.WIDTH, WXViewUtils.getWebPxByWidth(WXViewUtils.getWeexWidth(mInstanceId))); domObject.setModifyWidth(true); } if (!domObject.getStyles().containsKey(Constants.Name.HEIGHT)) { style.put( Constants.Name.HEIGHT, WXViewUtils.getWebPxByWidth(WXViewUtils.getWeexHeight(mInstanceId))); domObject.setModifyHeight(true); } WXDomObject.prepareRoot(domObject); domObject.updateStyle(style); transformStyle(domObject, true); try { final WXComponent component = mWXRenderManager.createBodyOnDomThread(mInstanceId, domObject); AddDomInfo addDomInfo = new AddDomInfo(); addDomInfo.component = component; mAddDom.put(domObject.getRef(), addDomInfo); mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); if (instance == null || instance.getContext() == null) { WXLogUtils.e("instance is null or instance is destroy!"); return; } try { mWXRenderManager.createBody(mInstanceId, component); } catch (Exception e) { WXLogUtils.e("create body failed.", e); } } @Override public String toString() { return "createBody"; } }); animations.add( new Pair<String, Map<String, Object>>(domObject.getRef(), domObject.getStyles())); mDirty = true; if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_SUCCESS); } } catch (Exception e) { WXLogUtils.e("create body in dom thread failed." + e.getMessage()); } }
/** * Create a command object for adding a dom node to its parent in a specific location. If dom's * parent doesn't exist or the dom has been added in current {@link WXSDKInstance}, this method * will return. If the above request is met, then put the command object in the queue. * * @param dom the dom object in the form of JSONObject * @param parentRef parent to which the dom is added. * @param index the location of which the dom is added. */ void addDom(JSONObject dom, final String parentRef, final int index) { if (mDestroy) { return; } WXDomObject parent = mRegistry.get(parentRef); WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); if (parent == null) { if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_ADDELEMENT); } return; } WXDomObject domObject = parseInner(dom); if (domObject == null || mRegistry.containsKey(domObject.getRef())) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.e("[WXDomStatement] addDom error!!"); } if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_ERR_DOM_ADDELEMENT); } return; } findFixed(domObject); parent.add(domObject, index); transformStyle(domObject, true); // Create component in dom thread final WXComponent component = mWXRenderManager.createComponentOnDomThread(mInstanceId, domObject, parentRef, index); if (component == null) { // stop redner, some fatal happened. return; } AddDomInfo addDomInfo = new AddDomInfo(); addDomInfo.component = component; mAddDom.put(domObject.getRef(), addDomInfo); mNormalTasks.add( new IWXRenderTask() { @Override public void execute() { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); if (instance == null || instance.getContext() == null) { WXLogUtils.e("instance is null or instance is destroy!"); return; } try { mWXRenderManager.addComponent(mInstanceId, component, parentRef, index); } catch (Exception e) { WXLogUtils.e("add component failed.", e); } } @Override public String toString() { return "AddDom"; } }); animations.add( new Pair<String, Map<String, Object>>(domObject.getRef(), domObject.getStyles())); mDirty = true; if (instance != null) { instance.commitUTStab(IWXUserTrackAdapter.DOM_MODULE, WXErrorCode.WX_SUCCESS); } }