mirror of
https://github.com/Tencent/Hippy.git
synced 2026-10-29 14:10:47 +08:00
refactor(android): v3.0 ripple background
This commit is contained in:
@@ -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<String> JUST_LAYOUT_PROPS = new HashSet<>(
|
||||
Arrays.asList(ALIGN_SELF, ALIGN_ITEMS, COLLAPSABLE, FLEX, FLEX_DIRECTION, FLEX_WRAP,
|
||||
|
||||
+109
-60
@@ -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<HippyViewGroup> {
|
||||
|
||||
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<View, Integer> mZIndexHash = new WeakHashMap<>();
|
||||
|
||||
public static final WeakHashMap<View, Integer> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+48
-21
@@ -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<Drawable> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+88
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user