Skip to content

Commit

Permalink
[MaterialCardView] Added option to set the checkedIcon gravity
Browse files Browse the repository at this point in the history
Resolves #1679

GIT_ORIGIN_REV_ID=a35c862071d83ced72c4d7257f2b7bc93101bfbb
PiperOrigin-RevId: 407662373
  • Loading branch information
gabrielemariotti authored and drchen committed Nov 10, 2021
1 parent a369449 commit 250d56d
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 11 deletions.
15 changes: 8 additions & 7 deletions docs/components/Card.md
Expand Up @@ -380,13 +380,14 @@ border, regardless of the `app:strokeWidth` value.

#### Checked icon attributes

Element | Attribute | Related method(s) | Default value
------------- | ------------------- | ------------------------------------------------------------------------------------ | -------------
**Icon** | `checkedIcon` | `setCheckedIcon`<br/>`setCheckedIconResource`<br/>`getCheckedIcon` | [`@drawable/ic_mtrl_checked_circle.xml`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/resources/res/drawable/ic_mtrl_checked_circle.xml)
**Color** | `checkedIconTint` | `setCheckedIconTint`<br/>`getCheckedIconTint` | `?attr/colorOutline` (unchecked)<br/>`?attr/colorSecondary` (checked)
**Checkable** | `android:checkable` | `setCheckable`<br/>`isCheckable` | `false`
**Size** | `checkedIconSize` | `setCheckedIconSize`<br/>`setCheckedIconSizeResource`<br/>`getCheckedIconSize` | `24dp`
**Margin** | `checkedIconMargin` | `setCheckedIconMargin`<br/>`setCheckedIconMarginResource`<br/>`getCheckedIconMargin` | `8dp`
Element | Attribute | Related method(s) | Default value
------------- | -------------------- | ------------------------------------------------------------------------------------ | -------------
**Icon** | `checkedIcon` | `setCheckedIcon`<br/>`setCheckedIconResource`<br/>`getCheckedIcon` | [`@drawable/ic_mtrl_checked_circle.xml`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/resources/res/drawable/ic_mtrl_checked_circle.xml)
**Color** | `checkedIconTint` | `setCheckedIconTint`<br/>`getCheckedIconTint` | `?attr/colorOutline` (unchecked)<br/>`?attr/colorSecondary` (checked)
**Checkable** | `android:checkable` | `setCheckable`<br/>`isCheckable` | `false`
**Size** | `checkedIconSize` | `setCheckedIconSize`<br/>`setCheckedIconSizeResource`<br/>`getCheckedIconSize` | `24dp`
**Margin** | `checkedIconMargin` | `setCheckedIconMargin`<br/>`setCheckedIconMarginResource`<br/>`getCheckedIconMargin` | `8dp`
**Gravity** | `checkedIconGravity` | `setCheckedIconGravity`<br/>`getCheckedIconGravity` | `TOP_END`

#### States

Expand Down
70 changes: 70 additions & 0 deletions lib/java/com/google/android/material/card/MaterialCardView.java
Expand Up @@ -40,6 +40,7 @@
import androidx.annotation.Dimension;
import androidx.annotation.DrawableRes;
import androidx.annotation.FloatRange;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
Expand All @@ -48,6 +49,8 @@
import com.google.android.material.shape.MaterialShapeUtils;
import com.google.android.material.shape.ShapeAppearanceModel;
import com.google.android.material.shape.Shapeable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* Provides a Material card.
Expand Down Expand Up @@ -94,6 +97,48 @@ public interface OnCheckedChangeListener {
private static final String LOG_TAG = "MaterialCardView";
private static final String ACCESSIBILITY_CLASS_NAME = "androidx.cardview.widget.CardView";

/**
* Gravity used to position the checked icon at the top|start of the Card.
*
* @see #setCheckedIconGravity(int)
* @see #getCheckedIconGravity()
*/
public static final int CHECKED_ICON_GRAVITY_TOP_START = 0x1;

/**
* Gravity used to position the checked icon at the bottom|start of the Card.
*
* @see #setCheckedIconGravity(int)
* @see #getCheckedIconGravity()
*/
public static final int CHECKED_ICON_GRAVITY_BOTTOM_START = 0x2;

/**
* Gravity used to position the checked icon at the top|end of the Card.
*
* @see #setCheckedIconGravity(int)
* @see #getCheckedIconGravity()
*/
public static final int CHECKED_ICON_GRAVITY_TOP_END = 0x3;

/**
* Gravity used to position the checked icon at the bottom|end of the Card.
*
* @see #setCheckedIconGravity(int)
* @see #getCheckedIconGravity()
*/
public static final int CHECKED_ICON_GRAVITY_BOTTOM_END = 0x4;

/** Positions the icon can be set to. */
@IntDef({
CHECKED_ICON_GRAVITY_TOP_START,
CHECKED_ICON_GRAVITY_BOTTOM_START,
CHECKED_ICON_GRAVITY_TOP_END,
CHECKED_ICON_GRAVITY_BOTTOM_END
})
@Retention(RetentionPolicy.SOURCE)
public @interface CheckedIconGravity{}

@NonNull private final MaterialCardViewHelper cardViewHelper;

/**
Expand Down Expand Up @@ -635,4 +680,29 @@ private void forceRippleRedrawIfNeeded() {
cardViewHelper.forceRippleRedraw();
}
}

/**
* Gets the checked icon gravity for this card
*
* @return Checked Icon gravity of the card.
* @attr ref com.google.android.material.R.styleable#MaterialCard_checkedIconGravity
* @see #setCheckedIconGravity(int)
*/
@CheckedIconGravity
public int getCheckedIconGravity() {
return cardViewHelper.getCheckedIconGravity();
}

/**
* Sets the checked icon gravity for this card
*
* @attr ref com.google.android.material.R.styleable#MaterialCard_checkedIconGravity
* @param checkedIconGravity checked icon gravity for this card
* @see #getCheckedIconGravity()
*/
public void setCheckedIconGravity(@CheckedIconGravity int checkedIconGravity) {
if (cardViewHelper.getCheckedIconGravity() != checkedIconGravity) {
cardViewHelper.setCheckedIconGravity(checkedIconGravity);
}
}
}
Expand Up @@ -19,6 +19,9 @@
import com.google.android.material.R;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static com.google.android.material.card.MaterialCardView.CHECKED_ICON_GRAVITY_BOTTOM_END;
import static com.google.android.material.card.MaterialCardView.CHECKED_ICON_GRAVITY_BOTTOM_START;
import static com.google.android.material.card.MaterialCardView.CHECKED_ICON_GRAVITY_TOP_END;

import android.content.res.ColorStateList;
import android.content.res.TypedArray;
Expand All @@ -45,6 +48,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleRes;
import androidx.cardview.widget.CardView;
import com.google.android.material.card.MaterialCardView.CheckedIconGravity;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.resources.MaterialResources;
import com.google.android.material.ripple.RippleUtils;
Expand Down Expand Up @@ -96,6 +100,7 @@ class MaterialCardViewHelper {

@Dimension private int checkedIconMargin;
@Dimension private int checkedIconSize;
@CheckedIconGravity private int checkedIconGravity;
@Dimension private int strokeWidth;

// If card is clickable, this is the clickableForegroundDrawable otherwise it draws the stroke.
Expand Down Expand Up @@ -161,6 +166,9 @@ void loadFromAttributes(@NonNull TypedArray attributes) {
attributes.getDimensionPixelSize(R.styleable.MaterialCardView_checkedIconSize, 0));
setCheckedIconMargin(
attributes.getDimensionPixelSize(R.styleable.MaterialCardView_checkedIconMargin, 0));
checkedIconGravity =
attributes.getInteger(
R.styleable.MaterialCardView_checkedIconGravity, CHECKED_ICON_GRAVITY_TOP_END);

rippleColor =
MaterialResources.getColorStateList(
Expand Down Expand Up @@ -413,15 +421,30 @@ void setCheckedIconMargin(@Dimension int checkedIconMargin) {

void onMeasure(int measuredWidth, int measuredHeight) {
if (clickableForegroundDrawable != null) {
int left = measuredWidth - checkedIconMargin - checkedIconSize;
int bottom = measuredHeight - checkedIconMargin - checkedIconSize;
int left =
isCheckedIconEnd()
? measuredWidth - checkedIconMargin - checkedIconSize
: checkedIconMargin;
int bottom =
isCheckedIconBottom()
? checkedIconMargin
: measuredHeight - checkedIconMargin - checkedIconSize;

boolean isPreLollipop = VERSION.SDK_INT < VERSION_CODES.LOLLIPOP;
if (isPreLollipop || materialCardView.getUseCompatPadding()) {
bottom -= (int) Math.ceil(2f * calculateVerticalBackgroundPadding());
left -= (int) Math.ceil(2f * calculateHorizontalBackgroundPadding());
}

int right = checkedIconMargin;
int right =
isCheckedIconEnd()
? checkedIconMargin
: measuredWidth - checkedIconMargin - checkedIconSize;
int top =
isCheckedIconBottom()
? measuredHeight - checkedIconMargin - checkedIconSize
: checkedIconMargin;

if (ViewCompat.getLayoutDirection(materialCardView) == ViewCompat.LAYOUT_DIRECTION_RTL) {
// swap left and right
int tmp = right;
Expand All @@ -430,7 +453,7 @@ void onMeasure(int measuredWidth, int measuredHeight) {
}

clickableForegroundDrawable.setLayerInset(
CHECKED_ICON_LAYER_INDEX, left, checkedIconMargin /* top */, right, bottom);
CHECKED_ICON_LAYER_INDEX, left, top /* top */, right, bottom);
}
}

Expand Down Expand Up @@ -656,4 +679,23 @@ public void setChecked(boolean checked) {
checkedIcon.setAlpha(checked ? 255 : 0);
}
}

@CheckedIconGravity
int getCheckedIconGravity() {
return checkedIconGravity;
}

void setCheckedIconGravity(@CheckedIconGravity int checkedIconGravity) {
this.checkedIconGravity = checkedIconGravity;
}

private boolean isCheckedIconEnd() {
return checkedIconGravity == CHECKED_ICON_GRAVITY_TOP_END
|| checkedIconGravity == CHECKED_ICON_GRAVITY_BOTTOM_END;
}

private boolean isCheckedIconBottom() {
return checkedIconGravity == CHECKED_ICON_GRAVITY_BOTTOM_START
|| checkedIconGravity == CHECKED_ICON_GRAVITY_BOTTOM_END;
}
}
Expand Up @@ -18,6 +18,7 @@
<public name="cardForegroundColor" type="attr"/>
<public name="checkedIconSize" type="attr"/>
<public name="checkedIconMargin" type="attr"/>
<public name="checkedIconGravity" type="attr"/>
<public name="materialCardViewStyle" type="attr"/>
<public name="materialCardViewOutlinedStyle" type="attr"/>
<public name="materialCardViewFilledStyle" type="attr"/>
Expand Down
11 changes: 11 additions & 0 deletions lib/java/com/google/android/material/card/res/values/attrs.xml
Expand Up @@ -45,6 +45,17 @@
<attr name="checkedIconSize" format="dimension"/>
<!-- Check icon margin for Checkable Cards-->
<attr name="checkedIconMargin" format="dimension"/>
<!-- Specifies how the checked icon should be positioned. -->
<attr name="checkedIconGravity">
<!-- Push icon to the top|start of the card. -->
<flag name="TOP_START" value="0x1"/>
<!-- Push icon to the bottom|start of the card. -->
<flag name="BOTTOM_START" value="0x2"/>
<!-- Push icon to the top|end of the card. -->
<flag name="TOP_END" value="0x3"/>
<!-- Push icon to the bottom|end of the card. -->
<flag name="BOTTOM_END" value="0x4"/>
</attr>
<!-- Ripple color for the Card. -->
<attr name="rippleColor"/>
<!-- State when a Card is being dragged. -->
Expand Down

0 comments on commit 250d56d

Please sign in to comment.