我们知道默认的CardView
是不能设置阴影颜色的,许多时候却又有这种需求,然后百度上解决方案很少,基本就是把官方的CardView
的源码改了再拷进工程。
看看效果:
反射修改的缺点和上面改源码的缺点一样,都是没有Android5.0以上的View
自带的阴影绘制那么平滑好看,且有半径限制,如图,TextView
是Api21
以上自带阴影,CardView
是反射修改的阴影。
先看看CardView
源码:
可以发现,
和CardView
属性有关(setCardBackgroundColor
,setCardElevation
等)的基本都在静态常量IMPL
上了,
CardView
里也没有绘制阴影的相关方法,所以阴影绘制很可能在IMPL
里,
IMPL
的类型CardViewImpl
本身是个接口,所以要找到它的子类,看他在哪里被赋值。
可以看到,
IMPL
根据android版本的不同被赋于了不同的 子类,从名字也可以看出,其实这个就是为了实现不同Android版本的兼容和优化。
然后需要进入
CardViewApi21Impl.java
CardViewApi17Impl.java
CardViewBaseImpl.java
三个地方分别看看他们的区别,
可以看出,
Android 5.0(
CardViewApi21Impl.java
)以上是调用View
里面的阴影绘制的。Android 4.2(
CardViewBaseImpl.java
)以下是利用Drawable
来绘制阴影的Android 4.2-5.0(
CardViewApi17Impl.java
)只在CardViewBaseImpl.java的基础上替换了画圆角矩形的方法。
Android 5.0(CardViewApi21Impl.java
)的 View
里面的阴影绘制过于复杂(可能调用native
方法),用反射也不一定能完成,就没有深入了,
所以这里考虑用修改Drawable
的属性来实现修改阴影颜色。
带阴影的圆角矩形 的Drawable
它已经写好了,
进去RoundRectDrawableWithShadow
类可以看到阴影的起始颜色和结束颜色两个属性,但是是private
的,所以我们要利用反射修改他的属性值就行了。
修改完mShadowStartColor
,mShadowEndColor
发现:
什么都没发生??
当然,因为IPML
仍然是Api21
以上的实现CardViewApi21Impl
,用的是View
的阴影绘制
所以需要先把它改成Api17
的实现CardViewApi17Impl
,才是用Drawable
实现阴影。
所以要在之前加一步替换IPML
和调用初始化IPML
的方法。
import android.os.Build;
import android.support.v7.widget.CardView;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class CardUtils {
private static boolean inited=false;
//设置obj的成员变量
private static void setMember(Object obj, String memberName, Object value) {
try {
if (obj instanceof Class) {
//静态变量
Field declaredField = ((Class) obj).getDeclaredField(memberName);
declaredField.setAccessible(true);
declaredField.set(null, value);
} else {
Field declaredField = obj.getClass().getDeclaredField(memberName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void init() {
if (Build.VERSION.SDK_INT >= 21&&!inited) {
inited=true;
try {
//new 一个CardViewApi17Impl
Constructor<?> constructor = Class.forName("android.support.v7.widget.CardViewApi17Impl").getDeclaredConstructor();
constructor.setAccessible(true);
Object impl = constructor.newInstance();
//用新的代替掉原来的
setMember(CardView.class, "IMPL", impl);
//执行方法IMPL.initStatic()
Method initStatic = impl.getClass().getDeclaredMethod("initStatic");
initStatic.setAccessible(true);
initStatic.invoke(impl);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void setCardShadowColor(CardView cardView, int startColor, int endColor) {
try {
//获取背景
Object background = cardView.getBackground();
//设置颜色
setMember(background, "mShadowStartColor", startColor);
setMember(background, "mShadowEndColor", endColor);
} catch (Exception e) {
e.printStackTrace();
}
}
}
CardUtils.init()
最好是在Application启动时调用。
网友评论