http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java index 51a4874..d8200a2 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java @@ -22,10 +22,8 @@ import android.annotation.TargetApi; import android.content.Context; import android.graphics.Point; import android.graphics.PointF; -import android.os.AsyncTask; import android.os.Build; import android.os.Looper; -import android.os.MessageQueue; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.util.ArrayMap; @@ -60,6 +58,7 @@ import com.taobao.weex.dom.flex.CSSLayoutContext; import com.taobao.weex.dom.flex.Spacing; import com.taobao.weex.el.parse.ArrayStack; import com.taobao.weex.ui.component.AppearanceHelper; +import com.taobao.weex.ui.component.ComponentUtils; import com.taobao.weex.ui.component.Scrollable; import com.taobao.weex.ui.component.WXBaseRefresh; import com.taobao.weex.ui.component.WXComponent; @@ -84,12 +83,10 @@ import com.taobao.weex.utils.WXViewUtils; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; import static com.taobao.weex.common.Constants.Name.LOADMOREOFFSET; @@ -101,8 +98,15 @@ import static com.taobao.weex.common.Constants.Name.LOADMOREOFFSET; @Component(lazyload = false) public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> implements IRecyclerAdapterListener<TemplateViewHolder>, IOnLoadMoreListener, Scrollable { + + /** + * trace log for template list + * */ + public static final boolean ENABLE_TRACE_LOG = false; + public static final String TAG = "WXRecyclerTemplateList"; + private static final String EMPTY_HOLDER_TEMPLATE_KEY = ""; private static final String NAME_HAS_FIXED_SIZE = "hasFixedSize"; private static final String NAME_ITEM_VIEW_CACHE_SIZE = "itemViewCacheSize"; private static final String NAME_TEMPLATE_CACHE_SIZE = "templateCacheSize"; @@ -134,7 +138,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp private Point mLastReport = new Point(-1, -1); private boolean mHasAddScrollEvent = false; - private JSONArray listData; + + private CellDataManager cellDataManager; + private String listDataKey = Constants.Name.Recycler.LIST_DATA; private String listDataItemKey = null; private String listDataIndexKey = null; @@ -143,13 +149,19 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp private Map<String, WXCell> mTemplateSources; - private String listDataTemplateKey = Constants.Name.Recycler.SLOT_TEMPLATE_TYPE; + private String listDataTemplateKey = Constants.Name.Recycler.SLOT_TEMPLATE_CASE; private Runnable listUpdateRunnable; private ConcurrentHashMap<String, TemplateCache> mTemplatesCache; private int templateCacheSize = 2; /** + * case default cell and key + * */ + private WXCell defaultTemplateCell; + private String defaultTemplateKey = "@default_template_cell"; + + /** * scroll start and scroll end event * */ private ScrollStartEndHelper mScrollStartEndHelper; @@ -175,9 +187,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp * */ private ArrayMap<Integer, Map<String,Map<Integer, List<Object>>>> mDisAppearWatchList = new ArrayMap<>(); - private ArrayStack bindIngStackContext = new ArrayStack(); - private Map bindIngMapContext = new HashMap(); - + private CellRenderContext cellRenderContext = new CellRenderContext(); public WXRecyclerTemplateList(WXSDKInstance instance, WXDomObject node, WXVContainer parent) { super(instance, node, parent); @@ -192,18 +202,19 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp updateRecyclerAttr(); } mTemplateViewTypes = new ArrayMap<>(); - mTemplateViewTypes.put("", 0); //empty view, when template was not sended + mTemplateViewTypes.put(EMPTY_HOLDER_TEMPLATE_KEY, 0); //empty view, when template was not sended mTemplateSources = new HashMap<>(); mTemplatesCache = new ConcurrentHashMap<>(); mStickyHelper = new TemplateStickyHelper(this); orientation = mDomObject.getOrientation(); - listDataTemplateKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_TEMPLATE_KEY), Constants.Name.Recycler.SLOT_TEMPLATE_TYPE); + listDataTemplateKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_TEMPLATE_SWITCH_KEY), Constants.Name.Recycler.SLOT_TEMPLATE_CASE); listDataItemKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_ITEM), listDataItemKey); listDataIndexKey = WXUtils.getString(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA_ITEM_INDEX), listDataIndexKey); - if( getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA) instanceof JSONArray) { + cellDataManager = new CellDataManager(this); + if(getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA) instanceof JSONArray) { JSONArray array = (JSONArray)getDomObject().getAttrs().get(Constants.Name.Recycler.LIST_DATA); if(array.size() > 0) { - listData = array; + cellDataManager.listData = array; } } /** @@ -211,23 +222,28 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp * list has layout and can archive better user experience and better load time, * which reduce waste cell layout when list layout changes. * */ - WXSDKManager.getInstance().getWXDomManager().post(new Runnable() { - @Override - public void run() { - if(isDestoryed()){ - return; - } - long start = System.currentTimeMillis(); - if(mDomObject != null && mDomObject.getCellList() != null){ - for(int i=0; i<mDomObject.getCellList().size(); i++){ - addChild(DomTreeBuilder.buildTree(mDomObject.getCellList().get(i), WXRecyclerTemplateList.this)); + if(mDomObject != null + && mDomObject.getCellList() != null + && mDomObject.getCellList().size() > 0){ + Runnable runnable = new Runnable() { + // @Override + public void run() { + if(isDestoryed()){ + return; + } + long start = System.currentTimeMillis(); + if(mDomObject != null && mDomObject.getCellList() != null){ + for(int i=0; i<mDomObject.getCellList().size(); i++){ + addChild(ComponentUtils.buildTree(mDomObject.getCellList().get(i), WXRecyclerTemplateList.this)); + } + } + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){ + WXLogUtils.d(TAG, "TemplateList BuildDomTree Used " + (System.currentTimeMillis() - start)); } } - if(WXEnvironment.isApkDebugable()){ - WXLogUtils.d(TAG, "TemplateList BuildDomTree Used " + (System.currentTimeMillis() - start)); - } - } - }); + }; + WXSDKManager.getInstance().getWXDomManager().post(runnable); + } } @Override @@ -331,15 +347,17 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp * compute sticky position * */ if(mStickyHelper != null){ - mStickyHelper.getStickyPositions().clear(); - if(listData != null){ - for(int i=0; i<listData.size(); i++){ - WXCell cell = getSourceTemplate(i); - if(cell == null){ - continue; - } - if(cell.isSticky()){ - mStickyHelper.getStickyPositions().add(i); + if(mStickyHelper.getStickyTypes().size() > 0){ + mStickyHelper.getStickyPositions().clear(); + if(cellDataManager.listData != null){ + for(int i = 0; i< cellDataManager.listData.size(); i++){ + WXCell cell = getSourceTemplate(i); + if(cell == null){ + continue; + } + if(cell.isSticky()){ + mStickyHelper.getStickyPositions().add(i); + } } } } @@ -347,7 +365,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp if(getHostView() != null && getHostView().getRecyclerViewBaseAdapter() != null){ getHostView().getRecyclerViewBaseAdapter().notifyDataSetChanged(); } - if(WXEnvironment.isApkDebugable()){ + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){ WXLogUtils.d(TAG, "WXTemplateList notifyDataSetChanged"); } } @@ -390,7 +408,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp if(template == null){ return; } - if(listData == null || mStickyHelper == null){ + if(cellDataManager.listData == null || mStickyHelper == null){ return; } if(!mStickyHelper.getStickyTypes().contains(template.getRef())){ @@ -403,7 +421,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp public void unbindStickStyle(WXComponent component) { WXComponent template = findParentType(component, WXCell.class); if(template == null - || listData == null + || cellDataManager.listData == null || mStickyHelper == null){ return; } @@ -425,14 +443,14 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } private void setAppearanceWatch(WXComponent component, int event, boolean enable) { - if(listData == null + if(cellDataManager.listData == null || mAppearHelpers == null || TextUtils.isEmpty(component.getRef())){ return; } - WXComponent cell = findCell(component); - int type = getCellItemType(cell); - if(type <= 0){ + WXCell cell = findCell(component); + int type = getCellTemplateItemType(cell); + if(type < 0){ return; } List<AppearanceHelper> mAppearListeners = mAppearHelpers.get(type); @@ -517,9 +535,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } WXCell cell = findCell(component); if(typeIndex >= 0){ - if(listData != null && component.getRef() != null){ + if(cellDataManager.listData != null && component.getRef() != null){ int typePosition = 0; - for(int i=0; i<listData.size(); i++){ + for(int i = 0; i< cellDataManager.listData.size(); i++){ WXCell template = getSourceTemplate(i); if(template == null){ continue; @@ -533,7 +551,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } } if(position < 0){ - position = listData.size() - 1; + position = cellDataManager.listData.size() - 1; } } } @@ -585,6 +603,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp @Override public void addChild(WXComponent child, int index) { + /** + * dom object in component is not tree, build tree + * */ if(!(child instanceof WXCell)) { super.addChild(child, index); } @@ -593,8 +614,30 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } if(child instanceof WXCell){ if(child.getDomObject() != null && child.getDomObject().getAttrs() != null){ - Object templateId = child.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_TYPE); + Object templateId = child.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_CASE); String key = WXUtils.getString(templateId, null); + if(getDomObject().getAttrs().containsKey(Constants.Name.Recycler.LIST_DATA_TEMPLATE_SWITCH_KEY)){ + if(defaultTemplateCell == null){ + defaultTemplateCell = (WXCell) child; + if(!TextUtils.isEmpty(key)){ + defaultTemplateKey = key; + }else{ + key = defaultTemplateKey; + child.getDomObject().getAttrs().put(Constants.Name.Recycler.SLOT_TEMPLATE_CASE, key); + } + } + }else{ + if(defaultTemplateCell == null + || child.getDomObject().getAttrs().containsKey(Constants.Name.Recycler.SLOT_TEMPLATE_DEFAULT)){ + defaultTemplateCell = (WXCell) child; + if(!TextUtils.isEmpty(key)){ + defaultTemplateKey = key; + }else{ + key = defaultTemplateKey; + child.getDomObject().getAttrs().put(Constants.Name.Recycler.SLOT_TEMPLATE_CASE, key); + } + } + } if(key != null){ if(child.getDomObject() instanceof WXCellDomObject && getDomObject() instanceof WXRecyclerDomObject){ @@ -602,7 +645,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp domObject.setRecyclerDomObject((WXRecyclerDomObject) getDomObject()); } mTemplateSources.put(key, (WXCell) child); - ensureSourceCellRenderWithData((WXCell)child); + renderTemplateCellWithData((WXCell)child); if(mTemplateViewTypes.get(key) == null){ mTemplateViewTypes.put(key, mTemplateViewTypes.size()); } @@ -614,6 +657,8 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp + + /** * RecyclerView manage its children in a way that different from {@link WXVContainer}. Therefore, * {@link WXVContainer#addSubView(View, int)} is an empty implementation in {@link @@ -671,9 +716,9 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp case Constants.Name.Recycler.LIST_DATA_ITEM_INDEX: listDataIndexKey = WXUtils.getString(param, listDataIndexKey); return true; - case Constants.Name.Recycler.LIST_DATA_TEMPLATE_KEY: - case Constants.Name.Recycler.SLOT_TEMPLATE_TYPE: - listDataTemplateKey = WXUtils.getString(param, Constants.Name.Recycler.SLOT_TEMPLATE_TYPE); + case Constants.Name.Recycler.LIST_DATA_TEMPLATE_SWITCH_KEY: + case Constants.Name.Recycler.SLOT_TEMPLATE_CASE: + listDataTemplateKey = WXUtils.getString(param, Constants.Name.Recycler.SLOT_TEMPLATE_CASE); return true; case LOADMOREOFFSET: return true; @@ -761,68 +806,112 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp @JSMethod public void setListData(Object param){ - boolean update = listData != null && listData != param; + boolean update = cellDataManager.listData != param; if(param instanceof JSONArray){ - listData = (JSONArray) param; - } - if(update){ - notifyUpdateList(); + if(update){ + cellDataManager.setListData((JSONArray) param); + notifyUpdateList(); + } } } @JSMethod - public void appendData(JSONArray arrayObject){ - if(listData == null){ - listData = new JSONArray(); + public void appendData(JSONArray data){ + if(data == null || data.size() == 0){ + return; + } + if(cellDataManager.listData == null){ + cellDataManager.listData = new JSONArray(); + } + int position = cellDataManager.listData.size(); + if(position < 0){ + position = 0; } - if(arrayObject instanceof JSONArray){ - listData.addAll(arrayObject); + if(data instanceof JSONArray){ + cellDataManager.listData.addAll(data); } - notifyUpdateList(); + getHostView().getRecyclerViewBaseAdapter().notifyItemRangeInserted(position, data.size()); } @JSMethod - public void insertData(JSONObject data, int index){ + public void insertData(int index, Object data){ if(data == null){ return; } - if(listData == null || index >= listData.size()){ + + if(cellDataManager.listData == null || index > cellDataManager.listData.size()){ return; } - listData.add(index, data); - notifyUpdateList(); + boolean renderStateChanged = cellDataManager.insertData(index, data); + if(renderStateChanged){ + notifyUpdateList(); + }else{ + getHostView().getRecyclerViewBaseAdapter().notifyItemInserted(index); + } + } + + @JSMethod + public void appendRange(int index, JSONArray data){ + insertRange(index, data); } + + /** + * when update data, list maybe contains sticky, may use position, when position changed should be rendered + * so use notifyUpdateList is better + * */ + @JSMethod - public void updateData(JSONObject data, int index){ + public void insertRange(int index, JSONArray data){ + if(data == null || data.size() == 0){ + return; + } + if(cellDataManager.listData == null || index > cellDataManager.listData.size()){ + return; + } + boolean renderStateChange = cellDataManager.insertRange(index, data); + if(renderStateChange){ + notifyUpdateList(); + }else{ + getHostView().getRecyclerViewBaseAdapter().notifyItemRangeInserted(index, data.size()); + } + + } + + @JSMethod + public void updateData(int index, Object data){ if(data == null){ return; } - if(listData == null || index >= listData.size()){ + if(cellDataManager.listData == null || index >= cellDataManager.listData.size()){ return; } - listData.set(index, data); - notifyUpdateList(); + boolean onlyDataChange = cellDataManager.updateData(data, index); + if(onlyDataChange) { + getHostView().getRecyclerViewBaseAdapter().notifyItemChanged(index, data); + }else{ + notifyUpdateList(); + } } @JSMethod - public void removeData(List<Integer> array){ - if(array == null || array.size() == 0){ + public void removeData(int index, int count){ + if(cellDataManager.listData == null + || index >= cellDataManager.listData.size()){ return; } - int offset = 0; - for(Integer index : array){ - if(listData == null - || index == null){ - return; - } - index -= offset; - if(index < listData.size()){ - listData.remove((int)index); - offset++; - } + if(count <= 0){ + count = 1; + } + int removeCount = 0; + while (count > 0 && index < cellDataManager.listData.size()){ + cellDataManager.removeData(index); + count--; + removeCount++; + } + if(removeCount > 0) { + notifyUpdateList(); } - notifyUpdateList(); } @@ -1009,8 +1098,8 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp getHostView().getInnerView().setAdapter(null); } } - if(listData != null){ - listData = null; + if(cellDataManager.listData != null){ + cellDataManager.setListData(null); } if(mStickyHelper != null){ mStickyHelper = null; @@ -1045,25 +1134,21 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp return; } long start = System.currentTimeMillis(); - boolean resuse = templateViewHolder.getHolderPosition() >= 0; templateViewHolder.setHolderPosition(position); - Object data = listData.get(position); - if(component.getRenderData() == data){ - component.setHasLayout(true); + Object data = cellDataManager.listData.get(position); + CellRenderState cellRenderState = cellDataManager.getRenderState(position); + if(component.getRenderData() == data && (cellRenderState == null || !cellRenderState.isDirty())){ + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){ + WXLogUtils.d(TAG, position + " position "+ getTemplateKey(position) + " onBindViewHolder none data update "); + } + return; //none update just return }else{ - List<WXComponent> updates = Statements.doRender(component, getStackContextForPosition(position, data)); + List<WXComponent> updates = doRenderTemplate(component, position); Statements.doInitCompontent(updates); component.setRenderData(data); - if(WXEnvironment.isApkDebugable()){ - WXLogUtils.d(TAG, position + getTemplateKey(position) + " onBindViewHolder render used " + (System.currentTimeMillis() - start)); - } - if(component.isHasLayout()){ - resuse = true; - } Layouts.doLayoutAsync(templateViewHolder, true); - component.setHasLayout(true); - if(WXEnvironment.isApkDebugable()){ - WXLogUtils.d(TAG, position + getTemplateKey(position) + " onBindViewHolder layout used " + (System.currentTimeMillis() - start) + resuse); + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG){ + WXLogUtils.d(TAG, position + " position "+ getTemplateKey(position) + " onBindViewHolder used " + (System.currentTimeMillis() - start)); } } } @@ -1077,41 +1162,34 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp view.setLayoutParams(new FrameLayout.LayoutParams(0, 0)); return new TemplateViewHolder(view, viewType); } - TemplateCache cache = mTemplatesCache.get(template); - WXCell component = null; + WXCell component = getCellTemplateFromCache(template); boolean cacheHit = true; - if(cache != null && cache.cells != null && cache.cells.size() > 0){ - component = cache.cells.poll(); - } - if(cache == null || !cache.isLoadIng){ - asyncLoadTemplateCache(template); - } if(component == null){ cacheHit = false; if(!source.isSourceUsed()){ source.setSourceUsed(true); - ensureSourceCellRenderWithData(source); + renderTemplateCellWithData(source); component = source; - if(WXEnvironment.isApkDebugable()) { + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) { WXLogUtils.d(TAG, template + " onCreateViewHolder source"); } } } if(component == null) { long start = System.currentTimeMillis(); - component = (WXCell) copyCell(source); - if(WXEnvironment.isApkDebugable()) { + component = (WXCell) copyComponentFromSourceCell(source); + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) { WXLogUtils.d(TAG, template + " onCreateViewHolder copy used " + (System.currentTimeMillis() - start)); } } if(component.isLazy()) { - doInitLazyCell(component, template, false); - if(WXEnvironment.isApkDebugable()) { - WXLogUtils.d(TAG, template + " onCreateViewHolder cache hit " + cacheHit + " idle init false "); + doCreateCellViewBindData(component, template, false); + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) { + WXLogUtils.d(TAG, template + " onCreateViewHolder cache hit " + cacheHit + " view not idle init"); } }else{ - if(WXEnvironment.isApkDebugable()) { - WXLogUtils.d(TAG, template + " onCreateViewHolder cache hit " + cacheHit + " idle init true"); + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) { + WXLogUtils.d(TAG, template + " onCreateViewHolder cache hit " + cacheHit + " view idle init"); } } TemplateViewHolder templateViewHolder = new TemplateViewHolder(component, viewType); @@ -1123,8 +1201,8 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp * copy cell component from source, init render data, and return source * if none data, return null * */ - private WXComponent copyCell(WXCell cell){ - ensureSourceCellRenderWithData(cell); + public WXComponent copyComponentFromSourceCell(WXCell cell){ + renderTemplateCellWithData(cell); WXCell component = (WXCell) Statements.copyComponentTree(cell); if(component.getDomObject() instanceof WXCellDomObject && getDomObject() instanceof WXRecyclerDomObject){ @@ -1135,15 +1213,20 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp return component; } - private void ensureSourceCellRenderWithData(WXCell cell){ + /** + * render init with cell with one data, + * if template has already render with data, done nothing + * @param cell + * */ + private synchronized void renderTemplateCellWithData(WXCell cell){ if(cell.getRenderData() == null){ - if(listData != null && listData.size() > 0){ + if(cellDataManager.listData != null && cellDataManager.listData.size() > 0){ synchronized (this){ if(cell.getRenderData() == null){ - for(int i=0; i<listData.size(); i++){ + for(int i = 0; i< cellDataManager.listData.size(); i++){ if(cell == getSourceTemplate(i)){ - Object data = listData.get(i); - Statements.doRender(cell, getStackContextForPosition(i, data)); + Object data = cellDataManager.listData.get(i); + doRenderTemplate(cell, i); Layouts.doSafeLayout(cell, new CSSLayoutContext()); cell.setRenderData(data); break; @@ -1162,37 +1245,33 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp * */ @Override public int getItemViewType(int position) { - JSONObject data = safeGetListData(position); - String template = data.getString(listDataTemplateKey); - if(TextUtils.isEmpty(template)){ - template = ""; - } + String template = getTemplateKey(position); int type = mTemplateViewTypes.indexOfKey(template); if(type < 0){ - type = 0; + type = mTemplateViewTypes.indexOfKey(EMPTY_HOLDER_TEMPLATE_KEY); } return type; } /** - * return code context for render component + * create code context for render component and do render * */ - private ArrayStack getStackContextForPosition(int position, Object item){ - if(!bindIngStackContext.isEmpty()){ - bindIngStackContext.getList().clear(); - } - if(!bindIngMapContext.isEmpty()){ - bindIngMapContext.clear(); - } - ArrayStack stack = bindIngStackContext; - Map map = bindIngMapContext; - if(listData != null){ - stack.push(listData); + private List<WXComponent> doRenderTemplate(WXCell cell, int position){ + this.cellRenderContext.clear(); + Object item = cellDataManager.listData.get(position); + CellRenderState cellRenderState = cellDataManager.getRenderState(position); + cellRenderContext.renderState = cellRenderState; + cellRenderContext.templateList = this; + cellRenderContext.position = position; + + ArrayStack stack = cellRenderContext.stack; + Map map = cellRenderContext.map; + if(cellDataManager.listData != null){ stack.push(map); - map.put(listDataKey, listData); + map.put(listDataKey, cellDataManager.listData); if(!TextUtils.isEmpty(listDataIndexKey)) { - map.put(listDataIndexKey, position); + map.put(listDataIndexKey, new PositionRef(cellRenderState)); } if(!TextUtils.isEmpty(listDataItemKey)) { map.put(listDataItemKey, item); @@ -1200,17 +1279,51 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp stack.push(item); } } - return stack; + if(cellRenderState.itemId <= 0){ + getItemId(position); + } + List<WXComponent> updates = Statements.doRender(cell, this.cellRenderContext); + if(cellRenderState.isDirty()){ + cellRenderState.resetDirty(); + } + return updates; + } + + public ArrayStack copyStack(CellRenderContext context, ArrayStack stack){ + ArrayStack onceStack = new ArrayStack(); + for(int index=0; index < stack.size(); index++) { + Object value = stack.get(index); + if(value instanceof Map){ + value = new HashMap((Map) value); + } + onceStack.push(value); + } + return onceStack; } + /** * return tepmlate key for position * */ public String getTemplateKey(int position){ - JSONObject data = safeGetListData(position); - String template = data.getString(listDataTemplateKey); + Object data = safeGetListData(position); + return getTemplateKey(data); + } + + /** + * return template key for position never be null + * */ + public String getTemplateKey(Object data){ + String template = null; + if(data instanceof JSONObject) { + template = ((JSONObject)data).getString(listDataTemplateKey); + } if(TextUtils.isEmpty(template)){ - template = ""; + if(defaultTemplateCell != null){ + template = defaultTemplateKey; + }else { + template = ""; + } } return template; } @@ -1226,17 +1339,17 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp /** - * get template key from cell; 0 for default type + * get template key from cell; -1 for cann't find type * */ - private int getCellItemType(WXComponent cell){ + private int getCellTemplateItemType(WXCell cell){ if(cell == null){ return -1; } if(cell.getDomObject() != null && cell.getDomObject().getAttrs() != null){ - Object templateId = cell.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_TYPE); + Object templateId = cell.getDomObject().getAttrs().get(Constants.Name.Recycler.SLOT_TEMPLATE_CASE); String template = WXUtils.getString(templateId, null); - if(template == null){ - return 0; + if(cell == defaultTemplateCell){ + template = defaultTemplateKey; } int type = mTemplateViewTypes.indexOfKey(template); if(type < 0){ @@ -1249,7 +1362,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp @Override public int getItemCount() { - if(listData == null){ + if(cellDataManager.listData == null){ return 0; } if(mTemplateViewTypes == null || mTemplateViewTypes.size() <= 1){ @@ -1258,7 +1371,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp if(mTemplateSources == null || mTemplateSources.size() == 0){ return 0; } - return listData.size(); + return cellDataManager.listData.size(); } @Override @@ -1274,18 +1387,22 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp * */ @Override public long getItemId(int position) { - if(getItemViewType(position) <= 0){ - return RecyclerView.NO_ID; - } - JSONObject data = safeGetListData(position); - if(data.containsKey(Constants.Name.Recycler.LIST_DATA_ITEM_ID)) { - String itemKey = data.getString(Constants.Name.Recycler.LIST_DATA_ITEM_ID); - if(TextUtils.isEmpty(itemKey)){ - return position; + CellRenderState renderState = cellDataManager.getRenderState(position); + if(renderState.itemId <= 0){ + String template = getTemplateKey(position); + if(TextUtils.isEmpty(template)){ + return RecyclerView.NO_ID; + } + Object data = safeGetListData(position); + if(data instanceof JSONObject && ((JSONObject)data).containsKey("keyItemId")){ + renderState.itemId = ((JSONObject)data).getLongValue("keyItemId"); + }else{ + long id = Math.abs(data.hashCode()); + long itemId = (id<< 24) + position; + renderState.itemId = itemId; } - return itemKey.hashCode(); } - return position; + return renderState.itemId; } @Override @@ -1305,16 +1422,16 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } float offsetParsed = WXViewUtils.getRealPxByWidth(Integer.parseInt(offset),getInstance().getInstanceViewPortWidth()); - if (offScreenY <= offsetParsed && listData != null) { - if (mListCellCount != listData.size() + if (offScreenY <= offsetParsed && cellDataManager.listData != null) { + if (mListCellCount != cellDataManager.listData.size() || mForceLoadmoreNextTime) { fireEvent(Constants.Event.LOADMORE); - mListCellCount = listData.size(); + mListCellCount = cellDataManager.listData.size(); mForceLoadmoreNextTime = false; } } } catch (Exception e) { - WXLogUtils.d(TAG + "onLoadMore :", e); + WXLogUtils.d(TAG + " onLoadMore : ", e); } } @@ -1432,13 +1549,13 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } - private JSONObject safeGetListData(int position){ + private Object safeGetListData(int position){ try{ - return listData.getJSONObject(position); + return cellDataManager.listData.get(position); }catch (Exception e){return JSONObject.parseObject("{}");} } - private void notifyUpdateList(){ + public void notifyUpdateList(){ if(getHostView() == null || getHostView().getInnerView() == null || listUpdateRunnable == null){ @@ -1454,10 +1571,10 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp private int calcContentSize() { int totalHeight = 0; - if(listData == null){ + if(cellDataManager.listData == null){ return totalHeight; } - for (int i = 0; i < listData.size(); i++) { + for (int i = 0; i < cellDataManager.listData.size(); i++) { WXCell child = getSourceTemplate(i); if (child != null) { totalHeight += child.getLayoutHeight(); @@ -1567,114 +1684,45 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } + /** - * copy cell async and save to cache + * @param template template name + * get cell template component from cache, if cell component not load + * start load cell template * */ - private void asyncLoadTemplateCache(final String template) { - if(Thread.currentThread() != Looper.getMainLooper().getThread()){ - if(listData == null || listData.size() == 0){ - return; - } - boolean firstScreenContains = false; - for(int i=0; i<listData.size(); i++){ - if(template.equals(getTemplateKey(i))){ - firstScreenContains = true; - break; - } - } - if(!firstScreenContains){ - return; - } - } - final WXCell source = mTemplateSources.get(template); - if(source == null){ - return; - } - TemplateCache cellCache = mTemplatesCache.get(template); - if(cellCache == null){ - cellCache = new TemplateCache(); - mTemplatesCache.put(template, cellCache); - } - if(cellCache.cells.size() > 0){ - cellCache.isLoadIng = false; - return; - } - if(cellCache.isLoadIng){ - return; + private WXCell getCellTemplateFromCache(final String template){ + TemplateCache cache = mTemplatesCache.get(template); + WXCell component = null; + if(cache != null && cache.cells != null && cache.cells.size() > 0){ + component = cache.cells.poll(); } - cellCache.isLoadIng = true; - AsyncTask<Void,Void, Void> preloadTask = new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - TemplateCache cellCache = mTemplatesCache.get(template); - if(cellCache == null || cellCache.cells == null){ - return null; - } - while (cellCache.cells.size() < templateCacheSize){ - WXCell component = (WXCell) copyCell(source); - if(component == null){ - return null; - } - if(source.getInstance() == null || source.getInstance().isDestroy()){ - return null; - } - cellCache.cells.add(component); - } - return null; + if(cache == null || !cache.isLoadIng){ + if(cache == null){ + cache = new TemplateCache(); + mTemplatesCache.put(template, cache); } - @Override - protected void onPostExecute(Void aVoid) { - if(source.getInstance() == null || source.getInstance().isDestroy()){ - return; + cache.isLoadIng = true; + WXCell source = mTemplateSources.get(template); + if(source != null){ + boolean allowPreload = WXUtils.getBoolean(source.getDomObject().getAttrs().get("preload"), true); + if(allowPreload) { + AsyncCellLoadTask asyncCellLoadTask = new AsyncCellLoadTask(template, source, this); + asyncCellLoadTask.startTask(); } - final TemplateCache cellCache = mTemplatesCache.get(template); - if(cellCache == null){ - return; - } - if(cellCache.cells == null - || cellCache.cells.size() == 0){ - cellCache.isLoadIng = false; - return; - } - Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { - @Override - public boolean queueIdle() { - if(source.getInstance() == null || source.getInstance().isDestroy()){ - return false; - } - ConcurrentLinkedQueue<WXCell> queue = cellCache.cells; - Iterator<WXCell> iterator = queue.iterator(); - while (iterator.hasNext()){ - WXCell component = iterator.next(); - if(component.isLazy()){ - doInitLazyCell(component, template, true); - return iterator.hasNext(); - } - } - return false; - } - }); - cellCache.isLoadIng = false; } - }; - preloadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + return component; } - private static void doInitLazyCell(WXCell component, String template, boolean inPreload){ + /** + * create view for lazy cell and bind data + * */ + public static void doCreateCellViewBindData(WXCell component, String template, boolean inPreload){ if(component.isLazy()){ long start = System.currentTimeMillis(); - component.lazy(false); - component.createView(); - if(WXEnvironment.isApkDebugable()) { - WXLogUtils.d(TAG, "doInitLazyCell idle" + inPreload + template + " createView used " + (System.currentTimeMillis() - start)); - } - component.applyLayoutAndEvent(component); - if(WXEnvironment.isApkDebugable()) { - WXLogUtils.d(TAG, "doInitLazyCell idle" + inPreload + template + " apply layout used " + (System.currentTimeMillis() - start)); - } - component.bindData(component); - if(WXEnvironment.isApkDebugable()) { - WXLogUtils.d(TAG, "doInitLazyCell idle" + inPreload + template + " bindData used " + (System.currentTimeMillis() - start)); + ComponentUtils.initLazyComponent(component, null); + if(WXEnvironment.isOpenDebugLog() && ENABLE_TRACE_LOG) { + WXLogUtils.d(TAG, " doCreateCellViewBindData " + template + " in preload "+ inPreload + " used " + (System.currentTimeMillis() - start)); } } } @@ -1685,4 +1733,20 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp } return mScrollStartEndHelper; } + + public int getTemplateCacheSize() { + return templateCacheSize; + } + + public ConcurrentHashMap<String, TemplateCache> getTemplatesCache() { + if(mTemplatesCache == null){ + mTemplatesCache = new ConcurrentHashMap<>(); + } + return mTemplatesCache; + } + + + public CellDataManager getCellDataManager() { + return cellDataManager; + } }
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java b/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java new file mode 100644 index 0000000..f887dca --- /dev/null +++ b/android/sdk/src/test/java/com/taobao/weex/el/FailedCaseTest.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.taobao.weex.el; + +import com.alibaba.fastjson.JSONObject; +import com.taobao.weex.el.parse.ArrayStack; +import com.taobao.weex.el.parse.Parser; +import com.taobao.weex.el.parse.Token; + +import junit.framework.TestCase; + +/** + * Created by furture on 2018/1/25. + */ + +public class FailedCaseTest extends TestCase { + + + public void testVElseIf0(){ + JSONObject data = new JSONObject(); + JSONObject item = new JSONObject(); + item.put("number", 0.0); + data.put("item", item); + ArrayStack stack = new ArrayStack(); + stack.push(data); + + Token token = Parser.parse("!(item.number%3 === 0) && (item.number%3 === 1)"); + + System.out.println(token.toString() + " " + token.execute(stack)); + + Token if2 = Parser.parse("!(!(item.number%3 === 0) && (item.number%3 === 1))"); + System.out.println(if2 + " " + if2.execute(stack)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/886d859a/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java b/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java index 3924a3a..2233acc 100644 --- a/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java +++ b/android/sdk/src/test/java/com/taobao/weex/ui/component/binding/StatementTest.java @@ -37,6 +37,7 @@ import com.taobao.weex.ui.component.WXComponentFactory; import com.taobao.weex.ui.component.WXDiv; import com.taobao.weex.ui.component.WXText; import com.taobao.weex.ui.component.list.WXCell; +import com.taobao.weex.ui.component.list.template.CellRenderContext; import junit.framework.Assert; @@ -57,7 +58,6 @@ import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; -import java.util.Stack; /** * Created by furture on 2017/8/18. @@ -166,7 +166,7 @@ public class StatementTest { return cell; } - private ArrayStack createContext(int count){ + private CellRenderContext createContext(int count){ JSONObject data = new JSONObject(); data.put("item", new JSONObject()); data.getJSONObject("item").put("name", "hello world"); @@ -178,7 +178,9 @@ public class StatementTest { data.put("dataList", datas); ArrayStack context = new ArrayStack(); context.push(data); - return context; + CellRenderContext cellRenderContext = new CellRenderContext(); + cellRenderContext.stack = context; + return cellRenderContext; } }
