我們知道默認的CardView
是不能設(shè)置陰影顏色的,許多時候卻又有這種需求秒啦,然后百度上解決方案很少潜慎,基本就是把官方的CardView
的源碼改了再拷進工程嗽测。
看看效果:
反射修改的缺點和上面改源碼的缺點一樣,都是沒有Android5.0以上的View
自帶的陰影繪制那么平滑好看蓉坎,且有半徑限制澳眷,如圖,TextView
是Api21
以上自帶陰影蛉艾,CardView
是反射修改的陰影钳踊。
先看看CardView
源碼:
可以發(fā)現(xiàn),
和CardView
屬性有關(guān)(setCardBackgroundColor
,setCardElevation
等)的基本都在靜態(tài)常量IMPL
上了勿侯,
CardView
里也沒有繪制陰影的相關(guān)方法拓瞪,所以陰影繪制很可能在IMPL
里,
IMPL
的類型CardViewImpl
本身是個接口助琐,所以要找到它的子類祭埂,看他在哪里被賦值。
可以看到兵钮,
IMPL
根據(jù)android版本的不同被賦于了不同的 子類沟堡,從名字也可以看出,其實這個就是為了實現(xiàn)不同Android版本的兼容和優(yōu)化矢空。
然后需要進入
CardViewApi21Impl.java
CardViewApi17Impl.java
CardViewBaseImpl.java
三個地方分別看看他們的區(qū)別航罗,
可以看出,
Android 5.0(
CardViewApi21Impl.java
)以上是調(diào)用View
里面的陰影繪制的屁药。Android 4.2(
CardViewBaseImpl.java
)以下是利用Drawable
來繪制陰影的Android 4.2-5.0(
CardViewApi17Impl.java
)只在CardViewBaseImpl.java的基礎(chǔ)上替換了畫圓角矩形的方法粥血。
Android 5.0(CardViewApi21Impl.java
)的 View
里面的陰影繪制過于復(fù)雜(可能調(diào)用native
方法),用反射也不一定能完成酿箭,就沒有深入了复亏,
所以這里考慮用修改Drawable
的屬性來實現(xiàn)修改陰影顏色。
帶陰影的圓角矩形 的Drawable
它已經(jīng)寫好了缭嫡,
進去RoundRectDrawableWithShadow
類可以看到陰影的起始顏色和結(jié)束顏色兩個屬性缔御,但是是private
的,所以我們要利用反射修改他的屬性值就行了妇蛀。
修改完mShadowStartColor
,mShadowEndColor
發(fā)現(xiàn):
什么都沒發(fā)生耕突?笤成?
當(dāng)然,因為IPML
仍然是Api21
以上的實現(xiàn)CardViewApi21Impl
眷茁,用的是View
的陰影繪制
所以需要先把它改成Api17
的實現(xiàn)CardViewApi17Impl
炕泳,才是用Drawable
實現(xiàn)陰影。
所以要在之前加一步替換IPML
和調(diào)用初始化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;
//設(shè)置obj的成員變量
private static void setMember(Object obj, String memberName, Object value) {
try {
if (obj instanceof Class) {
//靜態(tài)變量
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);
//執(zhí)行方法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();
//設(shè)置顏色
setMember(background, "mShadowStartColor", startColor);
setMember(background, "mShadowEndColor", endColor);
} catch (Exception e) {
e.printStackTrace();
}
}
}
CardUtils.init()
最好是在Application啟動時調(diào)用培遵。