diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/dom/node/NodeProps.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/dom/node/NodeProps.java index 0df475fae..92742b825 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/dom/node/NodeProps.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/dom/node/NodeProps.java @@ -128,7 +128,7 @@ public class NodeProps { public static final String ON_LONG_CLICK = "longclick"; public static final String ON_PRESS_IN = "pressin"; public static final String ON_PRESS_OUT = "pressout"; - public static final String ON_TOUCH_DOWN = "touchdown"; + public static final String ON_TOUCH_DOWN = "touchstart"; public static final String ON_TOUCH_MOVE = "touchmove"; public static final String ON_TOUCH_END = "touchend"; public static final String ON_TOUCH_CANCEL = "touchcancel"; @@ -171,6 +171,7 @@ public class NodeProps { public static final String VISIBLE = "visible"; public static final String REPEAT_COUNT = "repeatCount"; public static final String ATTRIBUTES = "attributes"; + public static final String BACKGROUND_RIPPLE = "nativeBackgroundAndroid"; private static final HashSet JUST_LAYOUT_PROPS = new HashSet<>( Arrays.asList(ALIGN_SELF, ALIGN_ITEMS, COLLAPSABLE, FLEX, FLEX_DIRECTION, FLEX_WRAP, diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/view/HippyViewGroupController.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/view/HippyViewGroupController.java index 46898cf32..d39050bbc 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/view/HippyViewGroupController.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/view/HippyViewGroupController.java @@ -13,11 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.tencent.mtt.hippy.views.view; +import static com.tencent.renderer.component.drawable.DrawableFactory.DrawableType.DRAWABLE_TYPE_RIPPLE; + import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.view.View; +import androidx.annotation.NonNull; + import com.tencent.mtt.hippy.annotation.HippyController; import com.tencent.mtt.hippy.annotation.HippyControllerProps; import com.tencent.mtt.hippy.dom.node.NodeProps; @@ -26,74 +33,116 @@ import com.tencent.mtt.hippy.utils.PixelUtil; import com.tencent.mtt.hippy.views.image.HippyImageView; import com.tencent.renderer.NativeRenderContext; +import com.tencent.renderer.component.drawable.DrawableFactory; +import com.tencent.renderer.utils.ArrayUtils; + +import java.util.List; +import java.util.Map; import java.util.WeakHashMap; @SuppressWarnings({"unused"}) @HippyController(name = HippyViewGroupController.CLASS_NAME) public class HippyViewGroupController extends HippyGroupController { - public static final String CLASS_NAME = "View"; + public static final String CLASS_NAME = "View"; + private static final String FUNC_SET_PRESSED = "setPressed"; + private static final String FUNC_SET_HOTSPOT = "setHotspot"; + private static final WeakHashMap mZIndexHash = new WeakHashMap<>(); - public static final WeakHashMap mZIndexHash = new WeakHashMap<>(); - - - public static void setViewZIndex(View view, int zIndex) { - mZIndexHash.put(view, zIndex); - } - - public static void removeViewZIndex(View view) { - mZIndexHash.remove(view); - } - - public static Integer getViewZIndex(View view) { - return mZIndexHash.get(view); - } - - @Override - protected View createViewImpl(Context context) { - return new HippyViewGroup(context); - } - - @HippyControllerProps(name = NodeProps.OVERFLOW, defaultType = HippyControllerProps.STRING, defaultString = "visible") - public void setOverflow(HippyViewGroup hippyViewGroup, String overflow) { - hippyViewGroup.setOverflow(overflow); - } - - @HippyControllerProps(name = NodeProps.BACKGROUND_IMAGE, defaultType = HippyControllerProps.STRING) - public void setBackgroundImage(HippyViewGroup hippyViewGroup, String url) { - hippyViewGroup.setUrl(getInnerPath((NativeRenderContext)hippyViewGroup.getContext(), url)); - } - - @HippyControllerProps(name = NodeProps.BACKGROUND_SIZE, defaultType = HippyControllerProps.STRING, defaultString = "origin") - public void setBackgroundImageSize(HippyImageView hippyImageView, String resizeModeValue) { - if ("contain".equals(resizeModeValue)) { - // 在保持图片宽高比的前提下缩放图片,直到宽度和高度都小于等于容器视图的尺寸 - // 这样图片完全被包裹在容器中,容器中可能留有空白 - hippyImageView.setScaleType(HippyImageView.ScaleType.CENTER_INSIDE); - } else if ("cover".equals(resizeModeValue)) { - // 在保持图片宽高比的前提下缩放图片,直到宽度和高度都大于等于容器视图的尺寸 - // 这样图片完全覆盖甚至超出容器,容器中不留任何空白 - hippyImageView.setScaleType(HippyImageView.ScaleType.CENTER_CROP); - } else if ("center".equals(resizeModeValue)) { - // 居中不拉伸 - hippyImageView.setScaleType(HippyImageView.ScaleType.CENTER); - } else if ("origin".equals(resizeModeValue)) { - // 不拉伸,居左上 - hippyImageView.setScaleType(HippyImageView.ScaleType.ORIGIN); - } else { - // stretch and other mode - // 拉伸图片且不维持宽高比,直到宽高都刚好填满容器 - hippyImageView.setScaleType(HippyImageView.ScaleType.FIT_XY); + public static void setViewZIndex(View view, int zIndex) { + mZIndexHash.put(view, zIndex); } - } - @HippyControllerProps(name = NodeProps.BACKGROUND_POSITION_X, defaultType = HippyControllerProps.NUMBER) - public void setBackgroundImagePositionX(HippyViewGroup hippyViewGroup, int positionX) { - hippyViewGroup.setImagePositionX((int) PixelUtil.dp2px(positionX)); - } + public static void removeViewZIndex(View view) { + mZIndexHash.remove(view); + } - @HippyControllerProps(name = NodeProps.BACKGROUND_POSITION_Y, defaultType = HippyControllerProps.NUMBER) - public void setBackgroundImagePositionY(HippyViewGroup hippyViewGroup, int positionY) { - hippyViewGroup.setImagePositionY((int) PixelUtil.dp2px(positionY)); - } + public static Integer getViewZIndex(View view) { + return mZIndexHash.get(view); + } + + @Override + protected View createViewImpl(Context context) { + return new HippyViewGroup(context); + } + + @HippyControllerProps(name = NodeProps.OVERFLOW, defaultType = HippyControllerProps.STRING, + defaultString = "visible") + public void setOverflow(HippyViewGroup hippyViewGroup, String overflow) { + hippyViewGroup.setOverflow(overflow); + } + + @HippyControllerProps(name = NodeProps.BACKGROUND_IMAGE, defaultType = HippyControllerProps.STRING) + public void setBackgroundImage(HippyViewGroup hippyViewGroup, String url) { + hippyViewGroup.setUrl(getInnerPath((NativeRenderContext) hippyViewGroup.getContext(), url)); + } + + @HippyControllerProps(name = NodeProps.BACKGROUND_SIZE, defaultType = HippyControllerProps.STRING, + defaultString = "origin") + public void setBackgroundImageSize(HippyImageView hippyImageView, String resizeModeValue) { + if ("contain".equals(resizeModeValue)) { + // 在保持图片宽高比的前提下缩放图片,直到宽度和高度都小于等于容器视图的尺寸 + // 这样图片完全被包裹在容器中,容器中可能留有空白 + hippyImageView.setScaleType(HippyImageView.ScaleType.CENTER_INSIDE); + } else if ("cover".equals(resizeModeValue)) { + // 在保持图片宽高比的前提下缩放图片,直到宽度和高度都大于等于容器视图的尺寸 + // 这样图片完全覆盖甚至超出容器,容器中不留任何空白 + hippyImageView.setScaleType(HippyImageView.ScaleType.CENTER_CROP); + } else if ("center".equals(resizeModeValue)) { + // 居中不拉伸 + hippyImageView.setScaleType(HippyImageView.ScaleType.CENTER); + } else if ("origin".equals(resizeModeValue)) { + // 不拉伸,居左上 + hippyImageView.setScaleType(HippyImageView.ScaleType.ORIGIN); + } else { + // stretch and other mode + // 拉伸图片且不维持宽高比,直到宽高都刚好填满容器 + hippyImageView.setScaleType(HippyImageView.ScaleType.FIT_XY); + } + } + + @HippyControllerProps(name = NodeProps.BACKGROUND_POSITION_X, defaultType = HippyControllerProps.NUMBER) + public void setBackgroundImagePositionX(HippyViewGroup hippyViewGroup, int positionX) { + hippyViewGroup.setImagePositionX((int) PixelUtil.dp2px(positionX)); + } + + @HippyControllerProps(name = NodeProps.BACKGROUND_POSITION_Y, defaultType = HippyControllerProps.NUMBER) + public void setBackgroundImagePositionY(HippyViewGroup hippyViewGroup, int positionY) { + hippyViewGroup.setImagePositionY((int) PixelUtil.dp2px(positionY)); + } + + @SuppressWarnings("rawtypes") + @HippyControllerProps(name = NodeProps.BACKGROUND_RIPPLE, defaultType = HippyControllerProps.MAP) + public void setNativeBackground(HippyViewGroup hippyViewGroup, Map params) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !params.isEmpty()) { + Drawable drawable = DrawableFactory.createDrawable(DRAWABLE_TYPE_RIPPLE, params); + if (drawable != null) { + hippyViewGroup.setRippleDrawable(drawable); + } + } + } + + @SuppressWarnings("rawtypes") + @Override + public void dispatchFunction(@NonNull HippyViewGroup viewGroup, @NonNull String functionName, + @NonNull List params) { + super.dispatchFunction(viewGroup, functionName, params); + switch (functionName) { + case FUNC_SET_PRESSED: { + boolean pressed = ArrayUtils.getBooleanValue(params, 0); + viewGroup.setPressed(pressed); + break; + } + case FUNC_SET_HOTSPOT: { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + float x = ArrayUtils.getFloatValue(params, 0); + float y = ArrayUtils.getFloatValue(params, 1); + viewGroup.drawableHotspotChanged(PixelUtil.dp2px(x), PixelUtil.dp2px(y)); + } + break; + } + default: + break; + } + } } diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/supportui/views/asyncimage/AsyncImageView.java b/renderer/native/android/src/main/java/com/tencent/mtt/supportui/views/asyncimage/AsyncImageView.java index 45fb029ff..221b101cc 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/supportui/views/asyncimage/AsyncImageView.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/supportui/views/asyncimage/AsyncImageView.java @@ -30,10 +30,15 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import android.os.Build; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + import java.util.ArrayList; /** @@ -64,6 +69,7 @@ public class AsyncImageView extends ViewGroup implements Animator.AnimatorListen protected int mTintColor; protected ScaleType mScaleType; protected Drawable mContentDrawable; + protected Drawable mRippleDrawable; private boolean mIsAttached; protected ImageLoaderAdapter mImageAdapter; @@ -375,27 +381,9 @@ public class AsyncImageView extends ViewGroup implements Animator.AnimatorListen if (!shouldSetContent()) { return; } - onSetContent(mUrl); updateContentDrawableProperty(sourceType); - - if (mBGDrawable != null) { - if (mContentDrawable instanceof ContentDrawable) { - ((ContentDrawable) mContentDrawable) - .setBorder(mBGDrawable.getBorderRadiusArray(), - mBGDrawable.getBorderWidthArray()); - ((ContentDrawable) mContentDrawable) - .setShadowOffsetX(mBGDrawable.getShadowOffsetX()); - ((ContentDrawable) mContentDrawable) - .setShadowOffsetY(mBGDrawable.getShadowOffsetY()); - ((ContentDrawable) mContentDrawable) - .setShadowRadius(mBGDrawable.getShadowRadius()); - } - setBackgroundDrawable( - new LayerDrawable(new Drawable[]{mBGDrawable, mContentDrawable})); - } else { - setBackgroundDrawable(mContentDrawable); - } + resetBackgroundDrawable(); afterSetContent(mUrl); } } @@ -404,13 +392,11 @@ public class AsyncImageView extends ViewGroup implements Animator.AnimatorListen if (!(mContentDrawable instanceof ContentDrawable)) { return; } - Bitmap bitmap = getBitmap(); if (sourceType == SOURCE_TYPE_DEFAULT_SRC && mDefaultSourceDrawable != null && (mUrlFetchState != IMAGE_LOADED || mSourceDrawable == null)) { bitmap = mDefaultSourceDrawable.getBitmap(); } - if (bitmap != null) { ((ContentDrawable) mContentDrawable).setSourceType(sourceType); ((ContentDrawable) mContentDrawable).setBitmap(bitmap); @@ -419,6 +405,17 @@ public class AsyncImageView extends ViewGroup implements Animator.AnimatorListen ((ContentDrawable) mContentDrawable).setImagePositionX(mImagePositionX); ((ContentDrawable) mContentDrawable).setImagePositionY(mImagePositionY); } + if (mBGDrawable != null) { + ((ContentDrawable) mContentDrawable) + .setBorder(mBGDrawable.getBorderRadiusArray(), + mBGDrawable.getBorderWidthArray()); + ((ContentDrawable) mContentDrawable) + .setShadowOffsetX(mBGDrawable.getShadowOffsetX()); + ((ContentDrawable) mContentDrawable) + .setShadowOffsetY(mBGDrawable.getShadowOffsetY()); + ((ContentDrawable) mContentDrawable) + .setShadowRadius(mBGDrawable.getShadowRadius()); + } } protected void handleGetImageStart() { @@ -587,4 +584,34 @@ public class AsyncImageView extends ViewGroup implements Animator.AnimatorListen } return mBGDrawable; } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public void setRippleDrawable(@NonNull Drawable rippleDrawable) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mRippleDrawable = rippleDrawable; + resetBackgroundDrawable(); + } + } + + private void resetBackgroundDrawable() { + ArrayList drawableList = new ArrayList<>(); + if (mBGDrawable != null) { + drawableList.add(mBGDrawable); + } + if (mContentDrawable != null) { + drawableList.add(mContentDrawable); + } + if (mRippleDrawable != null) { + drawableList.add(mRippleDrawable); + } + if (drawableList.size() > 0) { + super.setBackground(null); + Drawable[] drawables = new Drawable[drawableList.size()]; + for (int i = 0; i < drawableList.size(); i++) { + drawables[i] = drawableList.get(i); + } + LayerDrawable layerDrawable = new LayerDrawable(drawables); + super.setBackground(layerDrawable); + } + } } diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/component/drawable/DrawableFactory.java b/renderer/native/android/src/main/java/com/tencent/renderer/component/drawable/DrawableFactory.java new file mode 100644 index 000000000..8478cc5ab --- /dev/null +++ b/renderer/native/android/src/main/java/com/tencent/renderer/component/drawable/DrawableFactory.java @@ -0,0 +1,88 @@ +/* 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.renderer.component.drawable; + +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; + +import com.tencent.mtt.hippy.utils.PixelUtil; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +import java.util.Map; + +public class DrawableFactory { + + private static final String PROPERTY_RIPPLE_COLOR = "color"; + private static final String PROPERTY_RIPPLE_RADIUS = "rippleRadius"; + private static final String PROPERTY_RIPPLE_BORDERLESS = "borderless"; + + public enum DrawableType { + DRAWABLE_TYPE_RIPPLE + } + + @Nullable + public static Drawable createDrawable(DrawableType type, @Nullable Map params) { + Drawable drawable = null; + switch (type) { + case DRAWABLE_TYPE_RIPPLE: + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + drawable = createRippleDrawable(params); + } + break; + default: + } + + return drawable; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @NonNull + private static Drawable createRippleDrawable(@Nullable Map params) { + int color = Color.BLUE; + int radius = 0; + Drawable mask = null; + if (params != null) { + Object value = params.get(PROPERTY_RIPPLE_COLOR); + if (value instanceof Number) { + color = ((Number) value).intValue(); + } + value = params.get(PROPERTY_RIPPLE_RADIUS); + if (value instanceof Number) { + double rd = ((Number) value).doubleValue(); + radius = (int) (PixelUtil.dp2px(rd) + 0.5); + } + value = params.get(PROPERTY_RIPPLE_BORDERLESS); + if (value == null || (value instanceof Boolean && !((boolean) value))) { + mask = new ColorDrawable(Color.WHITE); + } + } + ColorStateList colorStateList = + new ColorStateList(new int[][]{new int[]{}}, new int[]{color}); + RippleDrawable drawable = new RippleDrawable(colorStateList, null, mask); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && radius > 0) { + drawable.setRadius(radius); + } + return drawable; + } +} diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/utils/ArrayUtils.java b/renderer/native/android/src/main/java/com/tencent/renderer/utils/ArrayUtils.java index deb5d6f84..91a7b97eb 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/utils/ArrayUtils.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/utils/ArrayUtils.java @@ -36,6 +36,17 @@ public class ArrayUtils { return DEFAULT_NUMBER_VALUE; } + public static float getFloatValue(@NonNull List params, int index) { + if (index >= params.size()) { + return DEFAULT_NUMBER_VALUE; + } + Object element = params.get(index); + if (element instanceof Number) { + return ((Number) element).floatValue(); + } + return DEFAULT_NUMBER_VALUE; + } + public static double getDoubleValue(@NonNull List params, int index) { if (index >= params.size()) { return DEFAULT_NUMBER_VALUE; diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/utils/PropertyUtils.java b/renderer/native/android/src/main/java/com/tencent/renderer/utils/PropertyUtils.java index 0b29d7998..c2e1299d1 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/utils/PropertyUtils.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/utils/PropertyUtils.java @@ -75,7 +75,7 @@ public class PropertyUtils { return result; } if (paramCls == boolean.class || paramCls == Boolean.class || paramCls == HashMap.class - || paramCls == ArrayList.class) { + || paramCls == ArrayList.class || paramCls == Map.class || paramCls == List.class) { return value; } // Although HippyMap and HippyArray has been deprecated, we need to be compatible with