常使用上述的写法来实现一个button样式。
问题一:
如果将其放入在一个dialog的bottom位置作为点击按钮,dialog的background
设置了圆角,但是显示button一角却还是直角。
问题二:
此时dialog更改bg颜色,发现CardView区域还是白色。
针对问题一,当然可以更改CardView来手动设置某一部分是圆角。
但两个问题的症结其实都是同一个点,那就是:
设置水波纹foreground的CardView默认背景就是白色
解决方案:将默认背景改为透明,则就能显示出dialog底背景的样式了。
可以看到此处是设置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
会始终覆盖父类的该方法实现。
说到底,就是个实现的先后覆盖问题。