<androidx.cardview.widget.CardView
android:id="@+id/actionOneCv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"/>
常使用上述的寫法來實(shí)現(xiàn)一個(gè)button樣式。
問題一:
如果將其放入在一個(gè)dialog的bottom位置作為點(diǎn)擊按鈕,dialog的background設(shè)置了圓角,但是顯示button一角卻還是直角。
問題二:
此時(shí)dialog更改bg顏色,發(fā)現(xiàn)CardView區(qū)域還是白色。
針對(duì)問題一,當(dāng)然可以更改CardView來手動(dòng)設(shè)置某一部分是圓角。
但兩個(gè)問題的癥結(jié)其實(shí)都是同一個(gè)點(diǎn),那就是:
設(shè)置水波紋foreground的CardView默認(rèn)背景就是白色
解決方案:將默認(rèn)背景改為透明,則就能顯示出dialog底背景的樣式了。
<androidx.cardview.widget.CardView
android:id="@+id/actionOneCv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
app:cardBackgroundColor="@color/transparent"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"/>
可以看到此處是設(shè)置app:cardBackgroundColor="@color/transparent".
問題來了,上述的方法只能設(shè)置color,如果要設(shè)置drawable該怎么辦?
其實(shí)解決方案很簡(jiǎn)單:CardView本身是繼承自FrameLayout的,這就意味著它是可以包裹內(nèi)容的,里面包裹其它控件(如ImageView)。通過包裹的控件來實(shí)現(xiàn)有drawable的背景即可。
那么又有問題了,既然CardView繼承自FrameLayout,這就說明其是可以設(shè)置Background的。但為什么設(shè)置后卻顯示不出來呢?
這個(gè)就涉及到整個(gè)View的構(gòu)造流程了:
public CardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//省略屬性獲取
IMPL.initialize(mCardViewDelegate, context, backgroundColor, radius,
elevation, maxElevation);
}
初始化View進(jìn)入構(gòu)造方法,會(huì)進(jìn)行IMPL.initialize方法的調(diào)用。這個(gè)IMPL是什么?
private static final CardViewImpl IMPL;
static {
if (Build.VERSION.SDK_INT >= 21) {
IMPL = new CardViewApi21Impl();
} else if (Build.VERSION.SDK_INT >= 17) {
IMPL = new CardViewApi17Impl();
} else {
IMPL = new CardViewBaseImpl();
}
IMPL.initStatic();
}
IMPL是CardView的實(shí)現(xiàn)類,不同的版本有不同的實(shí)現(xiàn)。此處進(jìn)入CardViewApi21Impl里面看看initialize實(shí)現(xiàn)。
@Override
public void initialize(CardViewDelegate cardView, Context context,
ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
final RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);
cardView.setCardBackground(background);
View view = cardView.getCardView();
view.setClipToOutline(true);
view.setElevation(elevation);
setMaxElevation(cardView, maxElevation);
}
可以發(fā)現(xiàn)在這里面進(jìn)行了cardView.setCardBackground調(diào)用,而background是根據(jù)設(shè)置的
backgroundColor = a.getColorStateList(R.styleable.CardView_cardBackgroundColor);
賦值RoundRectDrawable生成而來。
cardView.setCardBackground(background);中的CardView是CardViewDelegate,它是一個(gè)接口,所以也要看它的實(shí)現(xiàn)類。在CardView類中可以找到:
private final CardViewDelegate mCardViewDelegate = new CardViewDelegate() {
private Drawable mCardBackground;
@Override
public void setCardBackground(Drawable drawable) {
mCardBackground = drawable;
setBackgroundDrawable(drawable);
}
//...
}
而上面的setBackgroundDrawable則是進(jìn)入到了View層面了。
至此已經(jīng)很清楚了,因?yàn)?code>initialize在父類(FrameLayout)的構(gòu)造方法之后調(diào)用,導(dǎo)致最終設(shè)置的setBackgroundDrawable會(huì)始終覆蓋父類的該方法實(shí)現(xiàn)。
說到底,就是個(gè)實(shí)現(xiàn)的先后覆蓋問題。