Android CardView如何顯示出底背景樣式

<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)的先后覆蓋問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容