@Override public long getItemId(int position) { long id; try { id = Long.parseLong(getChild(position).getDomObject().getRef()); } catch (RuntimeException e) { WXLogUtils.e(TAG, WXLogUtils.getStackTrace(e)); id = RecyclerView.NO_ID; } return id; }
/** * These transform functions are supported: - `scale(x,y)`: scale item, x and y should be a * positive float number. - `translate(x,y)`: translate item, `x` and `y` shoule be integer * numbers. - `opacity(n)`: change the transparency of item, `n` must in `[0,1.0]`. - `rotate(n)`: * rotate item, n is integer number. * * @param raw * @return */ private RecyclerView.ItemDecoration parseTransforms(String raw) { if (raw == null) { return null; } float scaleX = 0f, scaleY = 0f; int translateX = 0, translateY = 0; float opacity = 0f; int rotate = 0; // public TransformItemDecoration(boolean isVertical,float alpha,int translateX,int // translateY,int rotation,float scale) Matcher matcher = transformPattern.matcher(raw); while (matcher.find()) { String match = matcher.group(); String name = matcher.group(1); try { switch (name) { case "scale": scaleX = Float.parseFloat(matcher.group(2)); scaleY = Float.parseFloat(matcher.group(3)); break; case "translate": translateX = Integer.parseInt(matcher.group(2)); translateY = Integer.parseInt(matcher.group(3)); break; case "opacity": opacity = Float.parseFloat(matcher.group(2)); break; case "rotate": rotate = Integer.parseInt(matcher.group(2)); break; default: WXLogUtils.e(TAG, "Invaild transform expression:" + match); break; } } catch (NumberFormatException e) { WXLogUtils.e("", e); WXLogUtils.e(TAG, "Invaild transform expression:" + match); } } return new TransformItemDecoration( getOrientation() == Constants.Orientation.VERTICAL, opacity, translateX, translateY, rotate, scaleX, scaleY); }
/** * Forbid ViewHolder cache if viewType > MAX_VIEWTYPE_ALLOW_CACHE * * @param viewType */ private void checkRecycledViewPool(int viewType) { try { if (mViewTypes.size() > MAX_VIEWTYPE_ALLOW_CACHE) mAllowCacheViewHolder = false; if (mDownForBidCacheViewHolder) if (getHostView() != null && getHostView().getInnerView() != null) getHostView().getInnerView().getRecycledViewPool().setMaxRecycledViews(viewType, 0); if (!mDownForBidCacheViewHolder) { if (!mAllowCacheViewHolder) { if (getHostView() != null && getHostView().getInnerView() != null) { for (int i = 0; i < mViewTypes.size(); i++) { getHostView() .getInnerView() .getRecycledViewPool() .setMaxRecycledViews(mViewTypes.keyAt(i), 0); } mDownForBidCacheViewHolder = true; } } } } catch (Exception e) { WXLogUtils.e(TAG, "Clear recycledViewPool error!"); } }
@Override public boolean onFailedToRecycleView(ListBaseViewHolder holder) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.d(TAG, "Failed to recycle " + holder); } return false; }
/** * Create an instance of {@link ListBaseViewHolder} for the given viewType (not for the given * index). This method will look up for the first component that fits the viewType requirement and * doesn't be used. Then create the certain type of view, detach the view f[rom the component. * * @param parent the ViewGroup into which the new view will be inserted * @param viewType the type of the new view * @return the created view holder. */ @Override public ListBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (mChildren != null) { if (mViewTypes == null) return createVHForFakeComponent(viewType); ArrayList<WXComponent> mTypes = mViewTypes.get(viewType); checkRecycledViewPool(viewType); if (mTypes == null) return createVHForFakeComponent(viewType); for (int i = 0; i < mTypes.size(); i++) { WXComponent component = mTypes.get(i); if (component == null || component.isUsing()) { continue; } if (component.getDomObject() != null && component.getDomObject().isFixed()) { return createVHForFakeComponent(viewType); } else { if (component instanceof WXCell) { if (component.getRealView() != null) { return new ListBaseViewHolder(component, viewType); } else { component.lazy(false); component.createView(this, -1); component.applyLayoutAndEvent(component); return new ListBaseViewHolder(component, viewType); } } else { WXLogUtils.e( TAG, "List cannot include element except cell、header、fixed、refresh and loading"); return createVHForFakeComponent(viewType); } } } } if (WXEnvironment.isApkDebugable()) { WXLogUtils.e(TAG, "Cannot find request viewType: " + viewType); } return createVHForFakeComponent(viewType); }
/** * Recycle viewHolder and its underlying view. This may because the view is removed or reused. * Either case, this method will be called. * * @param holder The view holder to be recycled. */ @Override public void onViewRecycled(ListBaseViewHolder holder) { long begin = System.currentTimeMillis(); holder.setComponentUsing(false); recycleViewList.add(holder); if (WXEnvironment.isApkDebugable()) { WXLogUtils.d( TAG, "Recycle holder " + (System.currentTimeMillis() - begin) + " Thread:" + Thread.currentThread().getName()); } }
/** Update all components' dom info stored in {@link #mAddDom} */ private void updateDomObj() { long start = System.currentTimeMillis(); Iterator<Map.Entry<String, AddDomInfo>> iterator = mAddDom.entrySet().iterator(); Map.Entry<String, AddDomInfo> entry; AddDomInfo value; while (iterator.hasNext()) { entry = iterator.next(); value = entry.getValue(); updateDomObj(value.component); } if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("updateDomObj", "time:" + (System.currentTimeMillis() - start)); } }
/** * generate viewtype by component * * @param component * @return */ private int generateViewType(WXComponent component) { long id; try { id = Integer.parseInt(component.getDomObject().getRef()); String type = component.getDomObject().getAttrs().getScope(); if (!TextUtils.isEmpty(type)) { if (mRefToViewType == null) { mRefToViewType = new ArrayMap<>(); } if (!mRefToViewType.containsKey(type)) { mRefToViewType.put(type, id); } id = mRefToViewType.get(type); } } catch (RuntimeException e) { WXLogUtils.eTag(TAG, e); id = RecyclerView.NO_ID; WXLogUtils.e( TAG, "getItemViewType: NO ID, this will crash the whole render system of WXListRecyclerView"); } return (int) id; }
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; } }
private void recycleImage(View view) { if (view instanceof ImageView) { if (getInstance().getImgLoaderAdapter() != null) { getInstance().getImgLoaderAdapter().setImage(null, (ImageView) view, null, null); } else { if (WXEnvironment.isApkDebugable()) { throw new WXRuntimeException("getImgLoaderAdapter() == null"); } WXLogUtils.e("Error getImgLoaderAdapter() == null"); } } else if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { recycleImage(((ViewGroup) view).getChildAt(i)); } } }
@Override public void remove(WXComponent child, boolean destroy) { int index = mChildren.indexOf(child); if (destroy) { child.detachViewAndClearPreInfo(); } unBindViewType(child); BounceRecyclerView view = getHostView(); if (view == null) { return; } view.getAdapter().notifyItemRemoved(index); if (WXEnvironment.isApkDebugable()) { WXLogUtils.d(TAG, "removeChild child at " + index); } super.remove(child, destroy); }
@Override public void notifyAppearStateChange( int firstVisible, int lastVisible, int directionX, int directionY) { // notify appear state Iterator<AppearanceHelper> it = mAppearComponents.values().iterator(); String direction = directionY > 0 ? "up" : "down"; if (getOrientation() == Constants.Orientation.HORIZONTAL) { direction = directionX > 0 ? "left" : "right"; } while (it.hasNext()) { AppearanceHelper item = it.next(); WXComponent component = item.getAwareChild(); if (!item.isWatch()) { continue; } boolean outOfVisibleRange = item.getCellPositionINScollable() < firstVisible || item.getCellPositionINScollable() > lastVisible; View view = component.getHostView(); if (view == null) { continue; } boolean visible = (!outOfVisibleRange) && item.isViewVisible(); int result = item.setAppearStatus(visible); if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("appear", "item " + item.getCellPositionINScollable() + " result " + result); } if (result == AppearanceHelper.RESULT_NO_CHANGE) { continue; } component.notifyAppearStateChange( result == AppearanceHelper.RESULT_APPEAR ? Constants.Event.APPEAR : Constants.Event.DISAPPEAR, direction); } }
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; }
/** * Bind the component of the position to the holder. Then flush the view. * * @param holder viewHolder, which holds reference to the view * @param position position of component in WXListComponent */ @Override public void onBindViewHolder(ListBaseViewHolder holder, int position) { if (holder == null) return; holder.setComponentUsing(true); WXComponent component = getChild(position); if (component == null || (component instanceof WXRefresh) || (component instanceof WXLoading) || (component.getDomObject() != null && component.getDomObject().isFixed())) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.d(TAG, "Bind WXRefresh & WXLoading " + holder); } return; } if (component != null && holder.getComponent() != null && holder.getComponent() instanceof WXCell) { holder.getComponent().bindData(component); // holder.getComponent().refreshData(component); } }
@Override public void onLoadMore(int offScreenY) { try { String offset = getDomObject().getAttrs().getLoadMoreOffset(); if (TextUtils.isEmpty(offset)) { offset = "0"; } if (offScreenY < Integer.parseInt(offset)) { String loadMoreRetry = getDomObject().getAttrs().getLoadMoreRetry(); if (mListCellCount != mChildren.size() || mLoadMoreRetry == null || !mLoadMoreRetry.equals(loadMoreRetry)) { getInstance().fireEvent(getDomObject().getRef(), Constants.Event.LOADMORE); mListCellCount = mChildren.size(); mLoadMoreRetry = loadMoreRetry; } } } catch (Exception e) { WXLogUtils.d(TAG + "onLoadMore :", e); } }
/** * 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); } }
/** * 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()); } }