From dbc60c4d673e31dfe2f0962ffdeef2fa31f510a4 Mon Sep 17 00:00:00 2001 From: maxli Date: Wed, 4 Mar 2020 10:38:32 +0800 Subject: [PATCH] feat(android): add tdk feature step 1 --- android/sdk/proguard-rules.pro | 1 + .../com/tencent/mtt/hippy/HippyEngine.java | 12 +- .../mtt/hippy/HippyEngineManagerImpl.java | 1 + .../mtt/hippy/bridge/HippyBridgeManager.java | 3 + .../hippy/bridge/HippyBridgeManagerImpl.java | 74 ++++--- .../mtt/hippy/bridge/HippyCoreAPI.java | 6 + .../nativemodules/network/NetworkModule.java | 5 + .../mtt/hippy/views/image/HippyImageView.java | 19 +- .../scroll/HippyHorizontalScrollView.java | 23 ++- .../hippy/views/scroll/HippyScrollView.java | 1 + .../scroll/HippyScrollViewController.java | 6 + .../scroll/HippyScrollViewEventHelper.java | 2 +- .../views/scroll/HippyVerticalScrollView.java | 87 ++++++-- .../mtt/tkd/views/list/TkdListItemView.java | 40 ++++ .../views/list/TkdListItemViewController.java | 44 ++++ .../mtt/tkd/views/list/TkdListView.java | 112 +++++++++++ .../tkd/views/list/TkdListViewAdapter.java | 129 ++++++++++++ .../tkd/views/list/TkdListViewController.java | 61 ++++++ .../views/scroll/TkdHorizontalScrollView.java | 188 ++++++++++++++++++ .../mtt/tkd/views/scroll/TkdScrollView.java | 22 ++ .../views/scroll/TkdScrollViewController.java | 116 +++++++++++ .../scroll/TkdScrollViewEventHelper.java | 36 ++++ .../views/scroll/TkdVerticalScrollView.java | 140 +++++++++++++ 23 files changed, 1068 insertions(+), 60 deletions(-) create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemView.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemViewController.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListView.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewAdapter.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewController.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdHorizontalScrollView.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollView.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewController.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewEventHelper.java create mode 100644 android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdVerticalScrollView.java diff --git a/android/sdk/proguard-rules.pro b/android/sdk/proguard-rules.pro index ed89d1eba19..e6d7ce6441d 100644 --- a/android/sdk/proguard-rules.pro +++ b/android/sdk/proguard-rules.pro @@ -167,6 +167,7 @@ public void forceUpdateNode(int); -keep class * extends com.tencent.mtt.hippy.dom.node.DomNode {*;} -keep class com.tencent.mtt.hippy.views.** {*;} +-keep class com.tencent.mtt.tkd.views.** {*;} -keep interface com.tencent.mtt.hippy.bridge.HippyBridge {*;} -keep class com.tencent.mtt.supportui.views.** {*;} diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngine.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngine.java index b6c5f5a415f..ef72bf3ef00 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngine.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngine.java @@ -41,6 +41,7 @@ import com.tencent.mtt.hippy.adapter.storage.HippyStorageAdapter; import com.tencent.mtt.hippy.bridge.HippyCoreAPI; import com.tencent.mtt.hippy.bridge.bundleloader.HippyBundleLoader; +import com.tencent.mtt.hippy.common.HippyJsException; import com.tencent.mtt.hippy.common.HippyMap; import com.tencent.mtt.hippy.utils.ContextHolder; import com.tencent.mtt.hippy.utils.LogUtils; @@ -425,8 +426,11 @@ public interface EngineListener */ public void onInitialized(int statusCode, String msg); } - public interface ModuleListener - { - public void onInitialized(int statusCode, String msg,HippyRootView hippyRootView); - } + + public interface ModuleListener + { + public void onInitialized(int statusCode, String msg,HippyRootView hippyRootView); + + boolean onJsException(HippyJsException exception); + } } diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java index 62536810d5f..61dcd177268 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java @@ -884,6 +884,7 @@ public void handleException(Throwable throwable) if (throwable instanceof HippyJsException) { mGlobalConfigs.getExceptionHandler().handleJsException((HippyJsException) throwable); + mEngineContext.getBridgeManager().notifyModuleJsException((HippyJsException) throwable); } else { diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java index 39b0548481c..8c6f4136ec7 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java @@ -19,6 +19,7 @@ import com.tencent.mtt.hippy.HippyRootView; import com.tencent.mtt.hippy.bridge.bundleloader.HippyBundleLoader; import com.tencent.mtt.hippy.common.Callback; +import com.tencent.mtt.hippy.common.HippyJsException; import com.tencent.mtt.hippy.common.HippyMap; @@ -33,6 +34,8 @@ public interface HippyBridgeManager void runBundle(int id, HippyBundleLoader loader, HippyEngine.ModuleListener listener, HippyRootView hippyRootView); + void notifyModuleJsException(final HippyJsException exception); + void loadInstance(String name, int id, HippyMap params); void resumeInstance(int id); diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java index 4117a7a35ae..9230cebb362 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java @@ -30,9 +30,9 @@ import com.tencent.mtt.hippy.utils.ArgumentUtils; import com.tencent.mtt.hippy.utils.DimensionsUtil; import com.tencent.mtt.hippy.utils.GrowByteBuffer; -import com.tencent.mtt.hippy.utils.UIThreadUtils; import com.tencent.mtt.hippy.adapter.thirdparty.HippyThirdPartyAdapter; import com.tencent.mtt.hippy.utils.LogUtils; +import com.tencent.mtt.hippy.utils.UIThreadUtils; import android.os.Build; import android.os.Handler; @@ -332,35 +332,51 @@ public void runBundle(int id, HippyBundleLoader loader, HippyEngine.ModuleListen Message message = mHandler.obtainMessage(MSG_CODE_RUN_BUNDLE, 0, id, loader); mHandler.sendMessage(message); } - private void notifyModuleLoaded(final int statusCode, final String msg,final HippyRootView hippyRootView) - { - if (mLoadModuleListener != null) - { - if (UIThreadUtils.isOnUiThread()) - { - if(mLoadModuleListener != null) - { - mLoadModuleListener.onInitialized(statusCode, msg,hippyRootView); - mLoadModuleListener = null; - } - } - else - { - UIThreadUtils.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if(mLoadModuleListener != null) { - mLoadModuleListener.onInitialized(statusCode, msg, hippyRootView); - mLoadModuleListener = null; - } - } - }); - } - } - } + public void notifyModuleJsException(final HippyJsException exception) + { + if (UIThreadUtils.isOnUiThread()) { + if(mLoadModuleListener != null && mLoadModuleListener.onJsException(exception)) { + mLoadModuleListener = null; + } + } + else + { + UIThreadUtils.runOnUiThread(new Runnable() + { + @Override + public void run() + { + if(mLoadModuleListener != null && mLoadModuleListener.onJsException(exception)) { + mLoadModuleListener = null; + } + } + }); + } + } + + private void notifyModuleLoaded(final int statusCode, final String msg,final HippyRootView hippyRootView) + { + if (UIThreadUtils.isOnUiThread()) { + if(mLoadModuleListener != null) { + mLoadModuleListener.onInitialized(statusCode, msg,hippyRootView); + //mLoadModuleListener = null; + } + } + else + { + UIThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + if(mLoadModuleListener != null) { + mLoadModuleListener.onInitialized(statusCode, msg, hippyRootView); + //mLoadModuleListener = null; + } + } + }); + } + } + @Override public void loadInstance(String name, int id, HippyMap params) { diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyCoreAPI.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyCoreAPI.java index 153fefaeb48..d29436a575a 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyCoreAPI.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/bridge/HippyCoreAPI.java @@ -54,6 +54,9 @@ import com.tencent.mtt.hippy.views.viewpager.HippyViewPagerController; import com.tencent.mtt.hippy.views.viewpager.HippyViewPagerItemController; import com.tencent.mtt.hippy.views.webview.HippyWebViewController; +import com.tencent.mtt.tkd.views.scroll.TkdScrollViewController; +import com.tencent.mtt.tkd.views.list.TkdListItemViewController; +import com.tencent.mtt.tkd.views.list.TkdListViewController; import java.util.ArrayList; import java.util.HashMap; @@ -224,6 +227,9 @@ public List> getControllers() components.add(HippyWebViewController.class); components.add(AudioViewController.class); components.add(VideoHippyViewController.class); + components.add(TkdScrollViewController.class); + components.add(TkdListItemViewController.class); + components.add(TkdListViewController.class); return components; } } diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java index d74e0eb3094..a33a0855e18 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java @@ -140,10 +140,15 @@ public void fetch(final HippyMap request, final Promise promise) HippyMap headers = request.getMap("headers"); if (headers != null) { + HippyArray requestCookies = headers.getArray("Cookie"); + saveCookie2Manager(url, requestCookies); hippyMapToRequestHeaders(httpRequest, headers); } String body = request.getString("body"); httpRequest.setBody(body); + String cookie = getCookieManager().getCookie(url); + if (!TextUtils.isEmpty(cookie)) + httpRequest.addHeader(HttpHeader.REQ.COOKIE, cookie); HippyGlobalConfigs configs = mContext.getGlobalConfigs(); HippyHttpAdapter adapter = null; diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/image/HippyImageView.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/image/HippyImageView.java index bfe9212fa07..833fd4b292d 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/image/HippyImageView.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/image/HippyImageView.java @@ -16,6 +16,7 @@ package com.tencent.mtt.hippy.views.image; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Movie; @@ -30,6 +31,7 @@ import com.tencent.mtt.hippy.HippyInstanceContext; import com.tencent.mtt.hippy.adapter.image.HippyDrawable; import com.tencent.mtt.hippy.adapter.image.HippyImageLoader; +import com.tencent.mtt.hippy.common.HippyMap; import com.tencent.mtt.hippy.uimanager.HippyViewBase; import com.tencent.mtt.hippy.uimanager.HippyViewController; import com.tencent.mtt.hippy.uimanager.HippyViewEvent; @@ -358,7 +360,18 @@ protected void handleGetImageSuccess() // send onLoadEnd event if (mShouldSendImageEvent[ImageEvent.ONLOAD_END.ordinal()]) { - getOnLoadEndEvent().send(this, null); + HippyMap map = new HippyMap(); + map.pushInt("success", 1); + if (mSourceDrawable != null) { + Bitmap bitmap = mSourceDrawable.getBitmap(); + if (bitmap != null) { + HippyMap imageSize = new HippyMap(); + imageSize.pushInt("width", bitmap.getWidth()); + imageSize.pushInt("height", bitmap.getHeight()); + map.pushMap("image", imageSize); + } + } + getOnLoadEndEvent().send(this, map); } } @@ -373,7 +386,9 @@ protected void handleGetImageFail(Throwable throwable) // send onLoadEnd event if (mShouldSendImageEvent[ImageEvent.ONLOAD_END.ordinal()]) { - getOnLoadEndEvent().send(this, null); + HippyMap map = new HippyMap(); + map.pushInt("success", 0); + getOnLoadEndEvent().send(this, map); } } diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyHorizontalScrollView.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyHorizontalScrollView.java index b101e31688a..0fb7f75461d 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyHorizontalScrollView.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyHorizontalScrollView.java @@ -58,6 +58,9 @@ public class HippyHorizontalScrollView extends HorizontalScrollView implements H protected int mScrollEventThrottle = 400; // 400ms最多回调一次 private long mLastScrollEventTimeStamp = -1; + protected int mScrollMinOffset = 0; + private int mLastX = 0; + public HippyHorizontalScrollView(Context context) { super(context); @@ -201,12 +204,15 @@ protected void onScrollChanged(int x, int y, int oldX, int oldY) if (mScrollEventEnable) { long currTime = System.currentTimeMillis(); - if (currTime - mLastScrollEventTimeStamp < mScrollEventThrottle) - { - return; - } + int offsetX = Math.abs(x - mLastX); + if (mScrollMinOffset > 0 && offsetX >= mScrollMinOffset) { + mLastX = x; + } else if ((mScrollMinOffset == 0) && (currTime - mLastScrollEventTimeStamp >= mScrollEventThrottle)) { + mLastScrollEventTimeStamp = currTime; + } else { + return; + } - mLastScrollEventTimeStamp = currTime; HippyScrollViewEventHelper.emitScrollEvent(this); } mDoneFlinging = false; @@ -376,4 +382,11 @@ public boolean horizontalCanScroll(int i) { return true; } + + @Override + public void setScrollMinOffset(int scrollMinOffset) + { + scrollMinOffset = Math.max(5, scrollMinOffset); + mScrollMinOffset = (int)PixelUtil.dp2px(scrollMinOffset); + } } diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollView.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollView.java index 0138ff479c1..cf6b3c23fc7 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollView.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollView.java @@ -36,4 +36,5 @@ public interface HippyScrollView void callSmoothScrollTo(int x,int y,int duration); + void setScrollMinOffset(int scrollMinOffset); } diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewController.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewController.java index c9411543277..7224f0bb200 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewController.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewController.java @@ -121,6 +121,12 @@ public void setScrollEventThrottle(HippyScrollView view, int scrollEventThrottle view.setScrollEventThrottle(scrollEventThrottle); } + @HippyControllerProps(name = "scrollMinOffset", defaultType = HippyControllerProps.NUMBER, defaultNumber = 5) + public void setScrollMinOffset(HippyScrollView view, int scrollMinOffset) + { + view.setScrollMinOffset(scrollMinOffset); + } + @Override public void dispatchFunction(View view, String functionName, HippyArray args) { super.dispatchFunction(view, functionName, args); diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewEventHelper.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewEventHelper.java index c0bca3bf649..3b7103ea3a6 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewEventHelper.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyScrollViewEventHelper.java @@ -63,7 +63,7 @@ public static void emitScrollAnimationEndEvent(ViewGroup view) emitScrollEvent(view, EVENT_TYPE_ANIMATION_END); } - private static void emitScrollEvent(ViewGroup view, String scrollEventType) + protected static void emitScrollEvent(ViewGroup view, String scrollEventType) { if(view == null) { diff --git a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyVerticalScrollView.java b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyVerticalScrollView.java index e46b7f61a54..e761c94536b 100644 --- a/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyVerticalScrollView.java +++ b/android/sdk/src/main/java/com/tencent/mtt/hippy/views/scroll/HippyVerticalScrollView.java @@ -52,9 +52,14 @@ public class HippyVerticalScrollView extends ScrollView implements HippyViewBase private boolean mFlingEnabled = true; + private boolean mPagingEnabled = false; + protected int mScrollEventThrottle = 400; // 400ms最多回调一次 private long mLastScrollEventTimeStamp = -1; + protected int mScrollMinOffset = 0; + private int mLastY = 0; + public HippyVerticalScrollView(Context context) { super(context); @@ -194,12 +199,15 @@ protected void onScrollChanged(int x, int y, int oldX, int oldY) if (mScrollEventEnable) { long currTime = System.currentTimeMillis(); - if (currTime - mLastScrollEventTimeStamp < mScrollEventThrottle) - { + int offsetY = Math.abs(y - mLastY); + if (mScrollMinOffset > 0 && offsetY >= mScrollMinOffset) { + mLastY = y; + } else if ((mScrollMinOffset == 0) && (currTime - mLastScrollEventTimeStamp >= mScrollEventThrottle)) { + mLastScrollEventTimeStamp = currTime; + } else { return; } - mLastScrollEventTimeStamp = currTime; HippyScrollViewEventHelper.emitScrollEvent(this); } @@ -216,32 +224,48 @@ public void fling(int velocityY) return; } - super.fling(velocityY); + if (mPagingEnabled) + { + smoothScrollToPage(velocityY); + } + else + { + super.fling(velocityY); + } + if (mMomentumScrollBeginEventEnable) { HippyScrollViewEventHelper.emitScrollMomentumBeginEvent(this); } Runnable runnable = new Runnable() { + private boolean mSnappingToPage = false; @Override - public void run() - { - if (mDoneFlinging) - { - if (mMomentumScrollEndEventEnable) - { - HippyScrollViewEventHelper.emitScrollMomentumEndEvent(HippyVerticalScrollView.this); + public void run() { + if (mDoneFlinging) { + boolean doneWithAllScrolling = true; + if (mPagingEnabled && !mSnappingToPage) { + mSnappingToPage = true; + smoothScrollToPage(0); + doneWithAllScrolling = false; } - } - else - { + + if (doneWithAllScrolling) { + if (mMomentumScrollEndEventEnable) { + HippyScrollViewEventHelper.emitScrollMomentumEndEvent(HippyVerticalScrollView.this); + } + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + postOnAnimationDelayed(this, HippyScrollViewEventHelper.MOMENTUM_DELAY); + } else { + HippyVerticalScrollView.this.getHandler().postDelayed(this, 16 + HippyScrollViewEventHelper.MOMENTUM_DELAY); + } + } + } else { mDoneFlinging = true; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { postOnAnimationDelayed(this, HippyScrollViewEventHelper.MOMENTUM_DELAY); - } - else - { + } else { HippyVerticalScrollView.this.getHandler().postDelayed(this, HippyScrollViewEventHelper.MOMENTUM_DELAY); } } @@ -257,6 +281,24 @@ public void run() } } + private void smoothScrollToPage(int velocity) + { + int height = getHeight(); + int currentY = getScrollY(); + int predictedY = currentY + velocity; + int page = 0; + if (height != 0) + { + page = currentY / height; + } + + if (predictedY > page * height + height / 2) + { + page = page + 1; + } + smoothScrollTo(getScrollX(), page * height); + } + @Override public void computeScroll() { @@ -306,6 +348,13 @@ public void setContentOffset4Reuse(HippyMap offsetMap) { @Override public void setPagingEnabled(boolean pagingEnabled) { + mPagingEnabled = pagingEnabled; + } + @Override + public void setScrollMinOffset(int scrollMinOffset) + { + scrollMinOffset = Math.max(5, scrollMinOffset); + mScrollMinOffset = (int)PixelUtil.dp2px(scrollMinOffset); } } diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemView.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemView.java new file mode 100644 index 00000000000..a9f78ae8e6d --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemView.java @@ -0,0 +1,40 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed 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.tencent.mtt.tkd.views.list; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.view.View; +import android.view.ViewGroup; + +import com.tencent.mtt.hippy.HippyRootView; +import com.tencent.mtt.hippy.utils.PixelUtil; +import com.tencent.mtt.hippy.views.list.HippyListItemView; +import com.tencent.mtt.hippy.views.view.HippyViewGroup; + +/** + * Created by leonardgong on 2017/12/7 0007. + */ + +public class TkdListItemView extends HippyListItemView +{ + public TkdListItemView(Context context) + { + super(context); + } +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemViewController.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemViewController.java new file mode 100644 index 00000000000..e34e6828c8d --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListItemViewController.java @@ -0,0 +1,44 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed 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.tencent.mtt.tkd.views.list; + +import android.content.Context; +import android.view.View; + +import com.tencent.mtt.hippy.HippyRootView; +import com.tencent.mtt.hippy.annotation.HippyController; +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.uimanager.ControllerManager; +import com.tencent.mtt.hippy.uimanager.HippyViewController; +import com.tencent.mtt.hippy.uimanager.ListItemRenderNode; +import com.tencent.mtt.hippy.uimanager.RenderNode; +import com.tencent.mtt.hippy.views.list.HippyListItemViewController; + +/** + * Created by leonardgong on 2017/12/7 0007. + */ + +@HippyController(name = TkdListItemViewController.CLASS_NAME, isLazyLoad = true) +public class TkdListItemViewController extends HippyListItemViewController +{ + public static final String CLASS_NAME = "tkdCell"; + + @Override + protected View createViewImpl(Context context) + { + return new TkdListItemView(context); + } +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListView.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListView.java new file mode 100644 index 00000000000..2246aecd615 --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListView.java @@ -0,0 +1,112 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed 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.tencent.mtt.tkd.views.list; + +import com.tencent.mtt.hippy.HippyEngineContext; +import com.tencent.mtt.hippy.HippyInstanceContext; +import com.tencent.mtt.hippy.common.HippyArray; +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.uimanager.HippyViewBase; +import com.tencent.mtt.hippy.uimanager.HippyViewEvent; +import com.tencent.mtt.hippy.uimanager.NativeGestureDispatcher; +import com.tencent.mtt.hippy.utils.LogUtils; +import com.tencent.mtt.hippy.utils.PixelUtil; +import com.tencent.mtt.hippy.views.list.HippyListAdapter; +import com.tencent.mtt.hippy.views.list.HippyListView; +import com.tencent.mtt.hippy.views.scroll.HippyScrollViewEventHelper; +import com.tencent.mtt.supportui.views.recyclerview.LinearLayoutManager; +import com.tencent.mtt.supportui.views.recyclerview.RecyclerView; +import android.content.Context; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ViewTreeObserver; + +/** + * Created by leonardgong on 2017/12/7 0007. + */ + +public class TkdListView extends HippyListView +{ + public TkdListView(Context context) + { + super(context); + } + + protected HippyListAdapter createAdapter(RecyclerView hippyRecyclerView, HippyEngineContext hippyEngineContext) + { + return new TkdListViewAdapter(hippyRecyclerView, hippyEngineContext); + } + + @Override + public void onScrollStateChanged(int oldState, int newState) + { + Log.e("maxli", "onScrollStateChanged: oldState=" + oldState + ", newState=" + newState); + super.onScrollStateChanged(oldState, newState); + checkExposureForReport(oldState, newState); + } + + public void checkExposureForReport(int oldState, int newState) + { + if (getAdapter() != null) { + ((TkdListViewAdapter)getAdapter()).checkExposureForReport(oldState, newState); + } + } + + public void setEnableExposureReport(boolean enableExposureReport) + { + if (getAdapter() != null) { + ((TkdListViewAdapter)getAdapter()).setEnableExposureReport(enableExposureReport); + } + } + + @Override + public void scrollToTopAtOnce() + { + super.scrollToTopAtOnce(); + checkExposureForReport(SCROLL_STATE_SETTLING, SCROLL_STATE_IDLE); + } + + @Override + public void scrollToPosition(int position) + { + super.scrollToPosition(position); + checkExposureForReport(SCROLL_STATE_SETTLING, SCROLL_STATE_IDLE); + } + + public static class ExposureForReport extends HippyViewEvent + { + public int mStartEdgePos = 0; + public int mEndEdgePos = 0; + public int mFirstVisibleRowIndex = 0; + public int mLastVisibleRowIndex = 0; + public int mVelocity = 0; + public int mScrollState = 0; + public HippyArray mVisibleRowFrames = null; + + public ExposureForReport(int tag, int startEdgePos, int endEdgePos, int firstVisiblePos, int lastVisiblePos, int velocity, int scrollState, + HippyArray visibleItemArray) + { + super("onExposureReport"); + mStartEdgePos = startEdgePos; + mEndEdgePos = endEdgePos; + mFirstVisibleRowIndex = firstVisiblePos; + mLastVisibleRowIndex = lastVisiblePos; + mVelocity = velocity; + mScrollState = scrollState; + mVisibleRowFrames = visibleItemArray; + } + } +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewAdapter.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewAdapter.java new file mode 100644 index 00000000000..64d90bbcb37 --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewAdapter.java @@ -0,0 +1,129 @@ +package com.tencent.mtt.tkd.views.list; + +import android.util.Log; +import android.view.View; + +import com.tencent.mtt.hippy.HippyEngineContext; +import com.tencent.mtt.hippy.common.HippyArray; +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.uimanager.HippyViewEvent; +import com.tencent.mtt.hippy.uimanager.RenderNode; +import com.tencent.mtt.hippy.utils.PixelUtil; +import com.tencent.mtt.hippy.utils.UrlUtils; +import com.tencent.mtt.hippy.views.image.HippyImageViewController; +import com.tencent.mtt.hippy.views.list.HippyListAdapter; +import com.tencent.mtt.supportui.views.recyclerview.ContentHolder; +import com.tencent.mtt.supportui.views.recyclerview.IRecyclerViewFooter; +import com.tencent.mtt.supportui.views.recyclerview.LinearLayoutManager; +import com.tencent.mtt.supportui.views.recyclerview.RecyclerView; +import com.tencent.mtt.supportui.views.recyclerview.RecyclerViewBase; + +/** + * Created by leonardgong on 2017/12/15 0015. + */ + +public class TkdListViewAdapter extends HippyListAdapter +{ + private boolean mEnableScrollForReport; + private boolean mEnableExposureReport = true; + private HippyMap mExposureReportResultMap; + int mScrollEventThrottle = 200; + int mScrollForReportThrottle = 200; + + public TkdListViewAdapter(RecyclerView recyclerView, HippyEngineContext HippyContext) + { + super(recyclerView, HippyContext); + } + + protected void setEnableScrollForReport(boolean enableScrollForReport) + { + mEnableScrollForReport = enableScrollForReport; + } + + protected void setEnableExposureReport(boolean enableExposureReport) + { + mEnableExposureReport = enableExposureReport; + } + + public void setScrollEventThrottle(int scrollEventThrottle) + { + mScrollEventThrottle = scrollEventThrottle; + } + + public void setScrollForReportThrottle(int scrollForReportThrottle) + { + mScrollForReportThrottle = scrollForReportThrottle; + } + + // 检查是否通知前端标准曝光数据 + protected void checkExposureForReport(int oldState, int newState) + { + if (!mEnableExposureReport) + return; + + TkdListView.ExposureForReport exposureForReport = getExposureForReportInner(oldState, newState); + if (exposureForReport == null) + { + return; + } + if (checkNeedToReport(exposureForReport.mVelocity, newState)) + { + + if (mExposureReportResultMap == null) + { + mExposureReportResultMap = new HippyMap(); + } + mExposureReportResultMap.clear(); + mExposureReportResultMap.pushInt("startEdgePos", exposureForReport.mStartEdgePos); + mExposureReportResultMap.pushInt("endEdgePos", exposureForReport.mEndEdgePos); + mExposureReportResultMap.pushInt("firstVisibleRowIndex", exposureForReport.mFirstVisibleRowIndex); + mExposureReportResultMap.pushInt("lastVisibleRowIndex", exposureForReport.mLastVisibleRowIndex); + // reportData.putInt("velocity", mVelocity); + mExposureReportResultMap.pushInt("scrollState", exposureForReport.mScrollState); + mExposureReportResultMap.pushArray("visibleRowFrames", exposureForReport.mVisibleRowFrames); + + exposureForReport.send(mParentRecyclerView, mExposureReportResultMap); + } + } + + protected TkdListView.ExposureForReport getExposureForReportInner(int oldState, int newState) + { + if (!mEnableExposureReport) + return null; + + int startEdgePos = (int) PixelUtil.px2dp(mParentRecyclerView.mOffsetY); + int endEdgePos = (int) PixelUtil.px2dp(mParentRecyclerView.getHeight() + mParentRecyclerView.mOffsetY); + int firstVisiblePos = ((LinearLayoutManager) mParentRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); + int lastVisiblePos = ((LinearLayoutManager) mParentRecyclerView.getLayoutManager()).findLastVisibleItemPosition(); + + // 传入包含item frames的数组 + HippyArray visibleItemArray = new HippyArray(); + int baseHeight = 0; + for (int i = 0; i < firstVisiblePos; i++) + { + baseHeight += getItemHeight(i); + baseHeight += getItemMaigin(RecyclerViewBase.Adapter.LOCATION_TOP, i); + baseHeight += getItemMaigin(RecyclerViewBase.Adapter.LOCATION_BOTTOM, i); + } + for (int i = firstVisiblePos; i <= lastVisiblePos; i++) + { + HippyMap itemData = new HippyMap(); + itemData.pushInt("x", 0); + itemData.pushInt("y", (int) PixelUtil.px2dp(baseHeight)); + baseHeight += getItemHeight(i); + itemData.pushInt("width", (int) PixelUtil.px2dp(getItemWidth(i))); + itemData.pushInt("height", (int) PixelUtil.px2dp(getItemHeight(i))); + + visibleItemArray.pushMap(itemData); + } + + float currentVelocity = Math.abs(mParentRecyclerView.mViewFlinger.getScroller().getCurrVelocity()); + return new TkdListView.ExposureForReport(mParentRecyclerView.getId(), startEdgePos, endEdgePos, firstVisiblePos, lastVisiblePos, + (int) currentVelocity, newState, visibleItemArray); + } + + protected boolean checkNeedToReport(float velocity, int scrollState) + { + return true; + } +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewController.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewController.java new file mode 100644 index 00000000000..9b7b1fb316f --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/list/TkdListViewController.java @@ -0,0 +1,61 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed 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.tencent.mtt.tkd.views.list; + +import com.tencent.mtt.hippy.HippyRootView; +import com.tencent.mtt.hippy.annotation.HippyController; +import com.tencent.mtt.hippy.annotation.HippyControllerProps; +import com.tencent.mtt.hippy.common.HippyArray; +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.uimanager.ControllerManager; +import com.tencent.mtt.hippy.uimanager.HippyViewController; +import com.tencent.mtt.hippy.uimanager.ListViewRenderNode; +import com.tencent.mtt.hippy.uimanager.RenderNode; +import com.tencent.mtt.hippy.views.list.HippyListView; +import com.tencent.mtt.hippy.views.list.HippyListViewController; +import com.tencent.mtt.tkd.views.list.TkdListView; +import com.tencent.mtt.supportui.views.recyclerview.RecyclerViewBase; +import com.tencent.mtt.supportui.views.recyclerview.RecyclerViewItem; + +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by leonardgong on 2017/12/7 0007. + */ + +@HippyController(name = TkdListViewController.CLASS_NAME) +public class TkdListViewController extends HippyListViewController +{ + public static final String CLASS_NAME = "tkdListView"; + + protected View createViewImpl(Context context) + { + return new TkdListView(context); + } + + @HippyControllerProps(name = "enableExposureReport") + public void setOnExposureReport(HippyListView hippyListView, boolean enable) + { + if (hippyListView instanceof TkdListView) + { + TkdListView listView = (TkdListView) hippyListView; + listView.setEnableExposureReport(enable); + } + } +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdHorizontalScrollView.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdHorizontalScrollView.java new file mode 100644 index 00000000000..50dec5f0d1a --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdHorizontalScrollView.java @@ -0,0 +1,188 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed 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.tencent.mtt.tkd.views.scroll; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.os.Build; +import android.util.Log; +import android.view.MotionEvent; +import android.widget.HorizontalScrollView; +import com.tencent.mtt.hippy.views.scroll.HippyHorizontalScrollView; +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.modules.Promise; +import com.tencent.mtt.hippy.uimanager.HippyViewBase; +import com.tencent.mtt.hippy.uimanager.NativeGestureDispatcher; +import com.tencent.mtt.hippy.utils.LogUtils; +import com.tencent.mtt.hippy.utils.PixelUtil; +import com.tencent.mtt.supportui.views.ScrollChecker; + +public class TkdHorizontalScrollView extends HippyHorizontalScrollView implements TkdScrollView +{ + private int mPreloadDistance = 0; + private boolean mIsLoading = false; + private float mLastScrollX = 0; + + public TkdHorizontalScrollView(Context context) + { + super(context); + mScrollMinOffset = (int)PixelUtil.dp2px(5); + mPreloadDistance = (int)PixelUtil.dp2px(200); + } + + @Override + public void setPreloadDistance(int preloadDistance) + { + preloadDistance = Math.max(0, preloadDistance); + mPreloadDistance = (int)PixelUtil.dp2px(preloadDistance); + } + + @Override + public void callLoadMoreFinish() { + setLoadMoreState(false); + } + + private void setLoadMoreState(boolean isLoading) { + mIsLoading = isLoading; + } + + @Override + public void callScrollToTop(boolean isSmoothScroll) { + if (isSmoothScroll) { + smoothScrollTo(0, 0); + } else { + scrollTo(0, 0); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + if (action == MotionEvent.ACTION_MOVE && !mIsLoading) { + if (getScrollX() == 0) { + if (mLastScrollX == 0) { + mLastScrollX = event.getX(); + } else if (event.getX() > mLastScrollX) { + TkdScrollViewEventHelper.emitScrollStartReachedEvent(this); + setLoadMoreState(true); + mLastScrollX = 0; + } + } else if (getChildCount() > 0) { + int contentWidth = getChildAt(0).getWidth(); + if ((getScrollX() + getWidth()) >= (contentWidth - 2)) { + if (mLastScrollX == 0) { + mLastScrollX = event.getX(); + } else if (event.getX() < mLastScrollX) { + TkdScrollViewEventHelper.emitScrollEndReachedEvent(this); + setLoadMoreState(true); + mLastScrollX = 0; + } + } + } + } + + return super.onTouchEvent(event); + } + + @Override + protected void onScrollChanged(int x, int y, int oldX, int oldY) + { + super.onScrollChanged(x, y, oldX, oldY); + if (mPreloadDistance > 0 && !mIsLoading) { + if (shouldEmitEndReachedEvent(x, oldX)) { + TkdScrollViewEventHelper.emitScrollEndReachedEvent(this); + setLoadMoreState(true); + } else if (shouldEmitStartReachedEvent(x, oldX)) { + TkdScrollViewEventHelper.emitScrollStartReachedEvent(this); + setLoadMoreState(true); + } + } + } + + @Override + public void callScrollToPosition(int distance, int duration, final Promise promise) { + int offset = getScrollX() + distance; + if (duration <= 0) { + scrollTo(offset, 0); + if (promise != null) { + HippyMap resultMap = new HippyMap(); + resultMap.pushString("msg", "on scroll end!"); + promise.resolve(resultMap); + } + } else { + ObjectAnimator xTranslate = ObjectAnimator.ofInt(this, "scrollX", offset); + ObjectAnimator yTranslate = ObjectAnimator.ofInt(this, "scrollY", 0); + + AnimatorSet animators = new AnimatorSet(); + animators.setDuration(duration); + animators.playTogether(xTranslate, yTranslate); + animators.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator arg0) { + // TODO Auto-generated method stub + } + @Override + public void onAnimationRepeat(Animator arg0) { + // TODO Auto-generated method stub + } + @Override + public void onAnimationEnd(Animator arg0) { + // TODO Auto-generated method stub + if (promise != null) { + HippyMap resultMap = new HippyMap(); + resultMap.pushString("msg", "on scroll end!"); + promise.resolve(resultMap); + } + } + @Override + public void onAnimationCancel(Animator arg0) { + // TODO Auto-generated method stub + } + }); + animators.start(); + } + } + + private boolean shouldEmitStartReachedEvent(int x, int oldX) { + if (x < oldX && x < mPreloadDistance) { + return true; + } + + return false; + } + + private boolean shouldEmitEndReachedEvent(int x, int oldX) { + int contentWidth = getWidth(); + int layoutWidth = getWidth(); + + if (getChildCount() > 0) { + contentWidth = getChildAt(0).getWidth(); + } + + if (contentWidth <= layoutWidth || contentWidth < mPreloadDistance) { + return true; + } + + int offset = x + layoutWidth; + if (x > 0 && x > oldX && (offset >= (contentWidth - mPreloadDistance))) { + return true; + } + + return false; + } +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollView.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollView.java new file mode 100644 index 00000000000..32eeeeeef90 --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollView.java @@ -0,0 +1,22 @@ +package com.tencent.mtt.tkd.views.scroll; + +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.modules.Promise; +import com.tencent.mtt.hippy.views.scroll.HippyScrollView; + +/** + * @Description: TODO + * @author: edsheng + * @date: 2018/8/24 19:28 + * @version: V1.0 + */ +public interface TkdScrollView extends HippyScrollView +{ + void setPreloadDistance(int preloadDistance); + + void callLoadMoreFinish(); + + void callScrollToTop(boolean isSmoothScroll); + + void callScrollToPosition(int distance, int duration, Promise promise); +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewController.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewController.java new file mode 100644 index 00000000000..916c1feddb8 --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewController.java @@ -0,0 +1,116 @@ +package com.tencent.mtt.tkd.views.scroll; + +import android.content.Context; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import com.tencent.mtt.hippy.annotation.HippyController; +import com.tencent.mtt.hippy.annotation.HippyControllerProps; +import com.tencent.mtt.hippy.common.HippyArray; +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.modules.Promise; +import com.tencent.mtt.hippy.uimanager.HippyGroupController; +import com.tencent.mtt.hippy.utils.PixelUtil; +import com.tencent.mtt.hippy.views.scroll.HippyScrollViewController; + +/** + * @Description: TODO + * @author: edsheng + * @date: 2018/8/27 9:55 + * @version: V1.0 + */ +@HippyController(name = TkdScrollViewController.CLASS_NAME) +public class TkdScrollViewController extends HippyScrollViewController +{ + public static final String CLASS_NAME = "tkdScrollView"; + + private static final String LOAD_MORE_FINISH = "loadMoreFinish"; + + private static final String SCROLL_TO_TOP = "scrollToTop"; + + private static final String SCROLL_TO_POSITION = "scrollToPosition"; + + @Override + protected View createViewImpl(Context context, HippyMap iniProps) + { + + if (iniProps != null && iniProps.containsKey("horizontal")) + { + return new TkdHorizontalScrollView(context); + } + else + { + return new TkdVerticalScrollView(context); + } + + // return super.createViewImpl(context, iniProps); + } + + @Override + protected View createViewImpl(Context context) + { + return null; + // return new HippyScrollView(context); + } + + @Override + public void dispatchFunction(View view, String functionName, HippyArray args) { + super.dispatchFunction(view, functionName, args); + if(view instanceof TkdScrollView) + { + if (TextUtils.equals(SCROLL_TO, functionName)) + { + int destX = Math.round(PixelUtil.dp2px(args.getDouble(0))); + int destY = Math.round(PixelUtil.dp2px(args.getDouble(1))); + boolean animated = args.getBoolean(2); + + if (animated) + { + ((TkdScrollView)view).callSmoothScrollTo(destX, destY); + } + else + { + view.scrollTo(destX, destY); + } + } else if (TextUtils.equals(LOAD_MORE_FINISH, functionName)) { + ((TkdScrollView)view).callLoadMoreFinish(); + } else if (TextUtils.equals(SCROLL_TO_TOP, functionName)) { + boolean isSmoothScroll = args.getBoolean(0); + ((TkdScrollView)view).callScrollToTop(isSmoothScroll); + } + } + } + + @Override + public void dispatchFunction(View view, String functionName, HippyArray params, Promise promise) + { + super.dispatchFunction(view, functionName, params, promise); + if(view instanceof TkdScrollView) { + if (TextUtils.equals(SCROLL_TO_POSITION, functionName)) { + int distance = 0; + try { + Integer temp = Integer.parseInt(params.getString(0)); + distance = (int)PixelUtil.dp2px(temp.floatValue()); + } catch (Exception e) { + + } + + int duration = params.getInt(4); + if (distance > 0) { + ((TkdScrollView)view).callScrollToPosition(distance, duration, promise); + } else if (promise != null) { + HippyMap resultMap = new HippyMap(); + resultMap.pushString("msg", "invalid distance parameter!"); + promise.resolve(resultMap); + } + } + } + } + + @HippyControllerProps(name = "preloadDistance", defaultType = HippyControllerProps.NUMBER, defaultNumber = 200) + public void setPreloadDistance(TkdScrollView view, int preloadDistance) + { + view.setPreloadDistance(preloadDistance); + } + +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewEventHelper.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewEventHelper.java new file mode 100644 index 00000000000..e0c5b9c1e7e --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdScrollViewEventHelper.java @@ -0,0 +1,36 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed 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.tencent.mtt.tkd.views.scroll; + +import android.view.ViewGroup; + +import com.tencent.mtt.hippy.views.scroll.HippyScrollViewEventHelper; + +public class TkdScrollViewEventHelper extends HippyScrollViewEventHelper +{ + public static final String EVENT_TYPE_END_REACHED = "onEndReached"; + public static final String EVENT_TYPE_START_REACHED = "onStartReached"; + + public static void emitScrollEndReachedEvent(ViewGroup view) + { + emitScrollEvent(view, EVENT_TYPE_END_REACHED); + } + + public static void emitScrollStartReachedEvent(ViewGroup view) + { + emitScrollEvent(view, EVENT_TYPE_START_REACHED); + } +} diff --git a/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdVerticalScrollView.java b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdVerticalScrollView.java new file mode 100644 index 00000000000..ac5fb6b2a53 --- /dev/null +++ b/android/sdk/src/main/java/com/tencent/mtt/tkd/views/scroll/TkdVerticalScrollView.java @@ -0,0 +1,140 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed 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.tencent.mtt.tkd.views.scroll; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.os.Build; +import android.util.Log; +import android.view.MotionEvent; +import android.widget.ScrollView; +import com.tencent.mtt.hippy.common.HippyMap; +import com.tencent.mtt.hippy.modules.Promise; +import com.tencent.mtt.hippy.uimanager.HippyViewBase; +import com.tencent.mtt.hippy.uimanager.NativeGestureDispatcher; +import com.tencent.mtt.hippy.utils.PixelUtil; +import com.tencent.mtt.hippy.views.scroll.HippyVerticalScrollView; + +public class TkdVerticalScrollView extends HippyVerticalScrollView implements TkdScrollView +{ + private int mPreloadDistance = 0; + private boolean mIsLoading = false; + + public TkdVerticalScrollView(Context context) + { + super(context); + mScrollMinOffset = (int)PixelUtil.dp2px(5); + mPreloadDistance = (int)PixelUtil.dp2px(200); + } + + @Override + public void setPreloadDistance(int preloadDistance) + { + preloadDistance = Math.max(0, preloadDistance); + mPreloadDistance = (int)PixelUtil.dp2px(preloadDistance); + } + + @Override + public void callLoadMoreFinish() { + mIsLoading = false; + } + + @Override + public void callScrollToTop(boolean isSmoothScroll) { + if (isSmoothScroll) { + smoothScrollTo(0, 0); + } else { + scrollTo(0, 0); + } + } + + @Override + protected void onScrollChanged(int x, int y, int oldX, int oldY) + { + super.onScrollChanged(x, y, oldX, oldY); + if (mPreloadDistance > 0 && !mIsLoading && shouldEmitEndReachedEvent(y, oldY)) { + TkdScrollViewEventHelper.emitScrollEndReachedEvent(this); + mIsLoading = true; + } + } + + @Override + public void callScrollToPosition(int distance, int duration, final Promise promise) { + int offset = getScrollY() + distance; + if (duration <= 0) { + scrollTo(0, offset); + if (promise != null) { + HippyMap resultMap = new HippyMap(); + resultMap.pushString("msg", "on scroll end!"); + promise.resolve(resultMap); + } + } else { + ObjectAnimator xTranslate = ObjectAnimator.ofInt(this, "scrollX", 0); + ObjectAnimator yTranslate = ObjectAnimator.ofInt(this, "scrollY", offset); + + AnimatorSet animators = new AnimatorSet(); + animators.setDuration(duration); + animators.playTogether(xTranslate, yTranslate); + animators.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator arg0) { + // TODO Auto-generated method stub + } + @Override + public void onAnimationRepeat(Animator arg0) { + // TODO Auto-generated method stub + } + @Override + public void onAnimationEnd(Animator arg0) { + // TODO Auto-generated method stub + if (promise != null) { + HippyMap resultMap = new HippyMap(); + resultMap.pushString("msg", "on scroll end!"); + promise.resolve(resultMap); + } + } + @Override + public void onAnimationCancel(Animator arg0) { + // TODO Auto-generated method stub + } + }); + animators.start(); + } + } + + private boolean shouldEmitEndReachedEvent(int y, int oldY) { + int contentHeight = getHeight(); + int layoutHeight = getHeight(); + + if (getChildCount() > 0) { + contentHeight = getChildAt(0).getHeight(); + } + + if (contentHeight <= layoutHeight || contentHeight < mPreloadDistance) { + return true; + } + + int offset = y + layoutHeight; + if (y > 0 && y > oldY && (offset >= (contentHeight - mPreloadDistance))) { + return true; + } + + return false; + } + +}