From cf6e4396cbbb9aca270fd3aa31283dd0508b0ff2 Mon Sep 17 00:00:00 2001 From: maxli Date: Wed, 11 Mar 2020 23:25:12 +0800 Subject: [PATCH] feat(android): add listView horizontal support --- .../hippy/views/list/HippyListAdapter.java | 32 +++- .../mtt/hippy/views/list/HippyListView.java | 142 +++++++++++++----- .../recyclerview/LinearLayoutManager.java | 14 +- .../views/recyclerview/RecyclerAdapter.java | 2 +- .../views/recyclerview/RecyclerViewBase.java | 74 +++++---- 5 files changed, 190 insertions(+), 74 deletions(-) diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListAdapter.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListAdapter.java index a2527093911..96883726530 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListAdapter.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListAdapter.java @@ -495,6 +495,21 @@ public int getItemHeight(int index) return 0; } + @Override + public int getItemWidth(int index) + { + RenderNode listNode = mHippyContext.getRenderManager().getRenderNode(mParentRecyclerView.getId()); + if (listNode != null && listNode.getChildCount() > index && index >= 0) + { + RenderNode listItemNode = listNode.getChildAt(index); + if (listItemNode != null) + { + return listItemNode.getWidth(); + } + } + return 0; + } + @Override public int getTotalHeight() { @@ -511,15 +526,22 @@ public int getTotalHeight() { for (int i = 0; i < itemCount; i++) { - mContentHeight += getItemHeight(i); - mContentHeight += getItemMaigin(LOCATION_TOP, i); - mContentHeight += getItemMaigin(LOCATION_BOTTOM, i); - + if (mParentRecyclerView.mLayout.canScrollHorizontally()) { + mContentHeight += getItemWidth(i); + mContentHeight += getItemMaigin(LOCATION_LEFT, i); + mContentHeight += getItemMaigin(LOCATION_RIGHT, i); + } else { + mContentHeight += getItemHeight(i); + mContentHeight += getItemMaigin(LOCATION_TOP, i); + mContentHeight += getItemMaigin(LOCATION_BOTTOM, i); + } } } } - return mContentHeight - getCustomFooterViewHeight(); + int footerViewSize = mParentRecyclerView.mLayout.canScrollHorizontally() ? + getCustomFooterViewWidth() : getCustomFooterViewHeight(); + return mContentHeight - footerViewSize; } @Override diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListView.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListView.java index 43df0a77795..4a284c83d2e 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListView.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/list/HippyListView.java @@ -229,18 +229,32 @@ public View getCustomFooterView() { public void onHeaderRefreshFinish() { if (mHeaderRefreshState == REFRESH_STATE_LOADING) { - if (mOffsetY < mState.mCustomHeaderHeight) { - smoothScrollBy(0, -mOffsetY + mState.mCustomHeaderHeight, false, true); + if (mLayout.canScrollHorizontally()) { + if (mOffsetX < mState.mCustomHeaderWidth) { + smoothScrollBy(-mOffsetX + mState.mCustomHeaderWidth, 0, false, true); + } + } else { + if (mOffsetY < mState.mCustomHeaderHeight) { + smoothScrollBy(0, -mOffsetY + mState.mCustomHeaderHeight, false, true); + } } + mHeaderRefreshState = REFRESH_STATE_IDLE; } } public void onFooterRefreshFinish() { if (mFooterRefreshState == REFRESH_STATE_LOADING) { - int contentOffsetY = getTotalHeight() - getHeight(); - if (mOffsetY > contentOffsetY) { - smoothScrollBy(0, contentOffsetY - mOffsetY, false, true); + if (mLayout.canScrollHorizontally()) { + int contentOffsetX = getTotalHeight() - getWidth(); + if (mOffsetX > contentOffsetX) { + smoothScrollBy(contentOffsetX - mOffsetX, 0, false, true); + } + } else { + int contentOffsetY = getTotalHeight() - getHeight(); + if (mOffsetY > contentOffsetY) { + smoothScrollBy(0, contentOffsetY - mOffsetY, false, true); + } } mFooterRefreshState = REFRESH_STATE_IDLE; } @@ -248,7 +262,11 @@ public void onFooterRefreshFinish() { public void onHeaderRefresh() { if (mHeaderRefreshState == REFRESH_STATE_IDLE) { - smoothScrollBy(0, -mOffsetY, false, true); + if (mLayout.canScrollHorizontally()) { + smoothScrollBy(-mOffsetX, 0, false, true); + } else { + smoothScrollBy(0, -mOffsetY, false, true); + } } } @@ -257,15 +275,84 @@ protected void onTouchMove(int x, int y) { HippyMap param = new HippyMap(); float contentOffset = 0; - if (getOffsetY() < mState.mCustomHeaderHeight) { - contentOffset = Math.abs((getOffsetY() - mState.mCustomHeaderHeight)); - param.pushDouble("contentOffset", PixelUtil.px2dp(contentOffset)); - sendPullHeaderEvent(EVENT_TYPE_HEADER_PULLING, param); - } else if (getOffsetY() > totalHeight - getHeight()) { - contentOffset = Math.abs((getOffsetY() - totalHeight - getHeight())); - param.pushDouble("contentOffset", PixelUtil.px2dp(contentOffset)); - sendPullHeaderEvent(EVENT_TYPE_FOOTER_PULLING, param); + if (mLayout.canScrollHorizontally()) { + if (mOffsetX < mState.mCustomHeaderWidth) { + contentOffset = Math.abs((mOffsetX - mState.mCustomHeaderWidth)); + } else if (mOffsetX > totalHeight - getWidth()) { + contentOffset = Math.abs((mOffsetX - totalHeight - getWidth())); + } + } else { + if (getOffsetY() < mState.mCustomHeaderHeight) { + contentOffset = Math.abs((getOffsetY() - mState.mCustomHeaderHeight)); + } else if (getOffsetY() > totalHeight - getHeight()) { + contentOffset = Math.abs((getOffsetY() - totalHeight - getHeight())); + } } + + param.pushDouble("contentOffset", PixelUtil.px2dp(contentOffset)); + sendPullHeaderEvent(EVENT_TYPE_FOOTER_PULLING, param); + } + + private boolean shouldStopReleaseGlowsForHorizontal() { + int totalHeight = mAdapter.getTotalHeight(); + if (mOffsetX <= 0 || getWidth() > (totalHeight - mState.mCustomHeaderWidth)) { + if (mHeaderRefreshState == REFRESH_STATE_IDLE) { + sendPullHeaderEvent(EVENT_TYPE_HEADER_RELEASED, new HippyMap()); + mHeaderRefreshState = REFRESH_STATE_LOADING; + } + smoothScrollBy(-mOffsetX, 0, false, true); + return true; + } else { + int refreshEnableOffsetX = totalHeight - getWidth() + mState.mCustomFooterWidth; + if ((totalHeight - mState.mCustomHeaderWidth) < getWidth() || mOffsetX >= refreshEnableOffsetX) { + if (mFooterRefreshState == REFRESH_STATE_IDLE) { + sendPullFooterEvent(EVENT_TYPE_FOOTER_RELEASED, new HippyMap()); + mFooterRefreshState = REFRESH_STATE_LOADING; + } + + View footerView = getCustomFooterView(); + if (footerView != null && footerView instanceof HippyPullFooterView) { + boolean stickEnabled = ((HippyPullFooterView) footerView).getStickEnabled(); + if (stickEnabled) { + smoothScrollBy(refreshEnableOffsetX - mOffsetX, 0, false, true); + return true; + } + } + } + } + + return false; + } + + private boolean shouldStopReleaseGlowsForVertical() { + int totalHeight = mAdapter.getTotalHeight(); + if (getOffsetY() <= 0 || getHeight() > (totalHeight - mState.mCustomHeaderHeight)) { + if (mHeaderRefreshState == REFRESH_STATE_IDLE) { + sendPullHeaderEvent(EVENT_TYPE_HEADER_RELEASED, new HippyMap()); + mHeaderRefreshState = REFRESH_STATE_LOADING; + } + smoothScrollBy(0, -mOffsetY, false, true); + return true; + } else { + int refreshEnableOffsetY = totalHeight - getHeight() + mState.mCustomFooterHeight; + if ((totalHeight - mState.mCustomHeaderHeight) < getHeight() || getOffsetY() >= refreshEnableOffsetY) { + if (mFooterRefreshState == REFRESH_STATE_IDLE) { + sendPullFooterEvent(EVENT_TYPE_FOOTER_RELEASED, new HippyMap()); + mFooterRefreshState = REFRESH_STATE_LOADING; + } + + View footerView = getCustomFooterView(); + if (footerView != null && footerView instanceof HippyPullFooterView) { + boolean stickEnabled = ((HippyPullFooterView)footerView).getStickEnabled(); + if (stickEnabled) { + smoothScrollBy(0, refreshEnableOffsetY - mOffsetY, false, true); + return true; + } + } + } + } + + return false; } @Override @@ -281,31 +368,10 @@ protected boolean shouldStopReleaseGlows(boolean canGoRefresh, boolean fromTouch return false; } - int totalHeight = mAdapter.getTotalHeight(); - if (getOffsetY() <= 0 || getHeight() > (totalHeight - mState.mCustomHeaderHeight)) { - if (mHeaderRefreshState == REFRESH_STATE_IDLE) { - sendPullHeaderEvent(EVENT_TYPE_HEADER_RELEASED, new HippyMap()); - mHeaderRefreshState = REFRESH_STATE_LOADING; - } - smoothScrollBy(0, -mOffsetY, false, true); - return true; + if (mLayout.canScrollHorizontally()) { + return shouldStopReleaseGlowsForHorizontal(); } else { - int refreshEnableOffsetY = totalHeight - getHeight() + mState.mCustomFooterHeight; - if ((totalHeight - mState.mCustomHeaderHeight) < getHeight() || getOffsetY() >= refreshEnableOffsetY) { - if (mFooterRefreshState == REFRESH_STATE_IDLE) { - sendPullFooterEvent(EVENT_TYPE_FOOTER_RELEASED, new HippyMap()); - mFooterRefreshState = REFRESH_STATE_LOADING; - } - - View footerView = getCustomFooterView(); - if (footerView != null && footerView instanceof HippyPullFooterView) { - boolean stickEnabled = ((HippyPullFooterView)footerView).getStickEnabled(); - if (stickEnabled) { - smoothScrollBy(0, refreshEnableOffsetY - mOffsetY, false, true); - return true; - } - } - } + return shouldStopReleaseGlowsForVertical(); } } diff --git a/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/LinearLayoutManager.java b/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/LinearLayoutManager.java index 1ffc2f7fa89..207082e0c78 100644 --- a/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/LinearLayoutManager.java +++ b/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/LinearLayoutManager.java @@ -6,6 +6,8 @@ import android.view.View; import static com.tencent.mtt.supportui.views.recyclerview.RecyclerViewBase.Adapter.LOCATION_BOTTOM; +import static com.tencent.mtt.supportui.views.recyclerview.RecyclerViewBase.Adapter.LOCATION_LEFT; +import static com.tencent.mtt.supportui.views.recyclerview.RecyclerViewBase.Adapter.LOCATION_RIGHT; import static com.tencent.mtt.supportui.views.recyclerview.RecyclerViewBase.Adapter.LOCATION_TOP; import static com.tencent.mtt.supportui.views.recyclerview.RecyclerViewBase.LAYOUT_TYPE_LIST; @@ -267,9 +269,15 @@ public void calculateOffsetMap(SparseIntArray offsetMap, int startOffset) for (int i = 0; i < itemCount; i++) { offsetMap.append(i, currOffset); - currOffset += ((RecyclerAdapter)mRecyclerView.getAdapter()).getItemHeight(i); - currOffset += mRecyclerView.getAdapter().getItemMaigin(LOCATION_TOP, i); - currOffset += mRecyclerView.getAdapter().getItemMaigin(LOCATION_BOTTOM, i); + if (mRecyclerView.mLayout.canScrollHorizontally()) { + currOffset += ((RecyclerAdapter)mRecyclerView.getAdapter()).getItemWidth(i); + currOffset += mRecyclerView.getAdapter().getItemMaigin(LOCATION_LEFT, i); + currOffset += mRecyclerView.getAdapter().getItemMaigin(LOCATION_RIGHT, i); + } else { + currOffset += ((RecyclerAdapter)mRecyclerView.getAdapter()).getItemHeight(i); + currOffset += mRecyclerView.getAdapter().getItemMaigin(LOCATION_TOP, i); + currOffset += mRecyclerView.getAdapter().getItemMaigin(LOCATION_BOTTOM, i); + } } } } diff --git a/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerAdapter.java b/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerAdapter.java index 24b3faa6b9d..26ac6394b9c 100644 --- a/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerAdapter.java +++ b/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerAdapter.java @@ -798,7 +798,7 @@ public void onBindViewHolder(final RecyclerView.ViewHolderWrapper holder, int po { if (mParentRecyclerView.mLayout.canScrollHorizontally()) { - params = new RecyclerViewBase.LayoutParams(getItemHeight(position), ViewGroup.LayoutParams.MATCH_PARENT); + params = new RecyclerViewBase.LayoutParams(getItemWidth(position), ViewGroup.LayoutParams.MATCH_PARENT); } else { diff --git a/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerViewBase.java b/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerViewBase.java index 346fb3292c4..9e5ccf87821 100644 --- a/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerViewBase.java +++ b/android/support_ui/src/main/java/com/tencent/mtt/supportui/views/recyclerview/RecyclerViewBase.java @@ -1383,33 +1383,48 @@ protected void onTouchMove(int x, int y) { } + protected void releaseGlowsForHorizontal() { + if (mOffsetX < mState.mCustomHeaderWidth || getWidth() > mState.mTotalHeight) { + scrollToTop(null); + } else if (mOffsetX > mState.mTotalHeight - getWidth()) { + smoothScrollBy(mState.mTotalHeight - getWidth() - mOffsetX, 0); + } + } + + protected void releaseGlowsForVertical() { + final int totalHeight = mState.mTotalHeight; + if (mOffsetY < mState.mCustomHeaderHeight || getHeight() > totalHeight) + { + scrollToTop(null); + } + else if (mOffsetY > totalHeight - getHeight()) + { + smoothScrollBy(0, totalHeight - getHeight() - mOffsetY); + } + else if (mOffsetY >= totalHeight - getHeight() && needNotifyFooter) + { + if (this.shouldPrebindItem() && mOffsetY + getHeight() != totalHeight) + { + return; + } + // Log.d("leo", "onrelease glows neednotify"); + needNotifyFooter = false; + checkNotifyFooterOnRelease = false; + mRecycler.notifyLastFooterAppeared(); + } + } + protected void releaseGlows(boolean canGoRefresh, boolean fromTouch) { - final int totalHeight = mState.mTotalHeight; if (shouldStopReleaseGlows(canGoRefresh, fromTouch)) { return; } - if (mOffsetY < mState.mCustomHeaderHeight || getHeight() > totalHeight) - { - scrollToTop(null); - } - else if (mOffsetY > totalHeight - getHeight()) - { - smoothScrollBy(0, totalHeight - getHeight() - mOffsetY); - } - else if (mOffsetY >= totalHeight - getHeight() && needNotifyFooter) - { - if (this.shouldPrebindItem() && mOffsetY + getHeight() != totalHeight) - { - return; - } - // Log.d("leo", "onrelease glows neednotify"); - needNotifyFooter = false; - checkNotifyFooterOnRelease = false; - mRecycler.notifyLastFooterAppeared(); - } - + if (mLayout.canScrollHorizontally()) { + releaseGlowsForHorizontal(); + } else { + releaseGlowsForVertical(); + } } // /*private*/ void scrollToInSpringBack(int x, int y) @@ -3234,7 +3249,8 @@ public void onChanged() if (pendingOffset == BaseLayoutManager.INVALID_OFFSET) { pendingOffset = mLayout.getDecoratedStart(first); - pendingOffset = pendingOffset + mState.mCustomHeaderHeight; + pendingOffset = mLayout.canScrollHorizontally() ? + pendingOffset + mState.mCustomHeaderWidth : pendingOffset + mState.mCustomHeaderHeight; } } @@ -8124,11 +8140,15 @@ public void run() public void scrollToTop(OnScrollFinishListener listener) { - { - smoothScrollBy(0, -mOffsetY + mState.mCustomHeaderHeight, false, true); - } - mViewFlinger.mScrollFinishListener = listener; - mViewFlinger.mTargetPosition = -mOffsetY + mState.mCustomHeaderHeight; + if (mLayout.canScrollHorizontally()) { + smoothScrollBy(-mOffsetX + mState.mCustomHeaderWidth, 0, false, true); + mViewFlinger.mScrollFinishListener = listener; + mViewFlinger.mTargetPosition = -mOffsetX + mState.mCustomHeaderWidth; + } else { + smoothScrollBy(0, -mOffsetY + mState.mCustomHeaderHeight, false, true); + mViewFlinger.mScrollFinishListener = listener; + mViewFlinger.mTargetPosition = -mOffsetY + mState.mCustomHeaderHeight; + } } public void scrollToTopAtOnce()