sticky offset supported, keep position lost when insert data fix
Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/cb1df915 Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/cb1df915 Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/cb1df915 Branch: refs/heads/release-0.16 Commit: cb1df915b867e6f549266b51d3d28c32dbb44a31 Parents: 45ae6f6 Author: jianbai.gbj <jianbai....@alibaba-inc.com> Authored: Mon Oct 16 13:59:17 2017 +0800 Committer: gurisxie <279483...@qq.com> Committed: Tue Oct 17 15:48:46 2017 +0800 ---------------------------------------------------------------------- .../java/com/taobao/weex/common/Constants.java | 3 + .../ui/component/list/BasicListComponent.java | 114 ++++++++++++++----- .../ui/component/list/StickyHeaderHelper.java | 43 ++++++- .../taobao/weex/ui/component/list/WXCell.java | 92 +++++++-------- .../list/template/WXRecyclerTemplateList.java | 2 +- 5 files changed, 178 insertions(+), 76 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/cb1df915/android/sdk/src/main/java/com/taobao/weex/common/Constants.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/common/Constants.java b/android/sdk/src/main/java/com/taobao/weex/common/Constants.java index e74e083..730ce84 100644 --- a/android/sdk/src/main/java/com/taobao/weex/common/Constants.java +++ b/android/sdk/src/main/java/com/taobao/weex/common/Constants.java @@ -182,6 +182,9 @@ public class Constants { String ARIA_LABEL = "ariaLabel"; String ARIA_HIDDEN = "ariaHidden"; + String STICKY_OFFSET = "stickyOffset"; + String HAS_FIXED_SIZE = "hasFixedSize"; + String KEEP_POSITION_LAYOUT_DELAY = "keepPositionLayoutDelay"; interface Recycler{ String LIST_DATA = "listData"; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/cb1df915/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java index 640a17e..5649832 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java @@ -150,6 +150,15 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView private WXStickyHelper stickyHelper; + + /** + * keep positon + * */ + private WXComponent keepPositionCell = null; + private Runnable keepPositionCellRunnable = null; + private long keepPositionLayoutDelay = 150; + + public BasicListComponent(WXSDKInstance instance, WXDomObject node, WXVContainer parent) { super(instance, node, parent); stickyHelper = new WXStickyHelper(this); @@ -236,6 +245,9 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView if (transforms != null) { bounceRecyclerView.getInnerView().addItemDecoration(RecyclerTransform.parseTransforms(getOrientation(), transforms)); } + if(getDomObject().getAttrs().get(Constants.Name.KEEP_POSITION_LAYOUT_DELAY) != null){ + keepPositionLayoutDelay = WXUtils.getNumberInt(getDomObject().getAttrs().get(Constants.Name.KEEP_POSITION_LAYOUT_DELAY), (int)keepPositionLayoutDelay); + } mItemAnimator=bounceRecyclerView.getInnerView().getItemAnimator(); @@ -245,6 +257,10 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView bounceRecyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER); bounceRecyclerView.getInnerView().clearOnScrollListeners(); bounceRecyclerView.getInnerView().addOnScrollListener(mViewOnScrollListener); + if(getDomObject().getAttrs().get(Constants.Name.HAS_FIXED_SIZE) != null){ + boolean hasFixedSize = WXUtils.getBoolean(getDomObject().getAttrs().get(Constants.Name.HAS_FIXED_SIZE), false); + bounceRecyclerView.getInnerView().setHasFixedSize(hasFixedSize); + } bounceRecyclerView.getInnerView().addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { @@ -485,53 +501,54 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView if (stickyComponent != null && stickyComponent.getDomObject() != null && stickyComponent instanceof WXCell) { - WXCell cell = (WXCell) stickyComponent; - if (cell.getHostView() == null) { - return; - } + WXCell cell = (WXCell) stickyComponent; + if (cell.getHostView() == null) { + return; + } + + int[] location = new int[2]; + stickyComponent.getHostView().getLocationOnScreen(location); + int[] parentLocation = new int[2]; + stickyComponent.getParentScroller().getView().getLocationOnScreen(parentLocation); + int top = location[1] - parentLocation[1]; + RecyclerView.LayoutManager layoutManager; boolean beforeFirstVisibleItem = false; boolean removeOldSticky = false; layoutManager = getHostView().getInnerView().getLayoutManager(); if (layoutManager instanceof LinearLayoutManager || layoutManager instanceof GridLayoutManager) { - int fVisible = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + int firstVisiblePosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + int lastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); int pos = mChildren.indexOf(cell); cell.setScrollPositon(pos); - - if (pos <= fVisible) { + if (pos <= firstVisiblePosition + || (cell.getStickyOffset() > 0 && firstVisiblePosition < pos && pos <= lastVisiblePosition && + top <= cell.getStickyOffset())) { beforeFirstVisibleItem = true; if(pos > currentStickyPos) { currentStickyPos = pos; } - } - - if(pos > fVisible){ + }else{ removeOldSticky = true; } } else if(layoutManager instanceof StaggeredGridLayoutManager){ int [] firstItems= new int[3]; - int fVisible = ((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(firstItems)[0]; + int firstVisiblePosition = ((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(firstItems)[0]; + int lastVisiblePosition = ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(firstItems)[0]; int pos = mChildren.indexOf(cell); - if (pos <= fVisible) { + if (pos <= firstVisiblePosition || (cell.getStickyOffset() > 0 && firstVisiblePosition < pos && pos <= lastVisiblePosition && + top <= cell.getStickyOffset())) { beforeFirstVisibleItem = true; - } - - if(pos > fVisible){ + }else{ removeOldSticky = true; } } - int[] location = new int[2]; - stickyComponent.getHostView().getLocationOnScreen(location); - int[] parentLocation = new int[2]; - stickyComponent.getParentScroller().getView().getLocationOnScreen(parentLocation); - - int top = location[1] - parentLocation[1]; - boolean showSticky = beforeFirstVisibleItem && cell.getLocationFromStart() >= 0 && top <= 0 && dy >= 0; - boolean removeSticky = cell.getLocationFromStart() <= 0 && top > 0 && dy <= 0; + boolean showSticky = beforeFirstVisibleItem && cell.getLocationFromStart() >= 0 && top <= cell.getStickyOffset() && dy >= 0; + boolean removeSticky = cell.getLocationFromStart() <= cell.getStickyOffset() && top > cell.getStickyOffset() && dy <= 0; if (showSticky) { bounceRecyclerView.notifyStickyShow(cell); } else if (removeSticky || removeOldSticky) { @@ -584,7 +601,6 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView @Override public void addChild(WXComponent child, int index) { super.addChild(child, index); - if (child == null || index < -1) { return; } @@ -593,7 +609,7 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView bindViewType(child); int adapterPosition = index == -1 ? mChildren.size() - 1 : index; - T view = getHostView(); + final T view = getHostView(); if (view != null) { boolean isAddAnimation = false; ImmutableDomObject domObject = child.getDomObject(); @@ -616,9 +632,52 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView } } if (isKeepScrollPosition) { - int last=((LinearLayoutManager)view.getInnerView().getLayoutManager()).findLastVisibleItemPosition(); - view.getInnerView().getLayoutManager().scrollToPosition(last); + if(view.getInnerView().getLayoutManager() instanceof LinearLayoutManager){ + if(!view.getInnerView().isLayoutFrozen()){ //frozen, prevent layout when scroll + view.getInnerView().setLayoutFrozen(true); + } + if(keepPositionCell == null){ + int last=((LinearLayoutManager)view.getInnerView().getLayoutManager()).findLastCompletelyVisibleItemPosition(); + ListBaseViewHolder holder = (ListBaseViewHolder) view.getInnerView().findViewHolderForAdapterPosition(last); + if(holder != null){ + keepPositionCell = holder.getComponent(); + } + if(keepPositionCell != null) { + if(keepPositionCellRunnable != null){ + view.removeCallbacks(keepPositionCellRunnable); + } + keepPositionCellRunnable = new Runnable() { + @Override + public void run() { + if(keepPositionCell != null){ + int keepPosition = indexOf(keepPositionCell); + int offset = 0; + if(keepPositionCell.getHostView() != null){ + offset = keepPositionCell.getHostView().getTop(); + } + if(offset > 0) { + ((LinearLayoutManager) view.getInnerView().getLayoutManager()).scrollToPositionWithOffset(keepPosition, offset); + }else{ + view.getInnerView().getLayoutManager().scrollToPosition(keepPosition); + + } + view.getInnerView().setLayoutFrozen(false); + keepPositionCell = null; + keepPositionCellRunnable = null; + } + } + }; + } + } + if(keepPositionCellRunnable == null){ + view.getInnerView().scrollToPosition(((LinearLayoutManager)view.getInnerView().getLayoutManager()).findLastVisibleItemPosition()); + } + } view.getRecyclerViewBaseAdapter().notifyItemInserted(adapterPosition); + if(keepPositionCellRunnable != null){ + view.removeCallbacks(keepPositionCellRunnable); + view.postDelayed(keepPositionCellRunnable, keepPositionLayoutDelay); + } } else { view.getRecyclerViewBaseAdapter().notifyItemChanged(adapterPosition); } @@ -628,6 +687,7 @@ public abstract class BasicListComponent<T extends ViewGroup & ListComponentView + private void relocateAppearanceHelper() { Iterator<Map.Entry<String, AppearanceHelper>> iterator = mAppearComponents.entrySet().iterator(); while (iterator.hasNext()) { http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/cb1df915/android/sdk/src/main/java/com/taobao/weex/ui/component/list/StickyHeaderHelper.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/StickyHeaderHelper.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/StickyHeaderHelper.java index 1534013..cedd86c 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/StickyHeaderHelper.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/StickyHeaderHelper.java @@ -21,6 +21,7 @@ package com.taobao.weex.ui.component.list; import android.view.View; import android.view.ViewGroup; +import com.taobao.weex.WXEnvironment; import com.taobao.weex.common.WXThread; import com.taobao.weex.utils.WXLogUtils; @@ -87,12 +88,20 @@ public class StickyHeaderHelper { if ((existedParent = (ViewGroup) headerView.getParent()) != null) { existedParent.removeView(headerView); } + headerView.setTag(headComponent.getRef()); mParent.addView(headerView); + headerView.setTag(this); + if(headComponent.getStickyOffset() > 0) { + ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) headerView.getLayoutParams(); + if(headComponent.getStickyOffset() != params.topMargin) { + params.topMargin = headComponent.getStickyOffset(); + } + } //recover translation, sometimes it will be changed on fling headerView.setTranslationX(translationX); headerView.setTranslationY(translationY); - } + changeFrontStickyVisible(); if (headComponent.getDomObject().getEvents().contains("sticky")) { headComponent.fireEvent("sticky"); } @@ -108,7 +117,8 @@ public class StickyHeaderHelper { if(component == null || headerView == null){ - WXLogUtils.e(" sticky header to remove not found."+compToRemove.getRef()); + if(WXEnvironment.isApkDebugable()) { + } return; } if(mCurrentStickyRef != null && mCurrentStickyRef.equals(compToRemove.getRef())){ @@ -118,7 +128,12 @@ public class StickyHeaderHelper { @Override public void run() { mParent.removeView(headerView); + if(headerView.getVisibility() != View.VISIBLE){ + headerView.setVisibility(View.VISIBLE); + } component.recoverySticky(); + changeFrontStickyVisible(); + } })); if (component.getDomObject().getEvents().contains("unsticky")) { @@ -141,6 +156,7 @@ public class StickyHeaderHelper { View view = mHeaderViews.get(cell.getRef()); if(view != null){ view.bringToFront(); + changeFrontStickyVisible(); } } } @@ -148,4 +164,27 @@ public class StickyHeaderHelper { notifyStickyRemove(cell); } } + + + private void changeFrontStickyVisible(){ + if(mHeaderViews.size() <= 0){ + return; + } + boolean fontVisible = false; + for(int i=mParent.getChildCount()-1; i>=0; i--){ + View view = mParent.getChildAt(i); + if(fontVisible && view.getTag() instanceof StickyHeaderHelper){ + if(view.getVisibility() != View.GONE){ + view.setVisibility(View.GONE); + } + }else{ + if(view.getTag() instanceof StickyHeaderHelper){ + fontVisible = true; + if(view != null && view.getVisibility() != View.VISIBLE){ + view.setVisibility(View.VISIBLE); + } + } + } + } + } } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/cb1df915/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java index c329e99..ebf81b4 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/WXCell.java @@ -37,6 +37,9 @@ import com.taobao.weex.ui.flat.WidgetContainer; import com.taobao.weex.ui.view.WXFrameLayout; import com.taobao.weex.utils.WXLogUtils; import com.taobao.weex.utils.WXUtils; +import com.taobao.weex.utils.WXViewUtils; + +import static com.taobao.weex.common.Constants.Name.STICKY_OFFSET; /** * Root component for components in {@link WXListComponent} @@ -55,17 +58,14 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { private int mScrollPositon = -1; private boolean mFlatUIEnabled = false; + private Object renderData; private boolean isSourceUsed = false; private boolean hasLayout = false; - private Object renderData; - private boolean isSourceUsed = false; - - private boolean hasLayout = false; @Deprecated @@ -149,28 +149,41 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { } public void removeSticky() { - mHeadView = getHostView().getChildAt(0); - int[] location = new int[2]; - int[] parentLocation = new int[2]; - getHostView().getLocationOnScreen(location); - getParentScroller().getView().getLocationOnScreen(parentLocation); - int headerViewOffsetX = location[0] - parentLocation[0]; - int headerViewOffsetY = getParent().getHostView().getTop(); - getHostView().removeView(mHeadView); - mRealView = (ViewGroup) mHeadView; - mTempStickyView = new FrameLayout(getContext()); - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) getDomObject().getLayoutWidth(), - (int) getDomObject().getLayoutHeight()); - getHostView().addView(mTempStickyView, lp); - mHeadView.setTranslationX(headerViewOffsetX); - mHeadView.setTranslationY(headerViewOffsetY); + if(getHostView().getChildCount() > 0) { + mHeadView = getHostView().getChildAt(0); + int[] location = new int[2]; + int[] parentLocation = new int[2]; + getHostView().getLocationOnScreen(location); + getParentScroller().getView().getLocationOnScreen(parentLocation); + int headerViewOffsetX = location[0] - parentLocation[0]; + int headerViewOffsetY = getParent().getHostView().getTop(); + getHostView().removeView(mHeadView); + mRealView = (ViewGroup) mHeadView; + mTempStickyView = new FrameLayout(getContext()); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) getDomObject().getLayoutWidth(), + (int) getDomObject().getLayoutHeight()); + getHostView().addView(mTempStickyView, lp); + mHeadView.setTranslationX(headerViewOffsetX); + mHeadView.setTranslationY(headerViewOffsetY); + } } public void recoverySticky() { - getHostView().removeView(mTempStickyView); - getHostView().addView(mHeadView); - mHeadView.setTranslationX(0); - mHeadView.setTranslationY(0); + if(mHeadView != null){ + if(mHeadView.getLayoutParams() != null){ + ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mHeadView.getLayoutParams(); + if(params.topMargin > 0){ + params.topMargin = 0; + } + } + if(mHeadView.getVisibility() != View.VISIBLE){ + mHeadView.setVisibility(View.VISIBLE); + } + getHostView().removeView(mTempStickyView); + getHostView().addView(mHeadView); + mHeadView.setTranslationX(0); + mHeadView.setTranslationY(0); + } } @Override @@ -192,28 +205,16 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { return getInstance().getFlatUIContext().isFlatUIEnabled(this) && WXCell.class.equals(getClass()) && !isSticky(); } - public Object getRenderData() { - return renderData; - } - - public void setRenderData(Object renderData) { - this.renderData = renderData; - } - - public boolean isSourceUsed() { - return isSourceUsed; - } - - public void setSourceUsed(boolean sourceUsed) { - isSourceUsed = sourceUsed; - } - - public boolean isHasLayout() { - return hasLayout; - } - - public void setHasLayout(boolean hasLayout) { - this.hasLayout = hasLayout; + public int getStickyOffset(){ + if(getDomObject() == null){ + return 0; + } + WXDomObject domObject = (WXDomObject) getDomObject(); + if(domObject.getAttrs().get(STICKY_OFFSET) == null){ + return 0; + } + float offset = WXUtils.getFloat(domObject.getAttrs().get(STICKY_OFFSET)); + return (int)(WXViewUtils.getRealPxByWidth(offset,domObject.getViewPortWidth())); } public Object getRenderData() { @@ -239,5 +240,4 @@ public class WXCell extends WidgetContainer<WXFrameLayout> { public void setHasLayout(boolean hasLayout) { this.hasLayout = hasLayout; } - } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/cb1df915/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 7e0746b..1918517 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 @@ -1306,7 +1306,7 @@ public class WXRecyclerTemplateList extends WXVContainer<BounceRecyclerView> imp continue; } TemplateViewHolder itemHolder = (TemplateViewHolder) recyclerView.findViewHolderForAdapterPosition(position); - if(itemHolder == null){ + if(itemHolder == null || itemHolder.getComponent() == null){ break; } List<WXComponent> childListeners = findChildListByRef(itemHolder.getComponent(), helper.getAwareChild().getRef());