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"/>

常使用上述的写法来实现一个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会始终覆盖父类的该方法实现。

说到底,就是个实现的先后覆盖问题

猜你喜欢

转载自blog.csdn.net/ganshenml/article/details/118583996