mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-17 10:21:51 +08:00
When content can be clipped to the shape (round rect, Lollipop+), the content will be clipped to the interior of the stroke. When preventCornerOverlap is true, if the content cannot be clipped, extra padding is added such that the entirety of the content is inside the stroke. Any contentPadding is additional to this padding. In order to keep centered content centered, the same amount of padding is added in each direction when preventCornerOverlap is true. PiperOrigin-RevId: 223045615
308 lines
9.6 KiB
Java
308 lines
9.6 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.android.material.card;
|
|
|
|
import com.google.android.material.R;
|
|
|
|
import static com.google.android.material.internal.ThemeEnforcement.createThemedContext;
|
|
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.ColorStateList;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build.VERSION;
|
|
import android.os.Build.VERSION_CODES;
|
|
import android.support.annotation.ColorInt;
|
|
import android.support.annotation.Dimension;
|
|
import android.support.annotation.Nullable;
|
|
import com.google.android.material.internal.ThemeEnforcement;
|
|
import android.support.v7.widget.CardView;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.FrameLayout;
|
|
|
|
/**
|
|
* Provides a Material card.
|
|
*
|
|
* <p>This class supplies Material styles for the card in the constructor. The widget will display
|
|
* the correct default Material styles without the use of a style flag.
|
|
*
|
|
* <p>Stroke width can be set using the {@code strokeWidth} attribute. Set the stroke color using
|
|
* the {@code strokeColor} attribute. Without a {@code strokeColor}, the card will not render a
|
|
* stroked border, regardless of the {@code strokeWidth} value.
|
|
*
|
|
* * <p><strong>Note:</strong> Avoid setting {@link View#setClipToOutline} to true. There is an
|
|
* intermediate view to clip the content, setting this will have negative performance consequences.
|
|
*
|
|
* <p><strong>Note:</strong> The actual view hierarchy present under MaterialCardView is
|
|
* <strong>NOT</strong> guaranteed to match the view hierarchy as written in XML. As a result, calls
|
|
* to getParent() on children of the MaterialCardView, will not return the MaterialCardView itself,
|
|
* but rather an intermediate View. If you need to access a MaterialCardView directly,
|
|
* set an {@code android:id} and use {@link View#findViewById(int)}.
|
|
*/
|
|
public class MaterialCardView extends CardView {
|
|
|
|
private static final int DEF_STYLE_RES = R.style.Widget_MaterialComponents_CardView;
|
|
private static final String LOG_TAG = "MaterialCardView";
|
|
|
|
private final MaterialCardViewHelper cardViewHelper;
|
|
private final FrameLayout contentLayout;
|
|
|
|
/**
|
|
* Keep track of when {@link CardView} is done initializing because we don't want to use the
|
|
* {@link Drawable} that it passes to {@link #setBackground(Drawable)}.
|
|
*/
|
|
private final boolean isParentCardViewDoneInitializing;
|
|
|
|
public MaterialCardView(Context context) {
|
|
this(context, null /* attrs */);
|
|
}
|
|
|
|
public MaterialCardView(Context context, AttributeSet attrs) {
|
|
this(context, attrs, R.attr.materialCardViewStyle);
|
|
}
|
|
|
|
public MaterialCardView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
super(createThemedContext(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr);
|
|
isParentCardViewDoneInitializing = true;
|
|
// Ensure we are using the correctly themed context rather than the context that was passed in.
|
|
context = getContext();
|
|
|
|
TypedArray attributes =
|
|
ThemeEnforcement.obtainStyledAttributes(
|
|
context, attrs, R.styleable.MaterialCardView, defStyleAttr, DEF_STYLE_RES);
|
|
|
|
// Loads and sets background drawable attributes.
|
|
cardViewHelper = new MaterialCardViewHelper(this, attrs, defStyleAttr, DEF_STYLE_RES);
|
|
// Get the card background color and content padding that CardView read from the attributes.
|
|
cardViewHelper.setCardBackgroundColor(super.getCardBackgroundColor());
|
|
cardViewHelper.setUserContentPadding(
|
|
super.getContentPaddingLeft(),
|
|
super.getContentPaddingTop(),
|
|
super.getContentPaddingRight(),
|
|
super.getContentPaddingBottom());
|
|
cardViewHelper.loadFromAttributes(attributes);
|
|
|
|
// Add a content view to allow the border to be drawn outside the outline.
|
|
contentLayout = new FrameLayout(context);
|
|
super.addView(contentLayout, -1, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
|
updateContentLayout();
|
|
|
|
attributes.recycle();
|
|
}
|
|
|
|
private void updateContentLayout() {
|
|
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
|
cardViewHelper.createOutlineProvider(contentLayout);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the stroke color of this card view.
|
|
*
|
|
* @param strokeColor The color of the stroke.
|
|
*/
|
|
public void setStrokeColor(@ColorInt int strokeColor) {
|
|
cardViewHelper.setStrokeColor(strokeColor);
|
|
}
|
|
|
|
/** Returns the stroke color of this card view. */
|
|
@ColorInt
|
|
public int getStrokeColor() {
|
|
return cardViewHelper.getStrokeColor();
|
|
}
|
|
|
|
/**
|
|
* Sets the stroke width of this card view.
|
|
*
|
|
* @param strokeWidth The width in pixels of the stroke.
|
|
*/
|
|
public void setStrokeWidth(@Dimension int strokeWidth) {
|
|
cardViewHelper.setStrokeWidth(strokeWidth);
|
|
updateContentLayout();
|
|
}
|
|
|
|
/** Returns the stroke width of this card view. */
|
|
@Dimension
|
|
public int getStrokeWidth() {
|
|
return cardViewHelper.getStrokeWidth();
|
|
}
|
|
|
|
@Override
|
|
public void setRadius(float radius) {
|
|
super.setRadius(radius);
|
|
cardViewHelper.setCornerRadius(radius);
|
|
updateContentLayout();
|
|
}
|
|
|
|
@Override
|
|
public float getRadius() {
|
|
return cardViewHelper.getCornerRadius();
|
|
}
|
|
|
|
float getCardViewRadius() {
|
|
return MaterialCardView.super.getRadius();
|
|
}
|
|
|
|
@Override
|
|
public void setContentPadding(int left, int top, int right, int bottom) {
|
|
cardViewHelper.setUserContentPadding(left, top, right, bottom);
|
|
}
|
|
|
|
@Override
|
|
public int getContentPaddingLeft() {
|
|
return cardViewHelper.getUserContentPadding().left;
|
|
}
|
|
|
|
@Override
|
|
public int getContentPaddingTop() {
|
|
return cardViewHelper.getUserContentPadding().top;
|
|
}
|
|
|
|
@Override
|
|
public int getContentPaddingRight() {
|
|
return cardViewHelper.getUserContentPadding().right;
|
|
}
|
|
|
|
@Override
|
|
public int getContentPaddingBottom() {
|
|
return cardViewHelper.getUserContentPadding().bottom;
|
|
}
|
|
|
|
@Override
|
|
public void setCardBackgroundColor(@ColorInt int color) {
|
|
cardViewHelper.setCardBackgroundColor(ColorStateList.valueOf(color));
|
|
}
|
|
|
|
@Override
|
|
public void setCardBackgroundColor(@Nullable ColorStateList color) {
|
|
cardViewHelper.setCardBackgroundColor(color);
|
|
}
|
|
|
|
@Override
|
|
public ColorStateList getCardBackgroundColor() {
|
|
return cardViewHelper.getCardBackgroundColor();
|
|
}
|
|
|
|
@Override
|
|
public void setLayoutParams(ViewGroup.LayoutParams params) {
|
|
super.setLayoutParams(params);
|
|
LayoutParams layoutParams = (LayoutParams) contentLayout.getLayoutParams();
|
|
if (params instanceof LayoutParams) {
|
|
layoutParams.gravity = ((LayoutParams) params).gravity;
|
|
contentLayout.requestLayout();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setClickable(boolean clickable) {
|
|
super.setClickable(clickable);
|
|
cardViewHelper.updateClickable();
|
|
}
|
|
|
|
@Override
|
|
public void setCardElevation(float elevation) {
|
|
super.setCardElevation(elevation);
|
|
cardViewHelper.updateElevation();
|
|
}
|
|
|
|
@Override
|
|
public void setMaxCardElevation(float maxCardElevation) {
|
|
super.setMaxCardElevation(maxCardElevation);
|
|
cardViewHelper.updateInsets();
|
|
}
|
|
|
|
@Override
|
|
public void setUseCompatPadding(boolean useCompatPadding) {
|
|
super.setUseCompatPadding(useCompatPadding);
|
|
cardViewHelper.updateInsets();
|
|
cardViewHelper.updateContentPadding();
|
|
}
|
|
|
|
@Override
|
|
public void setPreventCornerOverlap(boolean preventCornerOverlap) {
|
|
super.setPreventCornerOverlap(preventCornerOverlap);
|
|
cardViewHelper.updateInsets();
|
|
cardViewHelper.updateContentPadding();
|
|
}
|
|
|
|
@Override
|
|
public void addView(View child, int index, ViewGroup.LayoutParams params) {
|
|
contentLayout.addView(child, index, params);
|
|
}
|
|
|
|
@Override
|
|
public void removeAllViews() {
|
|
contentLayout.removeAllViews();
|
|
}
|
|
|
|
@Override
|
|
public void removeView(View view) {
|
|
contentLayout.removeView(view);
|
|
}
|
|
|
|
@Override
|
|
public void removeViewInLayout(View view) {
|
|
contentLayout.removeViewInLayout(view);
|
|
}
|
|
|
|
@Override
|
|
public void removeViewsInLayout(int start, int count) {
|
|
contentLayout.removeViewsInLayout(start, count);
|
|
}
|
|
|
|
@Override
|
|
public void removeViewAt(int index) {
|
|
contentLayout.removeViewAt(index);
|
|
}
|
|
|
|
@Override
|
|
public void removeViews(int start, int count) {
|
|
contentLayout.removeViews(start, count);
|
|
}
|
|
|
|
@Override
|
|
public void setBackground(Drawable drawable) {
|
|
setBackgroundDrawable(drawable);
|
|
}
|
|
|
|
@Override
|
|
public void setBackgroundDrawable(Drawable drawable) {
|
|
if (isParentCardViewDoneInitializing) {
|
|
if (!cardViewHelper.isBackgroundOverwritten()) {
|
|
Log.i(LOG_TAG, "Setting a custom background is not supported.");
|
|
cardViewHelper.setBackgroundOverwritten(true);
|
|
}
|
|
super.setBackgroundDrawable(drawable);
|
|
}
|
|
// Do nothing if CardView isn't done initializing because we don't want to use its background.
|
|
}
|
|
|
|
/** Allows {@link MaterialCardViewHelper} to set the background. */
|
|
void setBackgroundInternal(Drawable drawable) {
|
|
super.setBackgroundDrawable(drawable);
|
|
}
|
|
|
|
void setContentPaddingInternal(int left, int top, int right, int bottom) {
|
|
super.setContentPadding(left, top, right, bottom);
|
|
}
|
|
|
|
}
|