How does Android CardView display the bottom background style?

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

The above-mentioned writing method is often used to implement a button style.

Problem 1:
If you put it in the bottom position of a dialog as a click button, the dialog backgroundhas rounded corners, but the corner of the displayed button is still a right angle.

Question 2:
At this time, the dialog changes the bg color and finds that the CardView area is still white.

For problem one, of course, you can change CardView to manually set a certain part to be rounded.
But the crux of the two problems is actually the same point, that is:

The default background of CardView with water ripple foreground is white

Solution: Change the default background to transparent, then the background style of the dialog bottom can be displayed.
<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"/>

You can see the settings here app:cardBackgroundColor="@color/transparent".

Here comes the problem, the above method can only set the color, what should I do if I want to set the drawable?
In fact, the solution is very simple: CardViewit is inherited from FrameLayout, which means that it can wrap content, and wrap other controls (such as ImageView) inside. The background with drawable can be realized through the wrapped control.

Then there is another problem. Since CardView inherits from FrameLayout, it means that it can set Background. But why is it not displayed after setting it?

This involves the entire View construction process:

public CardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    
    
    super(context, attrs, defStyleAttr);
//省略属性获取
    IMPL.initialize(mCardViewDelegate, context, backgroundColor, radius,
            elevation, maxElevation);
}

Initialize the View and enter the construction method, and IMPL.initializethe method will be called. What is this 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 is the implementation class of CardView, and different versions have different implementations. Enter CardViewApi21Impl here to see initializethe implementation.

@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);
}

It can be found that the call is made here cardView.setCardBackground, and the background is set according to the setting

backgroundColor = a.getColorStateList(R.styleable.CardView_cardBackgroundColor);

Generated by assigning RoundRectDrawable.
cardView.setCardBackground(background);The CardView in is that CardViewDelegateit is an interface, so it also depends on its implementation class. In the CardView class can be found:

private final CardViewDelegate mCardViewDelegate = new CardViewDelegate() {
    
    
    private Drawable mCardBackground;

    @Override
    public void setCardBackground(Drawable drawable) {
    
    
        mCardBackground = drawable;
        setBackgroundDrawable(drawable);
    }
    //...
}

And the above setBackgroundDrawableis to enter the View level.

So far, it is clear that because initializeit is called after the constructor of the parent class (FrameLayout), the final setting setBackgroundDrawablewill always override the method implementation of the parent class.

In the final analysis, it is a matter of successive coverage of implementation .

Guess you like

Origin blog.csdn.net/ganshenml/article/details/118583996