<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"/>
常使用上述的寫法來實現一個button樣式。
問題一:
如果將其放入在一個dialog的bottom位置作為點擊按鈕,dialog的background
設置了圓角,但是顯示button一角卻還是直角。
問題二:
此時dialog更改bg顏色,發現CardView區域還是白色。
針對問題一,當然可以更改CardView來手動設置某一部分是圓角。
但兩個問題的癥結其實都是同一個點,那就是:
設置水波紋foreground的CardView默認背景就是白色
解決方案:將默認背景改為透明,則就能顯示出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"/>
可以看到此處是設置app:cardBackgroundColor="@color/transparent"
.
問題來了,上述的方法只能設置color,如果要設置drawable該怎么辦?
其實解決方案很簡單:CardView
本身是繼承自FrameLayout
的,這就意味著它是可以包裹內容的,里面包裹其它控件(如ImageView
)。通過包裹的控件來實現有drawable的背景即可。
那么又有問題了,既然CardView繼承自FrameLayout,這就說明其是可以設置Background的。但為什么設置后卻顯示不出來呢?
這個就涉及到整個View的構造流程了:
public CardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//省略屬性獲取
IMPL.initialize(mCardViewDelegate, context, backgroundColor, radius,
elevation, maxElevation);
}
初始化View進入構造方法,會進行IMPL.initialize
方法的調用。這個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的實現類,不同的版本有不同的實現。此處進入CardViewApi21Impl里面看看initialize
實現。
@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);
}
可以發現在這里面進行了cardView.setCardBackground
調用,而background是根據設置的
backgroundColor = a.getColorStateList(R.styleable.CardView_cardBackgroundColor);
賦值RoundRectDrawable生成而來。
cardView.setCardBackground(background);
中的CardView是CardViewDelegate
,它是一個接口,所以也要看它的實現類。在CardView類中可以找到:
private final CardViewDelegate mCardViewDelegate = new CardViewDelegate() {
private Drawable mCardBackground;
@Override
public void setCardBackground(Drawable drawable) {
mCardBackground = drawable;
setBackgroundDrawable(drawable);
}
//...
}
而上面的setBackgroundDrawable
則是進入到了View層面了。
至此已經很清楚了,因為initialize
在父類(FrameLayout)的構造方法之后調用,導致最終設置的setBackgroundDrawable
會始終覆蓋父類的該方法實現。
說到底,就是個實現的先后覆蓋問題。