Skip to content

Commit

Permalink
Optimize shadow
Browse files Browse the repository at this point in the history
  • Loading branch information
RuffianZhong committed Oct 11, 2022
1 parent 5f114d9 commit b8abd96
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,20 @@
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;

import androidx.annotation.ColorInt;
import androidx.annotation.StyleableRes;
import androidx.core.text.TextUtilsCompat;
import androidx.core.view.ViewCompat;

import com.ruffian.library.widget.R;
import com.ruffian.library.widget.clip.ClipHelper;
import com.ruffian.library.widget.clip.ClipPathManager;
import com.ruffian.library.widget.clip.IClip;
import com.ruffian.library.widget.shadow.ShadowDrawable;
import com.ruffian.library.widget.shadow.ShadowBitmapDrawable;

import java.util.Locale;

import androidx.annotation.ColorInt;
import androidx.annotation.RequiresApi;
import androidx.annotation.StyleableRes;
import androidx.core.text.TextUtilsCompat;
import androidx.core.view.ViewCompat;


/**
* BaseHelper
Expand Down Expand Up @@ -104,7 +105,7 @@ public class RBaseHelper<T extends View> implements IClip, ViewTreeObserver.OnGl
private GradientDrawable.Orientation mGradientOrientation = GradientDrawable.Orientation.TOP_BOTTOM;

//shadow
private ShadowDrawable mShadowDrawable;
private ShadowBitmapDrawable mShadowDrawable;
private int mShadowDx;
private int mShadowDy;
private int mShadowColor;
Expand Down Expand Up @@ -173,6 +174,7 @@ public RBaseHelper(Context context, T view, AttributeSet attrs) {
addOnGlobalLayoutListener();
}


/**
* 初始化控件属性
*
Expand Down Expand Up @@ -278,7 +280,6 @@ private void setup() {
mBackgroundSelected = new GradientDrawable();
mViewBackground = mView.getBackground();
mStateBackground = new StateListDrawable();
if (useShadow()) mShadowDrawable = new ShadowDrawable();
//unable,focused,pressed,checked,selected,normal
states[0] = new int[]{-android.R.attr.state_enabled};//unable
states[1] = new int[]{android.R.attr.state_focused};//focused
Expand Down Expand Up @@ -818,7 +819,6 @@ private void updateBackgroundValue() {
setBackgroundState();
}


/**
* 刷新StateListDrawable状态
* 更新drawable背景时时候刷新
Expand Down Expand Up @@ -874,16 +874,27 @@ private void setBackgroundState() {
//获取drawable
mBackgroundDrawable = getBackgroundDrawable(hasCustom, mRippleColor);
if (useShadow()) {
mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);//禁止硬件加速,must,导致绘制耗时(寻找优化路径)
if (mShadowDrawable == null) mShadowDrawable = new ShadowDrawable();

//1.早期实现方式禁止了硬件加速,并且由于 LAYER_TYPE_SOFTWARE 的特性导致在列表中卡顿(频繁创建缓存再渲染)(超过限制的view创建缓存过大导致无法展示背景)
//mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);//禁止硬件加速,must,导致绘制耗时(寻找优化路径)
//if (mShadowDrawable == null) mShadowDrawable = new ShadowDrawable();
//mShadowDrawable.updateParameter(mShadowColor, mShadowRadius, mShadowDx, mShadowDy, mBorderRadii);
//int shadowOffset = (int) mShadowDrawable.getShadowOffset();
//int left = shadowOffset + Math.abs(mShadowDx);
//int right = shadowOffset + Math.abs(mShadowDx);
//int top = shadowOffset + Math.abs(mShadowDy);
//int bottom = shadowOffset + Math.abs(mShadowDy);

//2.将阴影背景绘制成bitmap渲染,没有禁止硬件加速,和 LAYER_TYPE_SOFTWARE 的特性,大大提升性能。(在列表中大量使用阴影时,可能导致内存较高)
if (mShadowDrawable == null) mShadowDrawable = new ShadowBitmapDrawable();
mShadowDrawable.updateParameter(mShadowColor, mShadowRadius, mShadowDx, mShadowDy, mBorderRadii);

int shadowOffset = (int) mShadowDrawable.getShadowOffset();
int left = shadowOffset + Math.abs(mShadowDx);
int right = shadowOffset + Math.abs(mShadowDx);
int top = shadowOffset + Math.abs(mShadowDy);
int bottom = shadowOffset + Math.abs(mShadowDy);
int left = mShadowRadius + Math.abs(mShadowDx);
int right = mShadowRadius + Math.abs(mShadowDx);
int top = mShadowRadius + Math.abs(mShadowDy);
int bottom = mShadowRadius + Math.abs(mShadowDy);

//设置背景
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{mShadowDrawable, mBackgroundDrawable});
layerDrawable.setLayerInset(1, left, top, right, bottom);//设置第二层drawable四周偏移量
mBackgroundDrawable = layerDrawable;
Expand All @@ -901,7 +912,6 @@ private void setBackgroundState() {

}


/**
* 获取 BackgroundDrawable
*
Expand Down Expand Up @@ -947,6 +957,7 @@ private Drawable getBackgroundDrawable(boolean hasCustom, int rippleColor) {
* @param hasCustom 是否存在自定义背景
* @param rippleColor 水波纹颜色
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private Object[] getRippleDrawableWithTag(boolean hasCustom, int rippleColor) {
boolean isMaskNull;//是否不受限制的水波纹效果{ contentDrawable == null && maskDrawable == null }
RippleDrawable rippleDrawable;//水波纹drawable
Expand Down Expand Up @@ -1613,6 +1624,7 @@ public void onGlobalLayout() {
initClip();
}


/**
* 是否从右到左布局
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.ruffian.library.widget.shadow;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;

import com.ruffian.library.widget.utils.ReflectUtils;

import java.util.Arrays;

/**
* ShadowBitmapDrawable
*
* @author ZhongDaFeng
*/
public class ShadowBitmapDrawable extends BitmapDrawable {


private int mShadowColor;//阴影颜色
private float mShadowRadius;//阴影半径
private float[] mRoundRadii;//矩形圆角半径
private int mShadowDx;//阴影x偏移
private int mShadowDy;//阴影y偏移

private RectF mBoundsF;

public ShadowBitmapDrawable() {
mBoundsF = new RectF();
}

/**
* RBaseHelp中属性修改时调用
*
* @param shadowColor
* @param shadowRadius
* @param shadowDx
* @param shadowDy
* @param roundRadii
*/
public void updateParameter(int shadowColor, float shadowRadius, int shadowDx, int shadowDy, float[] roundRadii) {
boolean needUpdate = false;
if (mShadowColor != shadowColor || mShadowRadius != shadowRadius
|| mShadowDx != shadowDx || mShadowDy != shadowDy
|| !Arrays.equals(mRoundRadii, roundRadii)) {
needUpdate = true;
}
this.mShadowColor = shadowColor;
this.mRoundRadii = roundRadii;
this.mShadowRadius = shadowRadius;
this.mShadowDx = shadowDx;
this.mShadowDy = shadowDy;

if (needUpdate) {
//重新创建bitmap
Bitmap bitmap = makeShadowBitmap((int) mBoundsF.width(), (int) mBoundsF.height(), mShadowRadius, mShadowDx, mShadowDy, mShadowColor, mRoundRadii);

//反射调用函数 setBitmap 更新阴影图片
ReflectUtils.invokeMethod(this, "setBitmap", new Class[]{Bitmap.class}, new Object[]{bitmap});
}


}

@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
if (bounds.right - bounds.left > 0 && bounds.bottom - bounds.top > 0) {
updateBounds(bounds);
}
}

/**
* 更新Bounds
*
* @param bounds
*/
private void updateBounds(Rect bounds) {
if (bounds == null) bounds = getBounds();

float left = bounds.left + mShadowRadius + Math.abs(mShadowDx);
float right = bounds.right - mShadowRadius - Math.abs(mShadowDx);
float top = bounds.top + mShadowRadius + Math.abs(mShadowDy);
float bottom = bounds.bottom - mShadowRadius - Math.abs(mShadowDy);

mBoundsF.set(left, top, right, bottom);

//重新创建bitmap(不等高列表复用时可能会出现内存抖动,待优化)
Bitmap bitmap = makeShadowBitmap((int) mBoundsF.width(), (int) mBoundsF.height(), mShadowRadius, mShadowDx, mShadowDy, mShadowColor, mRoundRadii);

//反射调用函数 setBitmap 更新阴影图片
ReflectUtils.invokeMethod(this, "setBitmap", new Class[]{Bitmap.class}, new Object[]{bitmap});

}


/**
* 创建阴影Bitmap
*
* @param shadowWidth
* @param shadowHeight
* @param shadowRadius
* @param dx
* @param dy
* @param shadowColor
* @param radii
* @return
*/
public Bitmap makeShadowBitmap(int shadowWidth, int shadowHeight, float shadowRadius,
float dx, float dy, int shadowColor, float[] radii) {

//容错处理
if (shadowWidth <= 0 || shadowHeight <= 0)
return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444);


//阴影bitmap
Bitmap output = Bitmap.createBitmap(shadowWidth, shadowHeight, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(output);

RectF shadowRect = new RectF(shadowRadius, shadowRadius, shadowWidth - shadowRadius, shadowHeight - shadowRadius);


//存在偏移时重新计算阴影矩形大小
shadowRect.top += Math.abs(dy);
shadowRect.bottom -= Math.abs(dy);
shadowRect.left += Math.abs(dx);
shadowRect.right -= Math.abs(dx);

//阴影画笔
Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
shadowPaint.setColor(shadowColor);
shadowPaint.setShadowLayer(shadowRadius, dx, dy, shadowColor);

//路径
Path path = new Path();
path.addRoundRect(shadowRect, radii, Path.Direction.CW);
canvas.drawPath(path, shadowPaint);

return output;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.ruffian.library.widget.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* 反射工具类
*
* @author ZhongDaFeng
*/
public class ReflectUtils {

/**
* 循环向上转型, 获取对象的 DeclaredMethod
*
* @param object 子类对象
* @param methodName 父类中的方法名
* @param parameterTypes 父类中的方法参数类型
* @return 父类中的方法对象
*/
public static Method getDeclaredMethod(Object object, String methodName, Class<?>... parameterTypes) {
Method method = null;
for (Class<?> clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
try {
method = clazz.getDeclaredMethod(methodName, parameterTypes);
return method;
} catch (Exception e) {
//这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
//如果这里的异常打印或者往外抛,则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
}
}
return null;
}

/**
* 直接调用对象方法, 而忽略修饰符(private, protected, default)
*
* @param object 子类对象
* @param methodName 父类中的方法名
* @param parameterTypes 父类中的方法参数类型
* @param parameters 父类中的方法参数
* @return 父类中方法的执行结果
*/
public static Object invokeMethod(Object object, String methodName, Class<?>[] parameterTypes, Object[] parameters) {
//根据 对象、方法名和对应的方法参数 通过反射 调用上面的方法获取 Method 对象
Method method = getDeclaredMethod(object, methodName, parameterTypes);
try {
if (null != method) {
//抑制Java对方法进行检查,主要是针对私有方法而言
method.setAccessible(true);
//调用object 的 method 所代表的方法,其方法的参数是 parameters
return method.invoke(object, parameters);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}

0 comments on commit b8abd96

Please sign in to comment.