Add a way to disable min touch target size in fab

PiperOrigin-RevId: 239395098
This commit is contained in:
marianomartin 2019-03-20 10:46:19 -04:00 committed by cketcham
parent b3793edd2c
commit 05bc55723a
10 changed files with 175 additions and 11 deletions

View File

@ -277,10 +277,6 @@ public class Chip extends AppCompatCheckBox implements Delegate, Shapeable {
}
}
private boolean shouldEnsureMinTouchTargetSize() {
return ensureMinTouchTargetSize;
}
private void initMinTouchTarget(Context context, AttributeSet attrs, int defStyleAttr) {
if (attrs == null) {
return;
@ -2257,7 +2253,7 @@ public class Chip extends AppCompatCheckBox implements Delegate, Shapeable {
* @see #setEnsureMinTouchTargetSize(boolean)
* @attr ref com.google.android.material.R.styleable#Chip_ensureMinTouchTargetSize
*/
public boolean getEnsureMinTouchTargetSize() {
public boolean shouldEnsureMinTouchTargetSize() {
return ensureMinTouchTargetSize;
}

View File

@ -41,7 +41,7 @@
<!-- Minimum size of chip's touch target, by default, Android recommended 48dp. -->
<attr name="chipMinTouchTargetSize" format="dimension"/>
<!-- Whether to extend the bounds of chip to meet chipMinTouchTargetSize. -->
<attr name="ensureMinTouchTargetSize" format="boolean"/>
<attr name="ensureMinTouchTargetSize"/>
<!-- Text to display on the chip. -->
<attr name="android:text"/>

View File

@ -236,6 +236,9 @@ public class FloatingActionButton extends VisibilityAwareImageButton
new ShapeAppearanceModel(context, attrs, defStyleAttr, DEF_STYLE_RES, -1);
boolean usingDefaultCorner = isUsingDefaultCorner(shapeAppearance);
boolean ensureMinTouchTargetSize = a
.getBoolean(R.styleable.FloatingActionButton_ensureMinTouchTargetSize, false);
a.recycle();
imageHelper = new AppCompatImageHelper(this);
@ -252,6 +255,7 @@ public class FloatingActionButton extends VisibilityAwareImageButton
getImpl().setMaxImageSize(maxImageSize);
getImpl().setShowMotionSpec(showMotionSpec);
getImpl().setHideMotionSpec(hideMotionSpec);
getImpl().setEnsureMinTouchTargetSize(ensureMinTouchTargetSize);
setScaleType(ScaleType.MATRIX);
}
@ -514,6 +518,31 @@ public class FloatingActionButton extends VisibilityAwareImageButton
return checkNotNull(getImpl().getShapeAppearance());
}
/**
* Returns whether this fab will expand its bounds (if needed) to meet the minimum touch target
* size.
*
* @see #setEnsureMinTouchTargetSize(boolean)
* @attr ref com.google.android.material.R.styleable#FloatingActionButton_ensureMinTouchTargetSize
*/
public boolean shouldEnsureMinTouchTargetSize() {
return getImpl().getEnsureMinTouchTargetSize();
}
/**
* Sets whether this FloatingActionButton should expand its bounds (if needed) to meet the minimum
* touch target size.
*
* @attr ref com.google.android.material.R.styleable#FloatingActionButton_ensureMinTouchTargetSize
*/
public void setEnsureMinTouchTargetSize(boolean flag) {
if (flag != getImpl().getEnsureMinTouchTargetSize()) {
getImpl().setEnsureMinTouchTargetSize(flag);
requestLayout();
}
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);

View File

@ -83,6 +83,7 @@ class FloatingActionButtonImpl {
@Nullable Drawable contentBackground;
boolean usingDefaultCorner;
boolean ensureMinTouchTargetSize;
float elevation;
float hoveredFocusedTranslationZ;
float pressedTranslationZ;
@ -341,8 +342,16 @@ class FloatingActionButtonImpl {
hideMotionSpec = spec;
}
final boolean isAccessible() {
return view.getSizeDimension() >= minTouchTargetSize;
final boolean shouldExpandBoundsForA11y() {
return !ensureMinTouchTargetSize || view.getSizeDimension() >= minTouchTargetSize;
}
boolean getEnsureMinTouchTargetSize() {
return ensureMinTouchTargetSize;
}
void setEnsureMinTouchTargetSize(boolean flag) {
ensureMinTouchTargetSize = flag;
}
void onElevationsChanged(
@ -641,7 +650,10 @@ class FloatingActionButtonImpl {
}
void getPadding(Rect rect) {
final int minPadding = (minTouchTargetSize - view.getSizeDimension()) / 2;
final int minPadding = ensureMinTouchTargetSize
? (minTouchTargetSize - view.getSizeDimension()) / 2
: 0;
final float maxShadowSize = (getElevation() + pressedTranslationZ);
final int hPadding = Math.max(minPadding, (int) Math.ceil(maxShadowSize));
final int vPadding = Math.max(minPadding, (int) Math.ceil(maxShadowSize * SHADOW_MULTIPLIER));

View File

@ -173,7 +173,7 @@ class FloatingActionButtonImplLollipop extends FloatingActionButtonImpl {
@Override
boolean shouldAddPadding() {
return shadowViewDelegate.isCompatPaddingEnabled() || !isAccessible();
return shadowViewDelegate.isCompatPaddingEnabled() || !shouldExpandBoundsForA11y();
}
@Override
@ -236,7 +236,7 @@ class FloatingActionButtonImplLollipop extends FloatingActionButtonImpl {
void getPadding(Rect rect) {
if (shadowViewDelegate.isCompatPaddingEnabled()) {
super.getPadding(rect);
} else if (!isAccessible()) {
} else if (!shouldExpandBoundsForA11y()) {
int minPadding = (minTouchTargetSize - view.getSizeDimension()) / 2;
rect.set(minPadding, minPadding, minPadding, minPadding);
} else {

View File

@ -36,6 +36,9 @@
<attr name="fabCustomSize" format="dimension"/>
<!-- Elevation value for the FAB -->
<attr name="elevation"/>
<!-- Whether to extend the bounds of the FloatingActionButton to meet
@dimen/mtrl_fab_min_touch_target. -->
<attr name="ensureMinTouchTargetSize"/>
<!-- TranslationZ value for the FAB when hovered, focused, or hovered and focused. -->
<attr name="hoveredFocusedTranslationZ" format="dimension"/>
<!-- TranslationZ value for the FAB when pressed-->

View File

@ -35,6 +35,7 @@
<style name="Widget.MaterialComponents.FloatingActionButton" parent="Widget.Design.FloatingActionButton">
<item name="android:background">@null</item>
<item name="enforceMaterialTheme">true</item>
<item name="ensureMinTouchTargetSize">true</item>
<item name="elevation">@dimen/mtrl_fab_elevation</item>
<item name="backgroundTint">?attr/colorSecondary</item>
<item name="tint">?attr/colorOnSecondary</item>

View File

@ -21,4 +21,5 @@
<attr name="singleSelection" format="boolean"/>
<attr name="strokeColor" format="color"/>
<attr name="strokeWidth" format="dimension"/>
<attr name="ensureMinTouchTargetSize" format="boolean"/>
</resources>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.material.floatingactionbutton">
<uses-sdk
tools:overrideLibrary="androidx.test.core"/>
<application/>
</manifest>

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2019 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.floatingactionbutton;
import com.google.android.material.R;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static com.google.android.material.floatingactionbutton.FloatingActionButton.SIZE_MINI;
import static com.google.android.material.internal.ViewUtils.dpToPx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View.MeasureSpec;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.internal.DoNotInstrument;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = LOLLIPOP)
@DoNotInstrument
public class FabTest {
private static final double DELTA = 0.01;
private static final int MIN_SIZE_FOR_ALLY_DP = 48;
private Context activity;
@Before
public void createAndThemeApplicationContext() {
ApplicationProvider.getApplicationContext().setTheme(
R.style.Theme_MaterialComponents_Light_NoActionBar_Bridge);
activity = Robolectric.buildActivity(AppCompatActivity.class).setup().get();
}
@Test
public void ensureMinTouchTarget_is48dp() {
FloatingActionButton fab = createFabForTest(true);
float expectedSize = dpToPx(activity, MIN_SIZE_FOR_ALLY_DP);
android.util.Log.i("marian", String.valueOf(expectedSize));
assertEquals(
"Fab width was: " + fab.getMeasuredWidth(),
expectedSize, fab.getMeasuredWidth(), DELTA);
assertEquals(
"Fab width was: " + fab.getMeasuredHeight(),
expectedSize, fab.getMeasuredHeight(), DELTA);
}
@Test
public void ensureMinTouchTargetFalse_isLessThan48dp() {
FloatingActionButton fab = createFabForTest(false);
float minSize = dpToPx(activity, MIN_SIZE_FOR_ALLY_DP);
assertNotEquals(fab.getMeasuredWidth(), minSize, DELTA);
assertTrue(
"Fab width was: " + fab.getMeasuredWidth(),
fab.getMeasuredWidth() < minSize);
assertTrue(fab.getMeasuredHeight() < minSize);
}
private FloatingActionButton createFabForTest(boolean ensureMinTouchTarget) {
FloatingActionButton fab = new FloatingActionButton(activity);
float dimen = dpToPx(activity, MIN_SIZE_FOR_ALLY_DP);
fab.setSize(SIZE_MINI);
fab.setEnsureMinTouchTargetSize(ensureMinTouchTarget);
int measureSpec = MeasureSpec.makeMeasureSpec((int) (dimen * 2), MeasureSpec.AT_MOST);
fab.measure(measureSpec, measureSpec);
return fab;
}
}