Skip to content

Commit

Permalink
[MaterialSwitch] Amendments to thumb icon size support
Browse files Browse the repository at this point in the history
Resolves #3364

PiperOrigin-RevId: 549627031
  • Loading branch information
paulfthomas authored and pekingme committed Jul 20, 2023
1 parent ed9b541 commit db9a641
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 102 deletions.
@@ -0,0 +1,27 @@
<!--
~ Copyright (C) 2023 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="48dp"
android:height="48dp"
android:tint="?attr/colorControlNormal"
android:viewportHeight="960"
android:viewportWidth="960"
tools:ignore="NewApi">
<path
android:fillColor="@android:color/white"
android:pathData="M378,714L154,490L197,447L378,628L762,244L805,287L378,714Z" />
</vector>
Expand Up @@ -16,10 +16,10 @@
limitations under the License.
-->

<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<LinearLayout
android:id="@+id/main_viewGroup"
Expand Down Expand Up @@ -75,6 +75,30 @@
android:checked="false"
android:enabled="false"
android:text="@string/cat_switch_disabled" />

<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_columnSpan="2"
android:text="@string/cat_switch_icon_guide"
android:textSize="@dimen/cat_switch_header_size" />

<com.google.android.material.materialswitch.MaterialSwitch
android:layout_width="match_parent"
android:layout_height="match_parent"
android:checked="true"
android:enabled="true"
app:thumbIcon="@drawable/mtrl_ic_check"
android:text="@string/cat_switch_enabled" />

<com.google.android.material.materialswitch.MaterialSwitch
android:layout_width="match_parent"
android:layout_height="match_parent"
android:checked="true"
android:enabled="true"
app:thumbIcon="@drawable/mtrl_ic_check"
app:thumbIconSize="24dp"
android:text="@string/cat_switch_enabled" />
</GridLayout>

<ImageView
Expand Down
Expand Up @@ -33,6 +33,10 @@
description="The description of how switches look like when disabled. [CHAR LIMIT=NONE]">
Switch elements that are disabled cannot be interacted with and will appear faded.
</string>
<string name="cat_switch_icon_guide"
description="The description of how switches look like with icon. [CHAR LIMIT=NONE]">
Switch elements with an icon, default size (16dp) and 24dp.
</string>
<string name="cat_switch_enabled"
description="The label of enabled switches. [CHAR LIMIT=NONE]">
Enabled
Expand Down
14 changes: 5 additions & 9 deletions docs/components/Switch.md
Expand Up @@ -154,15 +154,11 @@ You can add an optional icon to enhance the on/off indication of your custom
switch by assigning `app:thumbIcon`. This icon will be centered and displayed on
top of the thumb drawable.

Element | Attribute | Related method(s) | Default value
---------- |-----------------------|---------------------------------------------------| -------------
**Icon** | `app:thumbIcon` | `setThumbIconDrawable`<br/>`getThumbIconDrawable` | `null`
**Width** | `app:thumbIconWidth` | `setThumbIconWidth`<br/>`getThumbIconWidth` | Intrinsic width
**Height** | `app:thumbIconHeight` | `setThumbIconHeight`<br/>`getThumbIconHeight` | Intrinsic height
**Color** | `app:thumbIconTint` | `setThumbIconTintList`<br/>`getThumbIconTintList` | `?attr/colorSurfaceVariant` (unchecked)<br/>`?attr/colorOnPrimaryContainer` (checked)

**Note:** Custom thumb icon width/height is supported only for API 23 and above.
For API < 23, the intrinsic size of the thumb icon will always be used.
Element | Attribute | Related method(s) | Default value
--------- |---------------------|---------------------------------------------------| -------------
**Icon** | `app:thumbIcon` | `setThumbIconDrawable`<br/>`getThumbIconDrawable` | `null`
**Size** | `app:thumbIconSize` | `setThumbIconSize`<br/>`getThumbIconSize` | `16dp`
**Color** | `app:thumbIconTint` | `setThumbIconTintList`<br/>`getThumbIconTintList` | `?attr/colorSurfaceVariant` (unchecked)<br/>`?attr/colorOnPrimaryContainer` (checked)

### Track attributes

Expand Down
63 changes: 29 additions & 34 deletions lib/java/com/google/android/material/drawable/DrawableUtils.java
Expand Up @@ -42,7 +42,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.XmlRes;
Expand All @@ -64,7 +63,7 @@ public final class DrawableUtils {

/**
* Indicates to use the intrinsic size of the {@link Drawable}.
*
*
* <p>Used in {@link #compositeTwoLayeredDrawable(Drawable, Drawable, int, int)}.
*/
public static final int INTRINSIC_SIZE = -1;
Expand Down Expand Up @@ -211,7 +210,7 @@ private static Drawable createTintableMutatedDrawableIfNeeded(
/**
* Composites two drawables, returning a drawable instance of {@link LayerDrawable},
* with the top layer centered.
*
*
* <p>If any of the drawables is null, this method will return the other.
*
* @param bottomLayerDrawable the drawable to be on the bottom layer
Expand All @@ -221,35 +220,16 @@ private static Drawable createTintableMutatedDrawableIfNeeded(
public static Drawable compositeTwoLayeredDrawable(
@Nullable Drawable bottomLayerDrawable,
@Nullable Drawable topLayerDrawable) {
if (VERSION.SDK_INT >= VERSION_CODES.M) {
return compositeTwoLayeredDrawable(
bottomLayerDrawable, topLayerDrawable, INTRINSIC_SIZE, INTRINSIC_SIZE);
}

if (bottomLayerDrawable == null) {
return topLayerDrawable;
}
if (topLayerDrawable == null) {
return bottomLayerDrawable;
}
LayerDrawable drawable =
new LayerDrawable(new Drawable[] {bottomLayerDrawable, topLayerDrawable});
int horizontalInset =
max(bottomLayerDrawable.getIntrinsicWidth()
- getTopLayerIntrinsicWidth(bottomLayerDrawable, topLayerDrawable), 0) / 2;
int verticalInset =
max(bottomLayerDrawable.getIntrinsicHeight()
- getTopLayerIntrinsicHeight(bottomLayerDrawable, topLayerDrawable), 0) / 2;
drawable.setLayerInset(1, horizontalInset, verticalInset, horizontalInset, verticalInset);
return drawable;
return compositeTwoLayeredDrawable(
bottomLayerDrawable, topLayerDrawable, INTRINSIC_SIZE, INTRINSIC_SIZE);
}

/**
* Composites two drawables, returning a drawable instance of {@link LayerDrawable},
* with the top layer centered to the bottom layer. The top layer will be scaled according to the
* provided desired width/height and the size of the bottom layer so the top layer can fit in the
* bottom layer and preserve its desired aspect ratio.
*
*
* <p>If any of the drawables is null, this method will return the other.
*
* @param bottomLayerDrawable the drawable to be on the bottom layer
Expand All @@ -259,7 +239,6 @@ public static Drawable compositeTwoLayeredDrawable(
* @param topLayerDesiredHeight top layer desired height in pixels, or {@link #INTRINSIC_SIZE} to
* use the intrinsic height.
*/
@RequiresApi(VERSION_CODES.M)
@Nullable
public static Drawable compositeTwoLayeredDrawable(
@Nullable Drawable bottomLayerDrawable,
Expand All @@ -272,8 +251,6 @@ public static Drawable compositeTwoLayeredDrawable(
if (topLayerDrawable == null) {
return bottomLayerDrawable;
}
LayerDrawable drawable =
new LayerDrawable(new Drawable[] {bottomLayerDrawable, topLayerDrawable});

if (topLayerDesiredWidth == INTRINSIC_SIZE) {
topLayerDesiredWidth = getTopLayerIntrinsicWidth(bottomLayerDrawable, topLayerDrawable);
Expand Down Expand Up @@ -308,23 +285,41 @@ public static Drawable compositeTwoLayeredDrawable(
}
}

drawable.setLayerSize(1, topLayerNewWidth, topLayerNewHeight);
drawable.setLayerGravity(1, Gravity.CENTER);
LayerDrawable drawable;
if (VERSION.SDK_INT >= VERSION_CODES.M) {
drawable = new LayerDrawable(new Drawable[] {bottomLayerDrawable, topLayerDrawable});

drawable.setLayerSize(1, topLayerNewWidth, topLayerNewHeight);
drawable.setLayerGravity(1, Gravity.CENTER);
} else {
Drawable scaledTopLayerDrawable =
new ScaledDrawableWrapper(topLayerDrawable, topLayerNewWidth, topLayerNewHeight)
.getDrawable();

drawable = new LayerDrawable(new Drawable[] {bottomLayerDrawable, scaledTopLayerDrawable});

final int horizontalInset =
max((bottomLayerDrawable.getIntrinsicWidth() - topLayerNewWidth) / 2, 0);
final int verticalInset =
max((bottomLayerDrawable.getIntrinsicHeight() - topLayerNewHeight) / 2, 0);
drawable.setLayerInset(1, horizontalInset, verticalInset, horizontalInset, verticalInset);
}

return drawable;
}

private static int getTopLayerIntrinsicWidth(
@NonNull Drawable bottomLayerDrawable, @NonNull Drawable topLayerDrawable) {
int topLayerIntrinsicWidth = topLayerDrawable.getIntrinsicWidth();
return topLayerIntrinsicWidth == UNSPECIFIED_WIDTH
? bottomLayerDrawable.getIntrinsicWidth() : topLayerIntrinsicWidth;
return topLayerIntrinsicWidth != UNSPECIFIED_WIDTH
? topLayerIntrinsicWidth : bottomLayerDrawable.getIntrinsicWidth();
}

private static int getTopLayerIntrinsicHeight(
@NonNull Drawable bottomLayerDrawable, @NonNull Drawable topLayerDrawable) {
int topLayerIntrinsicHeight = topLayerDrawable.getIntrinsicHeight();
return topLayerIntrinsicHeight == UNSPECIFIED_HEIGHT
? bottomLayerDrawable.getIntrinsicHeight() : topLayerIntrinsicHeight;
return topLayerIntrinsicHeight != UNSPECIFIED_HEIGHT
? topLayerIntrinsicHeight : bottomLayerDrawable.getIntrinsicHeight();
}

/** Returns a new state that adds the checked state to the input state. */
Expand Down
@@ -0,0 +1,49 @@
/*
* Copyright 2023 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.drawable;

import android.graphics.drawable.Drawable;
import androidx.appcompat.graphics.drawable.DrawableWrapperCompat;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;

/**
* An extension of {@link DrawableWrapperCompat} that will take a given Drawable and scale it by the
* specified width and height.
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public class ScaledDrawableWrapper extends DrawableWrapperCompat {
private final int width;
private final int height;

public ScaledDrawableWrapper(@NonNull Drawable drawable, int width, int height) {
super(drawable);
this.width = width;
this.height = height;
}

@Override
public int getIntrinsicWidth() {
return width;
}

@Override
public int getIntrinsicHeight() {
return height;
}
}

0 comments on commit db9a641

Please sign in to comment.