diff --git a/README.md b/README.md
index fa2554195ef..3fb45cd2037 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,10 @@ For usage questions: ask on
Bugs or feature requests should be submitted at our [GitHub Issues section](https://github.com/material-components/material-components-android/issues).
+Note: If your issue or feature request is for Material Jetpack Compose, please
+file it at the [Compose Issue Tracker](https://issuetracker.google.com/issues/new?component=742043&template=1346811)
+instead.
+
## Useful Links
- [All Components](https://github.com/material-components/material-components-android/tree/master/lib/)
- [Getting Started](docs/getting-started.md)
diff --git a/build.gradle b/build.gradle
index 3dfcf8ce1f6..ca8897bec18 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.4.2'
+ classpath libs.android.gradle.plugin
}
}
@@ -17,40 +17,10 @@ allprojects {
}
ext {
- compileSdkVersion = 34
+ compileSdkVersion = 35
minSdkVersion = 21
targetSdkVersion = 33
- androidXVersions = [
- activity : '1.8.0',
- annotation : '1.2.0',
- appCompat : '1.6.1',
- cardView : '1.0.0',
- constraintlayout : '2.0.1',
- coordinatorlayout : '1.1.0',
- core : '1.6.0',
- drawerlayout : '1.1.1',
- experimental : '1.0.0',
- fragment : '1.2.5',
- lifecycle : '2.0.0',
- recyclerView : '1.0.0',
- recyclerViewSelection : '1.0.0',
- resourceInspectionAnnotation : '1.0.1',
- resourceInspectionProcessor : '1.0.1',
- transition : '1.5.0',
- vectorDrawable : '1.1.0',
- viewpager2 : '1.0.0',
- dynamicanimation : '1.0.0',
- graphicsShapes : '1.0.1',
- ]
-
- errorproneVersion = '2.15.0'
- testRunnerVersion = '1.4.0'
- espressoVersion = '3.1.0'
- mockitoCoreVersion = '2.25.0'
- truthVersion = '0.45'
- robolectricVersion = '4.9'
-
// Enforce the use of prebuilt dependencies in all sub-projects. This is
// required for the doclava dependency.
usePrebuilts = "true"
@@ -59,7 +29,7 @@ ext {
? project.property('mavenRepoUrl') : '/tmp/myRepo/')
// Current version of the library (could be in-development/unreleased).
- mdcLibraryVersion = '1.13.0-alpha08'
+ mdcLibraryVersion = '1.14.0-alpha02'
mdcLibraryPackage = "com.google.android.material"
mdcLibraryDir = "com/google/android/material"
}
@@ -83,56 +53,6 @@ private def getTransformedProjectPath(projectPath) {
return result
}
-/**
- * Return the module dependency for the given compatibility library name.
- */
-def compatibility(name) {
- switch (name) {
- case "activity":
- return "androidx.activity:activity:${androidXVersions.activity}"
- case "annotation":
- return "androidx.annotation:annotation:${androidXVersions.annotation}"
- case "appcompat":
- return "androidx.appcompat:appcompat:${androidXVersions.appCompat}"
- case "cardview":
- return "androidx.cardview:cardview:${androidXVersions.cardView}"
- case "constraintlayout":
- return "androidx.constraintlayout:constraintlayout:${androidXVersions.constraintlayout}"
- case "coordinatorlayout":
- return "androidx.coordinatorlayout:coordinatorlayout:${androidXVersions.coordinatorlayout}"
- case "core":
- return "androidx.core:core:${androidXVersions.core}"
- case "drawerlayout":
- return "androidx.drawerlayout:drawerlayout:${androidXVersions.drawerlayout}"
- case "dynamicanimation":
- return "androidx.dynamicanimation:dynamicanimation:${androidXVersions.dynamicanimation}"
- case "fragment":
- return "androidx.fragment:fragment:${androidXVersions.fragment}"
- case "lifecycleRuntime":
- return "androidx.lifecycle:lifecycle-runtime:${androidXVersions.lifecycle}"
- case "recyclerview":
- return "androidx.recyclerview:recyclerview:${androidXVersions.recyclerView}"
- case "transition":
- return "androidx.transition:transition:${androidXVersions.transition}"
- case "vectordrawable":
- return "androidx.vectordrawable:vectordrawable:${androidXVersions.vectorDrawable}"
- case "recyclerViewSelection":
- return "androidx.recyclerview:recyclerview-selection:${androidXVersions.recyclerViewSelection}"
- case "resourceInspectionAnnotation":
- return "androidx.resourceinspection:resourceinspection-annotation:${androidXVersions.resourceInspectionAnnotation}"
- case "resourceInspectionProcessor":
- return "androidx.resourceinspection:resourceinspection-processor:${androidXVersions.resourceInspectionProcessor}"
- case "viewpager2":
- return "androidx.viewpager2:viewpager2:${androidXVersions.viewpager2}"
- case "graphicsShapes":
- return "androidx.graphics:graphics-shapes:${androidXVersions.graphicsShapes}"
- case "experimental":
- return "androidx.annotation:annotation-experimental:${androidXVersions.experimental}"
- default:
- throw new IllegalArgumentException("No mapping exists for name: $name.")
- }
-}
-
/**
* Return the project dependency for the given project path.
*/
@@ -161,12 +81,12 @@ def getArchivesBaseName(name) {
}
subprojects {
- tasks.withType(Test) {
- maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
- forkEvery = 80
- maxHeapSize = "2048m"
- minHeapSize = "1024m"
- }
+ tasks.withType(Test) {
+ maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
+ forkEvery = 80
+ maxHeapSize = "2048m"
+ minHeapSize = "1024m"
+ }
}
subprojects {
diff --git a/catalog/build.gradle b/catalog/build.gradle
index c353dc9e102..bf8f67b0f90 100644
--- a/catalog/build.gradle
+++ b/catalog/build.gradle
@@ -1,53 +1,71 @@
-apply plugin: 'com.android.application'
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin:2.2.0'
+ }
+}
+
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android' version "2.1.21"
+ id 'org.jetbrains.kotlin.plugin.compose' version "2.2.0"
+}
dependencies {
// Align kotlin versions
- implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22"))
+ implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.2.0'))
+
+ api libs.dagger
+ annotationProcessor libs.dagger.compiler
- api 'com.google.dagger:dagger:2.51.1'
- annotationProcessor 'com.google.dagger:dagger-compiler:2.51.1'
+ api libs.dagger.android
+ api libs.dagger.android.support
+ annotationProcessor libs.dagger.android.processor
- api 'com.google.dagger:dagger-android:2.51.1'
- api 'com.google.dagger:dagger-android-support:2.51.1'
- annotationProcessor 'com.google.dagger:dagger-android-processor:2.51.1'
+ api libs.androidx.multidex
+ api libs.androidx.constraintlayout
+ api libs.androidx.gridlayout
+ api libs.androidx.recyclerview
+ api libs.androidx.window
+ api libs.androidx.window.java
+ api libs.androidx.preference
- api 'androidx.multidex:multidex:2.0.1'
- api 'androidx.constraintlayout:constraintlayout:2.1.0'
- api 'androidx.gridlayout:gridlayout:1.0.0'
- api "androidx.multidex:multidex:2.0.1"
- api "androidx.recyclerview:recyclerview:1.2.1"
- api 'androidx.window:window:1.0.0-beta04'
- api "androidx.window:window-java:1.0.0-beta04"
- api "androidx.preference:preference:1.1.1"
+ api libs.androidx.activity.compose
+ api libs.androidx.compose.material.icons.core
+ api libs.androidx.compose.material.icons.extended
+ api libs.androidx.compose.material3
- api 'com.google.guava:guava:33.3.1-android'
+ api libs.guava
modules {
module("com.google.guava:listenablefuture") {
replacedBy("com.google.guava:guava", "listenablefuture is part of guava")
}
}
- api 'com.github.bumptech.glide:glide:4.16.0'
+ api libs.glide
api project(':lib')
- api compatibility("recyclerViewSelection")
+ api libs.androidx.recyclerview.selection
- implementation "com.google.errorprone:error_prone_annotations:${errorproneVersion}"
+ implementation libs.errorprone.annotations
- androidTestImplementation "androidx.test:core:${project.rootProject.ext.testRunnerVersion}"
- androidTestImplementation "androidx.test:runner:${project.rootProject.ext.testRunnerVersion}"
- androidTestImplementation "androidx.test:rules:${project.rootProject.ext.testRunnerVersion}"
- androidTestImplementation "androidx.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}"
- androidTestImplementation "androidx.test.espresso:espresso-contrib:${project.rootProject.ext.espressoVersion}"
+ androidTestImplementation libs.androidx.test.core
+ androidTestImplementation libs.androidx.test.runner
+ androidTestImplementation libs.androidx.test.rules
+ androidTestImplementation libs.androidx.espresso.core
+ androidTestImplementation libs.androidx.espresso.contrib
- testImplementation "androidx.test:core:${project.rootProject.ext.testRunnerVersion}"
- testImplementation "androidx.test:runner:${project.rootProject.ext.testRunnerVersion}"
- testImplementation "junit:junit:4.13.2"
- testImplementation "com.google.truth:truth:${project.rootProject.ext.truthVersion}"
- testImplementation "org.mockito:mockito-core:${project.rootProject.ext.mockitoCoreVersion}"
- testImplementation "org.robolectric:robolectric:${project.rootProject.ext.robolectricVersion}"
+ testImplementation libs.androidx.test.core
+ testImplementation libs.androidx.test.runner
+ testImplementation libs.junit
+ testImplementation libs.truth
+ testImplementation libs.mockito.core
+ testImplementation libs.robolectric
}
def srcDirs = [
@@ -71,6 +89,7 @@ def srcDirs = [
'dialog',
'divider',
'draggable',
+ 'dockedtoolbar',
'elevation',
'imageview',
'fab',
@@ -85,6 +104,7 @@ def srcDirs = [
'musicplayer',
'navigationrail',
'navigationdrawer',
+ 'overflow',
'preferences',
'progressindicator',
'radiobutton',
@@ -103,6 +123,7 @@ def srcDirs = [
]
android {
+ namespace "io.material.catalog"
defaultConfig {
manifestPlaceholders = [
application_name : 'CatalogApplication',
@@ -131,6 +152,13 @@ android {
java.excludes = [
'**/build/**',
]
+ kotlin.srcDir 'java'
+ kotlin.includes = srcDirs.collect {
+ 'io/material/catalog/' + it + '/**/*.kt'
+ }
+ kotlin.excludes = [
+ '**/build/**',
+ ]
res.srcDirs = ['java/io/material/catalog/res']
srcDirs.forEach {
res.srcDirs += 'java/io/material/catalog/' + it + '/res'
@@ -145,6 +173,12 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+ buildFeatures {
+ compose true
+ }
androidResources {
additionalParameters '--no-version-vectors'
}
diff --git a/catalog/java/io/material/catalog/AndroidManifest.xml b/catalog/java/io/material/catalog/AndroidManifest.xml
index 5eb4ce713cb..e59c1e2eaa0 100644
--- a/catalog/java/io/material/catalog/AndroidManifest.xml
+++ b/catalog/java/io/material/catalog/AndroidManifest.xml
@@ -110,6 +110,10 @@
android:name="io.material.catalog.search.SearchRecyclerDemoActivity"
android:windowSoftInputMode="adjustNothing"
android:exported="true" />
+
diff --git a/catalog/java/io/material/catalog/adaptive/res/layout/cat_adaptive_nav_header.xml b/catalog/java/io/material/catalog/adaptive/res/layout/cat_adaptive_nav_header.xml
index bca37c47b83..eea3a999165 100644
--- a/catalog/java/io/material/catalog/adaptive/res/layout/cat_adaptive_nav_header.xml
+++ b/catalog/java/io/material/catalog/adaptive/res/layout/cat_adaptive_nav_header.xml
@@ -24,18 +24,17 @@
android:clipToPadding="false"
android:clipChildren="false">
-
+ android:contentDescription="@string/cat_adaptive_nav_button"
+ app:icon="@drawable/ic_drawer_menu_24px"/>
-
+
-
-
diff --git a/catalog/java/io/material/catalog/navigationdrawer/CustomNavigationDrawerDemoActivity.java b/catalog/java/io/material/catalog/navigationdrawer/CustomNavigationDrawerDemoActivity.java
index 5f3726d0015..dcb8fa4b710 100644
--- a/catalog/java/io/material/catalog/navigationdrawer/CustomNavigationDrawerDemoActivity.java
+++ b/catalog/java/io/material/catalog/navigationdrawer/CustomNavigationDrawerDemoActivity.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.widget.Toolbar;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -36,6 +37,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.drawerlayout.widget.DrawerLayout.LayoutParams;
import androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener;
@@ -192,4 +194,15 @@ public void onBackCancelled() {
}
};
}
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
+ if (keyCode == KeyEvent.KEYCODE_ESCAPE
+ && (drawerLayout.isDrawerOpen(GravityCompat.START)
+ || drawerLayout.isDrawerOpen(GravityCompat.END))) {
+ drawerLayout.closeDrawers();
+ return true;
+ }
+ return super.onKeyDown(keyCode, keyEvent);
+ }
}
diff --git a/catalog/java/io/material/catalog/navigationdrawer/NavigationDrawerDemoActivity.java b/catalog/java/io/material/catalog/navigationdrawer/NavigationDrawerDemoActivity.java
index e1cb1e0626a..d1eb007a6b3 100644
--- a/catalog/java/io/material/catalog/navigationdrawer/NavigationDrawerDemoActivity.java
+++ b/catalog/java/io/material/catalog/navigationdrawer/NavigationDrawerDemoActivity.java
@@ -21,6 +21,7 @@
import android.os.Bundle;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.widget.Toolbar;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -123,6 +124,17 @@ private void initNavigationView(NavigationView navigationView) {
});
}
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
+ if (keyCode == KeyEvent.KEYCODE_ESCAPE
+ && (drawerLayout.isDrawerOpen(GravityCompat.START)
+ || drawerLayout.isDrawerOpen(GravityCompat.END))) {
+ drawerLayout.closeDrawers();
+ return true;
+ }
+ return super.onKeyDown(keyCode, keyEvent);
+ }
+
@Override
protected boolean shouldShowDefaultDemoActionBar() {
return false;
diff --git a/catalog/java/io/material/catalog/navigationdrawer/res/layout/cat_navigationdrawer.xml b/catalog/java/io/material/catalog/navigationdrawer/res/layout/cat_navigationdrawer.xml
index 728b677ce4d..640e8eb7776 100644
--- a/catalog/java/io/material/catalog/navigationdrawer/res/layout/cat_navigationdrawer.xml
+++ b/catalog/java/io/material/catalog/navigationdrawer/res/layout/cat_navigationdrawer.xml
@@ -33,6 +33,7 @@
diff --git a/catalog/java/io/material/catalog/navigationrail/res/layout/cat_navigation_rail_submenus_fragment.xml b/catalog/java/io/material/catalog/navigationrail/res/layout/cat_navigation_rail_submenus_fragment.xml
index 0d9a2cdaa2b..9f40bbc3047 100644
--- a/catalog/java/io/material/catalog/navigationrail/res/layout/cat_navigation_rail_submenus_fragment.xml
+++ b/catalog/java/io/material/catalog/navigationrail/res/layout/cat_navigation_rail_submenus_fragment.xml
@@ -26,6 +26,7 @@
app:labelVisibilityMode="labeled"
android:fitsSystemWindows="false"
app:scrollingEnabled="true"
+ app:submenuDividersEnabled="true"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/navigation_rail_submenus_menu"/>
Navigation Rail
- Navigation Rails make it easy to explore and switch between top-level views in a single
- tap. This navigational element resides at the bottom of the screen, and provides direct access
- for three to five destinations within an app.
+ description="Brief summary about navigation rail component demo. [CHAR_LIMIT=500]">
+ Navigation Rails make it easy to explore and switch between top-level views in a single
+ tap. It can be transitioned between collapsed or expanded states to show less or more items
+ It is recommended to only show three to seven elements in a collapsed navigation rail, and to use
+ Navigation Rails in mid-sized or larger devices.
+ app:iconPadding="4dp"
+ app:materialSizeOverlay="@style/SizeOverlay.Material3Expressive.Button.Xsmall"/>
diff --git a/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java b/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java
index 9fbf37189db..f0e13d18373 100644
--- a/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java
+++ b/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java
@@ -80,7 +80,7 @@ public void initDemoControls(@NonNull View view) {
Slider thicknessSlider = view.findViewById(R.id.thicknessSlider);
thicknessSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newThickness = Math.round(value * pixelsInDp);
+ int newThickness = (int) (value * pixelsInDp);
if (linearIndicator.getTrackThickness() != newThickness) {
linearIndicator.setTrackThickness(newThickness);
}
@@ -92,7 +92,7 @@ public void initDemoControls(@NonNull View view) {
Slider cornerSlider = view.findViewById(R.id.cornerSlider);
cornerSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newCornerRadius = Math.round(value * pixelsInDp);
+ int newCornerRadius = (int) (value * pixelsInDp);
if (linearIndicator.getTrackCornerRadius() != newCornerRadius) {
linearIndicator.setTrackCornerRadius(newCornerRadius);
}
@@ -104,7 +104,7 @@ public void initDemoControls(@NonNull View view) {
Slider gapSlider = view.findViewById(R.id.gapSlider);
gapSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newGapSize = Math.round(value * pixelsInDp);
+ int newGapSize = (int) (value * pixelsInDp);
if (linearIndicator.getIndicatorTrackGapSize() != newGapSize) {
linearIndicator.setIndicatorTrackGapSize(newGapSize);
}
@@ -125,7 +125,7 @@ public void initDemoControls(@NonNull View view) {
Slider linearStopIndicatorSlider = view.findViewById(R.id.linearStopIndicatorSizeSlider);
linearStopIndicatorSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newStopIndicatorSize = Math.round(value * pixelsInDp);
+ int newStopIndicatorSize = (int) (value * pixelsInDp);
if (linearIndicator.getTrackStopIndicatorSize() != newStopIndicatorSize) {
linearIndicator.setTrackStopIndicatorSize(newStopIndicatorSize);
}
@@ -134,7 +134,7 @@ public void initDemoControls(@NonNull View view) {
Slider circularSizeSlider = view.findViewById(R.id.circularSizeSlider);
circularSizeSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newCornerRadius = Math.round(value * pixelsInDp);
+ int newCornerRadius = (int) (value * pixelsInDp);
if (circularIndicator.getIndicatorSize() != newCornerRadius) {
circularIndicator.setIndicatorSize(newCornerRadius);
}
diff --git a/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorStandaloneDemoFragment.java b/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorStandaloneDemoFragment.java
index 08e05926670..27c586aea5c 100644
--- a/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorStandaloneDemoFragment.java
+++ b/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorStandaloneDemoFragment.java
@@ -61,6 +61,6 @@ public int getProgressIndicatorContentLayout() {
@StyleRes
protected int getSpecStyleResId() {
- return R.style.Widget_Material3_CircularProgressIndicator_ExtraSmall;
+ return com.google.android.material.R.style.Widget_Material3_CircularProgressIndicator_ExtraSmall;
}
}
diff --git a/catalog/java/io/material/catalog/search/SearchBarWithAppBarIconsDemoActivity.java b/catalog/java/io/material/catalog/search/SearchBarWithAppBarIconsDemoActivity.java
new file mode 100644
index 00000000000..dea547cf1eb
--- /dev/null
+++ b/catalog/java/io/material/catalog/search/SearchBarWithAppBarIconsDemoActivity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2025 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 io.material.catalog.search;
+
+import io.material.catalog.R;
+
+import static io.material.catalog.search.SearchDemoUtils.showSnackbar;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.materialswitch.MaterialSwitch;
+import com.google.android.material.search.SearchBar;
+import com.google.android.material.search.SearchView;
+import io.material.catalog.feature.DemoActivity;
+
+/** An activity that displays a SearchBar in an AppBar with outside icons demo. */
+public class SearchBarWithAppBarIconsDemoActivity extends DemoActivity {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ @NonNull LayoutInflater layoutInflater,
+ @Nullable ViewGroup viewGroup,
+ @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(R.layout.cat_search_appbar_icons_fragment, viewGroup, false);
+
+ SearchBar searchBar = view.findViewById(R.id.cat_search_bar);
+ SearchView searchView = view.findViewById(R.id.cat_search_view);
+ LinearLayout suggestionContainer = view.findViewById(R.id.cat_search_view_suggestion_container);
+
+ MaterialSwitch menuSwitch = view.findViewById(R.id.cat_search_bar_menu_switch);
+ menuSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ searchBar.inflateMenu(R.menu.cat_searchview_menu);
+ searchBar.setOnMenuItemClickListener(
+ menuItem -> {
+ showSnackbar(SearchBarWithAppBarIconsDemoActivity.this, menuItem);
+ return true;
+ });
+ } else {
+ searchBar.getMenu().clear();
+ }
+ });
+ searchView.setupWithSearchBar(searchBar);
+ SearchDemoUtils.setUpSearchView(this, searchBar, searchView);
+ SearchDemoUtils.setUpSuggestions(suggestionContainer, searchBar, searchView);
+ SearchDemoUtils.startOnLoadAnimation(searchBar, bundle);
+
+ return view;
+ }
+
+ @Override
+ protected boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+}
diff --git a/catalog/java/io/material/catalog/search/SearchFragment.java b/catalog/java/io/material/catalog/search/SearchFragment.java
index bb99e9424e5..cb164cb05a9 100644
--- a/catalog/java/io/material/catalog/search/SearchFragment.java
+++ b/catalog/java/io/material/catalog/search/SearchFragment.java
@@ -69,6 +69,13 @@ public Intent createActivityIntent() {
return new Intent(getContext(), SearchRecyclerDemoActivity.class);
}
});
+ additionalDemos.add(
+ new Demo(R.string.cat_searchbar_appbar_with_icons_title) {
+ @Override
+ public Intent createActivityIntent() {
+ return new Intent(getContext(), SearchBarWithAppBarIconsDemoActivity.class);
+ }
+ });
return additionalDemos;
}
diff --git a/catalog/java/io/material/catalog/search/SearchRecyclerDemoActivity.java b/catalog/java/io/material/catalog/search/SearchRecyclerDemoActivity.java
index 45114a1ba5e..06f6d2e0805 100644
--- a/catalog/java/io/material/catalog/search/SearchRecyclerDemoActivity.java
+++ b/catalog/java/io/material/catalog/search/SearchRecyclerDemoActivity.java
@@ -315,7 +315,7 @@ private interface OnItemSelectedStateChangedListener {
void onItemSelectedStateChanged(Item item);
}
- /** The Dagger module for {@link SearchBarRecyclerDemoActivity} dependencies. */
+ /** The Dagger module for {@link SearchRecyclerDemoActivity} dependencies. */
@dagger.Module
public abstract static class Module {
diff --git a/catalog/java/io/material/catalog/search/res/layout/cat_search_appbar_icons_fragment.xml b/catalog/java/io/material/catalog/search/res/layout/cat_search_appbar_icons_fragment.xml
new file mode 100644
index 00000000000..b3c9114d275
--- /dev/null
+++ b/catalog/java/io/material/catalog/search/res/layout/cat_search_appbar_icons_fragment.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/search/res/layout/cat_search_fragment.xml b/catalog/java/io/material/catalog/search/res/layout/cat_search_fragment.xml
index 559f60798af..11ff37e0c7b 100644
--- a/catalog/java/io/material/catalog/search/res/layout/cat_search_fragment.xml
+++ b/catalog/java/io/material/catalog/search/res/layout/cat_search_fragment.xml
@@ -18,7 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
diff --git a/catalog/java/io/material/catalog/search/res/layout/cat_search_nav_drawer_header.xml b/catalog/java/io/material/catalog/search/res/layout/cat_search_nav_drawer_header.xml
index 959f655fceb..0accae2bfac 100644
--- a/catalog/java/io/material/catalog/search/res/layout/cat_search_nav_drawer_header.xml
+++ b/catalog/java/io/material/catalog/search/res/layout/cat_search_nav_drawer_header.xml
@@ -24,8 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
- android:src="http://github.com/@drawable/cat_nav_drawer_header"
- tools:ignore="NewApi"/>
+ android:src="http://github.com/@drawable/cat_nav_drawer_header"/>
+ android:src="http://github.com/@drawable/cat_ic_avatar_72"/>
Trips
RecyclerView Demo
+ SearchBar in AppBarLayout with Icons DemoMessage
@@ -86,5 +88,12 @@
Userjdoe@no.where
-
+ Home
+ Cast
+ Search product
+ Enable menu within SearchBar
diff --git a/catalog/java/io/material/catalog/shapetheming/ShapeThemingDemoFragment.java b/catalog/java/io/material/catalog/shapetheming/ShapeThemingDemoFragment.java
index cf3118fb2b0..2ddffa9bb24 100644
--- a/catalog/java/io/material/catalog/shapetheming/ShapeThemingDemoFragment.java
+++ b/catalog/java/io/material/catalog/shapetheming/ShapeThemingDemoFragment.java
@@ -52,7 +52,7 @@ public View onCreateView(
final TypedValue value = new TypedValue();
wrappedContext
.getTheme()
- .resolveAttribute(R.attr.colorPrimaryDark, value, true);
+ .resolveAttribute(androidx.appcompat.R.attr.colorPrimaryDark, value, true);
window.setStatusBarColor(value.data);
return super.onCreateView(layoutInflaterWithThemedContext, viewGroup, bundle);
@@ -84,7 +84,7 @@ public View onCreateDemoView(
materialButton.setOnClickListener(v -> materialAlertDialogBuilder.show());
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(wrappedContext);
bottomSheetDialog.setContentView(R.layout.cat_shape_theming_bottomsheet_content);
- View bottomSheetInternal = bottomSheetDialog.findViewById(R.id.design_bottom_sheet);
+ View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheetInternal).setPeekHeight(300);
MaterialButton button = container.findViewById(R.id.material_button_2);
button.setOnClickListener(v -> bottomSheetDialog.show());
diff --git a/catalog/java/io/material/catalog/sidesheet/SideSheetMainDemoFragment.java b/catalog/java/io/material/catalog/sidesheet/SideSheetMainDemoFragment.java
index 849a565bcc7..3e79773ef4e 100644
--- a/catalog/java/io/material/catalog/sidesheet/SideSheetMainDemoFragment.java
+++ b/catalog/java/io/material/catalog/sidesheet/SideSheetMainDemoFragment.java
@@ -20,6 +20,8 @@
import static android.view.View.NO_ID;
+import android.content.Context;
+import android.graphics.Color;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
@@ -28,6 +30,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.BackEventCompat;
@@ -41,7 +44,9 @@
import androidx.annotation.StyleRes;
import androidx.coordinatorlayout.widget.CoordinatorLayout.LayoutParams;
import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowCompat;
import com.google.android.material.button.MaterialButtonToggleGroup;
+import com.google.android.material.resources.MaterialAttributes;
import com.google.android.material.sidesheet.SideSheetBehavior;
import com.google.android.material.sidesheet.SideSheetCallback;
import com.google.android.material.sidesheet.SideSheetDialog;
@@ -254,7 +259,7 @@ private void setUpDetachedModalSheet() {
setUpModalSheet(
getDetachedModalThemeOverlayResId(),
R.layout.cat_sidesheet_content,
- R.id.m3_side_sheet,
+ com.google.android.material.R.id.m3_side_sheet,
R.id.side_sheet_title_text,
R.string.cat_sidesheet_modal_detached_title,
showDetachedModalSheetButton,
@@ -264,7 +269,7 @@ private void setUpDetachedModalSheet() {
private void setUpModalSheet() {
setUpModalSheet(
R.layout.cat_sidesheet_content,
- R.id.m3_side_sheet,
+ com.google.android.material.R.id.m3_side_sheet,
R.id.side_sheet_title_text,
R.string.cat_sidesheet_modal_title,
showModalSheetButton,
@@ -288,6 +293,7 @@ private void setUpModalSheet(
closeIconButtonIdRes);
}
+ @SuppressWarnings("RestrictTo")
private void setUpModalSheet(
@StyleRes int sheetThemeOverlayRes,
@LayoutRes int sheetContentLayoutRes,
@@ -298,19 +304,29 @@ private void setUpModalSheet(
@IdRes int closeIconButtonIdRes) {
showSheetButton.setOnClickListener(
v1 -> {
+ Context context = requireContext();
SideSheetDialog sheetDialog =
sheetThemeOverlayRes == NO_ID
- ? new SideSheetDialog(requireContext())
- : new SideSheetDialog(requireContext(), sheetThemeOverlayRes);
+ ? new SideSheetDialog(context)
+ : new SideSheetDialog(context, sheetThemeOverlayRes);
sheetDialog.setContentView(sheetContentLayoutRes);
+
View modalSheetContent = sheetDialog.findViewById(sheetContentRootIdRes);
if (modalSheetContent != null) {
TextView modalSideSheetTitle = modalSheetContent.findViewById(sheetTitleIdRes);
modalSideSheetTitle.setText(sheetTitleStringRes);
}
- new WindowPreferencesManager(requireContext())
- .applyEdgeToEdgePreference(sheetDialog.getWindow());
+
+ boolean edgeToEdgeEnabled = new WindowPreferencesManager(context).isEdgeToEdgeEnabled();
+ boolean isLightTheme =
+ MaterialAttributes.resolveBoolean(
+ context, androidx.appcompat.R.attr.isLightTheme, true);
+ Window window = sheetDialog.getWindow();
+ sheetDialog.setFitsSystemWindows(!edgeToEdgeEnabled);
+ window.setNavigationBarColor(Color.TRANSPARENT);
+ WindowCompat.getInsetsController(window, window.getDecorView())
+ .setAppearanceLightStatusBars(edgeToEdgeEnabled && isLightTheme);
sheetDialog
.getBehavior()
diff --git a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content.xml b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content.xml
index 91916c6178a..0ea6ee0d89e 100644
--- a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content.xml
+++ b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content.xml
@@ -13,63 +13,69 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+ android:fitsSystemWindows="true">
-
+
-
+
-
+
-
+
-
+
+
+
+
diff --git a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar.xml b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar.xml
index f9ba13525ea..c9a447ec57d 100644
--- a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar.xml
+++ b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar.xml
@@ -13,72 +13,78 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+ android:fitsSystemWindows="true">
-
+
-
+
-
+
-
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/coplanar_side_sheet_title_text">
-
+ android:orientation="vertical">
-
+
-
-
-
-
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar_detached.xml b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar_detached.xml
index c44cb43a557..a5c38692d5c 100644
--- a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar_detached.xml
+++ b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_coplanar_detached.xml
@@ -13,72 +13,78 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+ android:fitsSystemWindows="true">
-
+
-
+
-
+
-
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/coplanar_detached_side_sheet_title_text">
-
+ android:orientation="vertical">
-
+
-
-
-
-
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_vertically_scrolling.xml b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_vertically_scrolling.xml
index ee194691e85..19754202889 100644
--- a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_vertically_scrolling.xml
+++ b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_content_vertically_scrolling.xml
@@ -13,72 +13,78 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+ android:fitsSystemWindows="true">
-
+
-
+
-
+
-
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/vertically_scrolling_side_sheet_title_text">
-
+ android:orientation="vertical">
-
+
-
-
-
-
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_detached_content.xml b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_detached_content.xml
index 8fae452453d..2e6ec9802e1 100644
--- a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_detached_content.xml
+++ b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheet_detached_content.xml
@@ -13,62 +13,68 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+ android:fitsSystemWindows="true">
-
+
-
+
-
+
-
+
-
+
+
+
+
diff --git a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheets.xml b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheets.xml
index 22b69f3cc35..69118c93763 100644
--- a/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheets.xml
+++ b/catalog/java/io/material/catalog/sidesheet/res/layout/cat_sidesheets.xml
@@ -22,8 +22,7 @@
android:layout_width="256dp"
android:layout_height="match_parent"
android:orientation="vertical"
- app:layout_behavior="@string/side_sheet_behavior"
- tools:targetApi="lollipop">
+ app:layout_behavior="@string/side_sheet_behavior">
@@ -33,8 +32,7 @@
android:layout_width="256dp"
android:layout_height="match_parent"
android:orientation="vertical"
- app:layout_behavior="@string/side_sheet_behavior"
- tools:targetApi="lollipop">
+ app:layout_behavior="@string/side_sheet_behavior">
@@ -44,8 +42,7 @@
android:layout_width="256dp"
android:layout_height="match_parent"
android:orientation="vertical"
- app:layout_behavior="@string/side_sheet_behavior"
- tools:targetApi="lollipop">
+ app:layout_behavior="@string/side_sheet_behavior">
@@ -56,8 +53,7 @@
android:layout_height="match_parent"
android:orientation="vertical"
app:coplanarSiblingViewId="@id/nested_scroll_view"
- app:layout_behavior="@string/side_sheet_behavior"
- tools:targetApi="lollipop">
+ app:layout_behavior="@string/side_sheet_behavior">
@@ -68,8 +64,7 @@
android:layout_height="match_parent"
android:orientation="vertical"
app:coplanarSiblingViewId="@id/nested_scroll_view"
- app:layout_behavior="@string/side_sheet_behavior"
- tools:targetApi="lollipop">
+ app:layout_behavior="@string/side_sheet_behavior">
diff --git a/catalog/java/io/material/catalog/slider/SliderCenteredDemoFragment.java b/catalog/java/io/material/catalog/slider/SliderCenteredDemoFragment.java
new file mode 100644
index 00000000000..8aa967db173
--- /dev/null
+++ b/catalog/java/io/material/catalog/slider/SliderCenteredDemoFragment.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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
+ *
+ * https://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 io.material.catalog.slider;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.slider.Slider;
+import io.material.catalog.feature.DemoFragment;
+
+/**
+ * Fragment to display a few basic uses of the centered {@link Slider} widget for the Catalog app.
+ */
+public class SliderCenteredDemoFragment extends DemoFragment {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ @NonNull LayoutInflater layoutInflater,
+ @Nullable ViewGroup viewGroup,
+ @Nullable Bundle bundle) {
+ return layoutInflater.inflate(
+ R.layout.cat_slider_demo_centered, viewGroup, false /* attachToRoot */);
+ }
+}
diff --git a/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java b/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java
index 06d233a0724..37f9b1bdf10 100644
--- a/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java
+++ b/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java
@@ -18,6 +18,8 @@
import io.material.catalog.R;
+import static java.lang.Math.max;
+
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -55,6 +57,18 @@ public View onCreateDemoView(
setUpSlider(sliderTwo, view.findViewById(R.id.value_2), "%.0f");
setUpSlider(sliderThree, view.findViewById(R.id.value_3), "%.2f");
+ view.findViewById(R.id.add_tick).setOnClickListener(v -> {
+ sliderOne.setContinuousModeTickCount(sliderOne.getContinuousModeTickCount() + 1);
+ sliderTwo.setContinuousModeTickCount(sliderTwo.getContinuousModeTickCount() + 1);
+ sliderThree.setContinuousModeTickCount(sliderThree.getContinuousModeTickCount() + 1);
+ });
+
+ view.findViewById(R.id.remove_tick).setOnClickListener(v -> {
+ sliderOne.setContinuousModeTickCount(max(sliderOne.getContinuousModeTickCount() - 1, 0));
+ sliderTwo.setContinuousModeTickCount(max(sliderTwo.getContinuousModeTickCount() - 1, 0));
+ sliderThree.setContinuousModeTickCount(max(sliderThree.getContinuousModeTickCount() - 1, 0));
+ });
+
return view;
}
diff --git a/catalog/java/io/material/catalog/slider/SliderFragment.java b/catalog/java/io/material/catalog/slider/SliderFragment.java
index 1031bda6172..3b2588a83c8 100644
--- a/catalog/java/io/material/catalog/slider/SliderFragment.java
+++ b/catalog/java/io/material/catalog/slider/SliderFragment.java
@@ -70,6 +70,13 @@ public Fragment createFragment() {
return new SliderDiscreteDemoFragment();
}
});
+ additionalDemos.add(
+ new Demo(R.string.cat_slider_demo_centered_title) {
+ @Override
+ public Fragment createFragment() {
+ return new SliderCenteredDemoFragment();
+ }
+ });
additionalDemos.add(
new Demo(R.string.cat_slider_demo_scroll_container_title) {
@Override
@@ -105,6 +112,13 @@ public Fragment createFragment() {
return new SliderVerticalDemoFragment();
}
});
+ additionalDemos.add(
+ new Demo(R.string.cat_slider_demo_vertical_scroll_container_title) {
+ @Override
+ public Fragment createFragment() {
+ return new SliderVerticalScrollContainerDemoFragment();
+ }
+ });
return additionalDemos;
}
diff --git a/catalog/java/io/material/catalog/slider/SliderVerticalScrollContainerDemoFragment.java b/catalog/java/io/material/catalog/slider/SliderVerticalScrollContainerDemoFragment.java
new file mode 100644
index 00000000000..af8a14f0148
--- /dev/null
+++ b/catalog/java/io/material/catalog/slider/SliderVerticalScrollContainerDemoFragment.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2025 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
+ *
+ * https://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 io.material.catalog.slider;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.slider.Slider;
+import com.google.android.material.slider.SliderOrientation;
+import io.material.catalog.feature.DemoFragment;
+
+/**
+ * A fragment that displays a list of vertical {@link Slider} inside a HorizontalScrollView to
+ * showcase how touch events are handled.
+ */
+public class SliderVerticalScrollContainerDemoFragment extends DemoFragment {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ @NonNull LayoutInflater layoutInflater,
+ @Nullable ViewGroup viewGroup,
+ @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_slider_demo_vertical_scroll, viewGroup, false /* attachToRoot */);
+ LinearLayout sliderContainer = view.findViewById(R.id.sliderContainer);
+ for (int i = 0; i < 50; i++) {
+ Slider slider = new Slider(layoutInflater.getContext());
+ slider.setValueTo(11f);
+ slider.setOrientation(SliderOrientation.VERTICAL);
+ sliderContainer.addView(
+ slider, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ }
+ return view;
+ }
+}
diff --git a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_centered.xml b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_centered.xml
new file mode 100644
index 00000000000..ad3e9e9ac32
--- /dev/null
+++ b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_centered.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_continuous.xml b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_continuous.xml
index 7309ab338ec..c244a84dccf 100644
--- a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_continuous.xml
+++ b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_continuous.xml
@@ -14,10 +14,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_discrete.xml b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_discrete.xml
index 85398e5ca25..755c641c861 100644
--- a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_discrete.xml
+++ b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_discrete.xml
@@ -163,7 +163,7 @@
android:valueFrom="0"
android:valueTo="10"
android:stepSize="1"
- app:tickVisible="false"/>
+ app:tickVisibilityMode="hidden" />
-
+ android:layout_height="match_parent">
-
+ android:paddingTop="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
-
+
-
-
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_scroll.xml b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_scroll.xml
index d8404c89546..d793f31adfb 100644
--- a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_scroll.xml
+++ b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_scroll.xml
@@ -14,19 +14,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+
-
-
-
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical" />
diff --git a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_vertical.xml b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_vertical.xml
index a33468f5c6c..e1ddfba7064 100644
--- a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_vertical.xml
+++ b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_vertical.xml
@@ -13,41 +13,42 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+ android:layout_height="match_parent">
-
-
-
-
+ android:padding="16dp"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_vertical_scroll.xml b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_vertical_scroll.xml
new file mode 100644
index 00000000000..befb0f33a1a
--- /dev/null
+++ b/catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_vertical_scroll.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/slider/res/values/strings.xml b/catalog/java/io/material/catalog/slider/res/values/strings.xml
index a1536089de4..0cf61b1924e 100644
--- a/catalog/java/io/material/catalog/slider/res/values/strings.xml
+++ b/catalog/java/io/material/catalog/slider/res/values/strings.xml
@@ -18,11 +18,13 @@
SliderContinuous Slider demoDiscrete Slider demo
+ Centered Slider demoSlider in scrolling container demoSlider label behavior demoSlider custom corners demoSlider track icons demoSlider vertical demo
+ Vertical Slider in scrolling container demo
Sliders let users select from a range of values by moving the slider thumb.
@@ -33,6 +35,8 @@
Enabled
+ Add Tick
+ Remove TickVerticalSetThis one goes to eleven
diff --git a/catalog/java/io/material/catalog/tableofcontents/TocAdapter.java b/catalog/java/io/material/catalog/tableofcontents/TocAdapter.java
index b42f127c83d..0ee162d9cd8 100644
--- a/catalog/java/io/material/catalog/tableofcontents/TocAdapter.java
+++ b/catalog/java/io/material/catalog/tableofcontents/TocAdapter.java
@@ -58,6 +58,7 @@ protected FilterResults performFiltering(CharSequence constraint) {
return filterResults;
}
+ @SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults filterResults) {
featureDemos.clear();
diff --git a/catalog/java/io/material/catalog/tableofcontents/TocModule.java b/catalog/java/io/material/catalog/tableofcontents/TocModule.java
index 296c3c86922..c51162da07a 100644
--- a/catalog/java/io/material/catalog/tableofcontents/TocModule.java
+++ b/catalog/java/io/material/catalog/tableofcontents/TocModule.java
@@ -32,6 +32,7 @@
import io.material.catalog.datepicker.DatePickerDemoLandingFragment;
import io.material.catalog.dialog.DialogDemoLandingFragment;
import io.material.catalog.divider.DividerFragment;
+import io.material.catalog.dockedtoolbar.DockedToolbarFragment;
import io.material.catalog.elevation.ElevationFragment;
import io.material.catalog.fab.FabFragment;
import io.material.catalog.floatingtoolbar.FloatingToolbarFragment;
@@ -71,6 +72,7 @@
DatePickerDemoLandingFragment.Module.class,
DialogDemoLandingFragment.Module.class,
DividerFragment.Module.class,
+ DockedToolbarFragment.Module.class,
ElevationFragment.Module.class,
FabFragment.Module.class,
FloatingToolbarFragment.Module.class,
diff --git a/catalog/java/io/material/catalog/timepicker/TimePickerMainDemoFragment.java b/catalog/java/io/material/catalog/timepicker/TimePickerMainDemoFragment.java
index 360a21e9cef..db19b65d9cb 100644
--- a/catalog/java/io/material/catalog/timepicker/TimePickerMainDemoFragment.java
+++ b/catalog/java/io/material/catalog/timepicker/TimePickerMainDemoFragment.java
@@ -23,6 +23,7 @@
import static com.google.android.material.timepicker.TimeFormat.CLOCK_24H;
import android.os.Bundle;
+import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.SwitchCompat;
import android.text.format.DateFormat;
import android.util.Log;
@@ -124,18 +125,28 @@ public View onCreateDemoView(
}
MaterialTimePicker materialTimePicker = materialTimePickerBuilder.build();
- materialTimePicker.show(requireFragmentManager(), "fragment_tag");
-
- materialTimePicker.addOnPositiveButtonClickListener(dialog -> {
- int newHour = materialTimePicker.getHour();
- int newMinute = materialTimePicker.getMinute();
- TimePickerMainDemoFragment.this.onTimeSet(newHour, newMinute);
- });
+ materialTimePicker.showNow(requireFragmentManager(), "fragment_tag");
+ setUpClickListener();
});
+ setUpClickListener();
return view;
}
+ private void setUpClickListener() {
+ Fragment fragment = getParentFragmentManager().findFragmentByTag("fragment_tag");
+ if (fragment instanceof MaterialTimePicker) {
+ MaterialTimePicker materialTimePicker = (MaterialTimePicker) fragment;
+ materialTimePicker.clearOnPositiveButtonClickListeners();
+ materialTimePicker.addOnPositiveButtonClickListener(
+ dialog -> {
+ int newHour = materialTimePicker.getHour();
+ int newMinute = materialTimePicker.getMinute();
+ TimePickerMainDemoFragment.this.onTimeSet(newHour, newMinute);
+ });
+ }
+ }
+
private void showFrameworkTimepicker() {
android.app.TimePickerDialog timePickerDialog =
new android.app.TimePickerDialog(
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingFilledActionDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingFilledActionDemoFragment.java
new file mode 100644
index 00000000000..b8e3fbff35a
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingFilledActionDemoFragment.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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 io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.snackbar.Snackbar;
+import io.material.catalog.feature.DemoFragment;
+
+/**
+ * A fragment that displays a medium Collapsing Toolbar Top App Bar with a filled action button demo
+ * for the Catalog app.
+ */
+public class TopAppBarCollapsingFilledActionDemoFragment extends DemoFragment {
+
+ @Override
+ @Nullable
+ public View onCreateDemoView(
+ @NonNull LayoutInflater layoutInflater,
+ @Nullable ViewGroup viewGroup,
+ @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_topappbar_collapsing_filled_action_fragment, viewGroup, false);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(toolbar);
+
+ Button actionButton = view.findViewById(R.id.action_button);
+ actionButton.setOnClickListener(
+ v -> Snackbar.make(v, "Action button is clicked.", Snackbar.LENGTH_SHORT).show());
+
+ return view;
+ }
+
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingLargeDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingLargeDemoFragment.java
index dd100a59094..209fa45611a 100644
--- a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingLargeDemoFragment.java
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingLargeDemoFragment.java
@@ -23,6 +23,7 @@
/** A fragment that displays a large collapsing Top App Bar demo for the Catalog app. */
public class TopAppBarCollapsingLargeDemoFragment extends BaseTopAppBarCollapsingDemoFragment {
+ @Override
@LayoutRes
protected int getCollapsingToolbarLayoutResId() {
return R.layout.cat_topappbar_collapsing_large_fragment;
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingLargeWithSubtitleDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingLargeWithSubtitleDemoFragment.java
new file mode 100644
index 00000000000..8b9be444aa0
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingLargeWithSubtitleDemoFragment.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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 io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import androidx.annotation.LayoutRes;
+
+/** A fragment that displays a large Collapsing Toolbar Top App Bar demo for the Catalog app. */
+public class TopAppBarCollapsingLargeWithSubtitleDemoFragment
+ extends BaseTopAppBarCollapsingDemoFragment {
+
+ @Override
+ @LayoutRes
+ protected int getCollapsingToolbarLayoutResId() {
+ return R.layout.cat_topappbar_collapsing_large_with_subtitle_fragment;
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMediumDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMediumDemoFragment.java
index 1170f322632..2d599a437c7 100644
--- a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMediumDemoFragment.java
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMediumDemoFragment.java
@@ -23,6 +23,7 @@
/** A fragment that displays a medium collapsing Top App Bar demo for the Catalog app. */
public class TopAppBarCollapsingMediumDemoFragment extends BaseTopAppBarCollapsingDemoFragment {
+ @Override
@LayoutRes
protected int getCollapsingToolbarLayoutResId() {
return R.layout.cat_topappbar_collapsing_medium_fragment;
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMediumWithSubtitleDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMediumWithSubtitleDemoFragment.java
new file mode 100644
index 00000000000..a0aba161174
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMediumWithSubtitleDemoFragment.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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 io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import androidx.annotation.LayoutRes;
+
+/** A fragment that displays a medium Collapsing Toolbar Top App Bar demo for the Catalog app. */
+public class TopAppBarCollapsingMediumWithSubtitleDemoFragment
+ extends BaseTopAppBarCollapsingDemoFragment {
+
+ @Override
+ @LayoutRes
+ protected int getCollapsingToolbarLayoutResId() {
+ return R.layout.cat_topappbar_collapsing_medium_with_subtitle_fragment;
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMultilineDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMultilineDemoFragment.java
index 87cf66f91fb..0bc77e94edb 100644
--- a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMultilineDemoFragment.java
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingMultilineDemoFragment.java
@@ -64,7 +64,7 @@ public View onCreateDemoView(
Toolbar toolbar = view.findViewById(R.id.toolbar);
AppCompatActivity activity = (AppCompatActivity) getActivity();
activity.setSupportActionBar(toolbar);
- colorPrimary = MaterialColors.getColor(view, R.attr.colorPrimary);
+ colorPrimary = MaterialColors.getColor(view, androidx.appcompat.R.attr.colorPrimary);
return view;
}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingToggleableActionDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingToggleableActionDemoFragment.java
new file mode 100644
index 00000000000..560ef590ef2
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingToggleableActionDemoFragment.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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 io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.button.MaterialButton;
+import com.google.android.material.snackbar.Snackbar;
+import io.material.catalog.feature.DemoFragment;
+
+/**
+ * A fragment that displays a medium Collapsing Toolbar Top App Bar with a toggleable action button
+ * demo for the Catalog app.
+ */
+public class TopAppBarCollapsingToggleableActionDemoFragment extends DemoFragment {
+
+ @Override
+ @Nullable
+ public View onCreateDemoView(
+ @NonNull LayoutInflater layoutInflater,
+ @Nullable ViewGroup viewGroup,
+ @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_topappbar_collapsing_toggleable_action_fragment, viewGroup, false);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(toolbar);
+
+ MaterialButton actionButton = view.findViewById(R.id.action_button);
+ actionButton.setOnClickListener(
+ v ->
+ Snackbar.make(
+ v,
+ (actionButton.isChecked() ? "Marked as favorite." : "Marked as not favorite"),
+ Snackbar.LENGTH_SHORT)
+ .show());
+
+ return view;
+ }
+
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCompressEffectFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCompressEffectFragment.java
index bd5d927885e..4b8fdf5adea 100644
--- a/catalog/java/io/material/catalog/topappbar/TopAppBarCompressEffectFragment.java
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCompressEffectFragment.java
@@ -60,7 +60,7 @@ public View onCreateDemoView(
AppBarLayout appBarLayout = view.findViewById(R.id.appbarlayout);
appBarLayout.setStatusBarForegroundColor(
- MaterialColors.getColor(appBarLayout, R.attr.colorSurface));
+ MaterialColors.getColor(appBarLayout, com.google.android.material.R.attr.colorSurface));
TabLayout tabs = view.findViewById(R.id.tabs);
ToggleButton showHideTabsButton = view.findViewById(R.id.show_hide_tabs_button);
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarFragment.java
index 1ac92acc294..71d405af41a 100644
--- a/catalog/java/io/material/catalog/topappbar/TopAppBarFragment.java
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarFragment.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import androidx.fragment.app.Fragment;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import dagger.Provides;
import dagger.android.ContributesAndroidInjector;
@@ -97,23 +98,54 @@ public Fragment createFragment() {
@NonNull
protected List getCollapsingToolbarDemos() {
return Arrays.asList(
- new Demo(R.string.cat_topappbar_collapsing_medium_title) {
+ new Demo(R.string.cat_topappbar_collapsing_medium_demo_title) {
+ @Nullable
@Override
public Fragment createFragment() {
return new TopAppBarCollapsingMediumDemoFragment();
}
},
- new Demo(R.string.cat_topappbar_collapsing_large_title) {
+ new Demo(R.string.cat_topappbar_collapsing_medium_with_subtitle_demo_title) {
+ @Nullable
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarCollapsingMediumWithSubtitleDemoFragment();
+ }
+ },
+ new Demo(R.string.cat_topappbar_collapsing_large_demo_title) {
+ @Nullable
@Override
public Fragment createFragment() {
return new TopAppBarCollapsingLargeDemoFragment();
}
},
- new Demo(R.string.cat_topappbar_collapsing_multiline_title) {
+ new Demo(R.string.cat_topappbar_collapsing_large_with_subtitle_demo_title) {
+ @Nullable
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarCollapsingLargeWithSubtitleDemoFragment();
+ }
+ },
+ new Demo(R.string.cat_topappbar_collapsing_multiline_demo_title) {
+ @Nullable
@Override
public Fragment createFragment() {
return new TopAppBarCollapsingMultilineDemoFragment();
}
+ },
+ new Demo(R.string.cat_topappbar_collapsing_with_filled_action_demo_title) {
+ @Nullable
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarCollapsingFilledActionDemoFragment();
+ }
+ },
+ new Demo(R.string.cat_topappbar_collapsing_with_toggleable_action_demo_title) {
+ @Nullable
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarCollapsingToggleableActionDemoFragment();
+ }
});
}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarScrollingDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarScrollingDemoFragment.java
index d7064fee960..317ddbd10bd 100644
--- a/catalog/java/io/material/catalog/topappbar/TopAppBarScrollingDemoFragment.java
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarScrollingDemoFragment.java
@@ -53,7 +53,7 @@ public View onCreateDemoView(
AppBarLayout appBarLayout = view.findViewById(R.id.appbarlayout);
appBarLayout.setStatusBarForegroundColor(
- MaterialColors.getColor(appBarLayout, R.attr.colorSurface));
+ MaterialColors.getColor(appBarLayout, com.google.android.material.R.attr.colorSurface));
return view;
}
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_filled_action_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_filled_action_fragment.xml
new file mode 100644
index 00000000000..211ef79f047
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_filled_action_fragment.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_large_with_subtitle_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_large_with_subtitle_fragment.xml
new file mode 100644
index 00000000000..792f254675b
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_large_with_subtitle_fragment.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_medium_with_subtitle_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_medium_with_subtitle_fragment.xml
new file mode 100644
index 00000000000..cf83c5e3599
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_medium_with_subtitle_fragment.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_multiline_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_multiline_fragment.xml
index ae84888545a..3af55446b8f 100644
--- a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_multiline_fragment.xml
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_multiline_fragment.xml
@@ -35,7 +35,8 @@
android:layout_width="match_parent"
android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
- app:maxLines="3">
+ app:titleMaxLines="3"
+ app:subtitleMaxLines="3">
+ app:subtitle="@string/cat_topappbar_collapsing_multiline_demo_toolbar_subtitle" />
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_toggleable_action_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_toggleable_action_fragment.xml
new file mode 100644
index 00000000000..96ce6eba3ce
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_toggleable_action_fragment.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/menu/cat_topappbar_menu.xml b/catalog/java/io/material/catalog/topappbar/res/menu/cat_topappbar_menu.xml
index 7bf07a819b3..a06b548ebed 100644
--- a/catalog/java/io/material/catalog/topappbar/res/menu/cat_topappbar_menu.xml
+++ b/catalog/java/io/material/catalog/topappbar/res/menu/cat_topappbar_menu.xml
@@ -19,7 +19,8 @@
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="AppCompatResource">
-
-
diff --git a/catalog/java/io/material/catalog/topappbar/res/values/strings.xml b/catalog/java/io/material/catalog/topappbar/res/values/strings.xml
index d4cadb4d7d4..64346e509bb 100644
--- a/catalog/java/io/material/catalog/topappbar/res/values/strings.xml
+++ b/catalog/java/io/material/catalog/topappbar/res/values/strings.xml
@@ -22,10 +22,14 @@
The Top App Bar, formerly known as the action bar in Android, is a special kind of toolbar that’s used for branding, navigation, search, and actions.Scrolling DemoScrolling Demo (transparent status bar)
- Collapsing Demo (Medium)
- Collapsing Demo (Large)
+ Collapsing Demo (Medium)
+ Collapsing with Subtitle Demo (Medium)
+ Collapsing Demo (Large)
+ Collapsing with Subtitle Demo (Large)
+ Collapsing Demo (Filled Action)
+ Collapsing Demo (Toggleable Action)
+ Collapsing Demo (Multiline title)Toolbar Demo
- Collapsing Demo (Multiline title)Action Bar DemoDark Action Bar DemoPreferences Demo
@@ -41,10 +45,16 @@
Regular TitleScrolling Title
-
- Collapsing Title Medium Size
-
- Collapsing Title Large Size
+ Medium Title
+ Medium Subtitle
+ Large Title
+ Large Subtitle
+ ActionEdit
@@ -53,7 +63,7 @@
SettingsHelp & feedback
-
+
Subtitle text
@@ -75,6 +85,8 @@
This Collapsing Title is
extremely long and thus will be displayed in multiple lines.
+ This Collapsing Subtitle is
+ extremely long and thus will be displayed in multiple lines.Max lines = %d
diff --git a/catalog/java/io/material/catalog/transition/TransitionContainerTransformDemoFragment.java b/catalog/java/io/material/catalog/transition/TransitionContainerTransformDemoFragment.java
index 2d35dad2fad..05fc734f369 100644
--- a/catalog/java/io/material/catalog/transition/TransitionContainerTransformDemoFragment.java
+++ b/catalog/java/io/material/catalog/transition/TransitionContainerTransformDemoFragment.java
@@ -167,7 +167,7 @@ private void configureTransitions(Fragment fragment) {
// For all 3 container layer colors, use colorSurface since this transform can be configured
// using any fade mode and some of the start views don't have a background and the end view
// doesn't have a background.
- int colorSurface = MaterialColors.getColor(requireView(), R.attr.colorSurface);
+ int colorSurface = MaterialColors.getColor(requireView(), com.google.android.material.R.attr.colorSurface);
MaterialContainerTransform enterContainerTransform = buildContainerTransform(true);
enterContainerTransform.setAllContainerColors(colorSurface);
diff --git a/catalog/java/io/material/catalog/transition/TransitionContainerTransformEndDemoActivity.java b/catalog/java/io/material/catalog/transition/TransitionContainerTransformEndDemoActivity.java
index 85e4825fbbf..1349c7d74e9 100644
--- a/catalog/java/io/material/catalog/transition/TransitionContainerTransformEndDemoActivity.java
+++ b/catalog/java/io/material/catalog/transition/TransitionContainerTransformEndDemoActivity.java
@@ -69,7 +69,7 @@ private MaterialContainerTransform buildContainerTransform(boolean entering) {
// and some of the start views don't have a background and the end view doesn't have a
// background.
transform.setAllContainerColors(
- MaterialColors.getColor(findViewById(android.R.id.content), R.attr.colorSurface));
+ MaterialColors.getColor(findViewById(android.R.id.content), com.google.android.material.R.attr.colorSurface));
transform.addTarget(android.R.id.content);
configurationHelper.configure(transform, entering);
return transform;
diff --git a/catalog/java/io/material/catalog/transition/res/layout/cat_transition_container_transform_view_fragment.xml b/catalog/java/io/material/catalog/transition/res/layout/cat_transition_container_transform_view_fragment.xml
index 4ffd9ae53a8..53cbe6f7835 100644
--- a/catalog/java/io/material/catalog/transition/res/layout/cat_transition_container_transform_view_fragment.xml
+++ b/catalog/java/io/material/catalog/transition/res/layout/cat_transition_container_transform_view_fragment.xml
@@ -20,8 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:targetApi="lollipop">
+ android:layout_height="match_parent">
diff --git a/catalog/java/io/material/catalog/transition/res/layout/cat_transition_fade_through_albums_fragment.xml b/catalog/java/io/material/catalog/transition/res/layout/cat_transition_fade_through_albums_fragment.xml
index b3d52c6ac58..017bfce36f4 100644
--- a/catalog/java/io/material/catalog/transition/res/layout/cat_transition_fade_through_albums_fragment.xml
+++ b/catalog/java/io/material/catalog/transition/res/layout/cat_transition_fade_through_albums_fragment.xml
@@ -42,8 +42,7 @@
app:columnCount="2"
app:rowCount="3"
android:gravity="center_horizontal"
- app:orientation="vertical"
- tools:targetApi="lollipop">
+ app:orientation="vertical">
+ app:rowCount="3">
+ android:padding="4dp">
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
+ android:transitionGroup="true">
+ android:orientation="vertical">
+ android:transitionGroup="true">
+ android:layout_height="match_parent">
-# `BadgeDrawable`
+# Badges
-## Design and API Documentation
+[Badges](https://m3.material.io/components/badges/overview) show notifications,
+counts, or status information on navigation items and icons. There are two
+variants of badges.
-* [Google Material3 Spec](https://material.io/components/badges/overview)
-* [API reference](https://developer.android.com/reference/com/google/android/material/badge/package-summary)
+ |
+----------------------------------------------------------------------------- | -----------------------------------------------------------------------------
+1 | 2
+
+1. Small badge
+2. Large badge
-## Using badges
+**Note:** Images use various dynamic color schemes.
-Badge | Badge with number | Badge with a maximum character count
---------------------------------------------- | ---------------------------------------------------- | ------------------------------------
- |  | 
+Before you can use Material badges, you need to add a dependency to the Material
+components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
**Note:** This component is still under development and may not support the full
range of customization Material Android components generally support, for
@@ -28,7 +35,53 @@ A `BadgeDrawable` represents dynamic information such as a number of pending
requests in a [`BottomNavigationView`](BottomNavigation.md) or
[`TabLayout`](Tabs.md).
-## Usage
+## Design & API documentation
+
+* [Material 3 (M3) spec](https://m3.material.io/components/badges/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/badge/package-summary)
+
+## Anatomy
+
+
+
+1. Small badge
+2. Large badge container
+3. Large badge label
+
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/badges/guidelines#07608fcc-43f7-47b3-b5cb-ee617753b877).
+
+## Key properties
+
+### `BadgeDrawable` Attributes
+
+| Feature | Relevant attributes |
+|----------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Color | `app:backgroundColor` `app:badgeTextColor` |
+| Width | `app:badgeWidth` `app:badgeWithTextWidth` |
+| Height | `app:badgeHeight` `app:badgeWithTextHeight` |
+| Shape | `app:badgeShapeAppearance` `app:badgeShapeAppearanceOverlay` `app:badgeWithTextShapeAppearance` `app:badgeWithTextShapeAppearanceOverlay` |
+| Label | `app:badgeText` (for text) `app:number` (for numbers) |
+| Label Length | `app:maxCharacterCount` (for all text) `app:maxNumber` (for numbers only) |
+| Label Text Color | `app:badgeTextColor` |
+| Label Text Appearance | `app:badgeTextAppearance` |
+| Badge Gravity | `app:badgeGravity` |
+| Offset Alignment | `app:offsetAlignmentMode` |
+| Horizontal Padding | `app:badgeWidePadding` |
+| Vertical Padding | `app:badgeVerticalPadding` |
+| Large Font Vertical Offset| `app:largeFontVerticalOffsetAdjustment` |
+| Badge Fixed Edge | `app:badgeFixedEdge` |
+
+**Note:** If both `app:badgeText` and `app:number` are specified, the badge
+label will be `app:badgeText`.
+
+## Code implementation
+
+
+
+1. Small badge on a navigation item
+2. Large badge on a navigation item
+3. Large badge with max characters on a navigation item
API and source code:
@@ -82,7 +135,7 @@ can specify a `FrameLayout` to display the badge instead.
BadgeUtils.attachBadgeDrawable(badgeDrawable, anchor, anchorFrameLayoutParent);
```
-### `BadgeDrawable` Gravity Modes
+### `BadgeDrawable` gravity modes
`BadgeDrawable` offers two gravity modes to control how the badge aligns with
its anchor view. By default, (`TOP_END`) badge aligns with the top and end edges
@@ -93,42 +146,21 @@ align the badge with the top and start edges of the anchor. Note that
### `BadgeDrawable` placement and offsets
By default, `BadgeDrawable` is aligned with the top and end edges of its anchor
-view (with some offsets if `offsetAlignmentMode` is `legacy`). Call `setBadgeGravity(int)` to change it to one of the
-other supported modes. To adjust the badge's offsets relative to the anchor's
-center, use `setHorizontalOffset(int)` or `setVerticalOffset(int)`
+view (with some offsets if `offsetAlignmentMode` is `legacy`). Call
+`setBadgeGravity(int)` to change it to one of the other supported modes. To
+adjust the badge's offsets relative to the anchor's center, use
+`setHorizontalOffset(int)` or `setVerticalOffset(int)`
Regardless of offsets, badges are automatically moved to within the bounds of
its first ancestor view that does not clip its children, to ensure that the
badge is not clipped if there is enough space.
-### `BadgeDrawable` Attributes
-
-| Feature | Relevant attributes |
-|----------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Color | `app:backgroundColor` `app:badgeTextColor` |
-| Width | `app:badgeWidth` `app:badgeWithTextWidth` |
-| Height | `app:badgeHeight` `app:badgeWithTextHeight` |
-| Shape | `app:badgeShapeAppearance` `app:badgeShapeAppearanceOverlay` `app:badgeWithTextShapeAppearance` `app:badgeWithTextShapeAppearanceOverlay` |
-| Label | `app:badgeText` (for text) `app:number` (for numbers) |
-| Label Length | `app:maxCharacterCount` (for all text) `app:maxNumber` (for numbers only) |
-| Label Text Color | `app:badgeTextColor` |
-| Label Text Appearance | `app:badgeTextAppearance` |
-| Badge Gravity | `app:badgeGravity` |
-| Offset Alignment | `app:offsetAlignmentMode` |
-| Horizontal Padding | `app:badgeWidePadding` |
-| Vertical Padding | `app:badgeVerticalPadding` |
-| Large Font Vertical Offset| `app:largeFontVerticalOffsetAdjustment` |
-| Badge Fixed Edge | `app:badgeFixedEdge` |
-
-
-**Note:** If both `app:badgeText` and `app:number` are specified, the badge label will be `app:badgeText`.
-
-### Talkback Support
+### TalkBack support
-`BadgeDrawable` provides a getter for its content description, which is based on the displayed
-number or text (if any). To specify the content description, the developer is provided
-with the following methods:
-- `setContentDescriptionForText(CharSequence)`
-- `setContentDescriptionQuantityStringsResource(@PluralsRes int)`
-- `setContentDescriptionExceedsMaxBadgeNumberStringResource(@StringRes int)`
-- `setContentDescriptionNumberless(CharSequence)`
+`BadgeDrawable` provides a getter for its content description, which is based on
+the displayed number or text (if any). To specify the content description, the
+developer is provided with the following methods:
+`setContentDescriptionForText(CharSequence)`
+`setContentDescriptionQuantityStringsResource(@PluralsRes int)`
+`setContentDescriptionExceedsMaxBadgeNumberStringResource(@StringRes int)`
+`setContentDescriptionNumberless(CharSequence)`
diff --git a/docs/components/BottomAppBar.md b/docs/components/BottomAppBar.md
index a27d17e3252..e104d2d1820 100644
--- a/docs/components/BottomAppBar.md
+++ b/docs/components/BottomAppBar.md
@@ -9,92 +9,135 @@ path: /catalog/bottom-app-bars/
# Bottom app bars
-A [bottom app bar](https://material.io/components/app-bars-bottom/) displays
+**Note:** The **bottom app bar** is being deprecated and should be replaced with
+the [docked toolbar](DockedToolbar.md), which functions similarly, but is
+shorter and has more flexibility.
+
+A [bottom app bar](https://m2.material.io/components/app-bars-bottom) displays
navigation and key actions at the bottom of mobile screens.

+**Note:** Images use various dynamic color schemes.
+
+Bottom app bars provide access to up to four actions, including the
+[floating action button](FloatingActionButton.md) (FAB).
+
+## Design & API documentation
+
+* [Material 2 (M2) spec](https://m2.material.io/components/app-bars-bottom)
+* [API reference](https://developer.android.com/reference/com/google/android/material/bottomappbar/package-summary)
+
+## Anatomy
+
+
+
+1. Container
+2. Floating action button (FAB) (optional)
+3. Action item(s) (optional)
+4. Navigation icon (optional)
+5. Overflow menu (optional)
+
**Note:** This doc reflects the Bottom App Bar after the changes in 1.7 to
reflect the current M3 style. Use `Widget.Material3.BottomAppBar.Legacy` to
revert back to the previous style.
-**Contents**
+## M3 Expressive update
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using bottom app bars](#using-bottom-app-bars)
-* [Bottom app bar](#bottom-app-bar)
-* [Theming bottom app bars](#theming-bottom-app-bars)
+Before you can use `Material3Expressive` component styles, follow the
+[`Material3Expressive` themes setup instructions](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md#material3expressive-themes).
-## Design and API Documentation
+The bottom app bar is being deprecated and should be replaced with the
+[docked toolbar](DockedToolbar.md), which functions similarly, but is shorter
+and has more flexibility.
-* [Google Material3 Spec](https://material.io/components/bottom-app-bar/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/bottomappbar/package-summary)
+**Types and naming:**
-## Using bottom app bars
+* Added **docked toolbar** to replace **bottom app bar**
-Before you can use Material bottom app bars, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
-[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page.
+ * Size: Shorter height
+ * Color: Standard or vibrant
+ * Flexibility: More layout and element options
-### Making bottom app bars accessible
+* **Bottom app bar** is still available, but not recommended
-Android's bottom app bar component APIs provide support for the navigation icon,
-action items, overflow menu and more to tell the user what each action performs.
-While optional, their use is strongly encouraged.
+## Key properties
-#### Content descriptions
+### Container attributes
-When using navigation icons, action items and other elements of bottom app bars,
-you should set a content description for them so that screen readers like
-TalkBack are able to announce their purpose or action.
+Element | Attribute | Related method(s) | Default value
+------------- | ------------------------ | ------------------------------------------ | -------------
+**Color** | `app:backgroundTint` | `setBackgroundTint` `getBackgroundTint` | `?attr/colorSurfaceContainer`
+**Elevation** | `app:elevation` | `setElevation` | `3dp`
+**Height** | `android:minHeight` | `setMinimumHeight` `getMinimumHeight` | `80dp`
+**Shadows** | `app:addElevationShadow` | N/A | `false`
-For an overall content description of the bottom app bar, set an
-`android:contentDescription` or use the `setContentDescription` method on the
-`BottomAppBar`.
+### Navigation icon attributes
-For the navigation icon, use the `app:navigationContentDescription` attribute or
-`setNavigationContentDescription` method.
+Element | Attribute | Related method(s) | Default value
+--------- | ------------------------ | ------------------------------------------ | -------------
+**Icon** | `app:navigationIcon` | `setNavigationIcon` `getNavigationIcon` | `null`
+**Color** | `app:navigationIconTint` | `setNavigationIconTint` | `?attr/colorOnSurfaceVariant` (as `Drawable` tint)
-For action items and items within the overflow menu, set the content description
-in the menu:
+### FAB attributes
-```xml
-
-```
+Element | Attribute | Related method(s) | Default value
+-------------------------------- | ---------------------------------- | ---------------------------------------------------------------------- | -------------
+**Alignment mode** | `app:fabAlignmentMode` | `setFabAlignmentMode` `getFabAlignmentMode` | `end`
+**Animation mode** | `app:fabAnimationMode` | `setFabAnimationMode` `getFabAnimationMode` | `slide`
+**Anchor mode** | `app:fabAnchorMode` | `setFabAnchorMode` `getFabAnchorMode` | `embed`
+**Cradle margin** | `app:fabCradleMargin` | `setFabCradleMargin` `getFabCradleMargin` | `6dp`
+**Cradle rounded corner radius** | `app:fabCradleRoundedCornerRadius` | `setFabCradleRoundedCornerRadius` `getFabCradleRoundedCornerRadius` | `4dp`
+**Cradle vertical offset** | `app:fabCradleVerticalOffset` | `setCradleVerticalOffset` `getCradleVerticalOffset` | `12dp`
+**End margin** | `app:fabAlignmentModeEndMargin` | `setFabAlignmentModeEndMargin` `getFabAlignmentModeEndMargin` | `16dp`
+**Embedded elevation** | `app:removeEmbeddedFabElevation` | N/A | `true`
-## Bottom app bar
+See the
+[FAB documentation](https://github.com/material-components/material-components-android/tree/master/docs/components/FloatingActionButton.md)
+for more attributes.
-Bottom app bars provide access to up to four actions, including the
-[floating action button](FloatingActionButton.md) (FAB).
+### Action item(s) attributes
-### Bottom app bar examples
+Element | Attribute | Related method(s) | Default value
+------------------ | ----------------------- | -------------------------------------------------- | -------------
+**Menu** | `app:menu` | `replaceMenu` `getMenu` | `null`
+**Icon color** | N/A | N/A | `?attr/colorControlNormal` (as `Drawable` tint)
+**Alignment mode** | `app:menuAlignmentMode` | `setMenuAlignmentMode` `getMenuAlignmentMode` | `start`
-API and source code:
+### Overflow menu attributes
-* `CoordinatorLayout`
- * [Class definition](https://developer.android.com/reference/androidx/coordinatorlayout/widget/CoordinatorLayout)
-* `BottomAppBar`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/bottomappbar/BottomAppBar)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomappbar/BottomAppBar.java)
-* `FloatingActionButton`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/floatingactionbutton/FloatingActionButton)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/floatingactionbutton/FloatingActionButton.java)
+Element | Attribute | Related method(s) | Default value
+------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------- | -------------
+**Icon** | `android:src` and `app:srcCompat` in `actionOverflowButtonStyle` (in app theme) | `setOverflowIcon` `getOverflowIcon` | `@drawable/abc_ic_menu_overflow_material` (before API 23) or `@drawable/ic_menu_moreoverflow_material` (after API 23)
+**Theme** | `app:popupTheme` | `setPopupTheme` `getPopupTheme` | `@style/ThemeOverlay.Material3.*`
+**Item typography** | `textAppearanceSmallPopupMenu` and `textAppearanceLargePopupMenu` in `app:popupTheme` or app theme | N/A | `?attr/textAppearanceTitleMedium`
+
+### Styles
+
+Element | Style | Theme attribute
+----------------- | ------------------------------- | -------------------
+**Default style** | `Widget.Material3.BottomAppBar` | `bottomAppBarStyle`
+
+See the full list of
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomappbar/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomappbar/res/values/attrs.xml).
+
+## Code implementation
+
+Before you can use Material bottom app bars, you need to add a dependency to the
+Material Components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
+
+### Bottom app bar examples
The following example shows a bottom app bar with a navigation icon, 3 action
icons, and an embedded FAB.
-
+
-In the layout:
+**In the layout:**
```xml
```
-In `menu/bottom_app_bar.xml`:
+**In `menu/bottom_app_bar.xml`:**
```xml
```
-## Anatomy and key properties
-
-A bottom app bar has a container and an optional navigation icon, anchored
-floating action button (FAB), action item(s) and an overflow menu.
-
-
-
-1. Container
-2. Floating action button (FAB) (optional)
-3. Action item(s) (optional)
-4. Navigation icon (optional)
-5. Overflow menu (optional)
-
-### Container attributes
-
-Element | Attribute | Related method(s) | Default value
-------------- | ------------------------ | ------------------------------------------ | -------------
-**Color** | `app:backgroundTint` | `setBackgroundTint` `getBackgroundTint` | `?attr/colorSurfaceContainer`
-**Elevation** | `app:elevation` | `setElevation` | `3dp`
-**Height** | `android:minHeight` | `setMinimumHeight` `getMinimumHeight` | `80dp`
-**Shadows** | `app:addElevationShadow` | N/A | `false`
-
-### Navigation icon attributes
-
-Element | Attribute | Related method(s) | Default value
---------- | ------------------------ | ------------------------------------------ | -------------
-**Icon** | `app:navigationIcon` | `setNavigationIcon` `getNavigationIcon` | `null`
-**Color** | `app:navigationIconTint` | `setNavigationIconTint` | `?attr/colorOnSurfaceVariant` (as `Drawable` tint)
-
-### FAB attributes
-
-Element | Attribute | Related method(s) | Default value
--------------------------------- | ---------------------------------- | ---------------------------------------------------------------------- | -------------
-**Alignment mode** | `app:fabAlignmentMode` | `setFabAlignmentMode` `getFabAlignmentMode` | `end`
-**Animation mode** | `app:fabAnimationMode` | `setFabAnimationMode` `getFabAnimationMode` | `slide`
-**Anchor mode** | `app:fabAnchorMode` | `setFabAnchorMode` `getFabAnchorMode` | `embed`
-**Cradle margin** | `app:fabCradleMargin` | `setFabCradleMargin` `getFabCradleMargin` | `6dp`
-**Cradle rounded corner radius** | `app:fabCradleRoundedCornerRadius` | `setFabCradleRoundedCornerRadius` `getFabCradleRoundedCornerRadius` | `4dp`
-**Cradle vertical offset** | `app:fabCradleVerticalOffset` | `setCradleVerticalOffset` `getCradleVerticalOffset` | `12dp`
-**End margin** | `app:fabAlignmentModeEndMargin` | `setFabAlignmentModeEndMargin` `getFabAlignmentModeEndMargin` | `16dp`
-**Embedded elevation** | `app:removeEmbeddedFabElevation` | N/A | `true`
-
-See the
-[FAB documentation](https://github.com/material-components/material-components-android/tree/master/docs/components/FloatingActionButton.md)
-for more attributes.
-
-#### Action item(s) attributes
+## Customizing bottom app bars
-Element | Attribute | Related method(s) | Default value
------------------- | ----------------------- | -------------------------------------------------- | -------------
-**Menu** | `app:menu` | `replaceMenu` `getMenu` | `null`
-**Icon color** | N/A | N/A | `?attr/colorControlNormal` (as `Drawable` tint)
-**Alignment mode** | `app:menuAlignmentMode` | `setMenuAlignmentMode` `getMenuAlignmentMode` | `start`
-
-### Overflow menu attributes
-
-Element | Attribute | Related method(s) | Default value
-------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------- | -------------
-**Icon** | `android:src` and `app:srcCompat` in `actionOverflowButtonStyle` (in app theme) | `setOverflowIcon` `getOverflowIcon` | `@drawable/abc_ic_menu_overflow_material` (before API 23) or `@drawable/ic_menu_moreoverflow_material` (after API 23)
-**Theme** | `app:popupTheme` | `setPopupTheme` `getPopupTheme` | `@style/ThemeOverlay.Material3.*`
-**Item typography** | `textAppearanceSmallPopupMenu` and `textAppearanceLargePopupMenu` in `app:popupTheme` or app theme | N/A | `?attr/textAppearanceTitleMedium`
-
-### Styles
-
-Element | Style
------------------ | -------------------------------
-**Default style** | `Widget.Material3.BottomAppBar`
-
-Default style theme attribute: `bottomAppBarStyle`
-
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomappbar/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomappbar/res/values/attrs.xml).
-
-## Theming bottom app bars
+### Theming bottom app bars
-Bottom app bars support
-[Material Theming](https://material.io/components/app-bars-bottom#theming) which
-can customize color, typography and shape.
+Bottom app bars support the customization of color, typography, and shape.
-### Bottom app bar theming example
+#### Bottom app bar theming example
API and source code:
@@ -315,11 +347,11 @@ API and source code:
* `BottomAppBarCutCornersTopEdge`:
* [Class source](https://github.com/material-components/material-components-android/tree/master/catalog/java/io/material/catalog/bottomappbar/BottomAppBarCutCornersTopEdge.java)
-The following example shows a bottom app bar with Material Theming.
+The following example shows a bottom app bar with Material theming.
-
+
-#### Implementing bottom app bar theming
+##### Implementing bottom app bar theming
Use theme attributes in `res/values/styles.xml`, which applies the theme to all
bottom app bars and FABs and affects other components:
@@ -327,12 +359,12 @@ bottom app bars and FABs and affects other components:
```xml
-
```
@@ -360,20 +392,20 @@ theme to all bottom app bars and FABs but does not affect other components:
@style/ThemeOverlay.App.BottomAppBar
-
-
-
```
@@ -390,18 +422,3 @@ Use the styles in the layout, which affects only this bottom app bar and FAB:
style="@style/Widget.App.FloatingActionButton"
/>
```
-
-In code:
-
-```kt
-val topEdge = BottomAppBarCutCornersTopEdge(
- bottomAppBar.fabCradleMargin,
- bottomAppBar.fabCradleRoundedCornerRadius,
- bottomAppBar.cradleVerticalOffset
-)
-val background = bottomAppBar.background as MaterialShapeDrawable
-background.shapeAppearanceModel = background.shapeAppearanceModel.toBuilder().setTopEdge(topEdge).build()
-```
-
-**Note:** Using `BottomAppBarCutCornersTopEdge` is not necessary with rounded
-shapeAppearance corners.
diff --git a/docs/components/BottomNavigation.md b/docs/components/BottomNavigation.md
index f6d8fd665c3..0b21f3870cb 100644
--- a/docs/components/BottomNavigation.md
+++ b/docs/components/BottomNavigation.md
@@ -7,32 +7,145 @@ iconId: bottom_navigation
path: /catalog/bottom-navigation/
-->
-# Bottom Navigation
+# Bottom navigation (Navigation bar)
-[Bottom navigation](https://material.io/components/bottom-navigation/#) bars
-allow movement between primary destinations in an app.
+**Note:** Bottom navigation has been renamed to navigation bar.
-
+[Navigation bar](https://m3.material.io/components/navigation-bar/overview) lets
+people switch between UI views on smaller devices.
-**Contents**
+
+Navigation bar on compact and medium window sizes
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using bottom navigation](#using-bottom-navigation)
-* [Bottom navigation bar](#bottom-navigation-bar)
-* [Theming](#theming-a-bottom-navigation-bar)
+**Note:** Images use various dynamic color schemes.
-## Design and API Documentation
+## Design & API documentation
-* [Google Material3 Spec](https://material.io/components/navigation-bar/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/bottomnavigation/package-summary)
+* [Material 3 (M3) spec](https://m3.material.io/components/navigation-bar/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/bottomnavigation/package-summary)
-## Using bottom navigation
+## Anatomy
-Before you can use the Material bottom navigation, you need to add a dependency
-to the Material Components for Android library. For more information, go to the
+
+
+1. Container
+2. Icon
+3. Label text
+4. Active indicator
+5. Small badge (optional)
+6. Large badge (optional)
+7. Large badge label
+
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/navigation-bar/guidelines#895b5b49-a166-4d30-90be-c71a4c970f04).
+
+## M3 Expressive update
+
+Before you can use `Material3Expressive` component styles, follow the
+[`Material3Expressive` themes setup instructions](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md#material3expressive-themes).
+
+A new expressive style for the bottom navigation bar has been introduced that's
+shorter and supports horizontal navigation items in medium-sized windows.
+[More on M3 Expressive](https://m3.material.io/blog/building-with-m3-expressive)
+
+
+The updated expressive navigation bar is shorter and can be used in medium
+windows with horizontal nav items
+
+**Color:**
+
+* Active label changed from **on-surface-variant** to **secondary**
+
+## Key properties
+
+### Container attributes
+
+Element | Attribute | Related methods | Default value
+------------------------------ | --------------------- | --------------- | -------------
+**Color** | `app:backgroundTint` | N/A | `?attr/colorSurfaceContainer`
+**Elevation** | `app:elevation` | `setElevation` | `3dp`
+**Compat Shadow** (deprecated) | `compatShadowEnabled` | N/A | `false`
+
+**Note:** `compatShadowEnabled` has no effect, as the library no longer supports
+pre-Lollipop.
+
+### Navigation bar item attributes
+
+Element | Attribute | Related methods | Default value
+------------------------- | ------------------------- | ----------------------------------------------------- | -------------
+**Menu resource** | `app:menu` | `inflateMenu` `getMenu` | N/A
+**Ripple (inactive)** | `app:itemRippleColor` | `setItemRippleColor` `getItemRippleColor` | Variations of `?attr/colorPrimary` and `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_ripple_color_selector.xml))
+**Ripple (active)** | " | " | Variations of `?attr/colorPrimary` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_ripple_color_selector.xml))
+**Label visibility mode** | `app:labelVisibilityMode` | `setLabelVisibilityMode` `getLabelVisibilityMode` | `LABEL_VISIBILITY_AUTO`
+**Item Gravity** | `app:itemGravity` | `setItemGravity` `getItemGravity` | `TOP_CENTER`
+
+### Active indicator attributes
+
+Element | Attribute | Related methods | Default value
+--------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------- | -------------
+**Color** | `android:color` | `setItemActiveIndicatorColor` `getItemActiveIndicatorColor` | `?attr/colorSecondaryContainer`
+**Width** | `android:width` | `setItemActiveIndicatorWidth` `getItemActiveIndicatorWidth` | `56dp`
+**Height** | `android:height` | `setItemActiveIndicatorHeight` `getItemActiveIndicatorHeight` | `32dp`
+**Shape** | `app:shapeAppearance` | `setItemActiveIndicatorShapeAppearance` `getItemActiveIndicatorShapeAppearance` | `50% rounded`
+**Margin horizontal** | `app:marginHorizontal` | `setItemActiveIndicatorMarginHorizontal` `getItemActiveIndicatorMarginHorizontal` | `4dp`
+**Padding between indicator and label** | `app:activeIndicatorLabelPadding` | `setActiveIndicatorLabelPadding` `getActiveIndicatorLabelPadding` | `4dp`
+**Expanded Width** | `app:expandedWidth` | `setItemActiveIndicatorExpandedWidth` `getItemActiveIndicatorExpandedWidth` | `HUG`
+**Expanded Height** | `app:expandedHeight` | `setItemActiveIndicatorExpandedHeight` `getItemActiveIndicatorExpandedHeight` | `56dp`
+**Expanded Margin horizontal** | `app:expandedMarginHorizontal` | `setItemActiveIndicatorExpandedMarginHorizontal` `getItemActiveIndicatorExpandedMarginHorizontal` | `20dp`
+**Expanded Start Padding** | `app:expandedActiveIndicatorPaddingStart` | `setItemExpandedActiveIndicatorPadding` | `16dp`
+**Expanded End Padding** | `app:expandedActiveIndicatorPaddingEnd` | `setItemExpandedActiveIndicatorPadding` | `16dp`
+**Expanded Top Padding** | `app:expandedActiveIndicatorPaddingTop` | `setItemExpandedActiveIndicatorPadding` | `0dp`
+**Expanded Bottom Padding** | `app:expandedActiveIndicatorPaddingBottom` | `setItemExpandedActiveIndicatorPadding` | `0dp`
+
+**Note:** The expanded active indicator refers to the active indicator that
+expands to wrap the content of the navigation bar item when the
+`itemIconGravity` value is equal to `START`.
+
+### Icon attributes
+
+Element | Attribute | Related methods | Default value
+--------------------------------- | ------------------------------------- | ------------------------------------------------------------------- | -------------
+**Icon** | `android:icon` in the `menu` resource | N/A | N/A
+**Size** | `app:itemIconSize` | `setItemIconSize` `setItemIconSizeRes` `getItemIconSize` | `24dp`
+**Color (inactive)** | `app:itemIconTint` | `setItemIconTintList` `getItemIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_icon_tint.xml))
+**Color (active)** | " | " | `?attr/colorOnSecondaryContainer` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_icon_tint.xml))
+**Gravity** | `app:itemIconGravity` | `setItemIconGravity` `getItemIconGravity` | `TOP`
+**Icon label horizontal padding** | `app:iconLabelHorizontalSpacing` | `setIconLabelHorizontalSpacing` `getIconLabelHorizontalSpacing` | `4dp`
+
+### Text label attributes
+
+Element | Attribute | Related methods | Default value
+------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------
+**Text label** | `android:title` in the `menu` resource | N/A | N/A
+**Color (inactive)** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_label_tint.xml))
+**Color (active)** | " | " | `?attr/colorOnSurface` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_label_tint.xml))
+**Typography (inactive)** | `app:itemTextAppearanceInactive` `app:horizontalItemTextAppearanceInactive` | `setItemTextAppearanceInactive` `getItemTextAppearanceInactive` `setHorizontalItemTextAppearanceInactive` `getHorizontalItemTextAppearanceInactive` | `?attr/textAppearanceTitleSmall`
+**Typography (active)** | `app:itemTextAppearanceActive` `app:horizontalItemTextAppearanceActive` | `setItemTextAppearanceActive` `getItemTextAppearanceActive` `setHorizontalItemTextAppearanceActive` `getHorizontalItemTextAppearanceActive` | `?attr/textAppearanceTitleSmall`
+**Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true`
+**Max lines** | `app:labelMaxLines` | `setLabelMaxLines` `getLabelMaxLines` | `1`
+**Scale with font size** | `app:scaleLabelWithFontSize` | `setScaleLabelTextWithFont` `getScaleLabelTextWithFont` | `false`
+
+### Styles
+
+Element | Style | Container color | Icon/Text label color (inactive) | Icon/Text label color (active) | Theme attribute
+----------------- | --------------------------------------- | -------------------- | -------------------------------- | -------------------------------------------------------------------------- | ---------------
+**Default style** | `Widget.Material3.BottomNavigationView` | `?attr/colorSurface` | `?attr/colorOnSurfaceVariant` | Icon: `?attr/colorOnSecondaryContainer` Text: `?attr/colorOnSurface` | `?attr/bottomNavigationStyle`
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/values/styles.xml),
+[navigation attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/res/values/attrs.xml),
+and
+[navigation bar attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/values/attrs.xml).
+
+## Code implementation
+
+Before you can use the Material navigation bar, you need to add a dependency to
+the Material components for Android library. For more information, go to the
[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
page.
+### Adding navigation bar
+
A typical layout looks like this:
```xml
@@ -106,8 +219,7 @@ bottomNavigation.setOnItemReselectedListener { item ->
That results in:
-
+
**Note:** We have deprecated the
`BottomNavigationView#setOnNavigationItemSelectedListener` and
@@ -115,85 +227,11 @@ another unselected icon.](assets/bottomnav/bottom-nav-default.png)
the listeners in `NavigationBarView`. This allows you to share selection
handling code between the `BottomNavigation` and `NavigationRail` view elements.
-### Making bottom navigation accessible
-
-You should set an `android:title` for each of your `menu` items so that screen
-readers like TalkBack can properly announce what each navigation item
-represents:
-
-```xml
-
-
- ...
-
-```
-
-The `labelVisibilityMode` attribute can be used to adjust the behavior of the
-text labels for each navigation item. There are four visibility modes:
-
-* `LABEL_VISIBILITY_AUTO` (default): The label behaves as “labeled” when there
- are 3 items or less, or “selected” when there are 4 items or more
-* `LABEL_VISIBILITY_SELECTED`: The label is only shown on the selected
- navigation item
-* `LABEL_VISIBILITY_LABELED`: The label is shown on all navigation items
-* `LABEL_VISIBILITY_UNLABELED`: The label is hidden for all navigation items
-
-### Adding badges
-
-
+### Navigation bar example
-Initialize and show a `BadgeDrawable` associated with `menuItemId`, subsequent
-calls to this method will reuse the existing `BadgeDrawable`:
-
-```kt
-var badge = bottomNavigation.getOrCreateBadge(menuItemId)
-badge.isVisible = true
-// An icon only badge will be displayed unless a number or text is set:
-badge.number = 99 // or badge.text = "New"
-```
+The following example shows a navigation bar with four icons:
-As a best practice, if you need to temporarily hide the badge, for instance
-until the next notification is received, change the visibility of
-`BadgeDrawable`:
-
-```kt
-val badgeDrawable = bottomNavigation.getBadge(menuItemId)
- if (badgeDrawable != null) {
- badgeDrawable.isVisible = false
- badgeDrawable.clearNumber() // or badgeDrawable.clearText()
- }
-```
-
-To remove any `BadgeDrawable`s that are no longer needed:
-
-```kt
-bottomNavigation.removeBadge(menuItemId)
-```
-
-See the [`BadgeDrawable`](BadgeDrawable.md) documentation for more information
-about badges.
-
-## Bottom navigation bar
-
-
-
-### Bottom navigation bar example
-
-API and source code:
-
-* `BottomNavigationView`
- * [Class description](https://developer.android.com/reference/com/google/android/material/bottomnavigation/BottomNavigationView)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/BottomNavigationView.java)
-
-The following example shows a bottom navigation bar with four icons:
-
-
+
In `layout.xml`:
@@ -246,101 +284,95 @@ In code:
bottomNavigation.selectedItemId = R.id.page_2
```
-### Anatomy and key properties
+### Adding navigation bar on larger screens
-The following is an anatomy diagram for the bottom navigation bar:
+On medium screen sizes and larger, navigation bars are recommended to be a
+horizontal item configuration, by setting `app:itemIconGravity` to be `start`
+instead of `top`. You can do this by
+[setting alternative layouts identified by resource qualifiers](https://developer.android.com/develop/ui/views/layout/responsive-adaptive-design-with-views#alternative_layout_resources).
-
+Here's an example:
-* (1) Container
-* (2) Icon
-* (3) Label text
-* (4) Active indicator
-* (5) Small badge (optional)
-* (6) Large badge (optional)
-* (7) Large badge number
+```xml
+
+```
-#### Container attributes
+
-| **Element** | **Attribute** | **Related methods** | **Default value** |
-|--------------------------------|-----------------------|---------------------|-------------------------------|
-| **Color** | `app:backgroundTint` | N/A | `?attr/colorSurfaceContainer` |
-| **Elevation** | `app:elevation` | `setElevation` | `3dp` |
-| **Compat Shadow** (deprecated) | `compatShadowEnabled` | N/A | `false` |
+### Making navigation bar accessible
-**Note:** `compatShadowEnabled` has no effect, as the library no longer supports pre-Lollipop.
+You should set an `android:title` for each of your `menu` items so that screen
+readers like TalkBack can properly announce what each navigation item
+represents:
-#### Navigation item attributes
+```xml
+
+
+ ...
+
+```
-**Element** | **Attribute** | **Related methods** | **Default value**
-------------------------- | ------------------------- | ----------------------------------------------------- | -----------------
-**Menu resource** | `app:menu` | `inflateMenu` `getMenu` | N/A
-**Ripple (inactive)** | `app:itemRippleColor` | `setItemRippleColor` `getItemRippleColor` | Variations of `?attr/colorPrimary` and `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_ripple_color_selector.xml))
-**Ripple (active)** | " | " | Variations of `?attr/colorPrimary` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_ripple_color_selector.xml))
-**Label visibility mode** | `app:labelVisibilityMode` | `setLabelVisibilityMode` `getLabelVisibilityMode` | `LABEL_VISIBILITY_AUTO`
-**Item Gravity** | `app:itemGravity` | `setItemGravity` `getItemGravity` | `TOP_CENTER`
+The `labelVisibilityMode` attribute can be used to adjust the behavior of the
+text labels for each navigation bar item. There are four visibility modes:
-#### Active indicator attributes
+* `LABEL_VISIBILITY_AUTO` (default): The label behaves as “labeled” when there
+ are 3 items or less, or “selected” when there are 4 items or more
+* `LABEL_VISIBILITY_SELECTED`: The label is only shown on the selected
+ navigation item
+* `LABEL_VISIBILITY_LABELED`: The label is shown on all navigation items
+* `LABEL_VISIBILITY_UNLABELED`: The label is hidden for all navigation items
-**Element** | **Attribute** | **Related methods** | **Default value**
---------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------- | -----------------
-**Color** | `android:color` | `setItemActiveIndicatorColor` `getItemActiveIndicatorColor` | `?attr/colorSecondaryContainer`
-**Width** | `android:width` | `setItemActiveIndicatorWidth` `getItemActiveIndicatorWidth` | `56dp`
-**Height** | `android:height` | `setItemActiveIndicatorHeight` `getItemActiveIndicatorHeight` | `32dp`
-**Shape** | `app:shapeAppearance` | `setItemActiveIndicatorShapeAppearance` `getItemActiveIndicatorShapeAppearance` | `50% rounded`
-**Margin horizontal** | `app:marginHorizontal` | `setItemActiveIndicatorMarginHorizontal` `getItemActiveIndicatorMarginHorizontal` | `4dp`
-**Padding between indicator and label** | `app:activeIndicatorLabelPadding` | `setActiveIndicatorLabelPadding` `getActiveIndicatorLabelPadding` | `4dp`
-**Expanded Width** | `expandedWidth` | `setItemExpandedActiveIndicatorWidth` `getItemExpandedActiveIndicatorWidth` | `HUG`
-**Expanded Height** | `expandedHeight` | `setItemExpandedActiveIndicatorHeight` `getItemExpandedActiveIndicatorHeight` | `56dp`
-**Expanded Margin horizontal** | `app:expandedMarginHorizontal` | `setItemExpandedActiveIndicatorMarginHorizontal` `getItemExpandedActiveIndicatorMarginHorizontal` | `20dp`
+### Adding badges
-**Note:** The expanded active indicator refers to the active indicator that
-expands to wrap the content of the Bottom Navigation item when the
-`itemIconGravity` value is equal to `START`.
+
-#### Icon attributes
+Initialize and show a `BadgeDrawable` associated with `menuItemId`, subsequent
+calls to this method will reuse the existing `BadgeDrawable`:
-**Element** | **Attribute** | **Related methods** | **Default value**
---------------------------------- | ------------------------------------- | ------------------------------------------------------------------- | -----------------
-**Icon** | `android:icon` in the `menu` resource | N/A | N/A
-**Size** | `app:itemIconSize` | `setItemIconSize` `setItemIconSizeRes` `getItemIconSize` | `24dp`
-**Color (inactive)** | `app:itemIconTint` | `setItemIconTintList` `getItemIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_icon_tint.xml))
-**Color (active)** | " | " | `?attr/colorOnSecondaryContainer` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_icon_tint.xml))
-**Gravity** | `app:itemIconGravity` | `setItemIconGravity` `getItemIconGravity` | `TOP`
-**Icon label horizontal padding** | `app:iconLabelHorizontalSpacing` | `setIconLabelHorizontalSpacing` `getIconLabelHorizontalSpacing` | `4dp`
+```kt
+var badge = bottomNavigation.getOrCreateBadge(menuItemId)
+badge.isVisible = true
+// An icon only badge will be displayed unless a number or text is set:
+badge.number = 99 // or badge.text = "New"
+```
-#### Text label attributes
+As a best practice, if you need to temporarily hide the badge, for instance
+until the next notification is received, change the visibility of
+`BadgeDrawable`:
-**Element** | **Attribute** | **Related methods** | **Default value**
-------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----------------
-**Text label** | `android:title` in the `menu` resource | N/A | N/A
-**Color (inactive)** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_label_tint.xml))
-**Color (active)** | " | " | `?attr/colorOnSurface` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_label_tint.xml))
-**Typography (inactive)** | `app:itemTextAppearanceInactive` `app:horizontalItemTextAppearanceInactive` | `setItemTextAppearanceInactive` `getItemTextAppearanceInactive` `setHorizontalItemTextAppearanceInactive` `getHorizontalItemTextAppearanceInactive` | `?attr/textAppearanceTitleSmall`
-**Typography (active)** | `app:itemTextAppearanceActive` `app:horizontalItemTextAppearanceActive` | `setItemTextAppearanceActive` `getItemTextAppearanceActive` `setHorizontalItemTextAppearanceActive` `getHorizontalItemTextAppearanceActive` | `?attr/textAppearanceTitleSmall`
-**Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true`
+```kt
+val badgeDrawable = bottomNavigation.getBadge(menuItemId)
+ if (badgeDrawable != null) {
+ badgeDrawable.isVisible = false
+ badgeDrawable.clearNumber() // or badgeDrawable.clearText()
+ }
+```
-#### Styles
+To remove any `BadgeDrawable`s that are no longer needed:
-**Element** | **Style** | **Container color** | **Icon/Text label color (inactive)** | **Icon/Text label color (active)**
------------------ | --------------------------------------- | -------------------- | ------------------------------------ | ----------------------------------
-**Default style** | `Widget.Material3.BottomNavigationView` | `?attr/colorSurface` | `?attr/colorOnSurfaceVariant` | Icon: `?attr/colorOnSecondaryContainer` Text: `?attr/colorOnSurface`
+```kt
+bottomNavigation.removeBadge(menuItemId)
+```
-Default style theme attribute: `?attr/bottomNavigationStyle`
+See the [`Badges`](BadgeDrawable.md) documentation for more information about
+badges.
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/values/styles.xml),
-[navigation bar attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/res/values/attrs.xml),
-and
-[bottom navigation attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/values/attrs.xml).
+## Customizing navigation bar
-## Theming a bottom navigation bar
+### Theming a navigation bar
-Bottom navigation supports
-[Material Theming](https://material.io/components/bottom-navigation#theming),
-which can customize color and typography.
+Navigation bars support the customization of color and typography.
-### Bottom navigation theming example
+#### Navigation bar theming example
API and source code:
@@ -348,15 +380,14 @@ API and source code:
* [Class description](https://developer.android.com/reference/com/google/android/material/bottomnavigation/BottomNavigationView)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/BottomNavigationView.java)
-The following example shows a bottom navigation bar with Material Theming.
+The following example shows a navigation bar with Material theming.
-
+
-#### Implementing bottom navigation theming
+##### Implementing navigation bar theming
Use theme attributes and a style in `res/values/styles.xml`, which applies to
-all bottom navigation bars and affects other components:
+all navigation bars and affects other components:
```xml
```
-Use the style in the layout, which affects only this specific bottom navigation
-bar:
+Use the style in the layout, which affects only this specific navigation bar:
```xml
-# Bottom Sheets
+# Bottom sheets
-[Bottom sheets](https://material.io/components/sheets-bottom) are surfaces
-containing supplementary content that are anchored to the bottom of the screen.
+[Bottom sheets](https://m3.material.io/components/bottom-sheets/overview) show
+secondary content anchored to the bottom of the screen. There are two variants
+of bottom sheets.
-
+
-**Contents**
+1. Standard bottom sheet
+2. Modal bottom sheet
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using bottom sheets](#using-bottom-sheets)
-* [Standard bottom sheet](#standard-bottom-sheet)
-* [Modal bottom sheet](#modal-bottom-sheet)
-* [Anatomy and key properties](#anatomy-and-key-properties)
-* [Predictive Back](#predictive-back)
-* [Theming](#theming-bottom-sheets)
+**Note:** Images use various dynamic color schemes.
-## Design and API Documentation
+## Design & API documentation
-* [Google Material3 Spec](https://material.io/components/bottom-sheets/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/bottomsheet/package-summary)
+* [Material 3 (M3) spec](https://m3.material.io/components/bottom-sheets/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/bottomsheet/package-summary)
-## Using bottom sheets
+## Anatomy
-Before you can use Material bottom sheets, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
-[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page.
-
-Standard bottom sheet basic usage:
-
-```xml
-
-
-
-
-
-
-
-
-
-```
-
-Modal bottom sheet basic usage:
-
-```kt
-class ModalBottomSheet : BottomSheetDialogFragment() {
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = inflater.inflate(R.layout.modal_bottom_sheet_content, container, false)
-
- companion object {
- const val TAG = "ModalBottomSheet"
- }
-}
-
-class MainActivity : AppCompatActivity() {
- ...
- val modalBottomSheet = ModalBottomSheet()
- modalBottomSheet.show(supportFragmentManager, ModalBottomSheet.TAG)
- ...
-}
-```
-
-More information on each individual section, below.
-
-### Setting behavior
-
-There are several attributes that can be used to adjust the behavior of both
-standard and modal bottom sheets.
-
-Behavior attributes can be applied to standard bottom sheets in xml by setting
-them on a child `View` set to `app:layout_behavior`, or programmatically:
-
-```kt
-val standardBottomSheetBehavior = BottomSheetBehavior.from(standardBottomSheet)
-// Use this to programmatically apply behavior attributes
-```
-
-Behavior attributes can be applied to modal bottom sheets using app-level theme
-attributes and styles:
-
-```xml
-
-
-
-
-
-```
-
-Or programmatically:
+Modal bottom sheets are above a scrim while standard bottom sheets don't have a
+scrim. Besides this, both types of bottom sheets have the same specs.
-```kt
-val modalBottomSheetBehavior = (modalBottomSheet.dialog as BottomSheetDialog).behavior
-// Use this to programmatically apply behavior attributes
-```
-
-More information about these attributes and their default values is available in
-the [behavior attributes](#behavior-attributes) section.
-
-### Retaining behavior on configuration change
-
-In order to save and restore specific behaviors of the bottom sheet on
-configuration change, the following flags can be set (or combined with bitwise
-OR operations):
-
-* `SAVE_PEEK_HEIGHT`: `app:behavior_peekHeight` is preserved.
-* `SAVE_HIDEABLE`: `app:behavior_hideable` is preserved.
-* `SAVE_SKIP_COLLAPSED`: `app:behavior_skipCollapsed` is preserved.
-* `SAVE_FIT_TO_CONTENTS`: `app:behavior_fitToContents` is preserved.
-* `SAVE_ALL`: All aforementioned attributes are preserved.
-* `SAVE_NONE`: No attribute is preserved. This is the default value.
-
-Behaviors can also be set in code:
-
-```kt
-bottomSheetBehavior.saveFlags = BottomSheetBehavior.SAVE_ALL
-```
-
-Or in xml using the `app:behavior_saveFlags` attribute.
-
-### Setting state
-
-Standard and modal bottom sheets have the following states:
-
-* `STATE_COLLAPSED`: The bottom sheet is visible but only showing its peek
- height. This state is usually the 'resting position' of a bottom sheet, and
- should have enough height to indicate there is extra content for the user to
- interact with.
-* `STATE_EXPANDED`: The bottom sheet is visible at its maximum height and it
- is neither dragging nor settling (see below).
-* `STATE_HALF_EXPANDED`: The bottom sheet is half-expanded (only applicable if
- `behavior_fitToContents` has been set to false), and is neither dragging nor
- settling (see below).
-* `STATE_HIDDEN`: The bottom sheet is no longer visible and can only be
- re-shown programmatically.
-* `STATE_DRAGGING`: The user is actively dragging the bottom sheet up or down.
-* `STATE_SETTLING`: The bottom sheet is settling to a specific height after a
- drag/swipe gesture. This will be the peek height, expanded height, or 0, in
- case the user action caused the bottom sheet to hide.
-
-You can set a state on the bottom sheet:
-
-```kt
-bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
-```
-
-**Note:** `STATE_SETTLING` and `STATE_DRAGGING` should not be set programmatically.
-
-### Listening to state and slide changes
+
-A `BottomSheetCallback` can be added to a `BottomSheetBehavior`:
+1. Container
+2. Drag handle (optional)
+3. Scrim
-```kt
-val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/bottom-sheets/guidelines#0dd76c6d-7f76-4ff4-b325-0abf28b00029).
- override fun onStateChanged(bottomSheet: View, newState: Int) {
- // Do something for new state.
- }
+## Key properties
- override fun onSlide(bottomSheet: View, slideOffset: Float) {
- // Do something for slide offset.
- }
-}
+### Sheet attributes
-// To add the callback:
-bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback)
+Element | Attribute | Related method(s) | Default value
+-------------- | --------------------- | --------------------------------- | -------------
+**Color** | `app:backgroundTint` | N/A | `?attr/colorSurfaceContainerLow`
+**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceCornerExtraLarge`
+**Elevation** | `android:elevation` | N/A | `1dp`
+**Max width** | `android:maxWidth` | `setMaxWidth` `getMaxWidth` | `640dp`
+**Max height** | `android:maxHeight` | `setMaxHeight` `getMaxHeight` | N/A
-// To remove the callback:
-bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallback)
-```
+### Behavior attributes
-### Handling insets and fullscreen
+More information about these attributes and how to use them in the
+[setting behavior](#setting-behavior) section.
-`BottomSheetBehavior` can automatically handle insets (such as for
-[edge to edge](https://developer.android.com/training/gestures/edge-to-edge)) by
-specifying any of these to true on the view:
+Behavior | Related method(s) | Default value
+------------------------------------------- | ------------------------------------------------------------------------- | -------------
+`app:behavior_peekHeight` | `setPeekHeight` `getPeekHeight` | `auto`
+`app:behavior_hideable` | `setHideable` `isHideable` | `false` for standard `true` for modal
+`app:behavior_skipCollapsed` | `setSkipCollapsed` `getSkipCollapsed` | `false`
+`app:behavior_fitToContents` | `setFitToContents` `isFitToContents` | `true`
+`app:behavior_draggable` | `setDraggable` `isDraggable` | `true`
+`app:behavior_draggableOnNestedScroll` | `setDraggableOnNestedScroll` `isDraggableOnNestedScroll` | `true`
+`app:behavior_halfExpandedRatio` | `setHalfExpandedRatio` `getHalfExpandedRatio` | `0.5`
+`app:behavior_expandedOffset` | `setExpandedOffset` `getExpandedOffset` | `0dp`
+`app:behavior_significantVelocityThreshold` | `setSignificantVelocityThreshold` `getSignificantVelocityThreshold` | `500 pixels/s`
-* `app:paddingBottomSystemWindowInsets`
-* `app:paddingLeftSystemWindowInsets`
-* `app:paddingRightSystemWindowInsets`
-* `app:paddingTopSystemWindowInsets`
+To save behavior on configuration change:
-On API 21 and above the modal bottom sheet will be rendered fullscreen (edge to
-edge) if the navigation bar is transparent and `app:enableEdgeToEdge` is true.
-To enable edge-to-edge by default for modal bottom sheets, you can override
-`?attr/bottomSheetDialogTheme` like the below example:
+Attribute | Related method(s) | Default value
+------------------------ | --------------------------------- | -------------
+`app:behavior_saveFlags` | `setSaveFlags` `getSaveFlags` | `SAVE_NONE`
-```xml
-
+### Styles
-
-```
+Element | Default value | Theme attribute
+------------------------- | ------------------------------------------- | ---------------
+**Default style (modal)** | `@style/Widget.Material3.BottomSheet.Modal` | `?attr/bottomSheetStyle`
-Insets can be added automatically if any of the padding attributes above are set
-to true in the style, either by updating the style passed to the constructor, or
-by updating the default style specified by the `?attr/bottomSheetDialogTheme`
-attribute in your theme.
+**Note**: The `?attr/bottomSheetStyle` default style theme attribute is for
+modal bottom sheets only. There is no default style theme attribute for standard
+bottom sheets, because `BottomSheetBehavior`s don't have a designated associated
+`View`.
-`BottomSheetDialog` will also add padding to the top when the bottom sheet
-slides under the status bar, to prevent content from being drawn underneath it.
+### Theme overlays
-### Making bottom sheets accessible
+Element | Theme overlay | Attribute
+------------------------- | ------------------------------------------ | ---------
+**Default theme overlay** | `ThemeOverlay.Material3.BottomSheetDialog` | `?attr/bottomSheetDialogTheme`
-The contents within a bottom sheet should follow their own accessibility
-guidelines, such as setting content descriptions for images.
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/res/values/styles.xml),
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/res/values/attrs.xml),
+and
+[themes and theme overlays](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/res/values/themes.xml).
-To support dragging bottom sheets with accessibility services such as TalkBack,
-Voice Access, Switch Access, etc., we provide a convenient widget
-`BottomSheetDragHandleView` which will automatically receive and handle
-accessibility commands to expand and collapse the attached bottom sheet when
-the accessibility mode is enabled. To use `BottomSheetDragHandleView`, you can
-add it to the top of your bottom sheet content. It will show a customizable
-visual indicator for all users. See the example in the below section for how to
-add a drag handle to your bottom sheet.
+## Variants of bottom sheets
-**Note:** `BottomSheetDragHandleView` has a default min width and height of 48dp
-to conform to the minimum touch target requirement. So you will need to preserve
-at least 48dp at the top to place a drag handle.
-
-## Standard bottom sheet
+### Standard bottom sheet
Standard bottom sheets co-exist with the screen’s main UI region and allow for
simultaneously viewing and interacting with both regions. They are commonly used
@@ -265,14 +119,14 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetBehavior)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/BottomSheetBehavior.java)
-### Standard bottom sheet example
+#### Standard bottom sheet example
The following example shows a standard bottom sheet in its collapsed and
expanded states:
-Collapsed | Expanded
------------------------------------------------------------------------------------------ | --------
- | 
+Collapsed | Expanded
+--------------------------------------------------------------------------------------------------------------------- | ----------------------------------------
+ |
`BottomSheetBehavior` works in tandem with `CoordinatorLayout` to let you
display content on a bottom sheet, perform enter/exit animations, respond to
@@ -341,7 +195,7 @@ val standardBottomSheetBehavior = BottomSheetBehavior.from(standardBottomSheet)
More information about using the behavior to set attributes is in the
[setting behavior](#setting-behavior) section.
-## Modal bottom sheet
+### Modal bottom sheet
Modal bottom sheets present a set of choices while blocking interaction with the
rest of the screen. They are an alternative to inline menus and simple dialogs
@@ -363,14 +217,14 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetDialogFragment)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/BottomSheetDialogFragment.java)
-### Modal bottom sheet example
+#### Modal bottom sheet example
The following example shows a modal bottom sheet in its collapsed and expanded
states:
-Collapsed | Expanded
------------------------------------------------------------------------------------ | --------
- | 
+Collapsed | Expanded
+--------------------------------------------------------------------------------------------------------------- | ----------------------------------------
+ |
First, subclass `BottomSheetDialogFragment` and overwrite `onCreateView` to
provide a layout for the contents of the sheet (in this example, it's
@@ -402,13 +256,15 @@ modalBottomSheet.show(supportFragmentManager, ModalBottomSheet.TAG)
you need to use `Activity.getSupportFragmentManager()`.
**Note:** Don't call `setOnCancelListener` or `setOnDismissListener` on a
-`BottomSheetDialogFragment`. You can override
-`onCancel(DialogInterface)` or `onDismiss(DialogInterface)` if necessary.
+`BottomSheetDialogFragment`. You can override `onCancel(DialogInterface)` or
+`onDismiss(DialogInterface)` if necessary.
`BottomSheetDialogFragment` wraps the view in a `BottomSheetDialog`, which has
its own `BottomSheetBehavior`. You can define your own `BottomSheetBehavior`
-through overriding `onCreateDialog`. Note that if overriding `onCreateDialog`,
-you should not override `onCreateView`.
+through overriding `onCreateDialog`.
+
+**Note:** If overriding `onCreateDialog`, you should not override
+`onCreateView`.
```kt
@@ -432,95 +288,106 @@ class ModalBottomSheet : BottomSheetDialogFragment() {
}
```
-## Anatomy and key properties
+## Code implementation
-Bottom sheets have a sheet, a drag handle, and, if modal, a scrim.
+Before you can use Material bottom sheets, you need to add a dependency to the
+Material components for Android library. For more information, see the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
-
+
+
Listening to state and slide changes
-1. Sheet
-2. Drag Handle
-3. Scrim (in modal bottom sheets)
+A `BottomSheetCallback` can be added to a `BottomSheetBehavior`:
-Content can also be added below the drag handle. (see [Using bottom sheets](#using-bottom-sheets))
+```kt
+val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
-### Sheet attributes
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ // Do something for new state.
+ }
-Element | Attribute | Related method(s) | Default value
--------------- | --------------------- | --------------------------------- | -------------
-**Color** | `app:backgroundTint` | N/A | `?attr/colorSurfaceContainerLow`
-**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceCornerExtraLarge`
-**Elevation** | `android:elevation` | N/A | `1dp`
-**Max width** | `android:maxWidth` | `setMaxWidth` `getMaxWidth` | `640dp`
-**Max height** | `android:maxHeight` | `setMaxHeight` `getMaxHeight` | N/A
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {
+ // Do something for slide offset.
+ }
+}
-### Behavior attributes
+// To add the callback:
+bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback)
-More info about these attributes and how to use them in the
-[setting behavior](#setting-behavior) section.
+// To remove the callback:
+bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallback)
+```
-Behavior | Related method(s) | Default value
-------------------------------------------- | ------------------------------------------------------------------------- | -------------
-`app:behavior_peekHeight` | `setPeekHeight` `getPeekHeight` | `auto`
-`app:behavior_hideable` | `setHideable` `isHideable` | `false` for standard `true` for modal
-`app:behavior_skipCollapsed` | `setSkipCollapsed` `getSkipCollapsed` | `false`
-`app:behavior_fitToContents` | `setFitToContents` `isFitToContents` | `true`
-`app:behavior_draggable` | `setDraggable` `isDraggable` | `true`
-`app:behavior_draggableOnNestedScroll` | `setDraggableOnNestedScroll` `isDraggableOnNestedScroll` | `true`
-`app:behavior_halfExpandedRatio` | `setHalfExpandedRatio` `getHalfExpandedRatio` | `0.5`
-`app:behavior_expandedOffset` | `setExpandedOffset` `getExpandedOffset` | `0dp`
-`app:behavior_significantVelocityThreshold` | `setSignificantVelocityThreshold` `getSignificantVelocityThreshold` | `500 pixels/s`
+
-To save behavior on configuration change:
+
+
Handling insets and fullscreen
-Attribute | Related method(s) | Default value
------------------------- | --------------------------------- | -------------
-`app:behavior_saveFlags` | `setSaveFlags` `getSaveFlags` | `SAVE_NONE`
+`BottomSheetBehavior` can automatically handle insets (such as for
+[edge to edge](https://developer.android.com/training/gestures/edge-to-edge)) by
+specifying any of these to true on the view:
-### Styles
+* `app:paddingBottomSystemWindowInsets`
+* `app:paddingLeftSystemWindowInsets`
+* `app:paddingRightSystemWindowInsets`
+* `app:paddingTopSystemWindowInsets`
-**Element** | **Default value**
-------------------------- | -------------------------------------------
-**Default style (modal)** | `@style/Widget.Material3.BottomSheet.Modal`
+On API 21 and above the modal bottom sheet will be rendered fullscreen (edge to
+edge) if the navigation bar is transparent and `enableEdgeToEdge` is true. To
+enable edge-to-edge by default for modal bottom sheets, you can override
+`?attr/bottomSheetDialogTheme` like the below example (`enableEdgeToEdge` is
+already true in `ThemeOverlay.Material3.BottomSheetDialog`):
-Default style theme attribute:`?attr/bottomSheetStyle`
+```xml
+
-Note: The `?attr/bottomSheetStyle` default style theme attribute is for modal
-bottom sheets only. There is no default style theme attribute for standard
-bottom sheets, because `BottomSheetBehavior`s don't have a designated associated
-`View`.
+
+```
-### Theme overlays
+Insets can be added automatically if any of the padding attributes above are set
+to true in the style, either by updating the style passed to the constructor, or
+by updating the default style specified by the `?attr/bottomSheetDialogTheme`
+attribute in your theme.
-**Element** | **Theme overlay**
-------------------------- | ------------------------------------------
-**Default theme overlay** | `ThemeOverlay.Material3.BottomSheetDialog`
+`BottomSheetDialog` will also add padding to the top when the bottom sheet
+slides under the status bar, to prevent content from being drawn underneath it.
-Default theme overlay attribute: `?attr/bottomSheetDialogTheme`
+`BottomSheetDialog` also supports
+[Protections](https://developer.android.com/reference/androidx/core/view/insets/Protection).
+If using
+[Gradient Protections](https://developer.android.com/reference/androidx/core/view/insets/GradientProtection),
+`BottomSheetBehavior` provides a `getDefaultBottomGradientProtection()` method
+that will return a bottom `GradientProtection` that is the color
+`?attr/colorSurfaceContainerLow` for Material3 or later, or `?attr/colorSurface`
+if otherwise not defined.
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/res/values/styles.xml),
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/res/values/attrs.xml),
-and
-[themes and theme overlays](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/res/values/themes.xml).
+
-## Predictive Back
+
+
Predictive back
-### Modal Bottom Sheets
+#### Modal bottom sheets
The modal `BottomSheetDialogFragment` and `BottomSheetDialog` components
-automatically support [Predictive Back](../foundations/PredictiveBack.md). No
-further integration is required on the app side other than the general
-Predictive Back prerequisites and migration steps mentioned
-[here](../foundations/PredictiveBack.md#usage).
+automatically support
+[predictive back](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md).
+No further integration is required on the app side other than the general
+predictive back prerequisites and migration steps mentioned
+[here](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md#usage).
Visit the
-[Predictive Back design guidelines](https://m3.material.io/components/bottom-sheets/guidelines#3d7735e2-73ea-4f3e-bd42-e70161fc1085)
+[predictive back design guidelines](https://m3.material.io/components/bottom-sheets/guidelines#3d7735e2-73ea-4f3e-bd42-e70161fc1085)
to see how the component behaves when a user swipes back.
-### Standard (Non-Modal) Bottom Sheets
+#### Standard (Non-Modal) bottom sheets
-To set up Predictive Back for standard (non-modal) bottom sheets using
+To set up predictive back for standard (non-modal) bottom sheets using
`BottomSheetBehavior`, create an AndroidX back callback that forwards
`BackEventCompat` objects to your `BottomSheetBehavior`:
@@ -564,13 +431,151 @@ bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetCallback() {
})
```
-## Theming bottom sheets
+
+
+
+
Setting state
+
+Standard and modal bottom sheets have the following states:
+
+* `STATE_COLLAPSED`: The bottom sheet is visible but only showing its peek
+ height. This state is usually the 'resting position' of a bottom sheet, and
+ should have enough height to indicate there is extra content for the user to
+ interact with.
+* `STATE_EXPANDED`: The bottom sheet is visible at its maximum height and it
+ is neither dragging nor settling
+* `STATE_HALF_EXPANDED`: The bottom sheet is half-expanded (only applicable if
+ `behavior_fitToContents` has been set to false), and is neither dragging nor
+ settling (see below).
+* `STATE_HIDDEN`: The bottom sheet is no longer visible and can only be
+ re-shown programmatically.
+* `STATE_DRAGGING`: The user is actively dragging the bottom sheet up or down.
+* `STATE_SETTLING`: The bottom sheet is settling to a specific height after a
+ drag/swipe gesture. This will be the peek height, expanded height, or 0, in
+ case the user action caused the bottom sheet to hide.
+
+**Note:** `STATE_SETTLING` and `STATE_DRAGGING` should not be set
+programmatically.
+
+You can set a state on the bottom sheet:
+
+```kt
+bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+```
+
+
+
+
+
Setting behavior
+
+There are several attributes that can be used to adjust the behavior of both
+standard and modal bottom sheets.
+
+Behavior attributes can be applied to standard bottom sheets in xml by setting
+them on a child `View` set to `app:layout_behavior`, or programmatically:
+
+```kt
+val standardBottomSheetBehavior = BottomSheetBehavior.from(standardBottomSheet)
+// Use this to programmatically apply behavior attributes
+```
+
+Behavior attributes can be applied to modal bottom sheets using app-level theme
+attributes and styles:
+
+```xml
+
+
+
+
+
+```
+
+Or programmatically:
+
+```kt
+val modalBottomSheetBehavior = (modalBottomSheet.dialog as BottomSheetDialog).behavior
+// Use this to programmatically apply behavior attributes
+```
+
+More information about these attributes and their default values is available in
+the [behavior attributes](#behavior-attributes) section.
+
+
+
+
+
Handling insets and fullscreen
+
+`BottomSheetBehavior` can automatically handle insets (such as for
+[edge to edge](https://developer.android.com/training/gestures/edge-to-edge)) by
+specifying any of these to true on the view:
+
+* `app:paddingBottomSystemWindowInsets`
+* `app:paddingLeftSystemWindowInsets`
+* `app:paddingRightSystemWindowInsets`
+* `app:paddingTopSystemWindowInsets`
+
+On API 21 and above the modal bottom sheet will be rendered fullscreen (edge to
+edge) if the navigation bar is transparent and `enableEdgeToEdge` is true. To
+enable edge-to-edge by default for modal bottom sheets, you can override
+`?attr/bottomSheetDialogTheme` like the below example (`enableEdgeToEdge` is
+already true in `ThemeOverlay.Material3.BottomSheetDialog`):
+
+```xml
+
+
+
+```
+
+Insets can be added automatically if any of the padding attributes above are set
+to true in the style, either by updating the style passed to the constructor, or
+by updating the default style specified by the `?attr/bottomSheetDialogTheme`
+attribute in your theme.
+
+`BottomSheetDialog` will also add padding to the top when the bottom sheet
+slides under the status bar, to prevent content from being drawn underneath it.
+
+
+
+
+
Making bottom sheets accessible
+
+The contents within a bottom sheet should follow their own accessibility
+guidelines, such as setting content descriptions for images.
+
+To support dragging bottom sheets with accessibility services such as TalkBack,
+Voice Access, Switch Access, etc., we provide a convenient widget,
+`BottomSheetDragHandleView`, which will automatically receive and handle
+accessibility commands to expand and collapse the attached bottom sheet when the
+accessibility mode is enabled. The handle also supports tapping to cycle through
+expanded and collapsed states as well as double tapping to hide. To
+use`BottomSheetDragHandleView`, you can add it to the top of your bottom sheet
+content. It will show a customizable visual indicator for all users. See the
+example in the below section for how to add a drag handle to your bottom sheet.
+
+**Note:** `BottomSheetDragHandleView` has a default min width and height of 48dp
+to conform to the minimum touch target requirement. So you will need to preserve
+at least 48dp at the top to place a drag handle.
+
+
+
+## Customizing bottom sheets
+
+### Theming bottom sheets
-Bottom sheets support
-[Material Theming](https://material.io/components/sheets-bottom#theming), which
-can customize color and shape.
+Bottom sheets support the customization of color and shape.
-### Bottom sheet theming example
+#### Bottom sheet theming example
API and source code:
@@ -581,13 +586,12 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetDialogFragment)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomsheet/BottomSheetDialogFragment.java)
-The following example shows a bottom sheet with Material Theming, in its
+The following example shows a bottom sheet with Material theming, in its
collapsed and expanded states.
-
+
-#### Implementing bottom sheet theming
+##### Implementing bottom sheet theming
Setting the theme attribute `bottomSheetDialogTheme` to your custom
`ThemeOverlay` will affect all bottom sheets.
diff --git a/docs/components/Button.md b/docs/components/Button.md
index abd4ca20717..8bc122b62de 100644
--- a/docs/components/Button.md
+++ b/docs/components/Button.md
@@ -9,1258 +9,16 @@ path: /catalog/buttons/
# Buttons
-[Buttons](https://material.io/components/buttons/) allow users to take actions,
-and make choices, with a single tap.
-
-
-
-**Contents**
-
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using buttons](#using-buttons)
-* [Elevated button](#elevated-button)
-* [Filled button](#filled-button)
-* [Filled tonal button](#filled-tonal-button)
-* [Outlined button](#outlined-button)
-* [Text button](#text-button)
-* [Button groups](#button-groups)
-* [Toggle button group](#toggle-button-groups)
-* [Icon button](#icon-button)
-* [Theming](#theming-buttons)
-
-## Design and API Documentation
-
-* [Google Material3 Spec](https://material.io/components/buttons/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/button/package-summary)
-
-## Using buttons
-
-Before you can use Material buttons, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
-[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page.
-
-**Note:** `
```
-
-**Note:** If you use our full themes (which we recommend), `TextView` will
-auto-inflate to `MaterialTextView`, otherwise, you will need to specify
-`
-**Contents**
+1. Dropdown menu
+2. Exposed dropdown menu
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using menus](#using-menus)
-* [Dropdown menus](#dropdown-menus)
-* [Exposed dropdown menus](#exposed-dropdown-menus)
-* [Theming](#theming-menus)
+Menus allow users to make a selection from multiple options. They appear when
+users interact with a button, action, or other control.They are less prominent
+and take up less space than selection controls, such as a set of radio
+buttons.
+
+**Note:** Images use various dynamic color schemes.
-## Design and API Documentation
+## Design & API documentation
-* [Google Material3 Spec](https://material.io/components/menus/overview)
+* [Material 3 (M3) spec](https://m3.material.io/components/menus/overview)
* [API reference](https://developer.android.com/reference/android/view/Menu)
-## Using menus
+## Anatomy
-A menu displays a list of choices on a temporary surface. They appear when users
-interact with a button, action, or other control.
+#### Dropdown menu
-Before you can use Material menus, you need to add a dependency to the Material
-Components for Android library. For more information, go to the
-[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page.
+
-A typical menu resource looks like this:
+1. List item
+2. Leading icon
+3. Trailing icon
+4. Trailing text
+5. Container
+6. Divider
-```xml
-
-
-
-
-
-
-```
+#### Exposed dropdown menu
-A typical exposed dropdown menu looks like this:
+
-```xml
-
+1. Text
+2. Container
+3. Label
+4. Selection/Input text
+5. Trailing icon
-
+More details on anatomy items in the [component guidelines](https://m3.material.io/components/menus/guidelines#732c1ddd-e298-4891-a1da-6adfa84da279).
-
-```
+## Key properties
-See the [dropdown menus](#dropdown-menus) and
-[exposed dropdown menus](#exposed-dropdown-menus) sections for detailed usage
-information.
+### Dropdown menu
-### Making menus accessible
+#### Container attributes
-Menus are readable by most screen readers, such as TalkBack. Text rendered in
-menus is automatically provided to accessibility services. Additional content
-labels are usually unnecessary.
+Element | Attribute | Related method(s) | Default value
+-------------- | ----------------------------- | ----------------- | -------------
+**Background** | `android:popupMenuBackground` | N/A | `?attr/popupMenuBackground`
+**Color** | N/A | N/A | `?attr/colorSurfaceContainer`
+**Elevation** | `android:popupElevation` | N/A | `3dp`
-Android's exposed dropdown menu component APIs support both label text and
-helper text, which tell the user what information is requested for a menu. While
-optional, their use is strongly encouraged. For more information about this
-component's accessibility, check out
-[the text field's a11y section](TextField.md#making-text-fields-accessible).
+#### Text attributes
-### Types
+Element | Attribute | Related method(s) | Default value
+-------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------ | -------------
+**Text label** | `android:title` | `getMenu().add` `getMenu().addSubMenu` `getMenu().getItem` | N/A
+**Typography** | `?attr/textAppearanceLargePopupMenu` `?attr/textAppearanceSmallPopupMenu` | N/A | `?attr/textAppearanceBodyLarge`
-Menus allow users to make a selection from multiple options. They are less
-prominent and take up less space than selection controls, such as a set of radio
-buttons.
+#### Styles
-There are two types of menus: 1\. [Dropdown menus](#dropdown-menus) (overflow,
-context, popup, and list popup window menus), 2\.
-[Exposed dropdown menus](#exposed-dropdown-menus).
+Element | Theme attribute | Default value
+--------------------------- | -------------------------------- | -----------------
+**Popup menus** | `?attr/popupMenuStyle` | `@style/Widget.Material3.PopupMenu`
+**List popup window style** | `?attr/listPopupWindowStyle` | `@style/Widget.Material3.PopupMenu.ListPopupWindow`
+**Context menus** | `?android:contextPopupMenuStyle` | `@style/Widget.Material3.PopupMenu.ContextMenu`
+**Overflow menus** | `?attr/actionOverflowMenuStyle` | `@style/Widget.Material3.PopupMenu.Overflow`
-
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/menu/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/menu/res/values/attrs.xml).
+
+### Exposed dropdown menu
+
+#### `TextInputLayout` attributes (container, label, trailing icon)
+
+For all attributes that apply to the `TextInputLayout`, see the
+[TextInputLayout documentation](TextField.md).
-## Dropdown menus
+#### `MaterialAutoCompleteTextView` attributes (input text, dropdown menu)
+
+Element | Attribute | Related method(s) | Default value
+----------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------
+**Input text** | `android:text` | `setText` `getText` | `@null`
+**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyLarge`
+**Input accepted** | `android:inputType` | `N/A` | framework's default
+**Input text color** | `android:textColor` | `setTextColor` `getTextColors` `getCurrentTextColor` | `?android:textColorPrimary`
+**Cursor color** | N/A (color comes from the theme attr `?attr/colorControlActivated`) | N/A | `?attr/colorPrimary`
+**Dropdown menu container color** | `app:dropDownBackgroundTint` | `setDropDownBackgroundTint` `setDropDownBackgroundTintList` `getDropDownBackgroundTintList` | `colorSurfaceContainer`
+**Dropdown menu elevation** | `android:popupElevation` | `getPopupElevation` | `3dp`
+**Simple items** | `app:simpleItems` | `setSimpleItems` | `null`
+**Simple item layout** | `app:simpleItemLayout` | N/A | `@layout/m3_auto_complete_simple_item`
+**Selected simple item color** | `app:simpleItemSelectedColor` | `setSimpleItemSelectedColor` `getSimpleItemSelectedColor` | `?attr/colorSurfaceContainerHighest`
+**Selected simple item ripple color** | `app:simpleItemSelectedRippleColor` | `setSimpleItemSelectedRippleColor` `getSimpleItemSelectedRippleColor` | `@color/m3_simple_item_ripple_color`
+
+#### Styles
+
+Element | Style
+------------------ | -----
+**Filled style** | `Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu`
+**Outlined style** | `Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu`
+**Filled dense** | `Widget.Material3.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu`
+**Outlined dense** | `Widget.Material3.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu`
+
+Default style theme attribute: `?attr/textInputStyle`
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml).
+
+## Variants of menus
+
+### Dropdown menus
Dropdown menus display a list of options, triggered by an icon, button, or
action. Their placement varies based on the element that opens them.
@@ -116,14 +146,14 @@ API and source code:
* `ListPopupWindow`
* [Class definition](https://developer.android.com/reference/androidx/appcompat/widget/ListPopupWindow)
-### Dropdown menu examples
+#### Dropdown menu examples
-#### Overflow menus
+
+
Overflow menus
The following example shows an overflow menu.
-
+
In code:
@@ -148,13 +178,15 @@ In `res/menu/overflow_menu.xml`:
```
-#### Context menus
+
+
+
+
Context menus
The following example shows a context menu that appears when a `TextView` is
pressed for a designated amount of time.
-
+
In code:
@@ -234,11 +266,14 @@ with a `res/menu/context_menu.xml`:
```
-#### Popup menus
+
+
+
+
Popup menus
The following example shows a popup menu that displays when a button is clicked.
-
+
In code:
@@ -289,7 +324,7 @@ In `res/menu/popup_menu.xml`:
```
-##### Adding icons to popup menus
+**Adding icons to popup menus**
Currently, there is no public API to add icons to popup menus. The following
workaround is for API 21+, and uses library-only APIs, and is not guaranteed to
@@ -297,8 +332,8 @@ work in future versions.
The following example shows a popup menu with icons.
-
+
In code:
@@ -350,13 +385,15 @@ In `res/menu/popup_menu.xml`:
android:title="@string/option_3" />
```
+
-#### List popup window menus
+
+
List popup window menus
The following example shows a list popup window menu that appears when a button
is clicked.
-
+
In code:
@@ -407,50 +444,9 @@ In the item layout `res/layout/list_popup_window_item.xml`:
android:textAppearance="?attr/textAppearanceBodyLarge"
/>
```
+
-### Anatomy and key properties
-
-The following are menu anatomy diagrams showing all possible elements:
-
-
-
-1. List item
-2. Leading icon
-3. Trailing icon
-4. Trailing text
-5. Container
-6. Divider
-
-#### Container attributes
-
-Element | Attribute | Related method(s) | Default value
--------------- | ----------------------------- | ----------------- | -------------
-**Background** | `android:popupMenuBackground` | N/A | `?attr/popupMenuBackground`
-**Color** | N/A | N/A | `?attr/colorSurfaceContainer`
-**Elevation** | `android:popupElevation` | N/A | `3dp`
-
-#### Text attributes
-
-Element | Attribute | Related method(s) | Default value
--------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------ | -------------
-**Text label** | `android:title` | `getMenu().add` `getMenu().addSubMenu` `getMenu().getItem` | N/A
-**Typography** | `?attr/textAppearanceLargePopupMenu` `?attr/textAppearanceSmallPopupMenu` | N/A | `?attr/textAppearanceBodyLarge`
-
-#### Styles
-
-Element | **Theme attribute** | **Default value**
---------------------------- | -------------------------------- | -----------------
-**Popup menus** | `?attr/popupMenuStyle` | `@style/Widget.Material3.PopupMenu`
-**List popup window style** | `?attr/listPopupWindowStyle` | `@style/Widget.Material3.PopupMenu.ListPopupWindow`
-**Context menus** | `?android:contextPopupMenuStyle` | `@style/Widget.Material3.PopupMenu.ContextMenu`
-**Overflow menus** | `?attr/actionOverflowMenuStyle` | `@style/Widget.Material3.PopupMenu.Overflow`
-
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/menu/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/menu/res/values/attrs.xml).
-
-## Exposed dropdown menus
+### Exposed dropdown menu
Exposed dropdown menus display the currently selected menu item above a list of
options. Some variations can accept user-entered input.
@@ -471,7 +467,8 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/textfield/MaterialAutoCompleteTextView)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/MaterialAutoCompleteTextView.java)
-### Exposed dropdown menu example
+The exposed dropdown menu is an `AutoCompleteTextView` within a
+`TextInputLayout`. It displays a dropdown menu below a text field.
**Note:** `MaterialComponentsViewInflater` auto-inflates
`` to
@@ -480,8 +477,7 @@ using `Theme.Material3.*` themes.
The following is an example of a filled exposed dropdown menu:
-
+
In the layout:
@@ -537,7 +533,7 @@ And a custom item layout (`list_item.xml`):
To use the exposed dropdown menu with an outlined text field, set the `style` to
`@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu`:
-
+
#### Non editable variation
@@ -550,67 +546,70 @@ In order to have a pre-selected value displayed, you can call
`setText(CharSequence text, boolean filter)` on the `AutoCompleteTextView` with
the filter set to `false`.
-### Anatomy and key properties
+## Code implementation
-The exposed dropdown menu is an `AutoCompleteTextView` within a
-`TextInputLayout`. It displays a dropdown menu below a text field.
+Before you can use Material menus, you need to add a dependency to the Material
+components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
-
+### Menu examples
-1. Text
-2. Container
-3. Label
-4. Selection/Input text
-5. Trailing icon
+A typical menu resource looks like this:
-#### `TextInputLayout` attributes (container, label, trailing icon)
+```xml
+
+
+
+
+
+
+```
-For all attributes that apply to the `TextInputLayout`, see the
-[TextInputLayout documentation](TextField.md).
+A typical exposed dropdown menu looks like this:
-#### `MaterialAutoCompleteTextView` attributes (input text, dropdown menu)
+```xml
+
-Element | Attribute | Related method(s) | Default value
------------------------------------------ | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------
-**Input text** | `android:text` | `setText` `getText` | `@null`
-**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyLarge`
-**Input accepted** | `android:inputType` | `N/A` | framework's default
-**Input text color** | `android:textColor` | `setTextColor` `getTextColors` `getCurrentTextColor` | `?android:textColorPrimary`
-**Cursor color** | N/A (color comes from the theme attr `?attr/colorControlActivated`) | N/A | `?attr/colorPrimary`
-**Dropdown menu container color** | `app:dropDownBackgroundTint` | `setDropDownBackgroundTint` `setDropDownBackgroundTintList` `getDropDownBackgroundTintList` | `colorSurfaceContainer`
-**Dropdown menu elevation** | `android:popupElevation` | `getPopupElevation` | `3dp`
-**Simple items** | `app:simpleItems` | `setSimpleItems` | `null`
-**Simple item layout** | `app:simpleItemLayout` | N/A | `@layout/m3_auto_complete_simple_item`
-**Selected simple item color** | `app:simpleItemSelectedColor` | `setSimpleItemSelectedColor` `getSimpleItemSelectedColor` | `?attr/colorSurfaceContainerHighest`
-**Selected simple item ripple color** | `app:simpleItemSelectedRippleColor` | `setSimpleItemSelectedRippleColor` `getSimpleItemSelectedRippleColor` | `@color/m3_simple_item_ripple_color`
+
-#### Styles
+
+```
-Element | Style
------------------- | -----
-**Filled style** | `Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu`
-**Outlined style** | `Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu`
-**Filled dense** | `Widget.Material3.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu`
-**Outlined dense** | `Widget.Material3.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu`
+### Making menus accessible
-Default style theme attribute: `?attr/textInputStyle`
+Menus are readable by most screen readers, such as TalkBack. Text rendered in
+menus is automatically provided to accessibility services. Additional content
+labels are usually unnecessary.
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml).
+Android's exposed dropdown menu component APIs support both label text and
+helper text, which tell the user what information is requested for a menu. While
+optional, their use is strongly encouraged. For more information about this
+component's accessibility, check out
+[the text field's a11y section](TextField.md#making-text-fields-accessible).
+
+## Customizing menus
-## Theming menus
+### Theming menus
-Menus drawers support
-[Material Theming](https://material.io/components/text-fields/#theming) which
-can customize color, typography and shape.
+Menus drawers support the customization of color, typography, and shape.
-### Menu theming examples
+#### Dropdown menu theming examples
-Popup, overflow, and list popup window menus support
-[Material Theming](https://material.io/components/chips/#theming) which can
-customize typography.
+Popup, overflow, and list popup window menus support the customization of
+typography.
API and source code:
@@ -625,11 +624,11 @@ API and source code:
* `ListPopupWindow`
* [Class definition](https://developer.android.com/reference/androidx/appcompat/widget/ListPopupWindow)
-The following example shows a menu with Material Theming.
+The following example shows a menu with Material theming.

-### Implementing menu theming
+##### Implementing menu theming
Use default style theme attributes, which affect all menus but do not affect
other components:
@@ -676,11 +675,10 @@ to set a custom background for one type of menu:
@drawable/custom_popupmenu_background
```
-
-### Exposed dropdown menu theming example
+#### Exposed dropdown menu theming examples
Exposed dropdown menus support
-[Material Theming](https://material.io/components/chips/#theming) which can
+[Material theming](https://material.io/components/chips/#theming) which can
customize color, typography, and shape.
**Note:** The exposed dropdown menu is implemented through the
@@ -700,13 +698,13 @@ API and source code:
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/MaterialAutoCompleteTextView.java)
The following example shows filled and outlined exposed dropdown menu types with
-Material Theming.
+Material theming.

-### Implementing exposed dropdown menu theming
+##### Implementing exposed dropdown menu theming
Use default style theme attributes, styles and theme overlays which adds themes
to all menus but does not affect other components:
diff --git a/docs/components/NavigationDrawer.md b/docs/components/NavigationDrawer.md
index 5b3d9380777..f38a8484efc 100644
--- a/docs/components/NavigationDrawer.md
+++ b/docs/components/NavigationDrawer.md
@@ -1,5 +1,5 @@
-# Navigation drawer
+# Navigation drawers
-[Navigation drawers](https://material.io/components/navigation-drawer) provide
-access to destinations in your app.
+Note: The navigation drawer is being deprecated in the Material 3 expressive
+update. For those who have updated, use an [expanded navigation rail](https://m3.material.io/components/navigation-rail/overview), which has
+mostly the same functionality of the navigation drawer and adapts better across
+window size classes.
-
+[Navigation drawers](https://m3.material.io/components/navigation-drawer/overview)
+provide access to destinations in your app. There are two variants of navigation
+drawers.
-**Contents**
+
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using navigation drawers](#using-navigation-drawers)
-* [Anatomy](#anatomy)
-* [Standard navigation drawer](#standard-navigation-drawer)
-* [Modal navigation drawer](#modal-navigation-drawer)
-* [Predictive Back](#predictive-back)
-* [Theming](#theming)
+1. Standard navigation drawer
+2. Modal navigation drawer
-## Design and API Documentation
+**Note:** Images use various dynamic color schemes.
-* [Google Material3 Spec](https://material.io/components/navigation-drawer/overview)
-* [API reference](https://developer.android.com/reference/com/google/android/material/navigation/package-summary)
-
-## Using navigation drawers
-
-Before you can use navigation drawers, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
-[getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page. For modal navigation drawers you also need to add a dependency to the
-AndroidX `DrawerLayout` library. For more information go to the
-[releases](https://developer.android.com/jetpack/androidx/releases/drawerlayout)
-page.
-
-The content of all navigation drawer types can be implemented using a
-`NavigationView`.
-
-```xml
-
-```
-
-**Note:** The `layout_width` and `layout_height` attributes should be set to
-`wrap_content`, `match_parent`, or a custom dimension depending on the
-navigation drawer type and parent `ViewGroup`.
-
-### Adding a menu
-
-
-
-In the layout:
-
-```xml
-
-```
-
-In `res/menu/navigation_drawer.xml`:
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### Adding a header
-
-
-
-In the layout:
-
-```xml
-
-```
-
-In `res/layout/header_navigation_drawer.xml`:
-
-```xml
-
-
-
-
-
-
-
-```
+## Design & API documentation
-### Adding dividers and subtitles
-
-
-
-Dividers are automatically added between `` groups with unique IDs or
-``s with unique IDs. When a sub-`` is added to an item it is
-treated as a subtitle.
-
-In `res/menu/navigation_drawer.xml`:
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### Making navigation drawers accessible
-
-Navigation drawers support content labeling for accessibility and are readable
-by most screen readers, such as TalkBack. Text rendered in menu items is
-automatically provided to accessibility services. Additional content labels are
-optional but recommended.
-
-For more information on content labels, go to the
-[Android accessibility help guide](https://support.google.com/accessibility/android/answer/7158690).
-
-#### Content descriptions
-
-A content description can be set on ``s in the `NavigationView` menu so
-that screen readers like TalkBack are able to announce their purpose or action.
-This can be done in XML using the `android:contentDescription` attribute or
-programmatically with
-`navigationView.menu.findItem(R.id.itemId)#setContentDescription` (on API 26 and
-above).
-
-Any `ImageView`s within the header layout should also have a content description
-set.
-
-#### Opening and closing navigation drawers
+* [Material 3 (M3) spec](https://m3.material.io/components/navigation-drawer/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/navigation/package-summary)
-To open navigation drawers, use clickable widgets that meet the minimum touch
-target size of `48dp` and are properly labeled for accessibility. To close
-navigation drawers, consider doing the same but bear in mind that clicking on
-menu items or an optional scrim should also serve this purpose.
+## Anatomy
-### Using navigation drawers with the Navigation component
+
-Navigation drawers can be used with the AndroidX Navigation library. For more
-information, go to the
-[documentation](https://developer.android.com/guide/navigation/navigation-ui#add_a_navigation_drawer).
+1. Container
+2. Headline
+3. Label text
+4. Icon
+5. Active indicator
+6. Badge label text
+7. Scrim
-## Anatomy
+More details on anatomy items are available in the
+[component guidelines](https://m3.material.io/components/navigation-drawer/guidelines#86ff751b-e510-4428-bfb2-cc5bf9206bb8).
-Navigation drawers have a container, an optional header, optional dividers,
-items with inactive text, active text, and an active text overlay, optional
-subtitles, and an optional scrim.
+## M3 Expressive update
-
+The navigation drawer is being deprecated. Use the [expanded navigation rail](https://m3.material.io/components/navigation-rail/overview)
+instead. [More on M3 Expressive](https://m3.material.io/blog/building-with-m3-expressive)
-1. Container
-2. Subheader (optional)
-3. Label text
-4. Icon (optional)
-5. Active indicator/Item shape
-6. Badge text (coming soon)
-7. Divider
-8. Scrim (modal drawer only)
+## Key properties
### Container attributes
@@ -295,12 +89,12 @@ Element | Attribute(s)
### Text attributes
-| Element | Attribute | Related method(s) | Default value |
-|-------------------------|-------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------|
-| **Color** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSecondaryContainer` when active else `?attr/colorOnSurfaceVariant` |
-| **Typography** | `app:itemTextAppearance` | `setItemTextAppearance` | `?attr/textAppearanceLabelLarge` |
-| **Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true` |
-| **Max lines** | `app:itemMaxLines` | `setItemMaxLines` `getItemMaxLines` | `1` |
+Element | Attribute | Related method(s) | Default value
+----------------------- | ----------------------------------------- | ---------------------------------------- | -------------
+**Color** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSecondaryContainer` when active else `?attr/colorOnSurfaceVariant`
+**Typography** | `app:itemTextAppearance` | `setItemTextAppearance` | `?attr/textAppearanceLabelLarge`
+**Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true`
+**Max lines** | `app:itemMaxLines` | `setItemMaxLines` `getItemMaxLines` | `1`
### Icon attributes
@@ -329,26 +123,21 @@ Element | Attribute
### `NavigationView` styles
-Element | Style
------------------ | ---------------------------------
-**Default style** | `Widget.Material3.NavigationView`
-
-Default style theme attribute: `?attr/navigationViewStyle`
+| Element | Style | Theme attribute |
+| --------- | --------------------------------- | --------------------------- |
+| **Default | `Widget.Material3.NavigationView` | `?attr/navigationViewStyle` |
+: style** : : :
### `DrawerLayout` styles
-Element | Style
------------------ | ---------------------------------
-**Default style** | `Widget.Material3.DrawerLayout`
-
-Default style theme attribute: `?attr/drawerLayoutStyle`
-### Types
+Element | Style | Theme attribute
+----------------- | ------------------------------- | -------------------------
+**Default style** | `Widget.Material3.DrawerLayout` | `?attr/drawerLayoutStyle`
-There are two types of navigation drawers: 1\.
-[Standard navigation drawer](#standard-navigation-drawer), 2\.
-[Modal navigation drawer](#modal-navigation-drawer)
+## Variants of navigation drawer
-## Standard navigation drawer
+
+
Standard navigation drawer
[Standard navigation drawers](https://material.io/components/navigation-drawer#standard-drawer)
allow interaction with both screen content and the drawer at the same time. They
@@ -359,15 +148,13 @@ API and source code:
* `NavigationView`
* [Class definition](https://developer.android.com/reference/com/google/android/material/navigation/NavigationView)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/NavigationView.java)
+ * [GitHub source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/NavigationView.java)
-### Standard navigation drawer example
+#### Standard navigation drawer example
The following example shows a permanently visible standard navigation drawer.
-
+
In the layout:
@@ -408,7 +195,10 @@ In `res/layout/header_navigation_drawer.xml`:
```
-## Modal navigation drawer
+
+
+
+
Modal navigation drawer
[Modal navigation drawers](https://material.io/components/navigation-drawer#modal-drawer)
block interaction with the rest of an app’s content with a scrim. They are
@@ -425,17 +215,17 @@ API and source code:
* `NavigationView`
* [Class definition](https://developer.android.com/reference/com/google/android/material/navigation/NavigationView)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/NavigationView.java)
+ * [GitHub source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/NavigationView.java)
* `DrawerLayout`
* [Class definition](https://developer.android.com/reference/androidx/drawerlayout/widget/DrawerLayout)
-### Modal navigation drawer example
+#### Modal navigation drawer example
The following example shows a modal navigation drawer.
-.
-## Predictive Back
+
+
+## Code implementation
+
+Before you can use navigation drawers, you need to add a dependency to the
+Material components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page. For modal navigation drawers you also need to add a dependency to the
+AndroidX `DrawerLayout` library. For more information go to the
+[releases](https://developer.android.com/jetpack/androidx/releases/drawerlayout)
+page.
+
+
+
Adding navigation drawer
+
+The content of all navigation drawer types can be implemented using a
+`NavigationView`.
+
+```xml
+
+```
+
+**Note:** The `layout_width` and `layout_height` attributes should be set to
+`wrap_content`, `match_parent`, or a custom dimension depending on the
+navigation drawer type and parent `ViewGroup`.
+
+
+
+
+
+
+
+
+Dividers are automatically added between `` groups with unique IDs or
+``s with unique IDs. When a sub-`` is added to an item it is
+treated as a subtitle.
+
+In `res/menu/navigation_drawer.xml`:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+
Making navigation drawers accessible
+
+Navigation drawers support content labeling for accessibility and are readable
+by most screen readers, such as TalkBack. Text rendered in menu items is
+automatically provided to accessibility services. Additional content labels are
+optional but recommended.
+
+For more information on content labels, go to the
+[Android accessibility help guide](https://support.google.com/accessibility/android/answer/7158690).
+
+Important: Ensure that there is a way to close the navigation drawer through
+keyboard navigation by listening for the `esc` key in your activity and closing
+open drawers.
+
+```java
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
+ if (keyCode == KeyEvent.KEYCODE_ESCAPE && drawerLayout.isDrawerOpen(navigationView)) {
+ drawerLayout.closeDrawer(navigationView);
+ return true;
+ }
+ return super.onKeyDown(keyCode, keyEvent);
+ }
+```
+
+
+
+
+
Setting content descriptions
+
+A content description can be set on ``s in the `NavigationView` menu so
+that screen readers like TalkBack are able to announce their purpose or action.
+This can be done in XML using the `android:contentDescription` attribute or
+programmatically with
+`navigationView.menu.findItem(R.id.itemId)#setContentDescription` (on API 26 and
+above).
+
+Any `ImageView`s within the header layout should also have a content description
+set.
+
+
+
+
+
Opening and closing navigation drawers
+
+To open navigation drawers, use clickable widgets that meet the minimum touch
+target size of `48dp` and are properly labeled for accessibility. To close
+navigation drawers, consider doing the same but bear in mind that clicking on
+menu items or an optional scrim should also serve this purpose.
+
+
+
+
+
Using navigation drawers with the navigation component
+
+Navigation drawers can be used with the AndroidX navigation library. For more
+information, go to the
+[documentation](https://developer.android.com/guide/navigation/navigation-ui#add_a_navigation_drawer).
+
+
+
+
+
Predictive back
The `NavigationView` component automatically supports
-[Predictive Back](../foundations/PredictiveBack.md) when it is set up within a
-`DrawerLayout`, as mentioned in the sections above. No further integration is
-required on the app side other than the general Predictive Back prerequisites
-and migration steps mentioned [here](../foundations/PredictiveBack.md#usage).
+[predictive back](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md)
+when it is set up within a `DrawerLayout`, as mentioned in the sections above.
+No further integration is required on the app side other than the general
+predictive back prerequisites and migration steps mentioned
+[here](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md#usage).
Visit the
-[Predictive Back design guidelines](https://m3.material.io/components/side-sheets/guidelines#d77245e3-1013-48f8-a9d7-76f484e1be13)
+[predictive back design guidelines](https://m3.material.io/components/side-sheets/guidelines#d77245e3-1013-48f8-a9d7-76f484e1be13)
to see how the component behaves when a user swipes back.
-## Theming
+
+
+## Customizing navigation drawers
+
+### Theming navigation drawers
-Navigation drawers support
-[Material Theming](https://material.io/components/text-fields/#theming) which
-can customize color, typography and shape.
+Navigation drawers support the customization of color, typography, and shape.
-### Navigation drawer theming example
+#### Navigation drawer theming example
API and source code:
@@ -530,13 +575,11 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/navigation/NavigationView)
* [GitHub source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/NavigationView.java)
-The following example shows a navigation drawer with Material Theming.
+The following example shows a navigation drawer with Material theming.
-
+
-#### Implementing navigation drawer theming
+##### Implementing navigation drawer theming
Use theme attributes, default style theme attributes, and styles in
`res/values/styles.xml`, which applies to all navigation drawers and affects
diff --git a/docs/components/NavigationRail.md b/docs/components/NavigationRail.md
index 3f4e499137f..5fb68e6da13 100644
--- a/docs/components/NavigationRail.md
+++ b/docs/components/NavigationRail.md
@@ -44,7 +44,7 @@ A typical layout will look similar to this:
```
**Note:** The width of a `NavigationRailView` will be 80dp wide by default.The
-width of the rail can be changed by setting the `android:layout_width`attribute
+width of the rail can be changed by setting the `android:layout_width` attribute
to a specific DP value.
In `navigation_rail_menu.xml` inside a `menu` resource directory:
@@ -74,11 +74,48 @@ In `navigation_rail_menu.xml` inside a `menu` resource directory:
```
-**Note:** `NavigationRailView` displays three to no more than seven app
-destinations, and can include a header view. Each destination is represented by
-an icon and a text label.
+`NavigationRailView` displays three to no more than seven app
+destinations when collapsed, and can include a header view. Each destination is
+represented by an icon and a text label.
-In code:
+You can also define submenus for the Navigation Rail like below:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+```
+
+Navigation rails are collapsed by default. When collapsed, only menu items that
+are not under a submenu will be shown, up to a limit of 7. There is no limit of
+items shown when expanded.
+
+You will need to set listeners for the menu items in code:
```kt
// Listeners are defined on the super class NavigationBarView
@@ -177,16 +214,32 @@ The rail provides a convenient container for anchoring a header view, such as a
The header view can also be added or removed at runtime using the following
methods:
-**Method** | **Description**
----------------------------------------- | ---------------
-`void addHeaderView(@NonNull View view)` | The specified header view will be attached to the NavigationRailView, so that it will appear at the top. If the view already has a header view attached to it, it will be removed first.
-`void removeHeaderView()` | Detaches the current header view if any, from the Navigation Rail.
+| **Method** | **Description** |
+|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `void addHeaderView(@NonNull View view)` | The specified header view will be attached to the NavigationRailView, so that it will appear at the top. If the view already has a header view attached to it, it will be removed first. |
+| `void removeHeaderView()` | Detaches the current header view if any, from the Navigation Rail. |
The following methods can be used to manipulate the header view at runtime.
-**Method** | **Description**
--------------------------------- | ---------------
-`@Nullable view getHeaderView()` | Returns an instance of the header view associated with the Navigation Rail, null if none was currently attached.
+| **Method** | **Description** |
+|----------------------------------|------------------------------------------------------------------------------------------------------------------|
+| `@Nullable view getHeaderView()` | Returns an instance of the header view associated with the Navigation Rail, null if none was currently attached. |
+
+### Expanding the Navigation Rail
+
+You can call `navigationRailView.expand()` and `navigationRailView.collapse()` to expand and collapse the navigation rail.
+
+When collapsed, only menu items not under a submenu will be shown, up to a limit of 7.
+
+When expanded, all menu items will be shown, including submenu items.
+
+Navigation rails are collapsed by default which animates into the expanded navigation rail when expanded. If the content beside
+the navigation rail takes into account the size of the navigation rail (ie., through setting constraints in `ConstraintLayout` or layout weights) then the content will also be animated to shrink. This animation is taken care of by a [ChangeBounds Transition](https://developer.android.com/reference/android/transition/ChangeBounds); any animations during the expansion of the navigation rail should be 'turned off' as
+it could result in a strange animation due to the `Transition`.
+
+| Collapsed Navigation Rail | Expanded Navigation Rail |
+|----------------------------------------------------------------------------|--------------------------------------------------------------------------|
+|  |  |
### Adding badges
@@ -287,22 +340,22 @@ The following is an anatomy diagram for the navigation rail:
#### Container attributes
-**Element** | **Attribute** | **Related methods** | **Default value**
---------------------------------------- | ------------------------------------- | ------------------------------------------------- | -----------------
-**Color** | `app:backgroundTint` | N/A | `?attr/colorSurface`
-**Elevation** | `app:elevation` | `setElevation` | `0dp`
-**Fits system windows** | `android:fitsSystemWindows` | `getFitsSystemWindows` `setFitsSystemWindows` | `true`
-**Padding top system window insets** | `app:paddingTopSystemWindowInsets` | N/A | `null`
-**Padding bottom system window insets** | `app:paddingBottomSystemWindowInsets` | N/A | `null`
-**Top margin** | `app:contentMarginTop` | N/A | N/A
-**Scrolling** | `app:scrollingEnabled` | N/A | `false`
+| **Element** | **Attribute** | **Related methods** | **Default value** |
+|-----------------------------------------|---------------------------------------|---------------------------------------------------|----------------------|
+| **Color** | `app:backgroundTint` | N/A | `?attr/colorSurface` |
+| **Elevation** | `app:elevation` | `setElevation` | `0dp` |
+| **Fits system windows** | `android:fitsSystemWindows` | `getFitsSystemWindows` `setFitsSystemWindows` | `true` |
+| **Padding top system window insets** | `app:paddingTopSystemWindowInsets` | N/A | `null` |
+| **Padding bottom system window insets** | `app:paddingBottomSystemWindowInsets` | N/A | `null` |
+| **Top margin** | `app:contentMarginTop` | N/A | N/A |
+| **Scrolling** | `app:scrollingEnabled` | N/A | `false` |
#### Header attributes
-**Element** | **Attribute** | **Related methods** | **Default value**
------------------------- | ------------------------ | ---------------------------------------------------------- | -----------------
-**Header view** | `app:headerLayout` | `addHeaderView` `removeHeaderView` `getHeaderView` | N/A
-**Header bottom margin** | `app:headerMarginBottom` | N/A | `8dp`
+| **Element** | **Attribute** | **Related methods** | **Default value** |
+|--------------------------|--------------------------|------------------------------------------------------------|-------------------|
+| **Header view** | `app:headerLayout` | `addHeaderView` `removeHeaderView` `getHeaderView` | N/A |
+| **Header bottom margin** | `app:headerMarginBottom` | N/A | `8dp` |
See the
[FAB documentation](https://github.com/material-components/material-components-android/tree/master/docs/components/FloatingActionButton.md)
@@ -310,37 +363,46 @@ for more attributes.
#### Navigation Menu attributes
-**Element** | **Attribute** | **Related methods** | **Default value**
----------------- | ---------------------- | ------------------------------------- | -----------------
-**Menu gravity** | `app:menuGravity` | `setMenuGravity` `getMenuGravity` | `TOP\|CENTER_HORIZONTAL`
+| **Element** | **Attribute** | **Related methods** | **Default value** |
+|------------------|------------------------------|-------------------------------------------------------------|--------------------------|
+| **Menu gravity** | `app:menuGravity` | `setMenuGravity` `getMenuGravity` | `TOP\|CENTER_HORIZONTAL` |
+| **Dividers** | `app:submenuDividersEnabled` | `setSubmenuDividersEnabled` `getSubmenuDividersEnabled` | `false` |
+
+**Note:** If dividers are enabled, they will be between all submenus, which are only visible when expanded.
#### Navigation item attributes
-**Element** | **Attribute** | **Related methods** | **Default value**
-------------------------- | ------------------------- | ----------------------------------------------------- | -----------------
-**Menu resource** | `app:menu` | `inflateMenu` `getMenu` | N/A
-**Ripple (inactive)** | `app:itemRippleColor` | `setItemRippleColor` `getItemRippleColor` | `?attr/colorPrimary` at 12% (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/res/color/mtrl_navigation_bar_ripple_color.xml))
-**Ripple (active)** | `app:itemRippleColor` | `setItemRippleColor` `getItemRippleColor` | `?attr/colorPrimary` at 12% (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/res/color/mtrl_navigation_bar_ripple_color.xml))
-**Label visibility mode** | `app:labelVisibilityMode` | `setLabelVisibilityMode` `getLabelVisibilityMode` | `LABEL_VISIBILITY_AUTO`
-**Item minimum height** | `app:itemMinHeight` | `setItemMinimumHeight` `getItemMinimumHeight` | `NO_ITEM_MINIMUM_HEIGHT`
-**Item spacing** | `app:itemSpacing` | `setItemSpacing` `getItemSpacing` | `0dp`
-**Item Gravity** | `app:itemGravity` | `setItemGravity` `getItemGravity` | `TOP_CENTER`
+| **Element** | **Attribute** | **Related methods** | **Default value** |
+|-----------------------------------|------------------------------|---------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Menu resource** | `app:menu` | `inflateMenu` `getMenu` | N/A |
+| **Ripple (inactive)** | `app:itemRippleColor` | `setItemRippleColor` `getItemRippleColor` | `?attr/colorPrimary` at 12% (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/res/color/mtrl_navigation_bar_ripple_color.xml)) |
+| **Ripple (active)** | `app:itemRippleColor` | `setItemRippleColor` `getItemRippleColor` | `?attr/colorPrimary` at 12% (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/navigation/res/color/mtrl_navigation_bar_ripple_color.xml)) |
+| **Label visibility mode** | `app:labelVisibilityMode` | `setLabelVisibilityMode` `getLabelVisibilityMode` | `LABEL_VISIBILITY_AUTO` |
+| **Item minimum height** | `app:itemMinHeight` | `setItemMinimumHeight` `getItemMinimumHeight` | `NO_ITEM_MINIMUM_HEIGHT` |
+| **Collapsed item minimum height** | `app:collapsedItemMinHeight` | `setCollapsedItemMinimumHeight` `getCollapsedItemMinimumHeight` | `NO_ITEM_MINIMUM_HEIGHT` |
+| **Expanded item minimum height** | `app:expandedItemMinHeight` | `setExpandedItemMinimumHeight` `getExpandedItemMinimumHeight` | `NO_ITEM_MINIMUM_HEIGHT` |
+| **Item spacing** | `app:itemSpacing` | `setItemSpacing` `getItemSpacing` | `0dp` |
+| **Item Gravity** | `app:itemGravity` | `setItemGravity` `getItemGravity` | `TOP_CENTER` |
**Note:** If there's not enough room, `itemMinHeight` and `itemSpacing` may not be respected in order to fit the items.
#### Active indicator attributes
-**Element** | **Attribute** | **Related methods** | **Default value**
---------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------- | -----------------
-**Color** | `android:color` | `setItemActiveIndicatorColor` `getItemActiveIndicatorColor` | `?attr/colorSecondaryContainer`
-**Width** | `android:width` | `setItemActiveIndicatorWidth` `getItemActiveIndicatorWidth` | `56dp`
-**Height** | `android:height` | `setItemActiveIndicatorHeight` `getItemActiveIndicatorHeight` | `32dp`
-**Shape** | `app:shapeAppearance` | `setItemActiveIndicatorShapeAppearance` `getItemActiveIndicatorShapeAppearance` | `50% rounded`
-**Margin horizontal** | `app:marginHorizontal` | `setItemActiveIndicatorMarginHorizontal` `getItemActiveIndicatorMarginHorizontal` | `4dp`
-**Padding between indicator and label** | `app:activeIndicatorLabelPadding` | `setActiveIndicatorLabelPadding` `getActiveIndicatorLabelPadding` | `4dp`
-**Expanded Width** | `expandedWidth` | `setItemExpandedActiveIndicatorWidth` `getItemExpandedActiveIndicatorWidth` | `HUG`
-**Expanded Height** | `expandedHeight` | `setItemExpandedActiveIndicatorHeight` `getItemExpandedActiveIndicatorHeight` | `56dp`
-**Expanded Margin horizontal** | `app:expandedMarginHorizontal` | `setItemExpandedActiveIndicatorMarginHorizontal` `getItemExpandedActiveIndicatorMarginHorizontal` | `20dp`
+| **Element** | **Attribute** | **Related methods** | **Default value** |
+|-----------------------------------------|--------------------------------------------|-------------------------------------------------------------------------------------------------------|---------------------------------|
+| **Color** | `android:color` | `setItemActiveIndicatorColor` `getItemActiveIndicatorColor` | `?attr/colorSecondaryContainer` |
+| **Width** | `android:width` | `setItemActiveIndicatorWidth` `getItemActiveIndicatorWidth` | `56dp` |
+| **Height** | `android:height` | `setItemActiveIndicatorHeight` `getItemActiveIndicatorHeight` | `32dp` |
+| **Shape** | `app:shapeAppearance` | `setItemActiveIndicatorShapeAppearance` `getItemActiveIndicatorShapeAppearance` | `50% rounded` |
+| **Margin horizontal** | `app:marginHorizontal` | `setItemActiveIndicatorMarginHorizontal` `getItemActiveIndicatorMarginHorizontal` | `4dp` |
+| **Padding between indicator and label** | `app:activeIndicatorLabelPadding` | `setActiveIndicatorLabelPadding` `getActiveIndicatorLabelPadding` | `4dp` |
+| **Expanded Width** | `app:expandedWidth` | `setItemActiveIndicatorExpandedWidth` `getItemActiveIndicatorExpandedWidth` | `HUG` |
+| **Expanded Height** | `app:expandedHeight` | `setItemActiveIndicatorExpandedHeight` `getItemActiveIndicatorExpandedHeight` | `56dp` |
+| **Expanded Margin horizontal** | `app:expandedMarginHorizontal` | `setItemActiveIndicatorExpandedMarginHorizontal` `getItemActiveIndicatorExpandedMarginHorizontal` | `20dp` |
+| **Expanded Start Padding** | `app:expandedActiveIndicatorPaddingStart` | `setItemExpandedActiveIndicatorPadding` | `16dp` |
+| **Expanded End Padding** | `app:expandedActiveIndicatorPaddingEnd` | `setItemExpandedActiveIndicatorPadding` | `16dp` |
+| **Expanded Top Padding** | `app:expandedActiveIndicatorPaddingTop` | `setItemExpandedActiveIndicatorPadding` | `16dp` |
+| **Expanded Bottom Padding** | `app:expandedActiveIndicatorPaddingBottom` | `setItemExpandedActiveIndicatorPadding` | `16dp` |
**Note:** The expanded active indicator refers to the active indicator that
expands to wrap the content of the Navigation Rail item when the
@@ -348,31 +410,33 @@ expands to wrap the content of the Navigation Rail item when the
#### Icon attributes
-**Element** | **Attribute** | **Related methods** | **Default value**
---------------------------------- | ------------------------------------- | ------------------------------------------------------------------- | -----------------
-**Icon** | `android:icon` in the `menu` resource | N/A | N/A
-**Size** | `app:itemIconSize` | `setItemIconSize` `setItemIconSizeRes` `getItemIconSize` | `24dp`
-**Color (inactive)** | `app:itemIconTint` | `setItemIconTintList` `getItemIconTintList` | `?attr/colorOnSurfaceVariant`
-**Color (active)** | `app:itemIconTint` | `setItemIconTintList` `getItemIconTintList` | `?attr/colorOnSecondaryContainer`
-**Gravity** | `app:itemIconGravity` | `setItemIconGravity` `getItemIconGravity` | `TOP`
-**Icon label horizontal padding** | `app:iconLabelHorizontalSpacing` | `setIconLabelHorizontalSpacing` `getIconLabelHorizontalSpacing` | `8dp`
+| **Element** | **Attribute** | **Related methods** | **Default value** |
+|-----------------------------------|---------------------------------------|---------------------------------------------------------------------|-----------------------------------|
+| **Icon** | `android:icon` in the `menu` resource | N/A | N/A |
+| **Size** | `app:itemIconSize` | `setItemIconSize` `setItemIconSizeRes` `getItemIconSize` | `24dp` |
+| **Color (inactive)** | `app:itemIconTint` | `setItemIconTintList` `getItemIconTintList` | `?attr/colorOnSurfaceVariant` |
+| **Color (active)** | `app:itemIconTint` | `setItemIconTintList` `getItemIconTintList` | `?attr/colorOnSecondaryContainer` |
+| **Gravity** | `app:itemIconGravity` | `setItemIconGravity` `getItemIconGravity` | `TOP` |
+| **Icon label horizontal padding** | `app:iconLabelHorizontalSpacing` | `setIconLabelHorizontalSpacing` `getIconLabelHorizontalSpacing` | `8dp` |
#### Text label attributes
-**Element** | **Attribute** | **Related methods** | **Default value**
-------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----------------
-**Text label** | `android:title` in the `menu` resource | N/A | N/A
-**Color (inactive)** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSurfaceVariant`
-**Color (active)** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSurface`
-**Typography (inactive)** | `app:itemTextAppearanceInactive` `app:horizontalItemTextAppearanceInactive` | `setItemTextAppearanceInactive` `getItemTextAppearanceInactive` `setHorizontalItemTextAppearanceInactive` `getHorizontalItemTextAppearanceInactive` | `?attr/textAppearanceTitleSmall` for regular item configuration, `?attr/textAppearanceLabelLarge` for horizontal
-**Typography (active)** | `app:itemTextAppearanceActive` `app:horizontalItemTextAppearanceActive` | `setItemTextAppearanceActive` `getItemTextAppearanceActive` `setHorizontalItemTextAppearanceActive` `getHorizontalItemTextAppearanceActive` | `?attr/textAppearanceTitleSmall` for regular item configuration, `?attr/textAppearanceLabelLarge` for horizontal
-**Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true`
+| **Element** | **Attribute** | **Related methods** | **Default value** |
+|---------------------------|---------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
+| **Text label** | `android:title` in the `menu` resource | N/A | N/A |
+| **Color (inactive)** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSurfaceVariant` |
+| **Color (active)** | `app:itemTextColor` | `setItemTextColor` `getItemTextColor` | `?attr/colorOnSurface` |
+| **Typography (inactive)** | `app:itemTextAppearanceInactive` `app:horizontalItemTextAppearanceInactive` | `setItemTextAppearanceInactive` `getItemTextAppearanceInactive` `setHorizontalItemTextAppearanceInactive` `getHorizontalItemTextAppearanceInactive` | `?attr/textAppearanceTitleSmall` for regular item configuration, `?attr/textAppearanceLabelLarge` for horizontal |
+| **Typography (active)** | `app:itemTextAppearanceActive` `app:horizontalItemTextAppearanceActive` | `setItemTextAppearanceActive` `getItemTextAppearanceActive` `setHorizontalItemTextAppearanceActive` `getHorizontalItemTextAppearanceActive` | `?attr/textAppearanceTitleSmall` for regular item configuration, `?attr/textAppearanceLabelLarge` for horizontal |
+| **Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true` |
+| **Max lines** | `app:labelMaxLines` | `setLabelMaxLines` `getLabelMaxLines` | `1` |
+| **Scale with font size** | `app:scaleLabelWithFontSize` | `setScaleLabelTextWithFont` `getScaleLabelTextWithFont` | `false` |
#### Styles
-**Element** | **Style** | **Container color** | **Icon/Text label color (inactive)** | **Icon/Text label color (active)**
------------------ | ------------------------------------- | -------------------- | ------------------------------------ | ----------------------------------
-**Default style** | `Widget.Material3.NavigationRailView` | `?attr/colorSurface` | `?attr/colorOnSurfaceVariant` | `?attr/colorOnSurface` `?attr/colorOnSecondaryContainer`
+| **Element** | **Style** | **Container color** | **Icon/Text label color (inactive)** | **Icon/Text label color (active)** |
+|-------------------|---------------------------------------|----------------------|--------------------------------------|--------------------------------------------------------------|
+| **Default style** | `Widget.Material3.NavigationRailView` | `?attr/colorSurface` | `?attr/colorOnSurfaceVariant` | `?attr/colorOnSurface` `?attr/colorOnSecondaryContainer` |
Default style theme attribute: `?attr/navigationRailStyle`
diff --git a/docs/components/OverflowLinearLayout.md b/docs/components/OverflowLinearLayout.md
new file mode 100644
index 00000000000..ea8f9806220
--- /dev/null
+++ b/docs/components/OverflowLinearLayout.md
@@ -0,0 +1,162 @@
+
+
+# Overflow linear layout
+
+The `OverflowLinearLayout` is usually used with the
+[floatingtoolbar](FloatingToolbar.md) and the [dockedtoolbar](DockedToolbar.md).
+It allows for its children to be automatically hidden/shown depending on its
+parent's max size. The hidden children are put in an overflow menu, and an
+overflow button is added as the last child of its parent layout.
+
+Note: if you'd like to hide/show children independently from this layout's
+decisions, you'll need to add/remove the desired view(s), instead of changing
+their visibility, as the `OverflowLinearLayout` will determine the final
+visibility value of its children.
+
+## Key properties
+
+### `OverflowLinearLayout` attributes
+
+Element | Attribute | Related methods | Default value
+------------------------ | ------------------------ | --------------------------------------------------------------------------------------- | -------------
+**Overflow button icon** | `app:overflowButtonIcon` | `setOverflowButtonIcon` `setOverflowButtonIconResource` `getOverflowButtonIcon` | `@drawable/abc_ic_menu_overflow_material`
+
+### `OverflowLinearLayout_Layout` attributes
+
+Attributes for the children of `OverflowLinearLayout`:
+
+| Element | Attribute | Related methods | Default value |
+| ----------- | ------------------------- | --------------- | ------------- |
+| **Overflow | `app:layout_overflowText` | N/A | `null` |
+: menu's item : : : :
+: text** : : : :
+| **Overflow | `app:layout_overflowIcon` | N/A | `null` |
+: menu's item : : : :
+: icon** : : : :
+
+### `OverflowLinearLayout` styles
+
+Element | Style | Theme attribute
+------------------- | ----------------------------------------- | ---------------
+**Style** | `Widget.Material3.OverflowLinearLayout` | `?attr/overflowLinearLayoutStyle`
+**Button overflow** | `overflowLinearLayoutOverflowButtonStyle` | `?attr/overflowLinearLayoutOverflowButtonStyle`
+**Popup overflow** | `overflowLinearLayoutPopupMenuStyle` | `?attr/overflowLinearLayoutPopupMenuStyle`
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/overflow/res/values/styles.xml)
+and
+[overflow linear layout attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/overflow/res/values/attrs.xml)
+
+## Code implementation
+
+### Adding overflow linear layout
+
+A common usage looks like:
+
+```xml
+
+
+
+
+ ...
+
+
+```
+
+When using `OverflowLinearLayout`, you should set `app:layout_overflowText` on
+on each child, as that will show as the text of the menu item that corresponds
+to the hidden child. Optionally, you can also set `app:layout_overflowIcon`.
+
+See [floatingtoolbar](FloatingToolbar.md) and [dockedtoolbar](DockedToolbar.md)
+for example usages with those components.
+
+API and source code:
+
+* `OverflowLinearLayout`
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/overflow/OverflowLinearLayout.java)
+
+### Making overflow linear layout accessible
+
+As mentioned above, you should set `app:layout_overflowText` on each direct
+child of `OverflowLinearLayout` that may be overflowed, so that the overflow
+menu items have text that can be read by screen readers.
+
+## Customizing overflow linear layout
+
+### Theming overflow linear layout
+
+Overflow linear layout supports
+[Material theming](https://m3.material.io/foundations/customization), which can
+customize color, shape and typography.
+
+#### Implementing overflow linear layout theming
+
+Use theme attributes and a style in `res/values/styles.xml` which apply to all
+overflow linear layouts and affect other components:
+
+```xml
+
+```
+
+Use a default style theme attribute, styles, and a theme overlay, which apply to
+all overflow linear layouts but do not affect other components:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+```
+
+Or use the style in the layout, which affects only this specific overflow linear
+layout:
+
+```xml
+
+
+
+
+```
diff --git a/docs/components/ProgressIndicator.md b/docs/components/ProgressIndicator.md
index 35f0eb901eb..573a77a5cf4 100644
--- a/docs/components/ProgressIndicator.md
+++ b/docs/components/ProgressIndicator.md
@@ -7,157 +7,158 @@ iconId: progress_indicator
path: /catalog/progress-indicators/
-->
-# Progress Indicators
+# Progress indicators
[Progress indicators](https://material.io/components/progress-indicators)
-express an unspecified wait time or display the length of a process.
+express an unspecified wait time or display the length of a process. Progress
+indicators show the status of a process in real time. There are two variants of
+progress indicators.
-
+
-**Contents**
+1. Linear progress indicator
+2. Circular progress indicator
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using progress indicators](#using-progress-indicators)
-* [Linear progress indicators](#linear-progress-indicators)
-* [Circular progress indicators](#circular-progress-indicators)
-* [Anatomy and key properties](#anatomy-and-key-properties)
-* [Theming progress indicators](#theming-progress-indicators)
+**Note:** Images use various dynamic color schemes.
-## Design and API Documentation
+## Design & API documentation
-* [Google Material3 Spec](https://material.io/components/progress-indicators/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/progressindicator/package-summary)
+* [Material 3 (M3) spec](https://m3.material.io/components/progress-indicators/overview/)
+* [API reference](https://developer.android.com/reference/com/google/android/material/progressindicator/package-summary)
-## Using progress indicators
+## Anatomy
-Before you can use Material sliders, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
-[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page.
+
-Progress indicators inform users about the status of ongoing processes, such as
-loading an app, submitting a form, or saving updates. They communicate an app’s
-state and indicate available actions, such as whether users can navigate away
-from the current screen.
+1. Active indicator
+2. Track
+3. Stop indicator
-**Note:** When displaying progress for a sequence of processes, indicate overall
-progress rather than the progress of each activity.
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/progress-indicators/guidelines#f4cc8d62-23b8-47e5-8ffa-5684ef4f1975).
-### Usage
+## M3 Expressive update
-A determinate progress indicator can be added to a layout:
+Before you can use `Material3Expressive` component styles, follow the
+[`Material3Expressive` themes setup instructions](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md#material3expressive-themes).
-```xml
-
-
-
-
-```
+
-An indeterminate progress indicator can be added:
+Progress indicators have a new rounded, colorful style, and more configurations
+to choose from, including a wavy shape and variable track height
-```xml
-
-
-
-
-```
+**New configurations:**
-
+* Track height: Thick (8dp)
+* Shape: Wavy
-1. Determinate
-2. Indeterminate
+## Key properties
-### Switching from indeterminate to determinate
+### Common attributes
-Indeterminate progress indicators can smoothly transit to determinate progress
-indicators by setting the `progress` programmatically:
+The following attributes are shared between linear and circular progress
+indicators:
-```kt
-int progress = getLoadingProgress()
-indicator.setProgressCompat(progress, true)
-```
+Element | Attribute | Related method(s) | Default value
+----------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------- | -------------
+**Track thickness** | `app:trackThickness` | `setTrackThickness` `getTrackThickness` | `4dp`
+**Indicator color** | `app:indicatorColor` | `setIndicatorColor` `getIndicatorColor` | `colorPrimary`
+**Track color** | `app:trackColor` | `setTrackColor` `getTrackColor` | `colorPrimaryContainer` (linear) `@android:color/transparent` (circular)
+**Track corner radius** | `app:trackCornerRadius` | `setTrackCornerRadius` `setTrackCornerRadiusFraction` `getTrackCornerRadius` | `50%`
+**Indicator track gap size** | `app:indicatorTrackGapSize` | `setIndicatorTrackGapSize` `getIndicatorTrackGapSize` | `4dp`
+**Show animation behavior** | `app:showAnimationBehavior` | `setShowAnimationBehavior` `getShowAnimationBehavior` | `none`
+**Hide animation behavior** | `app:hideAnimationBehavior` | `setHideAnimationBehavior` `getHideAnimationBehavior` | `none`
+**Delay (in ms) to show** | `app:showDelay` | N/A | 0
+**Min delay (in ms) to hide** | `app:minHideDelay` | N/A | 0
+**Wavelength** | `app:wavelength` | `setWavelength` | 0
+**Wavelength in determinate mode** | `app:wavelengthDeterminate` | `setWavelengthDeterminate` `getWavelenthDeterminate` | `wavelength`
+**Wavelength in indeterminate mode** | `app:wavelengthIndeterminate` | `setWavelengthIndeterminate` `getWavelengthIndeterminate` | `wavelength`
+**Wave amplitude** | `app:waveAmplitude` | `setWaveAmplitude` `getWaveAmplitude` | 0
+**Wave speed** | `app:waveSpeed` | `setWaveSpeed` `getWaveSpeed` | 0
+**Indeterminate animator duration scale** | `app:indeterminateAnimatorDurationScale` | `setIndeterminateAnimatorDurationScale` | 1
+
+### Linear type specific attributes
-**Note:** Once indeterminate progress indicators are switched to the
-determinate mode (or initialized as determinate), they can be set back to
-indeterminate mode via calling the `setIndeterminate(true)` method.
+Linear type progress indicators also have the following attributes:
-### Making progress indicators accessible
+Element | Attribute | Related method(s) | Default value
+-------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------- | -------------
+**Indeterminate animation type** | `app:indeterminateAnimationType` | `setIndeterminateAnimationType` `getIndeterminateAnimationType` | `disjoint`
+**Indicator direction** | `app:indicatorDirectionLinear` | `setIndicatorDirection` `getIndicatorDirection` | `leftToRight`
+**Track stop indicator size** | `app:trackStopIndicatorSize` | `setTrackStopIndicatorSize` `getTrackStopIndicatorSize` | `4dp`
+**Track stop indicator padding** | `app:trackStopIndicatorPadding` | `setTrackStopIndicatorPadding` `getTrackStopIndicatorPadding` | `none`
+**Track inner corner radius** | `app:trackInnerCornerRadius` | `setTrackInnerCornerRadius` `setTrackInnerCornerRadiusFraction` `getTrackInnerCornerRadius` | `none` (use `trackCornerRadius`)
-Progress indicators inherit accessibility support from the `ProgressBar` class
-in the framework. Please consider setting the content descriptor for use with
-screen readers.
+### Circular type specific attributes
-That can be done in XML via the `android:contentDescription` attribute or
-programmatically like so:
+Circular type progress indicators also have the following attributes:
-```kt
-progressIndicator.contentDescription = contentDescription
-```
+Element | Attribute | Related method(s) | Default value
+--------------------------------- | ---------------------------------------- | ------------------------------------------------------------------ | -------------
+**Spinner size (outer diameter)** | `app:indicatorSize` | `setIndicatorSize` `getIndicatorSize` | `40dp`
+**Inset** | `app:indicatorInset` | `setIndicatorInset` `getIndicatorInset` | `4dp`
+**Indicator direction** | `app:indicatorDirectionCircular` | `setIndicatorDirection` `getIndicatorDirection` | `clockwise`
+**Indeterminate animation type** | `app:indeterminateAnimationTypeCircular` | `setIndeterminateAnimationType` `getIndeterminateAnimationType` | `advance`
-**Note:** Depending on the track thickness of the linear progress indicator, the
-component could be less than or equal to 4dp tall. There's a known limitation in
-the focus indicator (green box) while using the talkback. It will fail to draw
-the focus indicator, if the component bounds is less than or equal to 4dp in
-either dimension. Consider to use `android:paddingTop` and
-`android:paddingBottom` to increase the bounds height when available.
+### Styles
-### Showing/hiding the progress indicator
+Element | Style | Theme attribute
+------------------------------------- | ------------------------------------------------------- | ---------------
+**Default linear** **style** | `Widget.Material3.LinearProgressIndicator` | `?attr/linearProgressIndicatorStyle`
+**Default circular** **style** | `Widget.Material3.CircularProgressIndicator` | `?attr/circularProgressIndicatorStyle`
+**Medium circular** **style** | `Widget.Material3.CircularProgressIndicator.Medium` | NA
+**Small circular** **style** | `Widget.Material3.CircularProgressIndicator.Small` | NA
+**Extra small circular** **style** | `Widget.Material3.CircularProgressIndicator.ExtraSmall` | NA
-By default, the progress indicator will be shown or hidden without animations.
-You can change the animation behaviors via `app:showAnimationBehavior` (or
-`setShowAnimationBehavior` method) and `app:hideAnimationBehavior` (or
-`setHideAnimationBehavior` method).
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/progressindicator/res/values/styles.xml)
+and
+[attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/progressindicator/res/values/attrs.xml).
-The modes of behaviors are:
+### Non-text contrast update
-* `none` (default) - shows/hides the view immediately when the visibility is
- being changed via `show`, `hide` or `setVisibility` method.
-* `outward` - for the linear type, shows the view by expanding from the
- baseline (or bottom edge) and hides the view by collapsing to the top edge;
- for the circular type, shows the view by expanding from the inner edge and
- hides the view by collapsing to the outer edge.
-* `inward` - for the linear type, shows the view by expanding from the top
- edge and hides the view by collapsing to the baseline (bottom edge); for the
- circular type, shows the view by expanding from the outer edge and hides the
- view by collapsing to the inner edge.
-* `escape` - for the linear type, escapes in the progression direction; for the
- circular type, no effect.
+In order to comply with the latest accessibility requirements, the
+`LinearProgressIndicator` and `CircularProgressIndicator` have been updated with
+additional attributes:
-When the hide animation behavior is not none, the visibility of the view will be
-changed after the animation finishes. Please use `setVisibilityAfterHide` method
-to set the target visibility as `Visibility.INVISIBLE` (default) or
-`Visibility.GONE`.
+- `app:indicatorTrackGapSize`: size of the gap between the indicator and the
+ track, 4dp by default.
+- `app:trackStopIndicatorSize`: size of the stop at the end of the track, 4dp
+ by default. Only applies to the linear determinate configuration.
+
+`*.Legacy` styles have been added to revert to the previous behavior (**not
+recommended**):
+
+- `Widget.Material3.LinearProgressIndicator.Legacy`
+- `Widget.Material3.CircularProgressIndicator.Legacy`
+
+## Variants of progress indicators
+
+Material design offers two visually distinct types of progress indicators:
+
+1. Linear
+2. Circular
-### Rounded corners
+**Note:** Only one type should represent each kind of activity in an app. For
+example, if a refresh action displays a circular indicator on one screen, that
+same action shouldn’t use a linear indicator elsewhere in the app.
-Progress indicators can have rounded corners via `app:trackCornerRadius` or the
-`setTrackCornerRadius` method.
+Progress indicators behave differently based on the time of progress being
+tracked:
-### Types
+* **Determinate progress indicators** fill from 0% to 100%. Use it when
+ progress and wait time is known.
+* **Indeterminate progress indicators** move along a fixed track, growing and
+ shrinking in size. Use it when progress and wait time is unknown.
-Material Design offers two visually distinct types of progress indicators: 1\.
-[linear](#linear-progress-indicators) 2\.
-[circular](#circular-progress-indicators)
+
-Only one type should represent each kind of activity in an app. For example, if
-a refresh action displays a circular indicator on one screen, that same action
-shouldn’t use a linear indicator elsewhere in the app.
+1. Determinate
+2. Indeterminate
-
+
-## Linear progress indicators
+
Linear progress indicators
Linear progress indicators display progress by animating an indicator along the
length of a fixed, visible track. The behavior of the indicator is dependent on
@@ -179,7 +180,8 @@ API and source code:
The following example shows a determinate linear progress indicator.
-
@@ -187,13 +189,13 @@ In the layout:
```xml
+ android:layout_width="match_parent" android:layout_height="wrap_content" />
```
The following example shows an indeterminate linear progress indicator.
-
@@ -201,9 +203,8 @@ In the layout:
```xml
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:indeterminate="true" />
```
#### Multi-color indeterminate animation type
@@ -211,19 +212,21 @@ In the layout:
For linear progress indicator, there are two indeterminate animation types:
* `disjoint` - animates as repeated cycles with two disjoint segments in the
- same color at a time.
+ same color at a time.
+
* `contiguous` - animates as repeated cycles with three adjacent segments in
- different colors.
+ different colors.
+
**Note:** There is a minimum requirement of 3 indicator colors to use the
**contiguous** animation. Otherwise, an IllegalArgumentException will be thrown.
-## Circular progress indicators
+
+
+
+
+
Circular progress indicators
Circular progress indicators display progress by animating an indicator along an
invisible circular track in a clockwise direction. They can be applied directly
@@ -245,7 +248,8 @@ API and source code:
The following example shows a determinate circular progress indicator.
-
@@ -253,13 +257,13 @@ In the layout:
```xml
+ android:layout_width="wrap_content" android:layout_height="wrap_content" />
```
The following example shows an indeterminate circular progress indicator.
-
@@ -267,107 +271,94 @@ In the layout:
```xml
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:indeterminate="true" />
```
-### Anatomy and key properties
-
-A progress indicator consists of a track and an indicator.
-
-
-
-1. Active indicator
-2. Track
-3. Stop indicator
+
-#### Common attributes
+## Code implementation
-The following attributes are shared between linear and circular progress
-indicators:
+Before you can use progress indicators, you need to add a dependency to the
+Material components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
-Element | Attribute | Related method(s) | Default value
------------------------------------------ | ---------------------------------------- | ------------------------------------------------------------- | -------------
-**Track thickness** | `app:trackThickness` | `setTrackThickness``getTrackThickness` | `4dp`
-**Indicator color** | `app:indicatorColor` | `setIndicatorColor``getIndicatorColor` | `colorPrimary`
-**Track color** | `app:trackColor` | `setTrackColor``getTrackColor` | `colorPrimaryContainer` (linear)`@android:color/transparent` (circular)
-**Track corner radius** | `app:trackCornerRadius` | `setTrackCornerRadius``getTrackCornerRadius` | `2dp`
-**Indicator track gap size** | `app:indicatorTrackGapSize` | `setIndicatorTrackGapSize``getIndicatorTrackGapSize` | `4dp`
-**Show animation behavior** | `app:showAnimationBehavior` | `setShowAnimationBehavior``getShowAnimationBehavior` | `none`
-**Hide animation behavior** | `app:hideAnimationBehavior` | `setHideAnimationBehavior``getHideAnimationBehavior` | `none`
-**Delay (in ms) to show** | `app:showDelay` | N/A | 0
-**Min delay (in ms) to hide** | `app:minHideDelay` | N/A | 0
-**Wavelength** | `app:wavelength` | `setWavelength` | 0
-**Wavelength in determinate mode** | `app:wavelengthDeterminate` | `setWavelengthDeterminate``getWavelenthDeterminate` | `wavelength`
-**Wavelength in indeterminate mode** | `app:wavelengthIndeterminate` | `setWavelengthIndeterminate``getWavelengthIndeterminate` | `wavelength`
-**Wave amplitude** | `app:waveAmplitude` | `setWaveAmplitude``getWaveAmplitude` | 0
-**Wave speed** | `app:waveSpeed` | `setWaveSpeed``getWaveSpeed` | 0
-**Indeterminate animator duration scale** | `app:indeterminateAnimatorDurationScale` | `setIndeterminateAnimatorDurationScale` | 1
-
-#### Linear type specific attributes
+Progress indicators inform users about the status of ongoing processes, such as
+loading an app, submitting a form, or saving updates. They communicate an app’s
+state and indicate available actions, such as whether users can navigate away
+from the current screen.
-Linear type progress indicators also have the following attributes:
+**Note:** When displaying progress for a sequence of processes, indicate overall
+progress rather than the progress of each activity.
-Element | Attribute | Related method(s) | Default value
--------------------------------- | -------------------------------- | ------------------------------------------------------------------- | -------------
-**Indeterminate animation type** | `app:indeterminateAnimationType` | `setIndeterminateAnimationType``getIndeterminateAnimationType` | `disjoint`
-**Indicator direction** | `app:indicatorDirectionLinear` | `setIndicatorDirection``getIndicatorDirection` | `leftToRight`
-**Track stop indicator size** | `app:trackStopIndicatorSize` | `setTrackStopIndicatorSize``getTrackStopIndicatorSize` | `4dp`
+### Adding determinate progress indicators
-#### Circular type specific attributes
+A determinate progress indicator can be added to a layout:
-Circular type progress indicators also have the following attributes:
+```xml
+
+
+
+```
-Element | Attribute | Related method(s) | Default value
---------------------------------- | ---------------------------------------- | ------------------------------------------------------------------- | -------------
-**Spinner size (outer diameter)** | `app:indicatorSize` | `setIndicatorSize``getIndicatorSize` | `40dp`
-**Inset** | `app:indicatorInset` | `setIndicatorInset``getIndicatorInset` | `4dp`
-**Indicator direction** | `app:indicatorDirectionCircular` | `setIndicatorDirection``getIndicatorDirection` | `clockwise`
-**Indeterminate animation type** | `app:indeterminateAnimationTypeCircular` | `setIndeterminateAnimationType``getIndeterminateAnimationType` | `advance`
+### Adding indeterminate progress indicators
-#### Styles
+An indeterminate progress indicator can be added:
-Element | Style
--------------------------------------- | -----
-**Default linear** **style** | `Widget.Material3.LinearProgressIndicator`
-**Default circular** **style** | `Widget.Material3.CircularProgressIndicator`
-**Medium circular** **style** | `Widget.Material3.CircularProgressIndicator.Medium`
-**Small circular** **style** | `Widget.Material3.CircularProgressIndicator.Small`
-**Extra small circular** **style** | `Widget.Material3.CircularProgressIndicator.ExtraSmall`
+```xml
+
+
+
+```
-Default linear style theme attribute: `?attr/linearProgressIndicatorStyle`
+### Switching from indeterminate to determinate
-Default circular style theme attribute: `?attr/circularProgressIndicatorStyle`
+Indeterminate progress indicators can smoothly transit to determinate progress
+indicators by setting the `progress` programmatically:
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/progressindicator/res/values/styles.xml)
-and
-[attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/progressindicator/res/values/attrs.xml).
+```kt
+int progress = getLoadingProgress ()
+indicator.setProgressCompat(progress, true)
+```
-#### Non-Text Contrast update
+**Note:** Once indeterminate progress indicators are switched to the determinate
+mode (or initialized as determinate), they can be set back to indeterminate mode
+via calling the `setIndeterminate(true)` method.
-In order to comply with the latest accessibility requirements, the
-`LinearProgressIndicator` and `CircularProgressIndicator` have been updated with
-additional attributes:
+### Making progress indicators accessible
-- `app:indicatorTrackGapSize`: size of the gap between the indicator and the
- track, 4dp by default.
-- `app:trackStopIndicatorSize`: size of the stop at the end of the track, 4dp by
- default. Only applies to the linear determinate configuration.
+Progress indicators inherit accessibility support from the `ProgressBar` class
+in the framework. Please consider setting the content descriptor for use with
+screen readers.
-`*.Legacy` styles have been added to revert to the previous behavior (**not
-recommended**):
+That can be done in XML via the `android:contentDescription` attribute or
+programmatically like so:
-- `Widget.Material3.LinearProgressIndicator.Legacy`
-- `Widget.Material3.CircularProgressIndicator.Legacy`
+```kt
+progressIndicator.contentDescription = contentDescription
+```
-## Theming
+**Note:** Depending on the track thickness of the linear progress indicator, the
+component could be less than or equal to 4dp tall. There's a known limitation in
+the focus indicator (green box) while using the talkback. It will fail to draw
+the focus indicator, if the component bounds is less than or equal to 4dp in
+either dimension. Consider to use `android:paddingTop` and
+`android:paddingBottom` to increase the bounds height when available.
-Progress indicators support Material theming which can customize color and size.
+## Customizing progress indicators
### Theming progress indicators
+Progress indicators support the customization of color and size.
+
API and source code:
* `LinearProgressIndicator`
@@ -377,7 +368,7 @@ API and source code:
* [Class description](https://developer.android.com/reference/com/google/android/material/progressindicator/CircularProgressIndicator)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/progressindicator/CircularProgressIndicator.java)
-The following example shows a circular progress indicator with Material Theming.
+The following example shows a circular progress indicator with Material theming.

-allow the user to select options.
+[Radio buttons](https://m3.material.io/components/radio-button/overview) let
+people select one option from a set of options.
-Use radio buttons to:
+
-* Select a single option from a list
-* Expose all available options
-* If available options can be collapsed, consider using a dropdown menu
- instead, as it uses less space.
+**Note:** Images use various dynamic color schemes.
-
+## Design & API documentation
-**Contents**
+* [Material 3 (M3) spec](https://m3.material.io/components/radio-button/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/radiobutton/package-summary)
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using radio buttons](#using-radio-buttons)
-* [Radio button](#radio-button)
-* [Theming radio buttons](#theming-radio-buttons)
+## Anatomy
-## Design and API Documentation
+
-* [Google Material3 Spec](https://material.io/components/radio-button/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/radiobutton/package-summary)
+1. Icon (selected)
+2. Adjacent label text
+3. Icon (unselected)
-## Using radio buttons
+More details on anatomy items are available in the
+[component guidelines](https://m3.material.io/components/radio-button/guidelines#4c0190e7-49da-43bf-b08b-828f71300425).
-Before you can use Material radio buttons, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
-[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page.
+## Key properties
-**Note:** `` is auto-inflated as
-`` via
-`MaterialComponentsViewInflater` when using a `Theme.Material3.*` theme.
+### Radio button attributes
-### Making radio buttons accessible
+Element | Attribute | Related method(s) | Default value
+-------------------------- | ------------------------------------------ | ---------------------------------------------------------- | -------------
+**To use material colors** | `app:useMaterialThemeColors` | `setUseMaterialThemeColors` `isUseMaterialThemeColors` | `true` (ignored if `app:buttonTint` is set)
+**Color** | `app:buttonTint` | `setButtonTintList` `getButtonTintList` | `?attr/colorOnSurface` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/radiobutton/res/color/m3_radiobutton_button_tint.xml))
+**Min size** | `android:minWidth` `android:minHeight` | `(set/get)MinWidth` `(set/get)MinHeight` | `?attr/minTouchTargetSize`
-Radio buttons support content labeling for accessibility and are readable by
-most screen readers, such as TalkBack. Text rendered in radio buttons is
-automatically provided to accessibility services. Additional content labels are
-usually unnecessary.
+The color of the radio button defaults to `?attr/colorOnSurface` (unchecked) and
+`?attr/colorPrimary` (checked) defined in your app theme. If you want to
+override this behavior, you could use a custom drawable that should not be
+tinted, set `app:useMaterialThemeColors` to `false`:
-### Grouping radio buttons
+```xml
+
+```
-Changes in the states of one radio button can affect other buttons in the group.
-Specifically, selecting a `RadioButton` in a `RadioGroup` will de-select all
-other buttons in that group. See the
-[example section below](#radio-buttons-example) for implementation details.
+### Text label attributes
+
+Element | Attribute | Related method(s) | Default value
+-------------- | ------------------------ | ---------------------------------- | -------------
+**Text label** | `android:text` | `setText` `getText` | `null`
+**Color** | `android:textColor` | `setTextColor` `getTextColors` | inherits from `AppCompatRadioButton`
+**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyMedium`
-## Radio button
+### Radio button states
+
+Radio buttons can be selected or unselected. Radio buttons have enabled,
+disabled, hover, focused, and pressed states.
+
+
+
+### Styles
+
+Element | Style | Theme attribute
+----------------- | --------------------------------------------- | ---------------
+**Default style** | `Widget.Material3.CompoundButton.RadioButton` | `?attr/radioButtonStyle`
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/radiobutton/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/radiobutton/res/values/attrs.xml).
+
+## Code implementation
+
+Before you can use Material radio buttons, you need to add a dependency to the
+Material components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
+
+### Adding radio button
A radio button is a circle that is filled in with an inset when selected. Radio
buttons allow the user to select one option from a set. Use radio buttons when
the user needs to see all available options. If available options can be
collapsed, consider using a dropdown menu because it uses less space.
-API and source code:
-
-* `MaterialRadioButton`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/radiobutton/MaterialRadioButton)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/radiobutton/MaterialRadioButton.java)
-* `RadioGroup`
- * [Class definition](https://developer.android.com/reference/android/widget/RadioGroup)
+**Note:** `` is auto-inflated as
+`` via
+`MaterialComponentsViewInflater` when using a `Theme.Material3.*` theme.
-### Radio buttons example
+
Radio button example
The following example shows a radio button group with three radio buttons.
-
+
In the layout:
@@ -127,65 +154,27 @@ radioButton.setOnCheckedChangeListener { buttonView, isChecked
}
```
-## Key properties
-
-### Radio button attributes
-
-Element | Attribute | Related method(s) | Default value
--------------------------- | ------------------------------------------ | ---------------------------------------------------------- | -------------
-**To use material colors** | `app:useMaterialThemeColors` | `setUseMaterialThemeColors` `isUseMaterialThemeColors` | `true` (ignored if `app:buttonTint` is set)
-**Color** | `app:buttonTint` | `setButtonTintList` `getButtonTintList` | `?attr/colorOnSurface` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/radiobutton/res/color/m3_radiobutton_button_tint.xml))
-**Min size** | `android:minWidth` `android:minHeight` | `(set/get)MinWidth` `(set/get)MinHeight` | `?attr/minTouchTargetSize`
-
-The color of the radio button defaults to `?attr/colorOnSurface` (unchecked) and
-`?attr/colorPrimary` (checked) defined in your app theme. If you want to
-override this behavior, you could use a custom drawable that should not be
-tinted, set `app:useMaterialThemeColors` to `false`:
-
-```xml
-
-```
-
-### Text label attributes
-
-Element | Attribute | Related method(s) | Default value
--------------- | ------------------------ | ---------------------------------- | -------------
-**Text label** | `android:text` | `setText` `getText` | `null`
-**Color** | `android:textColor` | `setTextColor` `getTextColors` | inherits from `AppCompatRadioButton`
-**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyMedium`
-
-### Radio button states
-
-Radio buttons can be selected or unselected. Radio buttons have enabled,
-disabled, hover, focused, and pressed states.
-
-
+### Making radio buttons accessible
-### Styles
+Radio buttons support content labeling for accessibility and are readable by
+most screen readers, such as Talkback. Text rendered in radio buttons is
+automatically provided to accessibility services. Additional content labels are
+usually unnecessary.
-Element | Style
------------------ | ------------------------------------------------------
-**Default style** | `Widget.Material3.CompoundButton.RadioButton`
+### Grouping radio buttons
-Default style theme attribute: `?attr/radioButtonStyle`
+Changes in the states of one radio button can affect other buttons in the group.
+Specifically, selecting a `RadioButton` in a `RadioGroup` will deselect all
+other buttons in that group. See the
+[example section](#radio-button-group-example) for implementation details.
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/radiobutton/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/radiobutton/res/values/attrs.xml).
+## Customizing radio buttons
-## Theming radio buttons
+### Theming radio buttons
-Radio buttons support
-[Material Theming](https://material.io/components/selection-controls#theming)
-which can customize color and typography.
+Radio buttons support the customization of color and typography.
-### Radio button theming example
+#### Radio button theming example
API and source code:
@@ -195,13 +184,13 @@ API and source code:
* `RadioGroup`
* [Class definition](https://developer.android.com/reference/android/widget/RadioGroup)
-The following example shows a radio button with Material Theming.
+The following example shows a radio button with Material theming.

-#### Implementing radio button theming
+##### Implementing radio button theming
Use theme attributes in `res/values/styles.xml` which applies to all radio
buttons and affects other components:
diff --git a/docs/components/Search.md b/docs/components/Search.md
index f590ee54050..b75f0f42edc 100644
--- a/docs/components/Search.md
+++ b/docs/components/Search.md
@@ -7,167 +7,173 @@ iconId: search
path: /catalog/search/
-->
-# Search Bar
+# Search
-Search is a navigation pattern which provides a floating search field with a
-surface that allows product-specific branding and additional navigation icons.
+[Search](https://m3.material.io/components/search/overview) is a navigation
+method that allows people to quickly find information across an app. Users input
+a query into the search bar or text field of the search view and then see
+related results.
-
+
-**Contents**
+1. Search bar
+2. Search view
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using search components](#using-search-components)
-* [Search Bar](#search-bar)
-* [Search View](#search-view)
-* [Putting it all together](#putting-it-all-together)
-* [Predictive Back](#predictive-back)
+**Search bar** is a persistent and prominent search field at the top of the
+screen and **search view** is a full-screen modal typically opened by selecting
+a search icon.
-## Design and API Documentation
+**Note:** Images use various dynamic color schemes.
-* [Google Material3 Spec](https://material.io/components/search/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/search/package-summary)
-
-## Using search components
-
-Before you can use the Material Search components, you need to add a dependency to
-the Material Components for Android library. For more information, go to the
+Before you can use the Material Search components, you need to add a dependency
+to the Material components for Android library. For more information, go to the
[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
page.
Note: Material Search was introduced in `1.8.0`. To use Material Search, make
-sure you're depending on [library version `1.8.0`](https://github.com/material-components/material-components-android/releases/tag/1.8.0)
+sure you're depending on
+[library version `1.8.0`](https://github.com/material-components/material-components-android/releases/tag/1.8.0)
or later.
-### Making Search Components accessible
+## Design & API documentation
-You should set a content description on a search bar and search view components
-via the `android:contentDescription` attribute or `setContentDescription` method
-so that screen readers such as TalkBack are able to announce their purpose or
-action. Text rendered in these components are automatically provided to
-accessibility services, so additional content labels are usually unnecessary.
+* [Material 3 (M3) spec](https://m3.material.io/components/search/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/search/package-summary)
-## Search Bar
+## Anatomy
-The `SearchBar` component provides an implementation of the floating search
-field. It extends `Toolbar`, so it supports a navigation icon, menu items, and
-any other `Toolbar` APIs. Additionally, the `SearchBar` comes with a hint
-`TextView` and supports nesting a centered branding element.
+#### Search bar
-Since `SearchBar` extends `Toolbar`, you can set up your `SearchBar` as an
-`ActionBar` via [`AppCompatActivity#setSupportActionBar`](https://developer.android.com/reference/kotlin/androidx/appcompat/app/AppCompatActivity#setSupportActionBar(androidx.appcompat.widget.Toolbar)), and inflate a menu by
-overriding the `onCreateOptionsMenu` method. However, if using the default
-magnifying glass `navigationIcon`, you may need to set
-`app:forceDefaultNavigationOnClickListener="true"` on your `SearchBar` so that
-the search icon doesn't act as a back button due to the Activity's `ActionBar`
-setup flow.
+
-Alternatively, you can choose to not set up your `SearchBar` as an `ActionBar`,
-and instead just use `Toolbar`'s `inflateMenu` and `setOnMenuItemClickListener`
-methods:
+1. Container
+2. Leading icon button
+3. Supporting text
+4. Avatar or trailing icon (optional)
-```java
-searchBar.inflateMenu(R.menu.searchbar_menu);
-searchBar.setOnMenuItemClickListener(
- menuItem -> {
- // Handle menuItem click.
- return true;
- });
-```
+#### Search view
-Note: `SearchBar` aims to provide a consistent search bar across all apps, so
-it does not support setting a custom background via `android:background`.
+
-API and source code:
+1. Container
+2. Header
+3. Leading icon button
+4. Supporting text
+5. Trailing icon button
+6. Input text
+7. Divider
-* `SearchBar`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/search/SearchBar)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/search/SearchBar.java)
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/search/guidelines#ea5fe21e-f47d-421c-ab40-c45811329e00).
-### Anatomy and key properties
+## Key properties
-The following is an anatomy diagram for the search bar:
+### Search bar
-
+#### Attributes
-1. Container
-2. Leading icon button
-3. Supporting text
-4. Avatar or trailing icon (optional)
+The following attributes can be changed for `SearchBar`:
-### Attributes
+Element | Attribute | Related method(s) | Default value
+---------------------------------------- | ----------------------------- | ------------------------------------------- | -------------
+**Max Width** | `android:maxWidth` | `setMaxWidth` `getMaxWidth` | `-1` (unset)
+**Flag for enabling adaptive max width** | `app:adaptiveMaxWidthEnabled` | -- | `false`
+**Min height** | `android:minHeight` | `setMinHeight` `getMinHeight` | `@dimen/m3_searchbar_height`
+**Search text appearance** | `android:textAppearance` | `setTextAppearance` `getTextAppearance` | `@style/TextAppearance.Material3.SearchBar`
+**Search text** | `android:text` | `setText` `getText` | `null`
+**Search hint** | `android:hint` | `setHint` `getHint` | `null`
+**Search text centered** | `app:textCentered` | `setTextCentered` `getTextCentered` | `false`
+**Color** | `app:backgroundTint` | -- | `?attr/colorSurfaceContainerHigh`
+**Lift On Scroll** | `app:liftOnScroll` | -- | `false`
+**Lift On Scroll Color** | `app:liftOnScrollColor` | -- | `?attr/colorSurfaceContainerHighest`
+**Flag for default margins** | `app:defaultMarginsEnabled` | -- | `true`
+**Flag for navigation icon** | `app:hideNavigationIcon` | -- | `false`
+
+#### Styles
+
+Element | Style
+------------------------------ | -------------------------------------
+**Search Bar Default style** | `Widget.Material3.SearchBar`
+**Search View Toolbar style** | `Widget.Material3.SearchView.Toolbar`
+**Search View Toolbar height** | `@dimen/m3_searchview_height`
-The following attributes can be changed for `SearchBar`:
+Default search bar style theme attribute: `?attr/materialSearchBarStyle`.
-Element | Attribute | Related method(s) | Default value
----------------------------- | --------------------------- | ------------------------------------------- | -------------
-**Min height** | `android:minHeight` | `setMinHeight` `getMinHeight` | `@dimen/m3_searchbar_height`
-**Search text appearance** | `android:textAppearance` | `setTextAppearance` `getTextAppearance` | `@style/TextAppearance.Material3.SearchBar`
-**Search text** | `android:text` | `setText` `getText` | `null`
-**Search hint** | `android:hint` | `setHint` `getHint` | `null`
-**Color** | `app:backgroundTint` | -- | `?attr/colorSurfaceContainerHigh`
-**Flag for default margins** | `app:defaultMarginsEnabled` | -- | `true`
-**Flag for navigation icon** | `app:hideNavigationIcon` | -- | `false`
+Search view toolbar theme attribute: `?attr/materialSearchViewToolbarStyle`.
+Search view toolbar height theme attribute:
+`?attr/materialSearchViewToolbarHeight`.
-## Styles
+### Search view
-Element | Style
----------------------------- | ----------------------------
-**Search Bar Default style** | `Widget.Material3.SearchBar`
+#### Attributes
-Default search bar style theme attribute: `?attr/materialSearchBarStyle`.
+The following attributes can be changed for `SearchView`:
-### Scrolling Behavior
+Element | Attribute | Related method(s) | Default value
+---------------------------------- | ---------------------------- | ------------------------------------------- | -------------
+**Search text appearance** | `android:textAppearance` | `setTextAppearance` `getTextAppearance` | `@style/TextAppearance.Material3.SearchBar`
+**Search text** | `android:text` | `setText` `getText` | `null`
+**Search hint** | `android:hint` | `setHint` `getHint` | `null`
+**Color** | `app:backgroundTint` | -- | `?attr/colorSurfaceContainerHigh`
+**Flag for navigation icon** | `app:hideNavigationIcon` | -- | `true`
+**Flag for `DrawerArrowDrawable`** | `app:useDrawerArrowDrawable` | -- | `false`
+**Flag for soft keyboard** | `app:autoShowKeyboard` | -- | `true`
-The `SearchBar` can either be used as a fixed or scroll-away search field.
+#### Styles
-#### Fixed Mode
+Element | Style
+----------------------------- | -----------------------------
+**Search View Default style** | `Widget.Material3.SearchView`
-To set up the fixed mode, simply position the `SearchBar` on top of the rest of
-your layout's contents and do not set up any scrolling behaviors or
-AppBarLayout. The `SearchBar` will remain fixed in place as the content is
-scrolled beneath it.
+Default search view style theme attribute: `?attr/materialSearchViewStyle`.
-#### Scroll-away Mode
+## Code implementation
-To set up the scroll-away mode, use a top-level `CoordinatorLayout` and place
-the `SearchBar` within an `AppBarLayout`. Then, place the `AppBarLayout` below
-the scrolling view (usually a `RecyclerView` or `NestedScrollView`) in the
-`CoordinatorLayout`, and set
-`app:layout_behavior="@string/searchbar_scrolling_view_behavior"` on the
-scrolling view. This scrolling behavior makes the `AppBarLayout` transparent and
-not elevated so there are no undesirable shadows. It also adjusts the scrolling
-child so that the `SearchBar` will overlap the rest of your content and appear
-to be floating above it. See the
-[Putting it all together](#putting-it-all-together) section below for an example
-of how to set up this behavior.
+API and source code
-Additionally, if your app is going edge-to-edge, consider adding
-`app:statusBarForeground="?attr/colorSurface"` to your `AppBarLayout` in order
-to avoid overlap between the `SearchBar` and status bar content on scroll.
+* [Class definition](https://developer.android.com/reference/com/google/android/material/search/SearchView)
-### Toolbar Transitions
+* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/search/SearchView.java)
-The `SearchBar` component also provides transitions to and from a `Toolbar`,
-e.g., for a contextual multi-select flow. These transitions are implemented as
-expand and collapse animations, and can be started by calling `SearchBar#expand`
-and `SearchBar#collapse`, respectively. Additionally, if you are using an
-`AppBarLayout` in conjunction with the `SearchBar`, you may pass in a reference
-to your `AppBarLayout` to these methods so that its visibility and offset can be
-taken into account for the animations.
+#### Search bar
-Lastly, make sure to add the following to your back pressed handling method, in
-order to collapse the contextual `Toolbar` into the `SearchBar` when the user
-presses the system back button:
+The `SearchBar` component provides an implementation of the floating search
+field. It extends `Toolbar`, so it supports a navigation icon, menu items, and
+any other `Toolbar` APIs. Additionally, the `SearchBar` comes with a hint
+`TextView` and supports nesting a centered branding element.
+
+Since `SearchBar` extends `Toolbar`, you can set up your `SearchBar` as an
+`ActionBar` via
+[`AppCompatActivity#setSupportActionBar`](https://developer.android.com/reference/kotlin/androidx/appcompat/app/AppCompatActivity#setSupportActionBar\(androidx.appcompat.widget.Toolbar\)),
+and inflate a menu by overriding the `onCreateOptionsMenu` method. However, if
+using the default magnifying glass `navigationIcon`, you may need to set
+`app:forceDefaultNavigationOnClickListener="true"` on your `SearchBar` so that
+the search icon doesn't act as a back button due to the Activity's `ActionBar`
+setup flow.
+
+Alternatively, you can choose to not set up your `SearchBar` as an `ActionBar`,
+and instead just use `Toolbar`'s `inflateMenu` and `setOnMenuItemClickListener`
+methods:
```java
-if (searchBar.collapse(contextualToolbar, appBarLayout)) {
- // Clear selection.
- return;
-}
+searchBar.inflateMenu(R.menu.searchbar_menu);
+searchBar.setOnMenuItemClickListener(
+ menuItem -> {
+ // Handle menuItem click.
+ return true;
+ });
```
-## Search View
+Note: `SearchBar` aims to provide a consistent search bar across all apps, so it
+does not support setting a custom background via `android:background`.
+
+API and source code:
+
+* `SearchBar`
+ * [Class definition](https://developer.android.com/reference/com/google/android/material/search/SearchBar)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/search/SearchBar.java)
+
+#### Search view
The `SearchView` component provides an implementation of a full-screen search
view which can be used to display back navigation, a search hint and text, menu
@@ -191,9 +197,9 @@ method, so you can use any of the traditional
[EditText APIs](https://developer.android.com/reference/android/widget/EditText)
to configure the search field (`setText()`, `addTextChangedListener()`, etc.).
-Here is an example of how you can carry over the search text to
-the `SearchBar`, as well as hide the `SearchView` when the user finishes typing
-and presses enter:
+Here is an example of how you can carry over the search text to the `SearchBar`,
+as well as hide the `SearchView` when the user finishes typing and presses
+enter:
```java
searchView
@@ -206,43 +212,46 @@ searchView
});
```
-### Anatomy and key properties
+## Making search components accessible
-The following is an anatomy diagram for the search view:
+You should set a content description on a search bar and search view components
+via the `android:contentDescription` attribute or `setContentDescription` method
+so that screen readers such as TalkBack are able to announce their purpose or
+action. Text rendered in these components are automatically provided to
+accessibility services, so additional content labels are usually unnecessary.
-
+## Transition listeners
-1. Container
-2. Header
-3. Leading icon button
-4. Supporting text
-5. Trailing icon button
-6. Input text
-7. Divider
+If you want to get callbacks for when the `SearchView` transitions between its
+different animation states, you can add an `SearchView.TransitionListener` via
+the `SearchView#addTransitionListener` method. E.g.:
-### Attributes
+```java
+searchView.addTransitionListener(
+ (searchView, previousState, newState) -> {
+ if (newState == TransitionState.SHOWING) {
+ // Handle search view opened.
+ }
+ });
+```
-The following attributes can be changed for `SearchView`:
+## Predictive back
-Element | Attribute | Related method(s) | Default value
----------------------------------- | ---------------------------- | ------------------------------------------- | -------------
-**Search text appearance** | `android:textAppearance` | `setTextAppearance` `getTextAppearance` | `@style/TextAppearance.Material3.SearchBar`
-**Search text** | `android:text` | `setText` `getText` | `null`
-**Search hint** | `android:hint` | `setHint` `getHint` | `null`
-**Color** | `app:backgroundTint` | -- | `?attr/colorSurfaceContainerHigh`
-**Flag for navigation icon** | `app:hideNavigationIcon` | -- | `true`
-**Flag for `DrawerArrowDrawable`** | `app:useDrawerArrowDrawable` | -- | `false`
-**Flag for soft keyboard** | `app:autoShowKeyboard` | -- | `true`
-
-## Styles
+The `SearchView` component automatically supports
+[predictive back](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md)
+when it is set up with and connected to a `SearchBar`, as mentioned in the
+sections above. No further integration is required on the app side other than
+the general predictive back prerequisites and migration steps mentioned
+[here](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md#usage).
-Element | Style
------------------------------ | -----------------------------
-**Search View Default style** | `Widget.Material3.SearchView`
+Visit the
+[predictive back design guidelines](https://m3.material.io/components/search/guidelines#3f2d4e47-2cf5-4c33-b6e1-5368ceaade55)
+to see how the component behaves when a user swipes back.
-Default search view style theme attribute: `?attr/materialSearchViewStyle`.
+## Customizing search bar
-### Expand and Collapse Animations
+
+
Expand and collapse animations
One of the biggest advantages of using the `SearchView` in conjunction with an
`SearchBar` is that you will get the expand and collapse animations for free. If
@@ -250,22 +259,10 @@ you are just using a standalone `SearchView` without an `SearchBar`, then
showing or hiding the `SearchView` will result in slide up and slide down
transitions.
-### Transition Listeners
-
-If you want to get callbacks for when the `SearchView` transitions between its
-different animation states, you can add an `SearchView.TransitionListener` via
-the `SearchView#addTransitionListener` method. E.g.:
-
-```java
-searchView.addTransitionListener(
- (searchView, previousState, newState) -> {
- if (newState == TransitionState.SHOWING) {
- // Handle search view opened.
- }
- });
-```
+
-### Soft Input Modes
+
+
Soft input modes
The recommended `windowSoftInputMode` when using an `SearchBar` and an
`SearchView` is `adjustNothing`. There are a couple reasons for this:
@@ -290,7 +287,10 @@ Lastly, if you don't want the soft keyboard to show automatically when the
`SearchView` is shown, you can set `app:autoShowKeyboard="false"` on your
`SearchView`.
-### Translucent Status Bar
+
+
+
+
Translucent status bar
`SearchBar` and `SearchView` come with support for a translucent status bar.
@@ -304,14 +304,20 @@ the `SearchView`. If you are using either `FLAG_TRANSLUCENT_STATUS`
`SearchView` will automatically add an extra spacer surface so that it fills the
space underneath the translucent status bar.
-### Menu to Back Arrow Animation
+
+
+
+
Menu to back arrow animation
If you are using the `SearchBar` with a `NavigationDrawer`, you can set the
`app:useDrawerArrowDrawable` attribute to `true` on your `SearchView` to enable
the "hamburger" menu to back arrow icon animation. This animation will happen
during the expand and collapse of the `SearchView`.
-### Search Prefix
+
+
+
+
Search prefix
If you would like to show some prefix text before the main search `EditText`,
you can make use of the `app:searchPrefixText` attribute. For example, setting
@@ -322,14 +328,17 @@ Additionally, with this pattern it is common to hide the back button to reduce
clutter, as navigation can be handled outside of the search view. This can be
accomplished by setting `app:hideNavigationIcon="true"` on your `SearchView`.
-## Search History, Suggestions, and Results
+
+
+
+
Search history, suggestions, and results
`SearchView` is a view group component, meaning you can nest content inside of
it such as:
-- Search history when the `SearchView` is first expanded
-- Search suggestions when the user is typing
-- Search results once the user submits the search
+- Search history when the `SearchView` is first expanded
+- Search suggestions when the user is typing
+- Search results once the user submits the search
```xml
```
-## Putting it all together
+
+
+
+
Scrolling behavior
+
+The `SearchBar` can either be used as a fixed, scroll-away, or lift on scroll
+search field.
+
+#### Fixed mode
+
+To set up the fixed mode, simply position the `SearchBar` on top of the rest of
+your layout's contents and do not set up any scrolling behaviors or
+AppBarLayout. The `SearchBar` will remain fixed in place as the content is
+scrolled beneath it.
+
+#### Scroll-away mode
+
+To set up the scroll-away mode, use a top-level `CoordinatorLayout` and place
+the `SearchBar` within an `AppBarLayout`. Then, place the `AppBarLayout` below
+the scrolling view (usually a `RecyclerView` or `NestedScrollView`) in the
+`CoordinatorLayout`, and set
+`app:layout_behavior="@string/searchbar_scrolling_view_behavior"` on the
+scrolling view. This scrolling behavior makes the `AppBarLayout` transparent and
+not elevated so there are no undesirable shadows. It also adjusts the scrolling
+child so that the `SearchBar` will overlap the rest of your content and appear
+to be floating above it. See the
+[putting it all together](#putting-it-all-together) section below for an example
+of how to set up this behavior.
+
+Additionally, if your app is going edge-to-edge, consider adding
+`app:statusBarForeground="?attr/colorSurface"` to your `AppBarLayout` in order
+to avoid overlap between the `SearchBar` and status bar content on scroll.
+
+#### Lift on scroll mode
+
+To set up the lift on scroll mode, use a top-level `CoordinatorLayout` and place
+the `SearchBar` within an `AppBarLayout`. Then, place the `AppBarLayout` below
+the scrolling view (usually a `RecyclerView` or `NestedScrollView`) in the
+`CoordinatorLayout`, and set
+`app:layout_behavior="@string/appbar_scrolling_view_behavior"` on the scrolling
+view. On the `SearchBar`, set `app:liftOnScroll=true` and set a
+`app:liftOnScrollColor` to change the color of the `SearchBar` as the
+`AppBarLayout` is lifting.
+
+See the [putting it all together](#putting-it-all-together) section below for an
+example of how to set up this behavior.
+
+
+
+
+
Putting it all together
Putting it all together and using the scroll-away mode, the `SearchBar` and
`SearchView` widgets can be used in your layout as such:
@@ -390,21 +449,67 @@ as well as the expand and collapse animations. If you can't use a
`CoordinatorLayout`, instead you can call the `SearchView#setUpWithSearchBar`
method to achieve the same result.
-## Predictive Back
+Alternatively, an example of the lift on scroll mode is below:
-The `SearchView` component automatically supports
-[Predictive Back](../foundations/PredictiveBack.md) when it is set up with and
-connected to a `SearchBar`, as mentioned in the sections above. No further
-integration is required on the app side other than the general Predictive Back
-prerequisites and migration steps mentioned
-[here](../foundations/PredictiveBack.md#usage).
+```xml
+
-Visit the
-[Predictive Back design guidelines](https://m3.material.io/components/search/guidelines#3f2d4e47-2cf5-4c33-b6e1-5368ceaade55)
-to see how the component behaves when a user swipes back.
+
+
+
+
-## API and source code
+
+
+
-* [Class definition](https://developer.android.com/reference/com/google/android/material/search/SearchView)
+
+
+
+
+
+
+```
+
+
+
+
+
Toolbar transitions
+
+The `SearchBar` component also provides transitions to and from a `Toolbar`,
+e.g., for a contextual multi-select flow. These transitions are implemented as
+expand and collapse animations, and can be started by calling `SearchBar#expand`
+and `SearchBar#collapse`, respectively. Additionally, if you are using an
+`AppBarLayout` in conjunction with the `SearchBar`, you may pass in a reference
+to your `AppBarLayout` to these methods so that its visibility and offset can be
+taken into account for the animations.
+
+Lastly, make sure to add the following to your back pressed handling method, in
+order to collapse the contextual `Toolbar` into the `SearchBar` when the user
+presses the system back button:
+
+```java
+if (searchBar.collapse(contextualToolbar, appBarLayout)) {
+ // Clear selection.
+ return;
+}
+```
-* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/search/SearchView.java)
+
diff --git a/docs/components/SideSheet.md b/docs/components/SideSheet.md
index 74599d991b4..279a1973478 100644
--- a/docs/components/SideSheet.md
+++ b/docs/components/SideSheet.md
@@ -7,121 +7,145 @@ iconId: side_sheet
path: /catalog/side-sheet-behavior/
-->
-# Side Sheets
+# Side sheets
-[Side sheets](https://material.io/components/sheets-side) are surfaces
-containing supplementary content that are anchored to the side of the screen.
+[Side sheets](https://m3.material.io/components/side-sheets/overview) are
+surfaces containing supplementary content that are anchored to the side of the
+screen. There are two variants of side sheets.
-See [Bottom Sheet documentation](BottomSheet.md) for documentation about
-[bottom sheets](https://m3.material.io/components/bottom-sheets/overview).
+
-**Contents**
+1. Standard side sheet
+2. Modal side sheet
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using side sheets](#using-side-sheets)
-* [Standard side sheet](#standard-side-sheet)
-* [Modal side sheet](#modal-side-sheet)
-* [Coplanar side sheet](#coplanar-side-sheet)
-* [Anatomy and key properties](#anatomy-and-key-properties)
-* [Predictive Back](#predictive-back)
-* [Theming](#theming-side-sheets)
+**Note:** Images use various dynamic color schemes.
-## Design and API Documentation
+## Design & API documentation
-* [Google Material3 Spec](https://material.io/components/side-sheets/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/sidesheet/package-summary)
+* [Material 3 (M3) spec](https://m3.material.io/components/side-sheets/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/sidesheet/package-summary)
-## Using side sheets
+## Anatomy
-Before you can use Material side sheets, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
-[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
-page.
+#### Standard side sheet
-Note: Side sheets were introduced in `1.8.0`. To use side sheets, make sure
-you're depending on [library version `1.8.0`](https://github.com/material-components/material-components-android/releases/tag/1.8.0)
-or later.
+
-Standard side sheet basic usage:
+1. Divider (optional)
+2. Headline
+3. Container
+4. Close affordance
-```xml
-
+#### Modal side sheet
-
+
-
+1. Back icon button (optional)
+2. Header
+3. Container
+4. Close icon button
+5. Divider (optional)
+6. Action (optional)
+7. Scrim
-
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/side-sheets/guidelines#4d992de0-362a-41b3-9537-9da4dec148af).
-
-```
+## Key properties
-### Setting behavior
+### Sheet attributes
-There are several attributes that can be used to adjust the behavior of standard
-and modal side sheets.
+Element | Attribute | Related method(s) | Default value
+---------------------------- | --------------------------- | ------------------------------------------------------- | -------------
+**Color** | `app:backgroundTint` | N/A | `?attr/colorSurface``?attr/colorSurfaceContainerLow` (modal)
+**Coplanar sibling view id** | `app:coplanarSiblingViewId` | `setCoplanarSiblingViewId` `setCoplanarSiblingView` | N/A
+**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceCornerLarge`
+**Sheet edge** | `android:layout_gravity` | `setSheetEdge` (modal only) | end
+**Elevation** | `android:elevation` | N/A | 0dp
+**Max width** | `android:maxWidth` | `setMaxWidth` `getMaxWidth` | N/A
+**Max height** | `android:maxHeight` | `setMaxHeight` `getMaxHeight` | N/A
-Behavior attributes can be applied to standard side sheets in xml by setting
-them on a child `View` set to `app:layout_behavior`, or programmatically:
+### Behavior attributes
-```kt
-val standardSideSheetBehavior = SideSheetBehavior.from(standardSideSheet)
-// Use this to programmatically apply behavior attributes
-```
+More info about these attributes and how to use them in the
+[setting behavior](#setting-behavior) section.
-More information about these attributes and their default values is available in
-the [behavior attributes](#behavior-attributes) section.
+Behavior | Related method(s) | Default value
+------------------------ | -------------------------------- | -------------
+`app:behavior_draggable` | `setDraggable` `isDraggable` | `true`
-### Setting state
+### Sheet edge
-Standard side sheets have the following states:
+You can set the sheet to originate from the left or right side of the screen.
+You can also automatically switch the sheet edge based on layout direction, by
+setting the sheet edge to `start` or `end`.
-* `STATE_EXPANDED`: The side sheet is visible at its maximum height and it
- is neither dragging nor settling (see below).
-* `STATE_HIDDEN`: The side sheet is no longer visible and can only be
- re-shown programmatically.
-* `STATE_DRAGGING`: The user is actively dragging the side sheet.
-* `STATE_SETTLING`: The side sheet is settling to a specific height after a
- drag/swipe gesture. This will be the peek height, expanded height, or 0, in
- case the user action caused the side sheet to hide.
+#### Standard and coplanar sheets
-You can set a state on the side sheet:
+To set a standard or coplanar sheet's edge, set the `gravity` property of the
+side sheet `View`'s `CoordinatorLayout.LayoutParams`, then request a layout pass
+on the side sheet `View`.
```kt
-sideSheetBehavior.state = Sheet.STATE_HIDDEN
+val layoutParams = sideSheetView.layoutParams
+if (layoutParams is CoordinatorLayout.LayoutParams) {
+ layoutParams.gravity = sheetGravity
+ sideSheetView.requestLayout()
+}
```
-**Note:** `STATE_SETTLING` and `STATE_DRAGGING` should not be set programmatically.
+You can also set the sheet edge with XML, by setting `android:layout_gravity` to
+the desired gravity:
-### Listening to state and slide changes
+```xml
+
+
+
+```
-`SideSheetCallback`s can be added to a `SideSheetBehavior` to listen for state
-and slide changes:
+#### Modal sheets
+
+To set a modal sheet's edge, pass a `Gravity` constant into `SideSheetDialog`'s
+dedicated `setSheetEdge` method. For example, set the sheet edge to `start` like
+this:
```kt
-val sideSheetCallback = object : SideSheetBehavior.SideSheetCallback() {
+sideSheetDialog.setSheetEdge(Gravity.START)
+```
- override fun onStateChanged(sideSheet: View, newState: Int) {
- // Do something for new state.
- }
+Note: Runtime changes to sheet edges are not supported for modal sheets and may
+not work as expected. If you'd like to change the sheet edge at runtime, you
+should recreate the sheet, then call `setSheetEdge` with the new gravity.
- override fun onSlide(sideSheet: View, slideOffset: Float) {
- // Do something for slide offset.
- }
-}
+### Styles
-// To add a callback:
-sideSheetBehavior.addCallback(sideSheetCallback)
+Element | Value | Theme attribute
+------------------------- | ----------------------------------------- | ---------------
+Standard side sheet style | `@style/Widget.Material3.SideSheet` | N/A
+Modal side sheet style | `@style/Widget.Material3.SideSheet.Modal` | `?attr/sideSheetModalStyle`
-// To remove a callback:
-sideSheetBehavior.removeCallback(sideSheetCallback)
-```
+Note: There is no default style theme attribute for standard side sheets,
+because `SideSheetBehavior`s don't have a designated associated `View`. Modal
+side sheets use `?attr/sideSheetModalStyle` as the default style, but there is
+no need to set `?attr/sideSheetModalStyle` on your modal side sheet layout
+because the style is automatically applied to the parent `SideSheetDialog`.
-## Standard side sheet
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/res/values/styles.xml),
+[attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/res/values/attrs.xml),
+and
+[themes and theme overlays](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/res/values/themes.xml).
+
+## Variants of side sheets
+
+
+
Standard side sheet
Standard side sheets co-exist with the screen’s main UI region and allow for
simultaneously viewing and interacting with both regions. They are commonly used
@@ -130,20 +154,20 @@ main UI region is frequently scrolled or panned.
`SideSheetBehavior` is applied to a child of
[CoordinatorLayout](https://developer.android.com/reference/androidx/coordinatorlayout/widget/CoordinatorLayout)
-to make that child a **standard side sheet**, which is a view that comes up
-from the side of the screen, elevated over the main content. It can be dragged
-vertically to expose more or less content.
+to make that child a standard side sheet, which is a view that comes up from the
+side of the screen, elevated over the main content. It can be dragged vertically
+to expose more or less content.
API and source code:
* `SideSheetBehavior`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/sidesheet/SideSheetBehavior)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java)
+ * [Class definition](https://developer.android.com/reference/com/google/android/material/sidesheet/SideSheetBehavior)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java)
-### Standard side sheet example
+
Standard side sheet example
-`SideSheetBehavior` works in tandem with `CoordinatorLayout` to let you
-display content in a side sheet, perform enter/exit animations, respond to
+`SideSheetBehavior` works in tandem with `CoordinatorLayout` to let you display
+content in a side sheet, perform enter/exit animations, respond to
dragging/swiping gestures, and more.
Apply the `SideSheetBehavior` to a direct child `View` of `CoordinatorLayout`:
@@ -187,7 +211,41 @@ Apply the `SideSheetBehavior` to a direct child `View` of `CoordinatorLayout`:
In this example, the side sheet is the `LinearLayout`.
-## Modal side sheet
+#### Coplanar side sheet
+
+Coplanar side sheets are standard side sheets that "squash" a sibling view's
+content as the side sheet expands. Coplanar side sheets are on the same plane as
+their sibling, as opposed to standard side sheets, which are displayed above the
+screen content.
+
+Note: Coplanar side sheets are not recommended for narrow screens.
+
+##### Coplanar side sheet example
+
+To add a coplanar side sheet to your app, follow the steps to
+[add a standard side sheet to your layout](#standard-side-sheet-example), above,
+and simply set `app:coplanarSiblingViewId` on the `View` that has the side sheet
+`layout_behavior` set on it. You can point the coplanar sibling view id to any
+child of the `CoordinatorLayout`. Make sure to set
+`style="@style/Widget.Material3.SideSheet"` on your side sheet view, as well.
+
+Alternatively, you can set the coplanar sibling view programmatically:
+
+```kt
+coplanarSideSheet.setCoplanarSiblingView(coplanarSiblingView)
+```
+
+To remove coplanar functionality from a side sheet, simply set the coplanar
+sibling view to `null`:
+
+```kt
+coplanarSideSheet.setCoplanarSiblingView(null)
+```
+
+
+
+
+
Modal side sheet
Modal side sheets present the sheet while blocking interaction with the rest of
the screen. They are an alternative to inline menus and simple dialogs on mobile
@@ -204,7 +262,7 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/sidesheet/SideSheetDialog)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/SideSheetDialog.java)
-### Modal side sheet example
+#### Modal side sheet example
To show a modal side sheet, instantiate a `SideSheetDialog` with the desired
`context`:
@@ -223,149 +281,107 @@ You can then show the side sheet with `sideSheetDialog.show()` and dismiss it
with `sideSheetDialog.hide()`. `SideSheetDialog` has built in functionality to
automatically cancel the dialog after it is swiped off the screen.
-## Coplanar side sheet
+
-Coplanar side sheets are standard side sheets that "squash" a sibling view's
-content as the side sheet expands. Coplanar side sheets are on the same plane as
-their sibling, as opposed to standard side sheets, which are displayed above the
-screen content.
+## Code implementation
-Note: Coplanar side sheets are not recommended for narrow screens.
-
-### Coplanar side sheet example
-
-To add a coplanar side sheet to your app, follow the steps to [add a standard
-side sheet to your layout](#standard-side-sheet-example), above, and simply set
-`app:coplanarSiblingViewId` on the `View` that has the side sheet
-`layout_behavior` set on it. You can point the coplanar sibling view id to any
-child of the `CoordinatorLayout`. Make sure to set
-`style="@style/Widget.Material3.SideSheet"` on your side sheet view, as well.
+Before you can use Material side sheets, you need to add a dependency to the
+Material components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
-Alternatively, you can set the coplanar sibling view programmatically:
+### Adding side sheets
-```kt
-coplanarSideSheet.setCoplanarSiblingView(coplanarSiblingView)
-```
+Note: Side sheets were introduced in `1.8.0`. To use side sheets, make sure
+you're depending on
+[library version `1.8.0`](https://github.com/material-components/material-components-android/releases/tag/1.8.0)
+or later.
-To remove coplanar functionality from a side sheet, simply set the coplanar
-sibling view to `null`:
+**Standard side sheet basic usage:**
-```kt
-coplanarSideSheet.setCoplanarSiblingView(null)
-```
+```xml
+
-## Anatomy and key properties
+
-Side sheets have a sheet, content, and, if modal, a scrim.
+
-1. Sheet
-2. Content
-3. Scrim (in modal side sheets)
+
-### Sheet attributes
+
+```
-Element | Attribute | Related method(s) | Default value
----------------------------- | --------------------------- | ------------------------------------------------------- | -------------
-**Color** | `app:backgroundTint` | N/A | `?attr/colorSurface``?attr/colorSurfaceContainerLow` (modal)
-**Coplanar sibling view id** | `app:coplanarSiblingViewId` | `setCoplanarSiblingViewId` `setCoplanarSiblingView` | N/A
-**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceLargeComponent`
-**Sheet edge** | `android:layout_gravity` | `setSheetEdge` (modal only) | end
-**Elevation** | `android:elevation` | N/A | 0dp
-**Max width** | `android:maxWidth` | `setMaxWidth` `getMaxWidth` | N/A
-**Max height** | `android:maxHeight` | `setMaxHeight` `getMaxHeight` | N/A
+### Listening to state and slide changes
-### Behavior attributes
+`SideSheetCallback`s can be added to a `SideSheetBehavior` to listen for state
+and slide changes:
-More info about these attributes and how to use them in the
-[setting behavior](#setting-behavior) section.
+```kt
+val sideSheetCallback = object : SideSheetBehavior.SideSheetCallback() {
-Behavior | Related method(s) | Default value
-------------------------------------------- | ------------------------------------------------------------------------- | -------------
-`app:behavior_draggable` | `setDraggable` `isDraggable` | `true`
+ override fun onStateChanged(sideSheet: View, newState: Int) {
+ // Do something for new state.
+ }
-### Sheet edge
-You can set the sheet to originate from the left or right side of the screen.
-You can also automatically switch the sheet edge based on layout direction, by
-setting the sheet edge to `start` or `end`.
+ override fun onSlide(sideSheet: View, slideOffset: Float) {
+ // Do something for slide offset.
+ }
+}
-#### Standard and Coplanar Sheets
-To set a standard or coplanar sheet's edge, set the `gravity` property of the
-side sheet `View`'s `CoordinatorLayout.LayoutParams`, then request a layout pass
-on the side sheet `View`.
+// To add a callback:
+sideSheetBehavior.addCallback(sideSheetCallback)
-```kt
-val layoutParams = sideSheetView.layoutParams
-if (layoutParams is CoordinatorLayout.LayoutParams) {
- layoutParams.gravity = sheetGravity
- sideSheetView.requestLayout()
-}
+// To remove a callback:
+sideSheetBehavior.removeCallback(sideSheetCallback)
```
-You can also set the sheet edge with XML, by setting `android:layout_gravity`
-to the desired gravity:
+
Setting behavior
-```xml
-
-
-
-```
+There are several attributes that can be used to adjust the behavior of standard
+and modal side sheets.
-#### Modal Sheets
-To set a modal sheet's edge, pass a `Gravity` constant into `SideSheetDialog`'s
-dedicated `setSheetEdge` method. For example, set the sheet edge to `start` like
-this:
+Behavior attributes can be applied to standard side sheets in xml by setting
+them on a child `View` set to `app:layout_behavior`, or programmatically:
```kt
-sideSheetDialog.setSheetEdge(Gravity.START)
+val standardSideSheetBehavior = SideSheetBehavior.from(standardSideSheet)
+// Use this to programmatically apply behavior attributes
```
-Note: Runtime changes to sheet edges are not supported for modal sheets and may
-not work as expected. If you'd like to change the sheet edge at runtime, you
-should recreate the sheet, then call `setSheetEdge` with the new gravity.
-
-### Styles
-
-**Element** | **Value**
-------------------------- | -----------------------------------------
-Standard side sheet style | `@style/Widget.Material3.SideSheet`
-Modal side sheet style | `@style/Widget.Material3.SideSheet.Modal`
+More information about these attributes and their default values is available in
+the [behavior attributes](#behavior-attributes) section.
-**Default style theme attribute:`?attr/sideSheetModalStyle`**
+### Setting state
-Note: There is no default style theme attribute for standard side sheets,
-because `SideSheetBehavior`s don't have a designated associated `View`. Modal
-side sheets use `?attr/sideSheetModalStyle` as the default style, but there is
-no need to set `?attr/sideSheetModalStyle` on your modal side sheet layout
-because the style is automatically applied to the parent `SideSheetDialog`.
+Standard side sheets have the following states:
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/res/values/styles.xml),
-[attributes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/res/values/attrs.xml),
-and
-[themes and theme overlays](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/res/values/themes.xml).
+* `STATE_EXPANDED`: The side sheet is visible at its maximum height and it is
+ neither dragging nor settling (see below).
+* `STATE_HIDDEN`: The side sheet is no longer visible and can only be re-shown
+ programmatically.
+* `STATE_DRAGGING`: The user is actively dragging the side sheet.
+* `STATE_SETTLING`: The side sheet is settling to a specific height after a
+ drag/swipe gesture. This will be the peek height, expanded height, or 0, in
+ case the user action caused the side sheet to hide.
-## Predictive Back
+You can set a state on the side sheet:
-### Modal Side Sheets
+```kt
+sideSheetBehavior.state = Sheet.STATE_HIDDEN
+```
-The modal `SideSheetDialog` component automatically supports
-[Predictive Back](../foundations/PredictiveBack.md). No further integration is
-required on the app side other than the general Predictive Back prerequisites
-and migration steps mentioned [here](../foundations/PredictiveBack.md#usage).
+**Note:** `STATE_SETTLING` and `STATE_DRAGGING` should not be set
+programmatically.
-Visit the
-[Predictive Back design guidelines](https://m3.material.io/components/side-sheets/guidelines#d77245e3-1013-48f8-a9d7-76f484e1be13)
-to see how the component behaves when a user swipes back.
+### Predictive back
-### Standard and Coplanar (Non-Modal) Side Sheets
+#### Standard and coplanar (non-modal) side sheets
-To set up Predictive Back for standard or coplanar (non-modal) side sheets using
+To set up predictive back for standard or coplanar (non-modal) side sheets using
`SideSheetBehavior`, create an AndroidX back callback that forwards
`BackEventCompat` objects to your `SideSheetBehavior`:
@@ -409,13 +425,34 @@ sideSheetBehavior.addCallback(object : SideSheetCallback() {
})
```
-## Theming side sheets
+#### Modal side sheets
+
+The modal `SideSheetDialog` component automatically supports
+[predictive back](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md).
+No further integration is required on the app side other than the general
+predictive back prerequisites and migration steps mentioned
+[here](/third_party/java_src/android_libs/material_components/docs/foundations/PredictiveBack.md#usage).
-Side sheets support
-[Material Theming](https://material.io/components/sheets-side#theming), which
-supports customization of color, shape and more.
+Visit the
+[predictive back design guidelines](https://m3.material.io/components/side-sheets/guidelines#d77245e3-1013-48f8-a9d7-76f484e1be13)
+to see how the component behaves when a user swipes back.
-### Side sheet theming example
+## Customizing side sheets
+
+### Theming side sheets
+
+Side sheets support the customization of color, shape, and more.
+
+#### Side sheet theming example
+
+API and source code:
+
+* `SideSheetBehavior`
+ * [Class definition](https://developer.android.com/reference/com/google/android/material/sidesheet/SideSheetBehavior)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java)
+* `SideSheetDialog`
+ * [Class definition](https://developer.android.com/reference/com/google/android/material/sidesheet/SideSheetDialog)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/SideSheetDialog.java)
Setting the theme attribute `sideSheetDialogTheme` to your custom `ThemeOverlay`
will affect all side sheets.
@@ -438,10 +475,10 @@ In `res/values/styles.xml`:
```xml
-
@@ -455,12 +492,3 @@ long as they're not overridden in your custom theme overlay. If you use a custom
exactly what attributes are included in each, but it also means you'll have to
duplicate any changes that you've made in your main theme into your custom
theme.
-
-API and source code:
-
-* `SideSheetBehavior`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/sidesheet/SideSheetBehavior)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java)
-* `SideSheetDialog`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/sidesheet/SideSheetDialog)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/sidesheet/SideSheetDialog.java)
diff --git a/docs/components/Slider.md b/docs/components/Slider.md
index dc194cb0b1e..225219bd4b3 100644
--- a/docs/components/Slider.md
+++ b/docs/components/Slider.md
@@ -292,29 +292,31 @@ slider also has tick marks.
#### Track attributes
-| Element | Attribute | Related method(s) | Default value |
-|--------------------------------------------|------------------------------|-------------------------------------------------------------|--------------------------------------|
-| **Orientation** | `android:orientation` | `setOrientation` `isVertical` | `horizontal` |
-| **Min value** | `android:valueFrom` | `setValueFrom` `getValueFrom` | N/A |
-| **Max value** | `android:valueTo` | `setValueTo` `getValueTo` | N/A |
-| **Step size (discrete)** | `android:stepSize` | `setStepSize` `getStepSize` | N/A |
-| **Initial selected value (Slider)** | `android:value` | `setValue` `getValue` | N/A |
-| **Initial selected values (RangeSlider)** | `app:values` | `setValues` `getValues` | N/A |
-| **Height** | `app:trackHeight` | `setTrackHeight` `getTrackHeight` | `16dp` |
-| **Color** | `app:trackColor` | `setTrackTintList` `getTrackTintList` | `null` |
-| **Color for track's active part** | `app:trackColorActive` | `setTrackActiveTintList` `getTrackActiveTintList` | `?attr/colorPrimary` |
-| **Color for track's inactive part** | `app:trackColorInactive` | `setTrackInactiveTintList` `getTrackInactiveTintList` | `?attr/colorSurfaceContainerHighest` |
-| **Corner size** | `app:trackCornerSize` | `setTrackCornerSize` `getTrackCornerSize` | `trackHeight / 2` |
-| **Inside corner size** | `app:trackInsideCornerSize` | `setTrackInsideCornerSize` `getTrackInsideCornerSize` | `2dp` |
-| **Stop indicator size** | `app:trackStopIndicatorSize` | `setTrackStopIndicatorSize` `getTrackStopIndicatorSize` | `4dp` |
-| **Minimum separation for adjacent thumbs** | `app:minSeparation` | `setMinSeparation` `getMinSeparation` | `0dp` |
-| **Active start icon** | `app:trackIconActiveStart` | `setTrackIconActiveStart` `getTrackIconActiveStart` | `null` |
-| **Active end icon** | `app:trackIconActiveEnd` | `setTrackIconActiveEnd` `getTrackIconActiveEnd` | `null` |
-| **Active icon color** | `app:trackIconActiveColor` | `setTrackIconActiveColor` `getTrackIconActiveColor` | N/A |
-| **Inactive start icon** | `app:trackIconInactiveStart` | `setTrackIconInactiveStart` `getTrackIconInactiveStart` | `null` |
-| **Inactive end icon** | `app:trackIconInactiveEnd` | `setTrackIconInactiveEnd` `getTrackIconInactiveEnd` | `null` |
-| **Inactive icon color** | `app:trackIconInactiveColor` | `setTrackIconInactiveColor` `getTrackIconInactiveColor` | N/A |
-| **Icon size** | `app:trackIconSize` | `setTrackIconSize` `getTrackIconSize` | N/A |
+| Element | Attribute | Related method(s) | Default value |
+|--------------------------------------------|-------------------------------|----------------------------------------------------------------|--------------------------------------|
+| **Orientation** | `android:orientation` | `setOrientation` `isVertical` | `horizontal` |
+| **Min value** | `android:valueFrom` | `setValueFrom` `getValueFrom` | N/A |
+| **Max value** | `android:valueTo` | `setValueTo` `getValueTo` | N/A |
+| **Step size (discrete)** | `android:stepSize` | `setStepSize` `getStepSize` | N/A |
+| **Initial selected value (Slider)** | `android:value` | `setValue` `getValue` | N/A |
+| **Initial selected values (RangeSlider)** | `app:values` | `setValues` `getValues` | N/A |
+| **Centered** | `app:centered` | `setCentered` `isCentered` | `false` |
+| **Continuous mode tick count** | `app:continuousModeTickCount` | `setContinuousModeTickCount` `getContinuousModeTickCount` | 0 |
+| **Height** | `app:trackHeight` | `setTrackHeight` `getTrackHeight` | `16dp` |
+| **Color** | `app:trackColor` | `setTrackTintList` `getTrackTintList` | `null` |
+| **Color for track's active part** | `app:trackColorActive` | `setTrackActiveTintList` `getTrackActiveTintList` | `?attr/colorPrimary` |
+| **Color for track's inactive part** | `app:trackColorInactive` | `setTrackInactiveTintList` `getTrackInactiveTintList` | `?attr/colorSurfaceContainerHighest` |
+| **Corner size** | `app:trackCornerSize` | `setTrackCornerSize` `getTrackCornerSize` | `trackHeight / 2` |
+| **Inside corner size** | `app:trackInsideCornerSize` | `setTrackInsideCornerSize` `getTrackInsideCornerSize` | `2dp` |
+| **Stop indicator size** | `app:trackStopIndicatorSize` | `setTrackStopIndicatorSize` `getTrackStopIndicatorSize` | `4dp` |
+| **Minimum separation for adjacent thumbs** | `app:minSeparation` | `setMinSeparation` `getMinSeparation` | `0dp` |
+| **Active start icon** | `app:trackIconActiveStart` | `setTrackIconActiveStart` `getTrackIconActiveStart` | `null` |
+| **Active end icon** | `app:trackIconActiveEnd` | `setTrackIconActiveEnd` `getTrackIconActiveEnd` | `null` |
+| **Active icon color** | `app:trackIconActiveColor` | `setTrackIconActiveColor` `getTrackIconActiveColor` | N/A |
+| **Inactive start icon** | `app:trackIconInactiveStart` | `setTrackIconInactiveStart` `getTrackIconInactiveStart` | `null` |
+| **Inactive end icon** | `app:trackIconInactiveEnd` | `setTrackIconInactiveEnd` `getTrackIconInactiveEnd` | `null` |
+| **Inactive icon color** | `app:trackIconInactiveColor` | `setTrackIconInactiveColor` `getTrackIconInactiveColor` | N/A |
+| **Icon size** | `app:trackIconSize` | `setTrackIconSize` `getTrackIconSize` | N/A |
**Note:** `app:trackColor` takes precedence over `app:trackColorActive` and
`app:trackColorInative`. It's a shorthand for setting both values to the same
@@ -364,12 +366,15 @@ Element | Attribute | Related method(s)
| **Color for tick's inactive part** | `app:tickColorInactive` | `setTickInactiveTintList` `getTickInactiveTintList` | `?attr/colorPrimary` |
| **Radius for tick's active part** | `app:tickRadiusActive` | `setTickActiveRadius` `getTickActiveRadius` | `null` (1/2 trackStopIndicatorSize) |
| **Radius for tick's inactive part** | `app:tickRadiusInactive` | `setTickInactiveRadius` `getTickInactiveRadius` | `null` (1/2 trackStopIndicatorSize) |
-| **Tick visible** | `app:tickVisible` | `setTickVisible` `isTickVisible()` | `true` |
+| **Tick visible** (deprecated) | `app:tickVisible` | `setTickVisible` `isTickVisible()` | `true` |
+| **Tick visibility mode** | `app:tickVisibilityMode` | `setTickVisibilityMode` `getTickVisibilityMode()` | `autoLimit` |
**Note:** `app:tickColor` takes precedence over `app:tickColorActive` and
-`app:tickColorInative`. It's a shorthand for setting both values to the same
+`app:tickColorInactive`. It's a shorthand for setting both values to the same
thing.
+**Note:** `app:tickVisible` is deprecated in favor of `app:tickVisibilityMode`.
+
#### Styles
Element | Style
diff --git a/docs/components/Snackbar.md b/docs/components/Snackbar.md
index 86b809ca954..c63d6954af0 100644
--- a/docs/components/Snackbar.md
+++ b/docs/components/Snackbar.md
@@ -1,5 +1,5 @@
-# Snackbars
+# Snackbar
-[Snackbars](https://material.io/components/snackbars) provide brief messages
+[Snackbars](https://m3.material.io/components/snackbar/overview) provide brief messages
about app processes at the bottom of the screen.
-
+
-**Contents**
+Snackbars inform users of a process that an app has performed or will perform.
+They appear temporarily, towards the bottom of the screen. They shouldn’t
+interrupt the user experience, and they don’t require user input to disappear.
+They disappear either after a timeout or after a user interaction elsewhere on
+the screen, but can also be swiped off the screen.
+
+Snackbars can also offer the ability to perform an action, such as undoing an
+action that was just taken, or retrying an action that had failed.
+
+**Note:** Images use various dynamic color schemes.
+
+## Design & API documentation
+
+* [Material 3 (M3) spec](https://m3.material.io/components/snackbar/overview/)
+* [API reference](https://developer.android.com/reference/com/google/android/material/snackbar/package-summary)
+
+## Anatomy
+
+
+
+1. Container
+2. Supporting text
+3. Action (optional)
+4. Close button (optional)
+
+More details on anatomy items in the [component guidelines](https://m3.material.io/components/snackbar/guidelines#fea592b7-eba4-4d65-88fe-34b64e884041).
+
+## Key properties
+
+### Text label attributes
+
+Element | Attribute | Related method(s) | Default value
+-------------------- | ------------------------ | ----------------- | -------------
+**Text label style** | N/A | N/A | `?attr/snackbarTextViewStyle`
+**Text label** | `android:text` | `setText` | `null`
+**Color** | `android:textColor` | `setTextColor` | `?attr/colorOnSurfaceInverse`
+**Typography** | `android:textAppearance` | N/A | `?attr/textAppearanceBodyMedium`
+
+### Container attributes
+
+Element | Attribute | Related method(s) | Default value
+----------------------- | ------------------------------------------------------ | ----------------------------------------------- | -------------
+**Color** | `app:backgroundTint` | `setBackgroundTint` `setBackgroundTintList` | `?attr/colorSurfaceInverse`
+**Color overlay alpha** | `app:backgroundOverlayColorAlpha` | N/A | `0.8f` (ignored if `app:backgroundTint` is set)
+**Shape** | `app:shapeAppearance` `app:shapeAppearanceOverlay` | N/A | `?attr/shapeAppearanceCornerExtraSmall`
+**Margin** | `android:layout_margin` | N/A | `8dp`
+**Elevation** | `app:elevation` | N/A | `6dp`
+**Animation mode** | `app:animationMode` | `setAnimationMode` `getAnimationMode` | `fade`
+
+### Action attributes
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using snackbars](#using-snackbars)
-* [Snackbar](#snackbar)
-* [Theming snackbars](#theming-snackbars)
+Element | Attribute | Related method(s) | Default value
+-------------------- | -------------------------- | -------------------- | -------------
+**Button style** | N/A | N/A | `?attr/snackbarButtonStyle`
+**Text color alpha** | `app:actionTextColorAlpha` | N/A | `1.0f`
+**Text Color** | `android:textColor` | `setTextActionColor` | `?attr/colorPrimaryInverse`
-## Design and API Documentation
+### Styles
-* [Google Material3 Spec](https://material.io/components/snackbar/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/snackbar/package-summary)
+Element | Theme attribute | Default value
+----------------------- | ----------------------------- | -----------------
+**Default style** | `?attr/snackbarStyle` | `@style/Widget.Material3.Snackbar`
+**Action button style** | `?attr/snackbarButtonStyle` | `@style/Widget.Material3.Button.TextButton.Snackbar`
+**Text label style** | `?attr/snackbarTextViewStyle` | `@style/Widget.Material3.Snackbar.TextView`
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/snackbar/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/snackbar/res/values/attrs.xml).
-## Using snackbars
+## Code implementation
Before you can use Material snackbars, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
+Material components for Android library. For more information, go to the
[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
page.
+### Adding snackbar
+
The `Snackbar` class provides static `make` methods to produce a snackbar
configured in the desired way. These methods take a `View`, which will be used
to find a suitable ancestor `ViewGroup` to display the snackbar, a text string
@@ -53,14 +111,27 @@ Available duration presets are:
[CoordinatorLayout](https://developer.android.com/reference/androidx/coordinatorlayout/widget/CoordinatorLayout),
which allows the snackbar to enable behavior like swipe-to-dismiss.
-### Making snackbars accessible
+The following is an example of a snackbar with an action button:
+
+
+
+In code:
+
+```kt
+Snackbar.make(contextView, "Text label", Snackbar.LENGTH_LONG)
+ .setAction("Action") {
+ // Responds to click on the action
+ }
+ .show()
+```
+### Making snackbar accessible
Snackbars support content labeling for accessibility and are readable by most
screen readers, such as TalkBack. Text rendered in snackbars is automatically
provided to accessibility services. Additional content labels are usually
unnecessary.
-### Showing a snackbar
+### Showing snackbar
Calling `make` creates the snackbar, but doesn't cause it to be visible on the
screen. To show it, use the `show` method on the returned `Snackbar` instance.
@@ -80,23 +151,7 @@ val contextView = findViewById(R.id.context_view)
Snackbar.make(contextView, R.string.text_label, Snackbar.LENGTH_SHORT)
.show()
```
-
-### Adding an action
-
-To add an action, use the `setAction` method on the object returned from `make`.
-Snackbars are automatically dismissed when the action is clicked.
-
-To show a snackbar with a message and an action:
-
-```kt
-Snackbar.make(contextView, R.string.text_label, Snackbar.LENGTH_LONG)
- .setAction(R.string.action_text) {
- // Responds to click on the action
- }
- .show()
-```
-
-### Anchoring a snackbar
+### Anchoring snackbar
By default, `Snackbar`s will be anchored to the bottom edge of their parent
view. However, you can use the `setAnchorView` method to make a `Snackbar`
@@ -126,97 +181,28 @@ Generally, snackbars are the preferred mechanism for displaying feedback
messages to users, because they can be displayed in the context of the UI where
the action occurred. Reserve `Toast` for cases where this cannot be done.
-## Snackbar
-
-Snackbars inform users of a process that an app has performed or will perform.
-They appear temporarily, towards the bottom of the screen. They shouldn’t
-interrupt the user experience, and they don’t require user input to disappear.
-They disappear either after a timeout or after a user interaction elsewhere on
-the screen, but can also be swiped off the screen.
-
-Snackbars can also offer the ability to perform an action, such as undoing an
-action that was just taken, or retrying an action that had failed.
-
-### Snackbars example
-
-API and source code:
-
-* `Snackbar`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/snackbar/Snackbar)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/snackbar/Snackbar.java)
-
-The following is an example of a snackbar with an action button:
+### Adding an action
-
+To add an action, use the `setAction` method on the object returned from `make`.
+Snackbars are automatically dismissed when the action is clicked.
-In code:
+To show a snackbar with a message and an action:
```kt
-Snackbar.make(contextView, "Text label", Snackbar.LENGTH_LONG)
- .setAction("Action") {
+Snackbar.make(contextView, R.string.text_label, Snackbar.LENGTH_LONG)
+ .setAction(R.string.action_text) {
// Responds to click on the action
}
.show()
```
-## Anatomy and key properties
-
-The following is an anatomy diagram of a snackbar:
-
-
-
-1. Text label
-1. Container
-1. Action (optional)
-
-### Text label attributes
-
-Element | Attribute | Related method(s) | Default value
--------------------- | ------------------------ | ----------------- | -------------
-**Text label style** | N/A | N/A | `?attr/snackbarTextViewStyle`
-**Text label** | `android:text` | `setText` | `null`
-**Color** | `android:textColor` | `setTextColor` | `?attr/colorOnSurfaceInverse`
-**Typography** | `android:textAppearance` | N/A | `?attr/textAppearanceBodyMedium`
-
-### Container attributes
-
-Element | Attribute | Related method(s) | Default value
------------------------ | ------------------------------------------------------ | ----------------------------------------------- | -------------
-**Color** | `app:backgroundTint` | `setBackgroundTint` `setBackgroundTintList` | `?attr/colorSurfaceInverse`
-**Color overlay alpha** | `app:backgroundOverlayColorAlpha` | N/A | `0.8f` (ignored if `app:backgroundTint` is set)
-**Shape** | `app:shapeAppearance` `app:shapeAppearanceOverlay` | N/A | `?attr/shapeAppearanceCornerExtraSmall`
-**Margin** | `android:layout_margin` | N/A | `8dp`
-**Elevation** | `app:elevation` | N/A | `6dp`
-**Animation mode** | `app:animationMode` | `setAnimationMode` `getAnimationMode` | `fade`
-
-### Action attributes
-
-Element | Attribute | Related method(s) | Default value
--------------------- | -------------------------- | -------------------- | -------------
-**Button style** | N/A | N/A | `?attr/snackbarButtonStyle`
-**Text color alpha** | `app:actionTextColorAlpha` | N/A | `1.0f`
-**Text Color** | `android:textColor` | `setTextActionColor` | `?attr/colorPrimaryInverse`
-
-### Styles
-
-Element | **Theme attribute** | **Default value**
------------------------ | ----------------------------- | -----------------
-**Default style** | `?attr/snackbarStyle` | `@style/Widget.Material3.Snackbar`
-**Action button style** | `?attr/snackbarButtonStyle` | `@style/Widget.Material3.Button.TextButton.Snackbar`
-**Text label style** | `?attr/snackbarTextViewStyle` | `@style/Widget.Material3.Snackbar.TextView`
-
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/snackbar/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/snackbar/res/values/attrs.xml).
+## Customizing snackbar
-## Theming snackbars
+### Theming snackbar
-Snackbars support
-[Material Theming](https://material.io/design/material-theming/overview.html#using-material-theming)
-which can customize color and typography.
+Snackbars support the customization of color and typography.
-### Snackbar theming example
+#### Snackbar theming example
API and source code:
@@ -225,13 +211,13 @@ API and source code:
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/snackbar/Snackbar.java)
The following is an example of a snackbar with an action button that uses the
-Material.io [Shrine](https://material.io/design/material-studies/shrine.html)
+Material.io [Shrine](https://m2.material.io/design/material-studies/shrine.html)
color theming:
-
+
-#### Implementing snackbar theming
+##### Implementing snackbar theming
Use theme attributes in `res/values/styles.xml` to style all snackbars. This
will affect other components:
diff --git a/docs/components/SplitButton.md b/docs/components/SplitButton.md
new file mode 100644
index 00000000000..3e1cf70fcd3
--- /dev/null
+++ b/docs/components/SplitButton.md
@@ -0,0 +1,269 @@
+
+
+# Split button
+
+[Split buttons](https://m3.material.io/components/split-button/overview) open a
+menu to give people more options related to an action. It is a specialized type
+of the connected button group. The trailing button is checkable with an animated
+icon.
+
+
+
+**Note:** Images use various dynamic color schemes.
+
+## Design & API documentation
+
+* [Material 3 (M3) spec](https://m3.material.io/components/split-button/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/button/package-summary)
+
+## Anatomy
+
+
+
+1. Leading button
+2. Icon
+3. Label text
+4. Trailing button
+
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/split-button/guidelines#551f6e11-0f95-41c2-9398-cd4066755806).
+
+## M3 Expressive update
+
+Before you can use `Material3Expressive` component styles, follow the
+[`Material3Expressive` themes setup instructions](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md#material3expressive-themes).
+
+
+Split buttons have the same five recommended sizes as label and icon buttons
+
+The split button has a separate menu button that spins and changes shape when
+activated. It can be used alongside other buttons of the same size.
+[More on M3 Expressive](https://m3.material.io/blog/building-with-m3-expressive)
+
+New component added to catalog.
+
+**Sizes:**
+
+* Extra small
+* Small
+* Medium
+* Large
+* Extra large
+
+**Color styles:**
+
+* Elevated
+* Filled
+* Tonal
+* Outlined
+
+## Key properties
+
+### Size and space attributes
+
+Element | Attribute | Related method(s) | Default value
+--------------------------- | ---------------------- | --------------------------------------------- | -------------
+**Size of inner corners** | `app:innerCornerSize` | `setInnerCornerSize` `getInnerCornerSize` | `none`
+**Spacing between buttons** | `android:spacing` | `setSpacing` `getSpacing` | `2dp`
+**Button size change** | `app:buttonSizeChange` | N/A | `0%`
+
+### Styles and theme attributes
+
+Element | Style | Theme attribute
+----------------------------------------- | --------------------------------------------------------- | ---------------
+**Default style** | `Widget.Material3.MaterialSplitButton` | `?attr/materialSplitButtonStyle`
+**Leading button primary (filled) style** | `Widget.Material3.SplitButton.LeadingButton.Filled` | `?attr/materialSplitButtonLeadingFilledStyle`
+**Leading button primary (tonal) style** | `Widget.Material3.SplitButton.LeadingButton.Filled.Tonal` | `?attr/materialSplitButtonLeadingFilledTonalStyle`
+**Trailing icon primary (filled) style** | `Widget.Material3.SplitButton.IconButton.Filled` | `?attr/materialSplitButtonIconFilledStyle`
+**Trailing icon secondary (tonal) style** | `Widget.Material3.SplitButton.IconButton.Filled.Tonal` | `?attr/materialSplitButtonIconFilledTonalStyle`
+
+The two new trailing icon styles `materialSplitButtonIconFilledStyle` – parented
+by `materialIconButtonFilledStyle` – and
+`materialSplitButtonIconFilledTonalStyle`-- parented by
+`materialIconButtonFilledTonalStyle` allow for the `MaterialSplitButton` custom
+behavior for `onChecked` and `onUnchecked` states.
+`materialSplitButtonIconFilledStyle` is paired with default leading button
+styling, with no style explicitly specified in the XML.
+`materialSplitButtonIconFilledTonalStyle` is paired with
+`materialButtonTonalStyle` for the leading button.
+
+## Code implementation
+
+Before you can use Material buttons, you need to add a dependency to the
+Material components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
+
+**Note:** `` is auto-inflated as
+`` via
+`MaterialComponentsViewInflater` when using a `Theme.Material3.*` theme.
+
+The leading button in split buttons can have an icon, label text, or both. The
+trailing button should always have a menu icon.
+
+
+1. Label + icon
+2. Label
+3. Icon
+
+### Adding split button
+
+Source code:
+
+* `MaterialSplitButton`
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/MaterialSplitButton.java)
+
+The following example shows a split button with a leading label button and a
+trailing icon button that has an AnimatedVectorDrawable.
+
+In the layout:
+
+```xml
+
+
+
+
+```
+
+### Making buttons accessible
+
+Buttons support content labeling for accessibility and are readable by most
+screen readers, such as TalkBack. Text rendered in buttons is automatically
+provided to accessibility services. Additional content labels are usually
+unnecessary.
+
+For more information on content labels, go to the
+[Android accessibility help guide](https://support.google.com/accessibility/android/answer/7158690).
+
+### Animating trailing icon
+
+The icon on the trailing button is animated. In the samples, this is done with
+an AVD, `m3_split_button_chevron_avd`
+[[source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/res/drawable/m3_split_button_chevron_avd.xml)].
+
+## Customizing split button
+
+### Theming buttons
+
+Buttons support the customization of color, typography, and shape.
+
+#### Button theming example
+
+API and source code:
+
+* `MaterialButton`
+ * [Class description](https://developer.android.com/reference/com/google/android/material/button/MaterialButton)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/MaterialButton.java)
+
+The following example shows text, outlined and filled button types with Material
+theming.
+
+
+
+##### Implementing button theming
+
+Use theme attributes and styles in `res/values/styles.xml` to add the theme to
+all buttons. This affects other components:
+
+```xml
+
+
+
+```
+
+Use default style theme attributes, styles and theme overlays. This adds the
+theme to all buttons but does not affect other components:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Use one of the styles in the layout. That will affect only this button:
+
+```xml
+
+
+```
+
+### Optical centering
+
+Optical centering means to offset the `MaterialButton`’s contents (icon and/or
+label) when the shape is asymmetric. Before optical centering, we only provided
+centering with horizontally asymmetrical shapes.
+
+To turn on optical centering for a given button, use
+`setOpticalCenterEnabled(true)`. Optical centering is disabled by default. When
+enabled, the shift amount of the icon and/or text is calculated as a value with
+the fixed ratio to the difference between left corner size in dp and right
+corner size in dp. The shift amount is applied to the padding start and padding
+end.
diff --git a/docs/components/Switch.md b/docs/components/Switch.md
index 6a8bd5377c3..b6d5efb52d2 100644
--- a/docs/components/Switch.md
+++ b/docs/components/Switch.md
@@ -1,5 +1,5 @@
-# Selection controls: switches
-
-[Selection controls](https://material.io/components/selection-controls#usage)
-allow the user to select options.
+# Switch
[Switches](https://m3.material.io/components/switch/overview) toggle the state
-of a single setting on or off. They are the preferred way to adjust settings on
-mobile devices.
+of a single setting on or off.

-**Contents**
+**Note:** Images use various dynamic color schemes.
+
+Switches are best used to adjust settings and other standalone options. They
+make a binary selection, like on and off or true and false.
+
+The effects of a switch should start immediately, without needing to save.
+
+## Design & API documentation
+
+* [Material 3 (M3) spec](https://m3.material.io/components/switch/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/switchmaterial/package-summary)
+
+## Anatomy
+
+
+
+1. Track
+2. Handle (formerly "thumb")
+3. Icon
+
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/switch/guidelines#9f55e13e-1327-4edf-9b81-6fa97db45bdd).
+
+## Key properties
+
+### Switch attributes
+
+Element | Attribute | Related method(s) | Default value
+-------------- | ------------------- | --------------------------------- | -------------
+**Min height** | `android:minHeight` | `setMinHeight` `getMinHeight` | `?attr/minTouchTargetSize`
+
+### Thumb attributes
+
+Element | Attribute | Related method(s) | Default value
+--------- | --------------- | ----------------------------------------- | -------------
+**Thumb** | `android:thumb` | `setThumbDrawable` `getThumbDrawable` | `@drawable/mtrl_switch_thumb`
+**Color** | `app:thumbTint` | `setThumbTintList` `getThumbTintList` | `?attr/colorOutline` (unchecked) `?attr/colorOnPrimary` (checked)
+
+### Icon attributes
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using switches](#using-switches)
-* [Switch](#switch)
-* [Theming switches](#theming-switches)
+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` `getThumbIconDrawable` | `null`
+**Size** | `app:thumbIconSize` | `setThumbIconSize` `getThumbIconSize` | `16dp`
+**Color** | `app:thumbIconTint` | `setThumbIconTintList` `getThumbIconTintList` | `?attr/colorSurfaceContainerHighest` (unchecked) `?attr/colorOnPrimaryContainer` (checked)
-## Design and API Documentation
+### Track attributes
-* [Google Material3 Spec](https://material.io/components/switch/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/switchmaterial/package-summary)
+Element | Attribute | Related method(s) | Default value
+-------------------- | ------------------------- | ------------------------------------------------------------- | -------------
+**Track** | `app:track` | `setTrackDrawable` `getTrackDrawable` | `@drawable/mtrl_switch_track`
+**Color** | `app:trackTint` | `setTrackTintList` `getTrackTintList` | `?attr/colorSurfaceContainerHighest` (unchecked) `?attr/colorPrimary` (checked)
+**Decoration** | `app:trackDecoration` | `setTrackDecorationDrawable` `getTrackDecorationDrawable` | `@drawable/mtrl_switch_track_decoration` (Shows an outline of the track.)
+**Decoration color** | `app:trackDecorationTint` | `setTrackDecorationTintList` `getTrackDecorationTintList` | `?attr/colorOutline` (unchecked) `@android:color/transparent` (checked)
-## Using switches
+### Text label attributes
+
+Element | Attribute | Related method(s) | Default value
+-------------- | ------------------------ | ----------------------------------------- | -------------
+**Text label** | `android:text` | `setText` `getText` | `null`
+**Color** | `android:textColor` | `setTextColor` `getTextColors` | `?android:attr/textColorPrimaryDisableOnly`
+**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyMedium`
+**Padding** | `app:switchPadding` | `setSwitchPadding` `getSwitchPadding` | `16dp`
+
+### Switch states
+
+Switches can be on or off. Switches have enabled, hover, focused, and pressed
+states.
+
+
+
+### Styles
+
+Element | Style | Theme attribute
+----------------- | ------------------------------------------------ | ---------------
+**Default style** | `Widget.Material3.CompoundButton.MaterialSwitch` | `?attr/materialSwitchStyle`
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/materialswitch/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/materialswitch/res/values/attrs.xml).
+
+## Code implementation
Before you can use Material switches, you need to add a dependency on the
-Material Components for Android library. For more information, go to the
+Material components for Android library. For more information, go to the
[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
page.
@@ -44,46 +115,25 @@ Material Design's switch component. It extends from the support library's
does not auto-inflate, unlike other selection controls, and must be explicitly
specified in layouts.
-Use switches to:
-
-* Toggle a single item on or off, on mobile and tablet
-* Immediately activate or deactivate something
-
-### Making switches accessible
-
-Switches support content labeling for accessibility and are readable by most
-screen readers, such as TalkBack. Text rendered in switches is automatically
-provided to accessibility services. Additional content labels are usually
-unnecessary.
+**Note:** For the old `SwitchMaterial` documentation, please refer to
+[Switch (deprecated)](#switch-deprecated) and
+[Theming switch (deprecated)](#theming-switch-deprecated).
-## Switch
+### Adding switch
A `Switch` represents a button with two states, on and off. Switches are most
often used on mobile devices to enable and disable options in an options menu. A
switch consists of a track and thumb; the thumb moves along the track to
indicate its current state.
-API and source code:
-
-* `MaterialSwitch`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/materialswitch/MaterialSwitch)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/materialswitch/MaterialSwitch.java)
-
-**Note:** Since version 1.7.0, the new `MaterialSwitch` class will replace the
-obsolete `SwitchMaterial` class. In most cases you should be able to just
-replace all `SwitchMaterial` class reference with `MaterialSwitch` to achieve
-the default look and feel. Please refer to the following sections if you need to
-customize the new styles.
-
-**Note:** For the old `SwitchMaterial` documentation, please refer to
-[Switch (deprecated)](#switch-deprecated) and
-[Theming switches (deprecated)](#theming-switches-deprecated).
+Use switches to:
-### Switches example
+* Toggle a single item on or off
+* Immediately activate or deactivate something
The following example shows a list of five switches.
-
+
In the layout:
@@ -124,94 +174,38 @@ materialSwitch.setOnCheckedChangeListener { buttonView, isChecked
}
```
-## Anatomy and key properties
-
-The following is an anatomy diagram that shows a switch thumb and a switch
-track:
-
-
-
-1. Track
-2. Thumb
-3. Icon (optional)
-
-### Switch attributes
-
-Element | Attribute | Related method(s) | Default value
--------------- | ------------------- | --------------------------------- | -------------
-**Min height** | `android:minHeight` | `setMinHeight` `getMinHeight` | `?attr/minTouchTargetSize`
-
-### Thumb attributes
-
-Element | Attribute | Related method(s) | Default value
---------- | --------------- | ----------------------------------------- | -------------
-**Thumb** | `android:thumb` | `setThumbDrawable` `getThumbDrawable` | `@drawable/mtrl_switch_thumb`
-**Color** | `app:thumbTint` | `setThumbTintList` `getThumbTintList` | `?attr/colorOutline` (unchecked) `?attr/colorOnPrimary` (checked)
-
-### Icon attributes
-
-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` `getThumbIconDrawable` | `null`
-**Size** | `app:thumbIconSize` | `setThumbIconSize` `getThumbIconSize` | `16dp`
-**Color** | `app:thumbIconTint` | `setThumbIconTintList` `getThumbIconTintList` | `?attr/colorSurfaceContainerHighest` (unchecked) `?attr/colorOnPrimaryContainer` (checked)
-
-### Track attributes
-
-Element | Attribute | Related method(s) | Default value
--------------------- | ------------------------- | ------------------------------------------------------------- | -------------
-**Track** | `app:track` | `setTrackDrawable` `getTrackDrawable` | `@drawable/mtrl_switch_track`
-**Color** | `app:trackTint` | `setTrackTintList` `getTrackTintList` | `?attr/colorSurfaceContainerHighest` (unchecked) `?attr/colorPrimary` (checked)
-**Decoration** | `app:trackDecoration` | `setTrackDecorationDrawable` `getTrackDecorationDrawable` | `@drawable/mtrl_switch_track_decoration` (Shows an outline of the track.)
-**Decoration color** | `app:trackDecorationTint` | `setTrackDecorationTintList` `getTrackDecorationTintList` | `?attr/colorOutline` (unchecked) `@android:color/transparent` (checked)
-
-### Text label attributes
-
-Element | Attribute | Related method(s) | Default value
--------------- | ------------------------ | ----------------------------------------- | -------------
-**Text label** | `android:text` | `setText` `getText` | `null`
-**Color** | `android:textColor` | `setTextColor` `getTextColors` | `?android:attr/textColorPrimaryDisableOnly`
-**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyMedium`
-**Padding** | `app:switchPadding` | `setSwitchPadding` `getSwitchPadding` | `16dp`
-
-### Switch states
+### Making switch accessible
-Switches can be on or off. Switches have enabled, hover, focused, and pressed
-states.
+Switches support content labeling for accessibility and are readable by most
+screen readers, such as Talkback. Text rendered in switches is automatically
+provided to accessibility services. Additional content labels are usually
+unnecessary.
-
+## Customizing switch
-### Styles
+### Theming switch
-Element | Style
------------------ | ------------------------------------------------
-**Default style** | `Widget.Material3.CompoundButton.MaterialSwitch`
+Switch supports the customization of color and typography.
-Default style theme attribute: `?attr/materialSwitchStyle`
+#### Switch theming example
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/materialswitch/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/materialswitch/res/values/attrs.xml).
-
-## Theming switches
+API and source code:
-Switches support
-[Material Theming](https://material.io/components/selection-controls#theming),
-which can customize color and typography.
+* `MaterialSwitch`
+ * [Class definition](https://developer.android.com/reference/com/google/android/material/materialswitch/MaterialSwitch)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/materialswitch/MaterialSwitch.java)
-### Switch theming example
+**Note:** Since version 1.7.0, the new `MaterialSwitch` class will replace the
+obsolete `SwitchMaterial` class. In most cases you should be able to just
+replace all `SwitchMaterial` class reference with `MaterialSwitch` to achieve
+the default look and feel. Please refer to the following sections if you need to
+customize the new styles.
-The following example shows a list of switches with Material Theming.
+The following example shows a list of switches with Material theming.
-
+
-#### Implementing switch theming
+##### Implementing switch theming
Use theme attributes in `res/values/styles.xml`, which applies to all switches
and affects other components:
@@ -253,19 +247,12 @@ Use the styles in the layout, which affects only this switch:
/>
```
-## Switch (deprecated)
-
-### Switches example (deprecated)
-
-API and source code:
-
-* `SwitchMaterial`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/switchmaterial/SwitchMaterial)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/switchmaterial/SwitchMaterial.java)
+
+
Switch (deprecated)
The following example shows a list of five switches.
-
+
In the layout:
@@ -306,17 +293,16 @@ switchmaterial.setOnCheckedChangeListener { buttonView, isChecked
}
```
-## Anatomy and key properties (deprecated)
-
-The following is an anatomy diagram that shows a switch thumb and a switch
-track:
+### Anatomy
-
+
1. Thumb
2. Track
-### Switch attributes (deprecated)
+### Key properties
+
+#### Switch attributes
Element | Attribute | Related method(s) | Default value
-------------------------- | ------------------------------------------ | ---------------------------------------------------------- | -------------
@@ -336,7 +322,7 @@ with a custom drawable that should not be tinted, set
/>
```
-### Thumb attributes (deprecated)
+#### Thumb attributes
Element | Attribute | Related method(s) | Default value
------------- | --------------- | ----------------------------------------- | -------------
@@ -344,14 +330,14 @@ Element | Attribute | Related method(s) | De
**Color** | `app:thumbTint` | `setThumbTintList` `getThumbTintList` | `?attr/colorOnSurface` (unchecked) `?attr/colorPrimary` (checked)
**Elevation** | N/A | N/A | `4dp`
-### Track attributes (deprecated)
+#### Track attributes
Element | Attribute | Related method(s) | Default value
--------- | --------------- | ----------------------------------------- | -------------
**Track** | `app:track` | `setTrackDrawable` `getTrackDrawable` | inherits from `SwitchCompat`
**Color** | `app:trackTint` | `setTrackTintList` `getTrackTintList` | `?attr/colorOutline` (unchecked) `?attr/colorPrimaryContainer` (checked)
-### Text label attributes (deprecated)
+#### Text label attributes
Element | Attribute | Related method(s) | Default value
-------------- | ------------------------ | ---------------------------------- | -------------
@@ -359,7 +345,7 @@ Element | Attribute | Related method(s) |
**Color** | `android:textColor` | `setTextColor` `getTextColors` | `?android:attr/textColorPrimaryDisableOnly`
**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyMedium`
-### Switch states (deprecated)
+#### Switch states
Switches can be on or off. Switches have enabled, hover, focused, and pressed
states.
@@ -372,7 +358,7 @@ For desktop, the radial reaction isn't needed.

-### Styles (deprecated)
+#### Styles
Element | Style
----------------- | ----------------------------------------
@@ -380,18 +366,16 @@ Element | Style
Default style theme attribute: `?attr/switchStyle`
-See the full list of
+For the full list, see
[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/switchmaterial/res/values/styles.xml)
and
[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/switchmaterial/res/values/attrs.xml).
-## Theming switches (deprecated)
+
Theming switch
-Switches support
-[Material Theming](https://material.io/components/selection-controls#theming),
-which can customize color and typography.
+Switch supports the customization of color and typography.
-### Switch theming example (deprecated)
+#### Switch theming example
API and source code:
@@ -399,11 +383,11 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/switchmaterial/SwitchMaterial)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/switchmaterial/SwitchMaterial.java)
-The following example shows a list of switches with Material Theming.
+The following example shows a list of switches with Material theming.
-
+
-#### Implementing switch theming (deprecated)
+##### Implementing switch theming
Use theme attributes in `res/values/styles.xml`, which applies to all switches
and affects other components:
@@ -444,3 +428,5 @@ Use the styles in the layout, which affects only this switch:
style="@style/Widget.App.Switch"
/>
```
+
+
diff --git a/docs/components/Tabs.md b/docs/components/Tabs.md
index e12dc86d15c..3682639b4e1 100644
--- a/docs/components/Tabs.md
+++ b/docs/components/Tabs.md
@@ -9,32 +9,129 @@ path: /catalog/tabs/
# Tabs
-[Tabs](https://material.io/components/tabs/) organize content across different
-screens, data sets, and other interactions.
+[Tabs](https://m3.material.io/components/tabs/overview) organize content across
+different screens, data sets, and other interactions. There are two variants of
+tabs.
-
+
-**Contents**
+1. Primary tabs
+2. Secondary tabs
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using tabs](#using-tabs)
-* [Fixed tabs](#fixed-tabs)
-* [Scrollable tabs](#scrollable-tabs)
-* [Theming tabs](#theming-tabs)
+**Primary tabs** are placed at the top of the content pane under an app bar.
+They display the main content destinations.
-## Design and API Documentation
+**Secondary tabs** are used within a content area to further separate related
+content and establish hierarchy.
-* [Google Material3 Spec](https://material.io/components/tabs/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/tabs/package-summary)
+**Note:** Images use various dynamic color schemes.
-## Using tabs
+## Design & API documentation
+
+* [Material 3 (M3) spec](https://m3.material.io/components/tabs/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/tabs/package-summary)
+
+## Anatomy
+
+#### Primary tabs
+
+
+
+1. Container
+2. Badge (optional)
+3. Icon (optional)
+4. Label
+5. Divider
+6. Active indicator
+
+#### Secondary tabs
+
+
+
+1. Container
+2. Badge (optional)
+3. Label
+4. Divider
+5. Active indicator
+
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/tabs/guidelines#9b89bb22-e4b2-4de2-9844-14ebf7524760).
+
+## Key properties
+
+### Container attributes
+
+Element | Attribute | Related method(s) | Default value
+------------- | -------------------- | ---------------------------------- | -------------
+**Color** | `android:background` | `setBackground` `getBackground` | `?attr/colorOnSurfaceVariant`
+**Elevation** | `android:elevation` | `setElevation` | `0dp`
+**Height** | N/A | N/A | `48dp` (inline text) or `72dp` (non-inline text and icon)
+**Tab mode** | `tabMode` | `setTabMode` `getTabMode` | `fixed`
+
+### Tab item icon attributes
+
+Element | Attribute | Related method(s) | Default value
+--------- | -------------- | ---------------------------------------------------------------- | -------------
+**Icon** | `android:icon` | `setIcon` `getIcon` | `null`
+**Color** | `tabIconTint` | `setTabIconTint` `setTabIconTintResource` `getTabIconTint` | `colorOnSurfaceVariant` and `colorPrimary` (activated) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/color/m3_tabs_icon_color.xml))
+
+### Tab item text label attributes
+
+Element | Attribute | Related method(s) | Default value
+------------------------- | --------------------------- | --------------------------------------------------------------- | -------------
+**Text** | `android:text` | `setText` `getText` | `null`
+**Color** | `tabTextColor` | `setTabTextColors` `getTabTextColors` | `colorOnSurfaceVariant` and `colorPrimary` (activated) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/color/m3_tabs_icon_color.xml))
+**Typography** | `tabTextAppearance` | N/A | `?attr/textAppearanceTitleSmall`
+**Active tab typography** | `tabSelectedTextAppearance` | N/A | None; will use `tabTextAppearance` instead
+**Inline label** | `tabInlineLabel` | `setInlineLabel` `setInlineLabelResource` `isInlineLabel` | `false`
+
+**Note:** When using `tabSelectedTextAppearance`, you must have matching text
+attributes in `tabTextAppearance` to avoid unintended behavior.
+
+### Tab item container attributes
+
+Element | Attribute | Related method(s) | Default value
+-------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------
+**Ripple color** | `tabRippleColor` | `setTabRippleColor` `setTabRippleColorResource` `getTabRippleColor` | `colorOnSurfaceVariant` at 16% opacity and `colorPrimary` at 16% opacity (activated) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/color/m3_tabs_ripple_color.xml))
+**Unbounded ripple** | `tabUnboundedRipple` | `setUnboundedRipple` `setUnboundedRippleResource` `hasUnboundedRipple` | `false`
+**Gravity** | `tabGravity` | `setTabGravity` `getTabGravity` | `fill`
+**Min width** | `tabMinWidth` | N/A | `72dp` (scrollable) or `wrap_content`
+**Max width** | `tabMaxWidth` | N/A | `264dp`
+**Padding** | `tabPaddingStart` `tabPaddingEnd` `tabPaddingTop` `tabPaddingBottom` `tabPadding` | N/A | `12dp` `12dp` `0dp` `0dp` `0dp`
+
+### Tab indicator attributes
+
+Element | Attribute | Related method(s) | Default value
+---------------------- | ------------------------------- | ---------------------------------------------------------------- | -------------
+**Color** | `tabIndicatorColor` | `setSelectedTabIndicatorColor` | `colorPrimary`
+**Drawable** | `tabIndicator` | `setSelectedTabIndicator` `getSelectedTabIndicator` | [`m3_tabs_rounded_line_indicator`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/drawable/m3_tabs_rounded_line_indicator.xml)
+**Height** | `tabIndicatorHeight` | `setSelectedTabIndicatorHeight` | `2dp`
+**Full width** | `tabIndicatorFullWidth` | `setTabIndicatorFullWidth` `isTabIndicatorFullWidth` | `false`
+**Animation mode** | `tabIndicatorAnimationMode` | `setTabIndicatorAnimationMode` `getTabIndicatorAnimationMode` | `elastic`
+**Gravity** | `tabIndicatorGravity` | `setSelectedTabIndicatorGravity` `getTabIndicatorGravity` | `bottom`
+**Animation duration** | `tabIndicatorAnimationDuration` | N/A | `250`
+
+### Styles
+
+Element | Style | Theme attribute
+--------------------------------- | -------------------------------------- | ---------------
+**Default style** | `Widget.Material3.TabLayout` | `?attr/tabStyle`
+**Style for elevatable surfaces** | `Widget.Material3.TabLayout.OnSurface` | N/A
+**Primary secondary color style** | `Widget.Material3.TabLayout.Secondary` | `?attr/tabSecondaryStyle`
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/values/attrs.xml).
+
+## Code implementation
Before you can use Material tabs, you need to add a dependency to the Material
-Components for Android library. For more information, go to the
+components for Android library. For more information, go to the
[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
page.
-### Basic usage
+### Adding tabs

@@ -95,23 +192,14 @@ tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
})
```
-API and source code:
-
-* `TabLayout`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/tabs/TabLayout)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/TabLayout.java)
-* `TabItem`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/tabs/TabItem)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/TabItem.java)
-
### Making tabs accessible
The Android tab components support screen reader descriptions for tabs and
badges. While optional, we strongly encourage their use.
-#### Content descriptions
+#### Content description
-Adding a content description to the entire `TabLayout` can be done in XML with
+Adding a content descriptions to the entire `TabLayout` can be done in XML with
the `android:contentDescription` attribute or programmatically:
```kt
@@ -223,22 +311,14 @@ badge.number = number
tab.removeBadge()
```
-### Types
-
-There are two types of tabs: 1\. [Fixed tabs](#fixed-tabs), 2\.
-[Scrollable tabs](#scrollable-tabs)
-
-
-
-## Fixed tabs
+### Adding fixed tabs
Fixed tabs display all tabs on one screen, with each tab at a fixed width. The
width of each tab is determined by dividing the number of tabs by the screen
width. They don’t scroll to reveal more tabs; the visible tab set represents the
only tabs available.
-### Fixed tabs example
+#### Fixed tabs example
The following example shows a row of fixed tabs.
@@ -276,12 +356,12 @@ In the layout:
```
-## Scrollable tabs
+### Adding scrollable tabs
Scrollable tabs are displayed without fixed widths. They are scrollable, such
that some tabs will remain off-screen until scrolled.
-### Scrollable tabs example
+#### Scrollable tabs example
The following example shows a row of scrollable tabs.
@@ -329,98 +409,13 @@ In the layout:
```
-### Anatomy and key properties
-
-Tabs have a container and each tab item has an optional icon and text label. Tab
-items can be in an active or inactive state. The tab indicator is shown below
-the active tab item.
-
-
-
-1. Container
-2. Active icon (optional if there’s a label)
-3. Active text label (optional if there’s an icon)
-4. Active tab indicator
-5. Inactive icon (optional if there’s a label)
-6. Inactive text label (optional if there’s an icon)
-7. Tab item
-
-### Container attributes
-
-Element | Attribute | Related method(s) | Default value
-------------- | -------------------- | ---------------------------------- | -------------
-**Color** | `android:background` | `setBackground` `getBackground` | `?attr/colorOnSurfaceVariant`
-**Elevation** | `android:elevation` | `setElevation` | `0dp`
-**Height** | N/A | N/A | `48dp` (inline text) or `72dp` (non-inline text and icon)
-**Tab mode** | `tabMode` | `setTabMode` `getTabMode` | `fixed`
-
-### Tab item icon attributes
-
-Element | Attribute | Related method(s) | Default value
---------- | -------------- | ---------------------------------------------------------------- | -------------
-**Icon** | `android:icon` | `setIcon` `getIcon` | `null`
-**Color** | `tabIconTint` | `setTabIconTint` `setTabIconTintResource` `getTabIconTint` | `colorOnSurfaceVariant` and `colorPrimary` (activated) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/color/m3_tabs_icon_color.xml))
-
-### Tab item text label attributes
-
-Element | Attribute | Related method(s) | Default value
-------------------------- | --------------------------- | --------------------------------------------------------------- | -------------
-**Text** | `android:text` | `setText` `getText` | `null`
-**Color** | `tabTextColor` | `setTabTextColors` `getTabTextColors` | `colorOnSurfaceVariant` and `colorPrimary` (activated) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/color/m3_tabs_icon_color.xml))
-**Typography** | `tabTextAppearance` | N/A | `?attr/textAppearanceTitleSmall`
-**Active tab typography** | `tabSelectedTextAppearance` | N/A | None; will use `tabTextAppearance` instead
-**Inline label** | `tabInlineLabel` | `setInlineLabel` `setInlineLabelResource` `isInlineLabel` | `false`
-
-**Note:** When using `tabSelectedTextAppearance`, you must have matching text
-attributes in `tabTextAppearance` to avoid unintended behavior.
-
-### Tab item container attributes
-
-Element | Attribute | Related method(s) | Default value
--------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------
-**Ripple color** | `tabRippleColor` | `setTabRippleColor` `setTabRippleColorResource` `getTabRippleColor` | `colorOnSurfaceVariant` at 16% opacity and `colorPrimary` at 16% opacity (activated) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/color/m3_tabs_ripple_color.xml))
-**Unbounded ripple** | `tabUnboundedRipple` | `setUnboundedRipple` `setUnboundedRippleResource` `hasUnboundedRipple` | `true`
-**Gravity** | `tabGravity` | `setTabGravity` `getTabGravity` | `fill`
-**Min width** | `tabMinWidth` | N/A | `72dp` (scrollable) or `wrap_content`
-**Max width** | `tabMaxWidth` | N/A | `264dp`
-**Padding** | `tabPaddingStart` `tabPaddingEnd` `tabPaddingTop` `tabPaddingBottom` `tabPadding` | N/A | `12dp` `12dp` `0dp` `0dp` `0dp`
-
-### Tab indicator attributes
-
-Element | Attribute | Related method(s) | Default value
----------------------- | ------------------------------- | ---------------------------------------------------------------- | -------------
-**Color** | `tabIndicatorColor` | `setSelectedTabIndicatorColor` | `colorPrimary`
-**Drawable** | `tabIndicator` | `setSelectedTabIndicator` `getSelectedTabIndicator` | [`m3_tabs_rounded_line_indicator`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/drawable/m3_tabs_rounded_line_indicator.xml)
-**Height** | `tabIndicatorHeight` | `setSelectedTabIndicatorHeight` | `2dp`
-**Full width** | `tabIndicatorFullWidth` | `setTabIndicatorFullWidth` `isTabIndicatorFullWidth` | `false`
-**Animation mode** | `tabIndicatorAnimationMode` | `setTabIndicatorAnimationMode` `getTabIndicatorAnimationMode` | `elastic`
-**Gravity** | `tabIndicatorGravity` | `setSelectedTabIndicatorGravity` `getTabIndicatorGravity` | `bottom`
-**Animation duration** | `tabIndicatorAnimationDuration` | N/A | `250`
-
-### Styles
-
-Element | Style
----------------------------------- | --------------------------------------
-**Default style** | `Widget.Material3.TabLayout`
-**Style for elevateable surfaces** | `Widget.Material3.TabLayout.OnSurface`
-**Primary secondary color style** | `Widget.Material3.TabLayout.Secondary`
-
-Default style theme attribute: `?attr/tabStyle`
-
-Additional style theme attributes: `?attr/tabSecondaryStyle`
-
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/res/values/attrs.xml).
+## Customizing tabs
-## Theming tabs
+### Theming tabs
-Tabs support
-[Material Theming](https://material.io/components/app-bars-bottom/#theming)
-which can customize color and typography.
+Tabs support the customization of color and typography.
-### Tabs theming example
+#### Tabs theming example
API and source code:
@@ -431,12 +426,12 @@ API and source code:
* [Class definition](https://developer.android.com/reference/com/google/android/material/tabs/TabItem)
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/tabs/TabItem.java)
-The following example shows a row of scrollable tabs with Material Theming.
+The following example shows a row of scrollable tabs with Material theming.

-#### Implementing tabs theming
+##### Implementing tabs theming
Use theme attributes and styles in `res/values/styles.xml` which applies to all
tabs and affects other components:
diff --git a/docs/components/TextField.md b/docs/components/TextField.md
index 1a6694aeb21..a2972278ccc 100644
--- a/docs/components/TextField.md
+++ b/docs/components/TextField.md
@@ -9,31 +9,394 @@ path: /catalog/text-fields/
# Text fields
-[Text fields](https://material.io/components/text-fields) let users enter and
-edit text.
+[Text fields](https://m3.material.io/components/text-fields/overview) let users
+enter and edit text. There are two variants of text fields.
-
+
+
+1. Filled text field
+2. Outlined text field
+
+**Note:** Images use various dynamic color schemes.
+
+## Design & API documentation
+
+* [Material 3 (M3) spec](https://m3.material.io/components/text-fields/overview)
+* [API reference](https://developer.android.com/reference/com/google/android/material/textfield/package-summary)
+
+## Anatomy
+
+#### Filled text field
+
+
+
+1. Container
+2. Leading icon (optional)
+3. Label text (empty)
+4. Label text (populated)
+5. Trailing icon (optional)
+6. Active indicator (focused)
+7. Caret
+8. Input text
+9. Supporting text (optional)
+10. Active Indicator (enabled)
+
+#### Outlined text field
+
+
+
+1. Container outline (enabled)
+2. Leading icon (optional)
+3. Label text (unpopulated)
+4. Label text (populated)
+5. Trailing icon (optional)
+6. Container outline (focused)
+7. Caret
+8. Input text
+9. Supporting text (optional)
+
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/text-fields/guidelines#6be8deda-2eed-4765-9e32-98c2563d6c1c).
+
+## Key properties
+
+
+
Filled text field
+
+#### Container attributes
+
+Element | Attribute | Related method(s) | Default value
+---------------------- | ------------------------ | --------------------------------------------------------------------------------------- | -------------
+**Color** | `app:boxBackgroundColor` | `setBoxBackgroundColor` `setBoxBackgroundColorResource` `getBoxBackgroundColor` | `?attr/colorSurfaceContainerHighest` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_filled_background_color.xml))
+**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceCornerExtraSmall`
+**Text field enabled** | `android:enabled` | `setEnabled` | `true`
+
+#### Leading icon attributes
+
+Element | Attribute | Related method(s) | Default value
+----------------------- | --------------------------------- | --------------------------------------------------------------------- | -------------
+**Icon** | `app:startIconDrawable` | `setStartIconDrawable` `getStartIconDrawable` | `null`
+**Content description** | `app:startIconContentDescription` | `setStartIconContentDescription` `getStartIconContentDescription` | `null`
+**Color** | `app:startIconTint` | `setStartIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Checkable** | `app:startIconCheckable` | `setStartIconCheckable` `isStartIconCheckable` | `false`
+**Size** | `app:startIconMinSize` | `setStartIconMinSize` `getStartIconMinSize` | `48dp`
+**Scale type** | `app:startIconScaleType` | `setStartIconScaleType` `getStartIconScaleType` | `ScaleType.CENTER`
+
+#### Label attributes
+
+Element | Attribute | Related method(s) | Default value
+------------------------------ | -------------------------- | ------------------------------------------------------- | -------------
+**Text** | `android:hint` | `setHint` `getHint` | `null`
+**Color** | `android:textColorHint` | `setDefaultHintTextColor` `getDefaultHintTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_label_color.xml))
+**Collapsed (floating) color** | `app:hintTextColor` | `setHintTextColor` `getHintTextColor` | `?attr/colorPrimary` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_label_color.xml))
+**Typography** | `app:hintTextAppearance` | `setHintTextAppearance` | `?attr/textAppearanceBodySmall`
+**Animation** | `app:hintAnimationEnabled` | `setHintAnimationEnabled` `isHintAnimationEnabled` | `true`
+**Expanded enabled** | `app:expandedHintEnabled` | `setExpandedHintEnabled` `isExpandedHintEnabled` | `true`
+
+**Note:** The `android:hint` should always be set on the `TextInputLayout`
+instead of on the `EditText` in order to avoid unintended behaviors.
+
+#### Input text attributes (set on the `TextInputEditText`)
+
+Element | Attribute | Related method(s) | Default value
+------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------ | -------------
+**Input text** | `android:text` | `setText` `getText` | `@null`
+**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyLarge`
+**Input text color** | `android:textColor` | `setTextColor` `getTextColors` `getCurrentTextColor` | `?attr/colorOnSurface`
+**Cursor color** | N/A (color comes from the theme attr `?attr/colorControlActivated`) | N/A | `?attr/colorPrimary`
+**Text highlight color** | N/A (color comes from the theme attr `?android:attr/textColorHighlight`) | N/A | [`@color/m3_highlighted_text`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/color/res/color/m3_highlighted_text.xml)
+
+#### Input text attributes (set on the `TextInputLayout`)
+
+Element | Attribute | Related method(s) | Default value
+---------------------- | ---------------------------------------- | --------------------------------------------------------------------- | -------------
+**Cursor color** | `app:cursorColor` on API levels 28+ | `setCursorColor` `getCursorColor` on API levels 28+ | `@null` (uses `?attr/colorControlActivated` by default)
+**Cursor error color** | `app:cursorErrorColor` on API levels 28+ | `setCursorErrorColor` `getCursorErrorColor` on API levels 28+ | `?attr/colorError` on API levels 28+, `?attr/colorControlActivated` otherwise
+
+#### Trailing icon attributes
+
+Element | Attribute | Related method(s) | Default value
+----------------------------------- | ------------------------------- | ----------------------------------------------------------------- | -------------
+**Mode** | `app:endIconMode` | `setEndIconMode` `getEndIconMode` | `END_ICON_NONE`
+**Color** | `app:endIconTint` | `setEndIconTintList` | `colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Custom icon** | `app:endIconDrawable` | `setEndIconDrawable` `getEndIconDrawable` | `null`
+**Custom icon content description** | `app:endIconContentDescription` | `setEndIconContentDescription` `getEndIconContentDescription` | `null`
+**Custom icon checkable** | `app:endIconCheckable` | `setEndIconCheckable` `isEndIconCheckable` | `true`
+**Error icon** | `app:errorIconDrawable` | `setErrorIconDrawable` `getErrorIconDrawable` | [`@drawable/mtrl_ic_error`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/drawable/mtrl_ic_error.xml)
+**Error icon color** | `app:errorIconTint` | `setErrorIconTintList` | `?attr/colorError`
+**Size** | `app:endIconMinSize` | `setEndIconMinSize` `getEndIconMinSize` | `48dp`
+**Scale type** | `app:endIconScaleType` | `setEndIconScaleType` `getEndIconScaleType` | `ScaleType.CENTER`
-**Contents**
+#### Activation indicator attributes
-* [Design and API Documentation](#design-and-api-documentation)
-* [Using text fields](#using-text-fields)
-* [Filled text field](#filled-text-field)
-* [Outlined text field](#outlined-text-field)
-* [Theming](#theming-text-fields)
+Element | Attribute | Related method(s) | Default value
+----------------- | --------------------------- | ---------------------------------------------------------------------------- | -------------
+**Color** | `app:boxStrokeColor` | `setBoxStrokeColor` `setBoxStrokeColorStateList` `getBoxStrokeColor` | `?attr/colorOutline` and `?attr/colorPrimary` (focused) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_stroke_color.xml))
+**Error color** | `app:boxStrokeErrorColor` | `setBoxStrokeErrorColor` `getBoxStrokeErrorColor` | `?attr/colorError`
+**Width** | `app:boxStrokeWidth` | N/A | `1dp`
+**Focused width** | `app:boxStrokeWidthFocused` | N/A | `2dp`
-## Design and API Documentation
+#### Helper/error/counter text attributes
-* [Google Material3 Spec](https://material.io/components/text-fields/overview)
-* [API Reference](https://developer.android.com/reference/com/google/android/material/textfield/package-summary)
+Element | Attribute | Related method(s) | Default value
+---------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------
+**Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled` `isHelperTextEnabled` | `false`
+**Helper text** | `app:helperText` | `setHelperText` `getHelperText` | `null`
+**Helper text color** | `app:helperTextColor` | `setHelperTextColor` `getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall`
+**Error text enabled** | `app:errorEnabled` | `setErrorEnabled` `isErrorEnabled` | `false`
+**Error text** | N/A | `setError` `getError` | `null`
+**Error text accessibility live region** | `app:errorAccessibilityLiveRegion` | `setErrorAccessibilityLiveRegion` `getErrorAccessibilityLiveRegion` | `ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE`
+**Error text color** | `app:errorTextColor` | `setErrorTextColor` `getErrorCurrentTextColors` | `?attr/colorError`
+**Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall`
+**Counter text enabled** | `app:counterEnabled` | `setCounterEnabled` `isCounterEnabled` | `false`
+**Counter text length** | `app:counterMaxLength` | `setCounterMaxLength` `getCounterMaxLength` | `-1`
+**Counter text typography** | `app:counterTextAppearance` `app:counterOverflowTextAppearance` | `setCounterTextAppearance` `setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall`
+**Counter text color** | `app:counterTextColor` `app:counterOverflowTextColor` | `setCounterTextColor` `setCounterOverflowTextColor` `getCounterTextColor` `getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) `?attr/colorError` (`app:counterOverflowTextColor`)
-## Using text fields
+#### Prefix/suffix attributes
+
+Element | Attribute | Related method(s) | Default value
+--------------------- | -------------------------- | --------------------------------------------- | -------------
+**Prefix** | `app:prefixText` | `setPrefixText` `getPrefixText` | `null`
+**Prefix color** | `app:prefixTextColor` | `setPrefixTextColor` `getPrefixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Prefix typography** | `app:prefixTextAppearance` | `setPrefixTextAppearance` | `?attr/textAppearanceTitleMedium`
+**Suffix** | `app:suffixText` | `setSuffixText` `getSuffixText` | `null`
+**Suffix color** | `app:suffixTextColor` | `setSuffixTextColor` `getSuffixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Suffix typography** | `app:suffixTextAppearance` | `setSuffixTextAppearance` | `?attr/textAppearanceTitleMedium`
+
+#### Styles
+
+Element | Style | Default style theme attribute
+------------------------------------- | ---------------------------------------------------------------------- | -----------------------------
+**Default style** | `Widget.Material3.TextInputLayout.FilledBox` | `?attr/textInputFilledStyle`
+**Dense style** | `Widget.Material3.TextInputLayout.FilledBox.Dense` | `?attr/textInputFilledDenseStyle`
+**Exposed dropdown menu style** | `Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu` | `?attr/textInputFilledExposedDropdownMenuStyle`
+**Dense exposed dropdown menu style** | `Widget.Material3.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu` | N/A
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml).
+
+
+
+
+
Outlined text field
+
+#### Container attributes
+
+Element | Attribute | Related method(s) | Default value
+------------------------ | --------------------------- | ---------------------------------------------------------------------------- | -------------
+**Stroke color** | `app:boxStrokeColor` | `setBoxStrokeColor` `setBoxStrokeColorStateList` `getBoxStrokeColor` | `?attr/colorOutline` and `?attr/colorPrimary` (focused) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_stroke_color.xml))
+**Stroke error color** | `app:boxStrokeErrorColor` | `setBoxStrokeErrorColor` `getBoxStrokeErrorColor` | `?attr/colorError`
+**Stroke width** | `app:boxStrokeWidth` | N/A | `1dp`
+**Stroke focused width** | `app:boxStrokeWidthFocused` | N/A | `2dp`
+**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceCornerExtraSmall`
+**Text field enabled** | `android:enabled` | `setEnabled` | `true`
+
+#### Leading icon attributes
+
+Element | Attribute | Related method(s) | Default value
+----------------------- | --------------------------------- | --------------------------------------------------------------------- | -------------
+**Icon** | `app:startIconDrawable` | `setStartIconDrawable` `getStartIconDrawable` | `null`
+**Content description** | `app:startIconContentDescription` | `setStartIconContentDescription` `getStartIconContentDescription` | `null`
+**Color** | `app:startIconTint` | `setStartIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Checkable** | `app:startIconCheckable` | `setStartIconCheckable` `isStartIconCheckable` | `false`
+
+#### Label attributes
+
+Element | Attribute | Related method(s) | Default value
+------------------------------ | ------------------------ | ------------------------------------------------------- | -------------
+**Text** | `android:hint` | `setHint` `getHint` | `null`
+**Color** | `android:textColorHint` | `setDefaultHintTextColor` `getDefaultHintTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_label_color.xml))
+**Collapsed (floating) color** | `app:hintTextColor` | `setHintTextColor` `getHintTextColor` | `?attr/colorPrimary`
+**Typography** | `app:hintTextAppearance` | `setHintTextAppearance` | `?attr/textAppearanceBodySmall`
+**Max number of lines** | `app:hintMaxLines` | `setHintMaxLines` `getHintMaxLines` | `1`
+
+**Note:** The `android:hint` should always be set on the `TextInputLayout`
+instead of on the `EditText` in order to avoid unintended behaviors.
+
+#### Input text attributes (set on the `TextInputEditText`)
+
+Element | Attribute | Related method(s) | Default value
+------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------ | -------------
+**Input text** | `android:text` | `setText` `getText` | `@null`
+**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyLarge`
+**Input text color** | `android:textColor` | `setTextColor` `getTextColors` `getCurrentTextColor` | `?attr/colorOnSurface`
+**Cursor color** | N/A (color comes from the theme attr `?attr/colorControlActivated`) | N/A | `?attr/colorPrimary`
+**Text highlight color** | N/A (color comes from the theme attr `?android:attr/textColorHighlight`) | N/A | [`@color/m3_highlighted_text`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/color/res/color/m3_highlighted_text.xml)
+
+#### Input text attributes (set on the `TextInputLayout`)
+
+Element | Attribute | Related method(s) | Default value
+---------------------- | ---------------------------------------- | --------------------------------------------------------------------- | -------------
+**Cursor color** | `app:cursorColor` on API levels 28+ | `setCursorColor` `getCursorColor` on API levels 28+ | `@null` (uses `?attr/colorControlActivated` by default)
+**Cursor error color** | `app:cursorErrorColor` on API levels 28+ | `setCursorErrorColor` `getCursorErrorColor` on API levels 28+ | `?attr/colorError` on API levels 28+, `?attr/colorControlActivated` otherwise
+
+#### Trailing icon attributes
+
+Element | Attribute | Related method(s) | Default value
+----------------------------------- | ------------------------------- | ----------------------------------------------------------------- | -------------
+**Mode** | `app:endIconMode` | `setEndIconMode` `getEndIconMode` | `END_ICON_NONE`
+**Color** | `app:endIconTint` | `setEndIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Custom icon** | `app:endIconDrawable` | `setEndIconDrawable` `getEndIconDrawable` | `null`
+**Custom icon content description** | `app:endIconContentDescription` | `setEndIconContentDescription` `getEndIconContentDescription` | `null`
+**Custom icon checkable** | `app:endIconCheckable` | `setEndIconCheckable` `isEndIconCheckable` | `true`
+**Error icon** | `app:errorIconDrawable` | `setErrorIconDrawable` `getErrorIconDrawable` | [`@drawable/mtrl_ic_error`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/drawable/mtrl_ic_error.xml)
+**Error icon color** | `app:errorIconTint` | `setErrorIconTintList` | `?attr/colorError`
+
+#### Helper/error/counter text attributes
+
+Element | Attribute | Related method(s) | Default value
+---------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------
+**Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled` `isHelperTextEnabled` | `false`
+**Helper text** | `app:helperText` | `setHelperText` `getHelperText` | `null`
+**Helper text color** | `app:helperTextColor` | `setHelperTextColor` `getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall`
+**Error text enabled** | `app:errorEnabled` | `setErrorEnabled` `isErrorEnabled` | `false`
+**Error text** | N/A | `setError` `getError` | `null`
+**Error text accessibility live region** | `app:errorAccessibilityLiveRegion` | `setErrorAccessibilityLiveRegion` `getErrorAccessibilityLiveRegion` | `ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE`
+**Error text color** | `app:errorTextColor` | `setErrorTextColor` `getErrorCurrentTextColors` | `?attr/colorError`
+**Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall`
+**Counter text enabled** | `app:counterEnabled` | `setCounterEnabled` `isCounterEnabled` | `false`
+**Counter text length** | `app:counterMaxLength` | `setCounterMaxLength` `getCounterMaxLength` | `-1`
+**Counter text typography** | `app:counterTextAppearance` `app:counterOverflowTextAppearance` | `setCounterTextAppearance` `setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall`
+**Counter text color** | `app:counterTextColor` `app:counterOverflowTextColor` | `setCounterTextColor` `setCounterOverflowTextColor` `getCounterTextColor` `getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) `?attr/colorError` (`app:counterOverflowTextColor`)
+
+#### Prefix/suffix attributes
+
+Element | Attribute | Related method(s) | Default value
+--------------------- | -------------------------- | --------------------------------------------- | -------------
+**Prefix** | `app:prefixText` | `setPrefixText` `getPrefixText` | `null`
+**Prefix color** | `app:prefixTextColor` | `setPrefixTextColor` `getPrefixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Prefix typography** | `app:prefixTextAppearance` | `setPrefixTextAppearance` | `?attr/textAppearanceTitleMedium`
+**Suffix** | `app:suffixText` | `setSuffixText` `getSuffixText` | `null`
+**Suffix color** | `app:suffixTextColor` | `setSuffixTextColor` `getSuffixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
+**Suffix typography** | `app:suffixTextAppearance` | `setSuffixTextAppearance` | `?attr/textAppearanceTitleMedium`
+
+#### Styles
+
+Element | Style | Default style theme attribute
+------------------------------------- | ------------------------------------------------------------------------ | -----------------------------
+**Default style** | `Widget.Material3.TextInputLayout.OutlinedBox` | `?attr/textInputStyle` and `?attr/textInputOutlinedStyle`
+**Dense style** | `Widget.Material3.TextInputLayout.OutlinedBox.Dense` | `?attr/textInputOutlinedDenseStyle`
+**Exposed dropdown menu style** | `Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu` | `?attr/textInputOutlinedExposedDropdownMenuStyle`
+**Dense exposed dropdown menu style** | `Widget.Material3.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu` | N/A
+
+For the full list, see
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml).
+
+
+
+## Variants of text fields
+
+### Filled text field
+
+[Filled text fields](https://material.io/components/text-fields/#filled-text-field)
+have more visual emphasis than outlined text fields, making them stand out when
+surrounded by other content and components.
+
+#### Filled text field examples
+
+The following example shows a filled text field with a label.
+
+
+
+In the layout:
+
+```xml
+
+
+
+
+
+```
+
+In code:
+
+```kt
+// Get input text
+val inputText = filledTextField.editText?.text.toString()
+
+filledTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
+ // Respond to input text change
+}
+```
+
+See the [code implementation](#code-implementation) section below for more
+examples.
+
+### Outlined text field
+
+[Outlined text fields](https://material.io/components/text-fields/#outlined-text-field)
+have less visual emphasis than filled text fields. When they appear in forms,
+for example, where many text fields are placed together, their reduced emphasis
+helps simplify the layout.
+
+**Note:** The outlined text field is the default style.
+
+#### Outlined text field examples
+
+The following example shows an outlined text field.
+
+
+
+In the layout:
+
+```xml
+
+
+
+
+
+```
+
+In code:
+
+```kt
+// Get input text
+val inputText = outlinedTextField.editText?.text.toString()
+
+outlinedTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
+ // Respond to input text change
+}
+```
+
+See the [code implementation](#code-implementation) section below for more
+examples.
+
+## Code implementation
Before you can use Material text fields, you need to add a dependency to the
-Material Components for Android library. For more information, go to the
+Material components for Android library. For more information, go to the
[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
page.
+
+
Adding text field
+
+
+
```xml
```
-API and source code:
-
-* `TextInputLayout`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/textfield/TextInputLayout)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/TextInputLayout.java)
-* `TextInputEditText`
- * [Class definition](https://developer.android.com/reference/com/google/android/material/textfield/TextInputEditText)
- * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/TextInputEditText.java)
-
**Note:** A text field is composed of a `TextInputLayout` and a
`TextInputEditText` as a direct child. Using an `EditText` as the child might
work, but `TextInputEditText` provides accessibility support for the text field
@@ -66,7 +420,10 @@ input text. If an `EditText` is being used, make sure to set its
`android:background` to `@null` so that `TextInputLayout` can set the proper
background on it.
-### Making text fields accessible
+
+
+
+
Making text fields accessible
Android's text field component APIs support both label text and helper text,
which explain what is requested for a text field. While optional, their use is
@@ -101,9 +458,27 @@ field requires different accessibility support than the one offered by
`setTextInputAccessibilityDelegate` method. This method should be used in place
of providing an `AccessibilityDelegate` directly on the `EditText`.
-### Adding a leading icon to a text field
+
+
+
+
Using text fields programmatically
+
+If you construct the `TextInputEditText` child of a `TextInputLayout`
+programmatically, you should use `TextInputLayout`'s context to create the view.
+This will allow `TextInputLayout` to pass along the appropriate styling to the
+edit text.
+
+```kt
+val textInputLayout = TextInputLayout(context)
+val editText = TextInputEditText(textInputLayout.context)
+```
-
+
+
+
+
Adding a leading icon to a text field
+
+
```xml
```
-### Adding a trailing icon to a text field
+
+
+
+
Adding a trailing icon to a text field
**Password toggle:**
-
+
When the `TextInputEditText` is set to display a password, an icon can be added
to toggle between masking the password or displaying the password as plain-text.
@@ -140,7 +518,7 @@ to toggle between masking the password or displaying the password as plain-text.
**Clear text:**
-
+
An icon can be set to display when text is present. The icon can be pressed to
clear the input text.
@@ -157,7 +535,7 @@ clear the input text.
**Custom icon:**
-
+
It is possible to set a custom `Drawable` as the text field's trailing icon via
`app:endIconMode="custom"`. You should specify a drawable and content
@@ -203,551 +581,208 @@ textField.addOnEndIconChangedListener {
// TransformationMethod is still PasswordTransformationMethod. Because of
// that, an OnEndIconChangedListener is used.
}
-```
-
-**Note:** You should opt to use the `EndIconMode` API instead of setting an
-end/right compound `Drawable` on the `TextInputEditText`. The same applies to
-the now-deprecated `passwordToggle*` attributes.
-
-**Important:** Calling `setEndIconMode` will initialize the icon with its
-default features, such as default drawables, and in the case of the custom mode,
-an empty drawable. You can add customizations after calling `setEndIconMode`.
-The exception for this is if a drawable was specified in XML via the
-`app:endIconDrawable` attribute. An end icon drawable set in XML will take
-precedence and override an existing default icon.
-
-See the full list of
-[end icon modes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml#L149).
-
-### Implementing an exposed dropdown menu
-
-
-
-In the layout:
-
-```xml
-
-
-
-
-
-```
-
-The string array specified by `app:simpleItems` will be used as the default
-item strings for auto-completion. Or you can also set it programmatically:
-
-```kt
-val items = arrayOf("Item 1", "Item 2", "Item 3", "Item 4")
-(textField.editText as? MaterialAutoCompleteTextView)?.setSimpleItems(items)
-```
-
-Alternatively, to have more control over the auto-completion items rendering,
-you can also provide a custom item adapter by:
-
-```kt
-val items = listOf("Item 1", "Item 2", "Item 3", "Item 4")
-val adapter = ArrayAdapter(requireContext(), R.layout.list_item, items)
-(textField.editText as? AutoCompleteTextView)?.setAdapter(adapter)
-```
-
-And a custom item layout (`list_item.xml`):
-
-```xml
-
-```
-
-### Adding helper text to a text field
-
-
-
-```xml
-
-
- ...
-
-
-```
-
-### Adding a counter to a text field
-
-
-
-```xml
-
-
- ...
-
-
-```
-
-### Adding errors to a text field
-
-
-
-In the layout:
-
-```xml
-
-
- ...
-
-
-```
-
-In code:
-
-```kt
-// Set error text
-passwordLayout.error = getString(R.string.error)
-
-// Clear error text
-passwordLayout.error = null
-```
-
-**Note:** Non-null error text will replace any existing helper text, and
-non-null helper text will replace any existing error text.
-
-### Adding a prefix/suffix to a text field
-
-
-
-```xml
-
-
- ...
-
-
-```
-
-### Text field dimensions
-
-The recommended default `android:layout_width` is `245dp`.
-
-By default, text fields have a maximum width of `488dp`, and a minimum width of
-`56dp` for layouts without a label. If a label is present, the minimum width
-recommended is `88dp`. `android:minWidth` and `android:maxWidth` (as well as
-`android:minEms` and `android:maxEms`) should be set on the `TextInputLayout`
-instead of on the `TextInputEditText` to avoid unintended behaviors.
-
-You can override those values in a custom style that inherits from a
-`TextInputLayout` style or by making changes directly on the layout:
-
-```xml
-
-
-
-
-
-```
-
-**Note:** The `android:layout_width` of the `TextInputLayout` should be
-`wrap_content` in order for those minimum and maximum dimensions to be used.
-
-### Using text fields programmatically
-
-If you construct the `TextInputEditText` child of a `TextInputLayout`
-programmatically, you should use `TextInputLayout`'s context to create the view.
-This will allow `TextInputLayout` to pass along the appropriate styling to the
-edit text.
-
-```kt
-val textInputLayout = TextInputLayout(context)
-val editText = TextInputEditText(textInputLayout.context)
-```
-
-### Types
-
-There are two types of text fields: 1\. [Filled text field](#filled-text-field),
-2\. [Outlined text field](#outlined-text-field)
-
-
-
-## Filled text field
-
-[Filled text fields](https://material.io/components/text-fields/#filled-text-field)
-have more visual emphasis than outlined text fields, making them stand out when
-surrounded by other content and components.
-
-### Filled text field examples
-
-The following example shows a filled text field with a label.
-
-
-
-In the layout:
-
-```xml
-
-
-
-
-
-```
-
-In code:
-
-```kt
-// Get input text
-val inputText = filledTextField.editText?.text.toString()
-
-filledTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
- // Respond to input text change
-}
-```
-
-See the [using text fields](#using-text-fields) section above for more examples.
-
-### Anatomy and key properties
-
-A filled text field has a filled container, input text, a label, an activation
-indicator, optional helper/error text and optional leading/trailing icons.
-
-
-
-1. Container
-2. Leading icon
-3. Label
-4. Input text
-5. Trailing icon
-6. Activation indicator
-7. Helper/error/counter text
-8. Prefix/suffix/placeholder (not shown)
-
-#### Container attributes
-
-Element | Attribute | Related method(s) | Default value
----------------------- | ------------------------ | --------------------------------------------------------------------------------------- | -------------
-**Color** | `app:boxBackgroundColor` | `setBoxBackgroundColor` `setBoxBackgroundColorResource` `getBoxBackgroundColor` | `?attr/colorSurfaceContainerHighest` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_filled_background_color.xml))
-**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceSmallComponent`
-**Text field enabled** | `android:enabled` | `setEnabled` | `true`
-
-#### Leading icon attributes
-
-Element | Attribute | Related method(s) | Default value
------------------------ | --------------------------------- | --------------------------------------------------------------------- | -------------
-**Icon** | `app:startIconDrawable` | `setStartIconDrawable` `getStartIconDrawable` | `null`
-**Content description** | `app:startIconContentDescription` | `setStartIconContentDescription` `getStartIconContentDescription` | `null`
-**Color** | `app:startIconTint` | `setStartIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Checkable** | `app:startIconCheckable` | `setStartIconCheckable` `isStartIconCheckable` | `false`
-**Size** | `app:startIconMinSize` | `setStartIconMinSize` `getStartIconMinSize` | `48dp`
-**Scale type** | `app:startIconScaleType` | `setStartIconScaleType` `getStartIconScaleType` | `ScaleType.CENTER`
-
-#### Label attributes
+```
-Element | Attribute | Related method(s) | Default value
------------------------------- | -------------------------- | ------------------------------------------------------- | -------------
-**Text** | `android:hint` | `setHint` `getHint` | `null`
-**Color** | `android:textColorHint` | `setDefaultHintTextColor` `getDefaultHintTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_label_color.xml))
-**Collapsed (floating) color** | `app:hintTextColor` | `setHintTextColor` `getHintTextColor` | `?attr/colorPrimary` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_label_color.xml))
-**Typography** | `app:hintTextAppearance` | `setHintTextAppearance` | `?attr/textAppearanceBodySmall`
-**Animation** | `app:hintAnimationEnabled` | `setHintAnimationEnabled` `isHintAnimationEnabled` | `true`
-**Expanded enabled** | `app:expandedHintEnabled` | `setExpandedHintEnabled` `isExpandedHintEnabled` | `true`
+**Note:** You should opt to use the `EndIconMode` API instead of setting an
+end/right compound `Drawable` on the `TextInputEditText`. The same applies to
+the now-deprecated `passwordToggle*` attributes.
-**Note:** The `android:hint` should always be set on the `TextInputLayout`
-instead of on the `EditText` in order to avoid unintended behaviors.
+**Important:** Calling `setEndIconMode` will initialize the icon with its
+default features, such as default drawables, and in the case of the custom mode,
+an empty drawable. You can add customizations after calling `setEndIconMode`.
+The exception for this is if a drawable was specified in XML via the
+`app:endIconDrawable` attribute. An end icon drawable set in XML will take
+precedence and override an existing default icon.
-#### Input text attributes (set on the `TextInputEditText`)
+For the full list, see
+[end icon modes](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml#L149).
-Element | Attribute | Related method(s) | Default value
------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------ | -------------
-**Input text** | `android:text` | `setText` `getText` | `@null`
-**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyLarge`
-**Input text color** | `android:textColor` | `setTextColor` `getTextColors` `getCurrentTextColor` | `?attr/colorOnSurface`
-**Cursor color** | N/A (color comes from the theme attr `?attr/colorControlActivated`) | N/A | `?attr/colorPrimary`
-**Text highlight color** | N/A (color comes from the theme attr `?android:attr/textColorHighlight`) | N/A | [`@color/m3_highlighted_text`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/color/res/color/m3_highlighted_text.xml)
+
-#### Input text attributes (set on the `TextInputLayout`)
+
+
Implementing an exposed dropdown menu
-Element | Attribute | Related method(s) | Default value
------------------------- |------------------------------------------|-----------------------------------------------------------------------| -------------
-**Cursor color** | `app:cursorColor` on API levels 28+ | `setCursorColor` `getCursorColor` on API levels 28+ | `@null` (uses `?attr/colorControlActivated` by default)
-**Cursor error color** | `app:cursorErrorColor` on API levels 28+ | `setCursorErrorColor` `getCursorErrorColor` on API levels 28+ | `?attr/colorError` on API levels 28+, `?attr/colorControlActivated` otherwise
+
+In the layout:
-#### Trailing icon attributes
+```xml
+
-Element | Attribute | Related method(s) | Default value
------------------------------------ | ------------------------------- | ----------------------------------------------------------------- | -------------
-**Mode** | `app:endIconMode` | `setEndIconMode` `getEndIconMode` | `END_ICON_NONE`
-**Color** | `app:endIconTint` | `setEndIconTintList` | `colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Custom icon** | `app:endIconDrawable` | `setEndIconDrawable` `getEndIconDrawable` | `null`
-**Custom icon content description** | `app:endIconContentDescription` | `setEndIconContentDescription` `getEndIconContentDescription` | `null`
-**Custom icon checkable** | `app:endIconCheckable` | `setEndIconCheckable` `isEndIconCheckable` | `true`
-**Error icon** | `app:errorIconDrawable` | `setErrorIconDrawable` `getErrorIconDrawable` | [`@drawable/mtrl_ic_error`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/drawable/mtrl_ic_error.xml)
-**Error icon color** | `app:errorIconTint` | `setErrorIconTintList` | `?attr/colorError`
-**Size** | `app:endIconMinSize` | `setEndIconMinSize` `getEndIconMinSize` | `48dp`
-**Scale type** | `app:endIconScaleType` | `setEndIconScaleType` `getEndIconScaleType` | `ScaleType.CENTER`
+
-#### Activation indicator attributes
+
+```
-Element | Attribute | Related method(s) | Default value
------------------ | --------------------------- | ---------------------------------------------------------------------------- | -------------
-**Color** | `app:boxStrokeColor` | `setBoxStrokeColor` `setBoxStrokeColorStateList` `getBoxStrokeColor` | `?attr/colorOutline` and `?attr/colorPrimary` (focused) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_stroke_color.xml))
-**Error color** | `app:boxStrokeErrorColor` | `setBoxStrokeErrorColor` `getBoxStrokeErrorColor` | `?attr/colorError`
-**Width** | `app:boxStrokeWidth` | N/A | `1dp`
-**Focused width** | `app:boxStrokeWidthFocused` | N/A | `2dp`
+The string array specified by `app:simpleItems` will be used as the default item
+strings for auto-completion. Or you can also set it programmatically:
-#### Helper/error/counter text attributes
+```kt
+val items = arrayOf("Item 1", "Item 2", "Item 3", "Item 4")
+(textField.editText as? MaterialAutoCompleteTextView)?.setSimpleItems(items)
+```
-| Element | Attribute | Related method(s) | Default value |
-|------------------------------------------|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled` `isHelperTextEnabled` | `false` |
-| **Helper text** | `app:helperText` | `setHelperText` `getHelperText` | `null` |
-| **Helper text color** | `app:helperTextColor` | `setHelperTextColor` `getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) |
-| **Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall` |
-| **Error text enabled** | `app:errorEnabled` | `setErrorEnabled` `isErrorEnabled` | `false` |
-| **Error text** | N/A | `setError` `getError` | `null` |
-| **Error text accessibility live region** | `app:errorAccessibilityLiveRegion` | `setErrorAccessibilityLiveRegion` `getErrorAccessibilityLiveRegion` | `ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE` |
-| **Error text color** | `app:errorTextColor` | `setErrorTextColor` `getErrorCurrentTextColors` | `?attr/colorError` |
-| **Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall` |
-| **Counter text enabled** | `app:counterEnabled` | `setCounterEnabled` `isCounterEnabled` | `false` |
-| **Counter text length** | `app:counterMaxLength` | `setCounterMaxLength` `getCounterMaxLength` | `-1` |
-| **Counter text typography** | `app:counterTextAppearance` `app:counterOverflowTextAppearance` | `setCounterTextAppearance` `setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall` |
-| **Counter text color** | `app:counterTextColor` `app:counterOverflowTextColor` | `setCounterTextColor` `setCounterOverflowTextColor` `getCounterTextColor` `getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) `?attr/colorError` (`app:counterOverflowTextColor`) |
+Alternatively, to have more control over the auto-completion items rendering,
+you can also provide a custom item adapter by:
-#### Prefix/suffix attributes
+```kt
+val items = listOf("Item 1", "Item 2", "Item 3", "Item 4")
+val adapter = ArrayAdapter(requireContext(), R.layout.list_item, items)
+(textField.editText as? AutoCompleteTextView)?.setAdapter(adapter)
+```
-Element | Attribute | Related method(s) | Default value
---------------------- | -------------------------- | --------------------------------------------- | -------------
-**Prefix** | `app:prefixText` | `setPrefixText` `getPrefixText` | `null`
-**Prefix color** | `app:prefixTextColor` | `setPrefixTextColor` `getPrefixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Prefix typography** | `app:prefixTextAppearance` | `setPrefixTextAppearance` | `?attr/textAppearanceTitleMedium`
-**Suffix** | `app:suffixText` | `setSuffixText` `getSuffixText` | `null`
-**Suffix color** | `app:suffixTextColor` | `setSuffixTextColor` `getSuffixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Suffix typography** | `app:suffixTextAppearance` | `setSuffixTextAppearance` | `?attr/textAppearanceTitleMedium`
+And a custom item layout (`list_item.xml`):
-#### Styles
+```xml
+
+```
-Element | Style | Default style theme attribute
-------------------------------------- | ---------------------------------------------------------------------- | -----------------------------
-**Default style** | `Widget.Material3.TextInputLayout.FilledBox` | `?attr/textInputFilledStyle`
-**Dense style** | `Widget.Material3.TextInputLayout.FilledBox.Dense` | `?attr/textInputFilledDenseStyle`
-**Exposed dropdown menu style** | `Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu` | `?attr/textInputFilledExposedDropdownMenuStyle`
-**Dense exposed dropdown menu style** | `Widget.Material3.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu` | N/A
+
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml).
+
+
Adding helper text to a text field
-## Outlined text field
+
-[Outlined text fields](https://material.io/components/text-fields/#outlined-text-field)
-have less visual emphasis than filled text fields. When they appear in forms,
-for example, where many text fields are placed together, their reduced emphasis
-helps simplify the layout.
+```xml
+
-**Note:** The outlined text field is the default style.
+ ...
-### Outlined text field examples
+
+```
-The following example shows an outlined text field.
+
-
+
+
Adding a counter to a text field
-In the layout:
+
```xml
+ ...
+ app:counterEnabled="true"
+ app:counterMaxLength="20">
-
+ ...
```
-In code:
-
-```kt
-// Get input text
-val inputText = outlinedTextField.editText?.text.toString()
+
-outlinedTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
- // Respond to input text change
-}
-```
+
+
Adding errors to a text field
-See the [using text fields](#using-text-fields) section above for more examples.
+
-### Anatomy and key properties
+In the layout:
-An outlined text field has a stroked container, input text, a label, optional
-helper/error text and optional leading/trailing icons.
+```xml
+
-
+ ...
-1. Container
-2. Leading icon
-3. Label
-4. Input text
-5. Trailing icon
-6. Helper/error/counter text
-7. Prefix/suffix/placeholder (not shown)
+
+```
-#### Container attributes
+In code:
-Element | Attribute | Related method(s) | Default value
------------------------- | --------------------------- | ---------------------------------------------------------------------------- | -------------
-**Stroke color** | `app:boxStrokeColor` | `setBoxStrokeColor` `setBoxStrokeColorStateList` `getBoxStrokeColor` | `?attr/colorOutline` and `?attr/colorPrimary` (focused) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_stroke_color.xml))
-**Stroke error color** | `app:boxStrokeErrorColor` | `setBoxStrokeErrorColor` `getBoxStrokeErrorColor` | `?attr/colorError`
-**Stroke width** | `app:boxStrokeWidth` | N/A | `1dp`
-**Stroke focused width** | `app:boxStrokeWidthFocused` | N/A | `2dp`
-**Shape** | `app:shapeAppearance` | N/A | `?attr/shapeAppearanceSmallComponent`
-**Text field enabled** | `android:enabled` | `setEnabled` | `true`
+```kt
+// Set error text
+passwordLayout.error = getString(R.string.error)
-#### Leading icon attributes
+// Clear error text
+passwordLayout.error = null
+```
-Element | Attribute | Related method(s) | Default value
------------------------ | --------------------------------- | --------------------------------------------------------------------- | -------------
-**Icon** | `app:startIconDrawable` | `setStartIconDrawable` `getStartIconDrawable` | `null`
-**Content description** | `app:startIconContentDescription` | `setStartIconContentDescription` `getStartIconContentDescription` | `null`
-**Color** | `app:startIconTint` | `setStartIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Checkable** | `app:startIconCheckable` | `setStartIconCheckable` `isStartIconCheckable` | `false`
+**Note:** Non-null error text will replace any existing helper text, and
+non-null helper text will replace any existing error text.
-#### Label attributes
+
-Element | Attribute | Related method(s) | Default value
------------------------------- | ------------------------ | ------------------------------------------------------- | -------------
-**Text** | `android:hint` | `setHint` `getHint` | `null`
-**Color** | `android:textColorHint` | `setDefaultHintTextColor` `getDefaultHintTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_label_color.xml))
-**Collapsed (floating) color** | `app:hintTextColor` | `setHintTextColor` `getHintTextColor` | `?attr/colorPrimary`
-**Typography** | `app:hintTextAppearance` | `setHintTextAppearance` | `?attr/textAppearanceBodySmall`
-**Max number of lines** | `app:hintMaxLines` | `setHintMaxLines` `getHintMaxLines` | `1`
+
+
Adding a prefix/suffix to a text field
-**Note:** The `android:hint` should always be set on the `TextInputLayout`
-instead of on the `EditText` in order to avoid unintended behaviors.
+
-#### Input text attributes (set on the `TextInputEditText`)
+```xml
+
-Element | Attribute | Related method(s) | Default value
------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------ | -------------
-**Input text** | `android:text` | `setText` `getText` | `@null`
-**Typography** | `android:textAppearance` | `setTextAppearance` | `?attr/textAppearanceBodyLarge`
-**Input text color** | `android:textColor` | `setTextColor` `getTextColors` `getCurrentTextColor` | `?attr/colorOnSurface`
-**Cursor color** | N/A (color comes from the theme attr `?attr/colorControlActivated`) | N/A | `?attr/colorPrimary`
-**Text highlight color** | N/A (color comes from the theme attr `?android:attr/textColorHighlight`) | N/A | [`@color/m3_highlighted_text`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/color/res/color/m3_highlighted_text.xml)
+ ...
-#### Input text attributes (set on the `TextInputLayout`)
+
+```
-Element | Attribute | Related method(s) | Default value
------------------------- |------------------------------------------|-----------------------------------------------------------------------| -------------
-**Cursor color** | `app:cursorColor` on API levels 28+ | `setCursorColor` `getCursorColor` on API levels 28+ | `@null` (uses `?attr/colorControlActivated` by default)
-**Cursor error color** | `app:cursorErrorColor` on API levels 28+ | `setCursorErrorColor` `getCursorErrorColor` on API levels 28+ | `?attr/colorError` on API levels 28+, `?attr/colorControlActivated` otherwise
+
+
+
Setting text field dimensions
-#### Trailing icon attributes
+The recommended default `android:layout_width` is `245dp`.
-Element | Attribute | Related method(s) | Default value
------------------------------------ | ------------------------------- | ----------------------------------------------------------------- | -------------
-**Mode** | `app:endIconMode` | `setEndIconMode` `getEndIconMode` | `END_ICON_NONE`
-**Color** | `app:endIconTint` | `setEndIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Custom icon** | `app:endIconDrawable` | `setEndIconDrawable` `getEndIconDrawable` | `null`
-**Custom icon content description** | `app:endIconContentDescription` | `setEndIconContentDescription` `getEndIconContentDescription` | `null`
-**Custom icon checkable** | `app:endIconCheckable` | `setEndIconCheckable` `isEndIconCheckable` | `true`
-**Error icon** | `app:errorIconDrawable` | `setErrorIconDrawable` `getErrorIconDrawable` | [`@drawable/mtrl_ic_error`](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/drawable/mtrl_ic_error.xml)
-**Error icon color** | `app:errorIconTint` | `setErrorIconTintList` | `?attr/colorError`
+By default, text fields have a maximum width of `488dp`, and a minimum width of
+`56dp` for layouts without a label. If a label is present, the minimum width
+recommended is `88dp`. `android:minWidth` and `android:maxWidth` (as well as
+`android:minEms` and `android:maxEms`) should be set on the `TextInputLayout`
+instead of on the `TextInputEditText` to avoid unintended behaviors.
-#### Helper/error/counter text attributes
+You can override those values in a custom style that inherits from a
+`TextInputLayout` style or by making changes directly on the layout:
-| Element | Attribute | Related method(s) | Default value |
-|------------------------------------------|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled` `isHelperTextEnabled` | `false` |
-| **Helper text** | `app:helperText` | `setHelperText` `getHelperText` | `null` |
-| **Helper text color** | `app:helperTextColor` | `setHelperTextColor` `getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) |
-| **Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall` |
-| **Error text enabled** | `app:errorEnabled` | `setErrorEnabled` `isErrorEnabled` | `false` |
-| **Error text** | N/A | `setError` `getError` | `null` |
-| **Error text accessibility live region** | `app:errorAccessibilityLiveRegion` | `setErrorAccessibilityLiveRegion` `getErrorAccessibilityLiveRegion` | `ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE` |
-| **Error text color** | `app:errorTextColor` | `setErrorTextColor` `getErrorCurrentTextColors` | `?attr/colorError` |
-| **Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall` |
-| **Counter text enabled** | `app:counterEnabled` | `setCounterEnabled` `isCounterEnabled` | `false` |
-| **Counter text length** | `app:counterMaxLength` | `setCounterMaxLength` `getCounterMaxLength` | `-1` |
-| **Counter text typography** | `app:counterTextAppearance` `app:counterOverflowTextAppearance` | `setCounterTextAppearance` `setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall` |
-| **Counter text color** | `app:counterTextColor` `app:counterOverflowTextColor` | `setCounterTextColor` `setCounterOverflowTextColor` `getCounterTextColor` `getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) `?attr/colorError` (`app:counterOverflowTextColor`) |
+```xml
+
-#### Prefix/suffix attributes
+
-Element | Attribute | Related method(s) | Default value
---------------------- | -------------------------- | --------------------------------------------- | -------------
-**Prefix** | `app:prefixText` | `setPrefixText` `getPrefixText` | `null`
-**Prefix color** | `app:prefixTextColor` | `setPrefixTextColor` `getPrefixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Prefix typography** | `app:prefixTextAppearance` | `setPrefixTextAppearance` | `?attr/textAppearanceTitleMedium`
-**Suffix** | `app:suffixText` | `setSuffixText` `getSuffixText` | `null`
-**Suffix color** | `app:suffixTextColor` | `setSuffixTextColor` `getSuffixTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Suffix typography** | `app:suffixTextAppearance` | `setSuffixTextAppearance` | `?attr/textAppearanceTitleMedium`
+
+```
-#### Styles
+**Note:** The `android:layout_width` of the `TextInputLayout` should be
+`wrap_content` in order for those minimum and maximum dimensions to be used.
-Element | Style | Default style theme attribute
-------------------------------------- | ------------------------------------------------------------------------ | -----------------------------
-**Default style** | `Widget.Material3.TextInputLayout.OutlinedBox` | `?attr/textInputStyle` and `?attr/textInputOutlinedStyle`
-**Dense style** | `Widget.Material3.TextInputLayout.OutlinedBox.Dense` | `?attr/textInputOutlinedDenseStyle`
-**Exposed dropdown menu style** | `Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu` | `?attr/textInputOutlinedExposedDropdownMenuStyle`
-**Dense exposed dropdown menu style** | `Widget.Material3.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu` | N/A
+
-See the full list of
-[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/styles.xml)
-and
-[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/values/attrs.xml).
+## Customizing text fields
-## Theming text fields
+### Theming text fields
-Text fields support
-[Material Theming](https://material.io/components/text-fields/#theming) which
-provides color, typography and shape customization.
+Text fields support the customization of color, typography, and shape.
-### Text field theming example
+#### Text field theming example
API and source code:
@@ -759,12 +794,12 @@ API and source code:
* [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/TextInputEditText.java)
The following example shows filled and outlined text field types with Material
-Theming.
+theming.

-#### Implementing text field theming
+##### Implementing text field theming
Using theme attributes and styles in `res/values/styles.xml` adds themes to all
text fields and affects other components:
@@ -777,7 +812,7 @@ text fields and affects other components:
@color/shrine_red@style/TextAppearance.App.TitleMedium@style/TextAppearance.App.BodySmall
- @style/ShapeAppearance.App.SmallComponent
+ @style/ShapeAppearance.App.Corner.ExtraSmall
-
@@ -807,7 +842,7 @@ all text fields but does not affect other components:
diff --git a/docs/components/TimePicker.md b/docs/components/TimePicker.md
index 0c6cc916fd2..44aee292d08 100644
--- a/docs/components/TimePicker.md
+++ b/docs/components/TimePicker.md
@@ -1,5 +1,5 @@
+
+# Toggle button groups
+
+**Note:** Segmented buttons are being deprecated in the Material 3 expressive
+update. For those who have updated, use the
+[connected button group](https://github.com/material-components/material-components-android/tree/master/docs/components/ButtonGroup.md)
+instead, which has mostly the same functionality but with an updated visual
+design.
+
+[Toggle button group (Segmented buttons)](https://m3.material.io/components/segmented-buttons)
+can be used to select from a group of choices. There are two types of toggle
+buttons that can be placed in a toggle button group:
+
+* [Toggle button](#toggle-button)
+* [Icon toggle button](#icon-toggle-button)
+
+**Note:** Images use various dynamic color schemes.
+
+## Design & API documentation
+
+* [Material 3 (M3) spec](https://m3.material.io/components/segmented-buttons)
+* [API reference](https://developer.android.com/reference/com/google/android/material/button/package-summary)
+
+## Anatomy
+
+A toggle button has a shared stroked container, icons and/or text labels.
+
+
+
+1. Container
+2. Icon (optional for unselected state)
+3. Label text
+
+More details on anatomy items in the
+[component guidelines](https://m3.material.io/components/segmented-buttons/guidelines#75ec9219-0196-4c59-bd6a-ed9a1b481013).
+
+## M3 Expressive update
+
+The segmented button is being deprecated. Use the
+[connected button group](https://github.com/material-components/material-components-android/tree/master/docs/components/ButtonGroup.md)
+instead.
+[More on M3 Expressive](https://m3.material.io/blog/building-with-m3-expressive)
+
+## Key properties
+
+### Selection attributes
+
+Element | Attribute | Related method(s) | Default value
+------------------------------------- | ----------------------- | ------------------------------------------------ | -------------
+**Single selection** | `app:singleSelection` | `setSingleSelection` `isSingleSelection` | `false`
+**Selection required** | `app:selectionRequired` | `setSelectionRequired` `isSelectionRequired` | `false`
+**Enable the group and all children** | `android:enabled` | `setEnabled` `isEnabled` | `true`
+
+### Container attributes
+
+Element | Attribute | Related method(s) | Default value
+------------------------------- | --------------------- | --------------------------------------------- | -------------
+**Size of inner corners** | `app:innerCornerSize` | `setInnerCornerSize` `getInnerCornerSize` | `0dp`
+**Spacing between buttons** | `android:spacing` | `setSpacing` `getSpacing` | `0dp`
+**Group shape (outer corners)** | `app:shapeAppearance` | `setShapeAppearance``getShapeAppearance` | `none`
+
+### Styles
+
+Element | Style | Theme attribute
+----------------- | -------------------------------------------- | ---------------
+**Default style** | `Widget.Material3.MaterialButtonToggleGroup` | `?attr/materialButtonToggleGroupStyle`
+
+See the full list of
+[styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/res/values/styles.xml)
+and
+[attrs](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/res/values/attrs.xml).
+
+## Code implementation
+
+Before you can use Material buttons, you need to add a dependency to the
+Material components for Android library. For more information, go to the
+[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
+page.
+
+**Note:** `` is auto-inflated as
+`` via
+`MaterialComponentsViewInflater` when using a `Theme.Material3.*` theme.
+
+
Adding toggle button
+
+To emphasize groups of related toggle buttons, a group should share a common
+container.
+
+#### Toggle button examples
+
+API and source code:
+
+* `MaterialButtonToggleGroup`
+ * [Class description](https://developer.android.com/reference/com/google/android/material/button/MaterialButtonToggleGroup)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/MaterialButtonToggleGroup.java)
+* `MaterialButton`
+ * [Class description](https://developer.android.com/reference/com/google/android/material/button/MaterialButton)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/MaterialButton.java)
+
+The following example shows a toggle button with three buttons that have text
+labels.
+
+
+
+In the layout:
+
+```xml
+
+
+
+
+
+```
+
+In code:
+
+```kt
+toggleButton.addOnButtonCheckedListener { toggleButton, checkedId, isChecked ->
+ // Respond to button selection
+}
+```
+
+### Adding an icon-only toggle button
+
+The following example shows a toggle button with three buttons that have icons.
+
+
+
+In `res/values/styles.xml`:
+
+```xml
+
+```
+
+In the layout:
+
+```xml
+
+
+
+
+
+```
+
+
Adding icon toggle button
+
+Icons can be used as toggle buttons when they allow selection, or deselection,
+of a single choice, such as marking an item as a favorite.
+
+#### Icon toggle example
+
+API and source code:
+
+* `CheckBox`
+ * [Class description](https://developer.android.com/reference/android/widget/CheckBox)
+
+**Note:** The `CheckBox` API is just one of several inputs that can implement
+the icon button. See other
+[selection controls](https://material.io/components/selection-controls/) for
+more details.
+
+The following example shows an icon that can be used independently or in items
+of a `RecyclerView`.
+
+
+
+In the layout:
+
+```xml
+
+```
+
+In `res/drawable/sl_favourite_24dp.xml`:
+
+```xml
+
+
+
+
+
+```
+
+In code:
+
+```kt
+icon.setOnCheckedChangeListener { checkBox, isChecked ->
+ // Respond to icon toggle
+}
+```
+
+### Making buttons accessible
+
+Buttons support content labeling for accessibility and are readable by most
+screen readers, such as TalkBack. Text rendered in buttons is automatically
+provided to accessibility services. Additional content labels are usually
+unnecessary.
+
+For more information on content labels, go to the
+[Android accessibility help guide](https://support.google.com/accessibility/android/answer/7158690).
+
+## Customizing toggle button groups
+
+### Theming buttons
+
+Buttons support the customization of color, typography, and shape.
+
+#### Button theming example
+
+API and source code:
+
+* `MaterialButton`
+ * [Class description](https://developer.android.com/reference/com/google/android/material/button/MaterialButton)
+ * [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/button/MaterialButton.java)
+
+The following example shows text, outlined and filled button types with Material
+theming.
+
+
+
+##### Implementing button theming
+
+Use theme attributes and styles in `res/values/styles.xml` to add the theme to
+all buttons. This affects other components:
+
+```xml
+
+
+
+```
+
+Use default style theme attributes, styles and theme overlays. This adds the
+theme to all buttons but does not affect other components:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Use one of the styles in the layout. That will affect only this button:
+
+```xml
+
+
+```
+
+### Optical centering
+
+Optical centering means to offset the `MaterialButton`’s contents (icon and/or
+label) when the shape is asymmetric. Before optical centering, we only provided
+centering with horizontally asymmetrical shapes.
+
+To turn on optical centering for a given button, use
+`setOpticalCenterEnabled(true)`. Optical centering is disabled by default. When
+enabled, the shift amount of the icon and/or text is calculated as a value with
+the fixed ratio to the difference between left corner size in dp and right
+corner size in dp. The shift amount is applied to the padding start and padding
+end.
diff --git a/docs/components/TopAppBar.md b/docs/components/TopAppBar.md
index 74f69a9138d..b57007751da 100644
--- a/docs/components/TopAppBar.md
+++ b/docs/components/TopAppBar.md
@@ -378,23 +378,31 @@ Element | Attribute | Related method(s)
#### Title attributes
-Element | Attribute | Related method(s) | Default value
--------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------ | -------------
-**`MaterialToolbar` title text** | `app:title` | `setTitle` `getTitle` | `null`
-**`MaterialToolbar` subtitle text** | `app:subtitle` | `setSubtitle` `getSubtitle` | `null`
-**`MaterialToolbar` title color** | `app:titleTextColor` | `setTitleTextColor` | `?attr/colorOnSurface`
-**`MaterialToolbar` subtitle color** | `app:subtitleTextColor` | `setSubtitleTextColor` | `?attr/colorOnSurfaceVariant`
-**`MaterialToolbar` title typography** | `app:titleTextAppearance` | `setTitleTextAppearance` | `?attr/textAppearanceTitleLarge`
-**`MaterialToolbar` subtitle typography** | `app:subtitleTextAppearance` | `setSubtitleTextAppearance` | `?attr/textAppearanceTitleMedium`
-**`MaterialToolbar` title centering** | `app:titleCentered` | `setTitleCentered` | `false`
-**`MaterialToolbar` subtitle centering** | `app:subtitleCentered` | `setSubtitleCentered` | `false`
-**`CollapsingToolbarLayout` collapsed title typography** | `app:collapsedTitleTextAppearance` | `setCollapsedTitleTextAppearance` | `?attr/textAppearanceTitleLarge`
-**`CollapsingToolbarLayout` expanded title typography** | `app:expandedTitleTextAppearance` | `setExpandedTitleTextAppearance` | `?attr/textAppearanceHeadlineSmall` for Medium`?attr/textAppearanceHeadlineMedium` for Large
-**`CollapsingToolbarLayout` collapsed title color** | `android:textColor` (in `app:collapsedTitleTextAppearance`) or `app:collapsedTitleTextColor` | `setCollapsedTitleTextColor` | `?attr/colorOnSurface`
-**`CollapsingToolbarLayout` expanded title color** | `android:textColor` (in `app:expandedTitleTextAppearance`) or `app:expandedTitleTextColor` | `setExpandedTitleTextColor` | `?attr/colorOnSurface`
-**`CollapsingToolbarLayout` expanded title margins** | `app:expandedTitleMargin*` | `setExpandedTitleMargin*` | `16dp`
-**`CollapsingToolbarLayout` title max lines** | `app:maxLines` | `setMaxLines` `getMaxLines` | `1`
-**`CollapsingToolbarLayout` title ellipsize** | `app:titleTextEllipsize` | `setTitleEllipsize` `getTitleEllipsize` | `end`
+Element | Attribute | Related method(s) | Default value
+------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | -------------
+**`MaterialToolbar` title text** | `app:title` | `setTitle` `getTitle` | `null`
+**`MaterialToolbar` subtitle text** | `app:subtitle` | `setSubtitle` `getSubtitle` | `null`
+**`MaterialToolbar` title color** | `app:titleTextColor` | `setTitleTextColor` | `?attr/colorOnSurface`
+**`MaterialToolbar` subtitle color** | `app:subtitleTextColor` | `setSubtitleTextColor` | `?attr/colorOnSurfaceVariant`
+**`MaterialToolbar` title typography** | `app:titleTextAppearance` | `setTitleTextAppearance` | `?attr/textAppearanceTitleLarge`
+**`MaterialToolbar` subtitle typography** | `app:subtitleTextAppearance` | `setSubtitleTextAppearance` | `?attr/textAppearanceTitleMedium`
+**`MaterialToolbar` title centering** | `app:titleCentered` | `setTitleCentered` | `false`
+**`MaterialToolbar` subtitle centering** | `app:subtitleCentered` | `setSubtitleCentered` | `false`
+**`CollapsingToolbarLayout` collapsed title typography** | `app:collapsedTitleTextAppearance` | `setCollapsedTitleTextAppearance` | `?attr/textAppearanceTitleLarge`
+**`CollapsingToolbarLayout` expanded title typography** | `app:expandedTitleTextAppearance` | `setExpandedTitleTextAppearance` | `?attr/textAppearanceHeadlineSmall` for Medium`?attr/textAppearanceHeadlineMedium` for Large
+**`CollapsingToolbarLayout` collapsed title color** | `android:textColor` (in `app:collapsedTitleTextAppearance`) or `app:collapsedTitleTextColor` | `setCollapsedTitleTextColor` | `?attr/colorOnSurface`
+**`CollapsingToolbarLayout` expanded title color** | `android:textColor` (in `app:expandedTitleTextAppearance`) or `app:expandedTitleTextColor` | `setExpandedTitleTextColor` | `?attr/colorOnSurface`
+**`CollapsingToolbarLayout` collapsed subtitle typography** | `app:collapsedSubtitleTextAppearance` | `setCollapsedSubtitleTextAppearance` | `?attr/textAppearanceTitleMedium`
+**`CollapsingToolbarLayout` expanded subtitle typography** | `app:expandedSubtitleTextAppearance` | `setExpandedSubtitleTextAppearance` | `?attr/textAppearanceTitleLarge` for Medium`?attr/textAppearanceHeadlineSmall` for Large
+**`CollapsingToolbarLayout` collapsed subtitle color** | `android:textColor` (in `app:collapsedSubtitleTextAppearance`) or `app:collapsedSubtitleTextColor` | `setCollapsedSubtitleTextColor` | `?attr/colorOnSurface`
+**`CollapsingToolbarLayout` expanded subtitle color** | `android:textColor` (in `app:expandedSubtitleTextAppearance`) or `app:expandedSubtitleTextColor` | `setExpandedSubtitleTextColor` | `?attr/colorOnSurface`
+**`CollapsingToolbarLayout` expanded title margins** | `app:expandedTitleMargin*` | `setExpandedTitleMargin*` | `16dp`
+**`CollapsingToolbarLayout` padding between expanded title and subtitle** | `app:expandedTitlePadding` | `setExpandedTitlePadding` | `0dp`
+**`CollapsingToolbarLayout` title max lines** | `app:maxLines` | `setMaxLines` `getMaxLines` | `1`
+**`CollapsingToolbarLayout` title ellipsize** | `app:titleTextEllipsize` | `setTitleEllipsize` `getTitleEllipsize` | `end`
+**`CollapsingToolbarLayout` collapsed title gravity** | `app:collapsedTitleGravity` | `setCollapsedTitleGravity` `getCollapsedTitleGravity` | `start\|center_vertical`
+**`CollapsingToolbarLayout` collapsed title gravity mode** | `app:collapsedTitleGravityMode` | -- | `availableSpace`
+**`CollapsingToolbarLayout` expanded title gravity** | `app:expandedTitleGravity` | `setExpandedTitleGravity` `getExpandedTitleGravity` | `start\|bottom`
#### Action items attributes
diff --git a/docs/components/assets/badge/badges_anatomy.png b/docs/components/assets/badge/badges_anatomy.png
new file mode 100644
index 00000000000..eac077fd0f2
Binary files /dev/null and b/docs/components/assets/badge/badges_anatomy.png differ
diff --git a/docs/components/assets/badge/badges_hero.png b/docs/components/assets/badge/badges_hero.png
new file mode 100644
index 00000000000..0f7053aa66f
Binary files /dev/null and b/docs/components/assets/badge/badges_hero.png differ
diff --git a/docs/components/assets/badge/large_badge_hero.png b/docs/components/assets/badge/large_badge_hero.png
new file mode 100644
index 00000000000..463a965583c
Binary files /dev/null and b/docs/components/assets/badge/large_badge_hero.png differ
diff --git a/docs/components/assets/badge/small_badge_hero.png b/docs/components/assets/badge/small_badge_hero.png
new file mode 100644
index 00000000000..eb370f16abd
Binary files /dev/null and b/docs/components/assets/badge/small_badge_hero.png differ
diff --git a/docs/components/assets/bottomappbar/bottomappbar_theming.png b/docs/components/assets/bottomappbar/bottomappbar_theming.png
index 091fff7c80f..e67a3eabb16 100644
Binary files a/docs/components/assets/bottomappbar/bottomappbar_theming.png and b/docs/components/assets/bottomappbar/bottomappbar_theming.png differ
diff --git a/docs/components/assets/bottomnav/bottom-nav-anatomy.png b/docs/components/assets/bottomnav/bottom-nav-anatomy.png
deleted file mode 100644
index 0ed08f2b2da..00000000000
Binary files a/docs/components/assets/bottomnav/bottom-nav-anatomy.png and /dev/null differ
diff --git a/docs/components/assets/bottomnav/bottomnav_hero.png b/docs/components/assets/bottomnav/bottomnav_hero.png
deleted file mode 100644
index 6424a37f14f..00000000000
Binary files a/docs/components/assets/bottomnav/bottomnav_hero.png and /dev/null differ
diff --git a/docs/components/assets/bottomnav/bottomnav_horizontal.png b/docs/components/assets/bottomnav/bottomnav_horizontal.png
new file mode 100644
index 00000000000..c88c3ceba0b
Binary files /dev/null and b/docs/components/assets/bottomnav/bottomnav_horizontal.png differ
diff --git a/docs/components/assets/bottomnav/navigationbar_anatomy.png b/docs/components/assets/bottomnav/navigationbar_anatomy.png
new file mode 100644
index 00000000000..fdd8cfa1327
Binary files /dev/null and b/docs/components/assets/bottomnav/navigationbar_anatomy.png differ
diff --git a/docs/components/assets/bottomnav/navigationbar_expressive.png b/docs/components/assets/bottomnav/navigationbar_expressive.png
new file mode 100644
index 00000000000..af03941d64d
Binary files /dev/null and b/docs/components/assets/bottomnav/navigationbar_expressive.png differ
diff --git a/docs/components/assets/bottomnav/navigationbar_hero.png b/docs/components/assets/bottomnav/navigationbar_hero.png
new file mode 100644
index 00000000000..fa278d24103
Binary files /dev/null and b/docs/components/assets/bottomnav/navigationbar_hero.png differ
diff --git a/docs/components/assets/bottomsheet/bottomsheets_types.png b/docs/components/assets/bottomsheet/bottomsheets_types.png
new file mode 100644
index 00000000000..2dd4908f079
Binary files /dev/null and b/docs/components/assets/bottomsheet/bottomsheets_types.png differ
diff --git a/docs/components/assets/buttons/buttongroup_anatomy.png b/docs/components/assets/buttons/buttongroup_anatomy.png
new file mode 100644
index 00000000000..60f014ff28f
Binary files /dev/null and b/docs/components/assets/buttons/buttongroup_anatomy.png differ
diff --git a/docs/components/assets/buttons/buttongroup_expressive.png b/docs/components/assets/buttons/buttongroup_expressive.png
new file mode 100644
index 00000000000..7c2d887b048
Binary files /dev/null and b/docs/components/assets/buttons/buttongroup_expressive.png differ
diff --git a/docs/components/assets/buttons/buttongroup_types.png b/docs/components/assets/buttons/buttongroup_types.png
new file mode 100644
index 00000000000..64a462e0056
Binary files /dev/null and b/docs/components/assets/buttons/buttongroup_types.png differ
diff --git a/docs/components/assets/buttons/commonbutton_anatomy.png b/docs/components/assets/buttons/commonbutton_anatomy.png
new file mode 100644
index 00000000000..f350094ab08
Binary files /dev/null and b/docs/components/assets/buttons/commonbutton_anatomy.png differ
diff --git a/docs/components/assets/buttons/commonbutton_expressive.png b/docs/components/assets/buttons/commonbutton_expressive.png
new file mode 100644
index 00000000000..f35b417f7d1
Binary files /dev/null and b/docs/components/assets/buttons/commonbutton_expressive.png differ
diff --git a/docs/components/assets/buttons/commonbutton_styles.png b/docs/components/assets/buttons/commonbutton_styles.png
new file mode 100644
index 00000000000..4c4dfe54a36
Binary files /dev/null and b/docs/components/assets/buttons/commonbutton_styles.png differ
diff --git a/docs/components/assets/buttons/commonbutton_types.png b/docs/components/assets/buttons/commonbutton_types.png
new file mode 100644
index 00000000000..df60a43f7ac
Binary files /dev/null and b/docs/components/assets/buttons/commonbutton_types.png differ
diff --git a/docs/components/assets/buttons/commonbuttons_types.png b/docs/components/assets/buttons/commonbuttons_types.png
new file mode 100644
index 00000000000..4eb59e22f92
Binary files /dev/null and b/docs/components/assets/buttons/commonbuttons_types.png differ
diff --git a/docs/components/assets/buttons/connected_button_group.png b/docs/components/assets/buttons/connected_button_group.png
index 1264f9580f7..76c4c9c9cec 100644
Binary files a/docs/components/assets/buttons/connected_button_group.png and b/docs/components/assets/buttons/connected_button_group.png differ
diff --git a/docs/components/assets/buttons/default_button_group.png b/docs/components/assets/buttons/default_button_group.png
deleted file mode 100644
index 03be73eb1d1..00000000000
Binary files a/docs/components/assets/buttons/default_button_group.png and /dev/null differ
diff --git a/docs/components/assets/buttons/fillediconbutton_anatomy.png b/docs/components/assets/buttons/fillediconbutton_anatomy.png
new file mode 100644
index 00000000000..802dbdba3ad
Binary files /dev/null and b/docs/components/assets/buttons/fillediconbutton_anatomy.png differ
diff --git a/docs/components/assets/buttons/filledtonaliconbutton_anatomy.png b/docs/components/assets/buttons/filledtonaliconbutton_anatomy.png
new file mode 100644
index 00000000000..dabc0f24d89
Binary files /dev/null and b/docs/components/assets/buttons/filledtonaliconbutton_anatomy.png differ
diff --git a/docs/components/assets/buttons/group_arrangement_fixed.png b/docs/components/assets/buttons/group_arrangement_fixed.png
new file mode 100644
index 00000000000..f624a18463c
Binary files /dev/null and b/docs/components/assets/buttons/group_arrangement_fixed.png differ
diff --git a/docs/components/assets/buttons/group_arrangement_flexible.png b/docs/components/assets/buttons/group_arrangement_flexible.png
new file mode 100644
index 00000000000..e5d7e487e5b
Binary files /dev/null and b/docs/components/assets/buttons/group_arrangement_flexible.png differ
diff --git a/docs/components/assets/buttons/group_arrangement_mixed.png b/docs/components/assets/buttons/group_arrangement_mixed.png
new file mode 100644
index 00000000000..706bb2dca21
Binary files /dev/null and b/docs/components/assets/buttons/group_arrangement_mixed.png differ
diff --git a/docs/components/assets/buttons/iconbutton_types.png b/docs/components/assets/buttons/iconbutton_types.png
new file mode 100644
index 00000000000..8f6eb9318fd
Binary files /dev/null and b/docs/components/assets/buttons/iconbutton_types.png differ
diff --git a/docs/components/assets/buttons/iconbuttons_anatomy.png b/docs/components/assets/buttons/iconbuttons_anatomy.png
new file mode 100644
index 00000000000..b0a7dbd2bca
Binary files /dev/null and b/docs/components/assets/buttons/iconbuttons_anatomy.png differ
diff --git a/docs/components/assets/buttons/iconbuttons_codeimplementation.png b/docs/components/assets/buttons/iconbuttons_codeimplementation.png
new file mode 100644
index 00000000000..3f56235d9e2
Binary files /dev/null and b/docs/components/assets/buttons/iconbuttons_codeimplementation.png differ
diff --git a/docs/components/assets/buttons/iconbuttons_color.png b/docs/components/assets/buttons/iconbuttons_color.png
new file mode 100644
index 00000000000..bfca998235f
Binary files /dev/null and b/docs/components/assets/buttons/iconbuttons_color.png differ
diff --git a/docs/components/assets/buttons/iconbuttons_expressive.png b/docs/components/assets/buttons/iconbuttons_expressive.png
new file mode 100644
index 00000000000..f436fd504b4
Binary files /dev/null and b/docs/components/assets/buttons/iconbuttons_expressive.png differ
diff --git a/docs/components/assets/buttons/iconbuttons_types.png b/docs/components/assets/buttons/iconbuttons_types.png
new file mode 100644
index 00000000000..012edc80c5f
Binary files /dev/null and b/docs/components/assets/buttons/iconbuttons_types.png differ
diff --git a/docs/components/assets/buttons/outlinediconbutton_anatomy.png b/docs/components/assets/buttons/outlinediconbutton_anatomy.png
new file mode 100644
index 00000000000..b2e26eaa411
Binary files /dev/null and b/docs/components/assets/buttons/outlinediconbutton_anatomy.png differ
diff --git a/docs/components/assets/buttons/split_button.png b/docs/components/assets/buttons/split_button.png
index 6d0f3cab17d..76e9544cde5 100644
Binary files a/docs/components/assets/buttons/split_button.png and b/docs/components/assets/buttons/split_button.png differ
diff --git a/docs/components/assets/buttons/splitbutton_anatomy.png b/docs/components/assets/buttons/splitbutton_anatomy.png
new file mode 100644
index 00000000000..5f36b5266e6
Binary files /dev/null and b/docs/components/assets/buttons/splitbutton_anatomy.png differ
diff --git a/docs/components/assets/buttons/splitbutton_expressive.png b/docs/components/assets/buttons/splitbutton_expressive.png
new file mode 100644
index 00000000000..17d515b8353
Binary files /dev/null and b/docs/components/assets/buttons/splitbutton_expressive.png differ
diff --git a/docs/components/assets/buttons/splitbutton_hero.png b/docs/components/assets/buttons/splitbutton_hero.png
new file mode 100644
index 00000000000..f8d4f3b5af7
Binary files /dev/null and b/docs/components/assets/buttons/splitbutton_hero.png differ
diff --git a/docs/components/assets/buttons/standard_button_group.png b/docs/components/assets/buttons/standard_button_group.png
new file mode 100644
index 00000000000..f1780380ff5
Binary files /dev/null and b/docs/components/assets/buttons/standard_button_group.png differ
diff --git a/docs/components/assets/buttons/standardiconbutton_anatomy.png b/docs/components/assets/buttons/standardiconbutton_anatomy.png
new file mode 100644
index 00000000000..eef90da657f
Binary files /dev/null and b/docs/components/assets/buttons/standardiconbutton_anatomy.png differ
diff --git a/docs/components/assets/buttons/togglebuttongroup_anatomy.png b/docs/components/assets/buttons/togglebuttongroup_anatomy.png
new file mode 100644
index 00000000000..3e50b87c445
Binary files /dev/null and b/docs/components/assets/buttons/togglebuttongroup_anatomy.png differ
diff --git a/docs/components/assets/cards/card_anatomy.png b/docs/components/assets/cards/card_anatomy.png
index 431c12250e1..757e89f746f 100644
Binary files a/docs/components/assets/cards/card_anatomy.png and b/docs/components/assets/cards/card_anatomy.png differ
diff --git a/docs/components/assets/cards/cards_types.png b/docs/components/assets/cards/cards_types.png
new file mode 100644
index 00000000000..6d0607276c2
Binary files /dev/null and b/docs/components/assets/cards/cards_types.png differ
diff --git a/docs/components/assets/carousel/carousel-header.png b/docs/components/assets/carousel/carousel-header.png
deleted file mode 100644
index 68351da4eef..00000000000
Binary files a/docs/components/assets/carousel/carousel-header.png and /dev/null differ
diff --git a/docs/components/assets/carousel/carousel-header1.png b/docs/components/assets/carousel/carousel-header1.png
new file mode 100644
index 00000000000..374986ea84c
Binary files /dev/null and b/docs/components/assets/carousel/carousel-header1.png differ
diff --git a/docs/components/assets/carousel/centeralignedhero_anatomy.png b/docs/components/assets/carousel/centeralignedhero_anatomy.png
new file mode 100644
index 00000000000..38cf62ac05b
Binary files /dev/null and b/docs/components/assets/carousel/centeralignedhero_anatomy.png differ
diff --git a/docs/components/assets/carousel/custom.png b/docs/components/assets/carousel/custom.png
deleted file mode 100644
index e280dd6ea98..00000000000
Binary files a/docs/components/assets/carousel/custom.png and /dev/null differ
diff --git a/docs/components/assets/carousel/fullscreen_anatomy.png b/docs/components/assets/carousel/fullscreen_anatomy.png
new file mode 100644
index 00000000000..d054359546b
Binary files /dev/null and b/docs/components/assets/carousel/fullscreen_anatomy.png differ
diff --git a/docs/components/assets/carousel/hero_anatomy.png b/docs/components/assets/carousel/hero_anatomy.png
new file mode 100644
index 00000000000..1c88e663bf9
Binary files /dev/null and b/docs/components/assets/carousel/hero_anatomy.png differ
diff --git a/docs/components/assets/carousel/multibrowse_anatomy.png b/docs/components/assets/carousel/multibrowse_anatomy.png
new file mode 100644
index 00000000000..091604a5f10
Binary files /dev/null and b/docs/components/assets/carousel/multibrowse_anatomy.png differ
diff --git a/docs/components/assets/carousel/uncontained_anatomy.png b/docs/components/assets/carousel/uncontained_anatomy.png
new file mode 100644
index 00000000000..da121b62b5c
Binary files /dev/null and b/docs/components/assets/carousel/uncontained_anatomy.png differ
diff --git a/docs/components/assets/checkbox/checkbox_anatomy.png b/docs/components/assets/checkbox/checkbox_anatomy.png
index 48542b4b12f..5a434e57716 100644
Binary files a/docs/components/assets/checkbox/checkbox_anatomy.png and b/docs/components/assets/checkbox/checkbox_anatomy.png differ
diff --git a/docs/components/assets/checkbox/checkbox_checked_unchecked_.png b/docs/components/assets/checkbox/checkbox_checked_unchecked_.png
new file mode 100644
index 00000000000..73a3ca054d8
Binary files /dev/null and b/docs/components/assets/checkbox/checkbox_checked_unchecked_.png differ
diff --git a/docs/components/assets/checkbox/checkbox_hero.png b/docs/components/assets/checkbox/checkbox_hero.png
index c657f3b89ed..e5fcce5116e 100644
Binary files a/docs/components/assets/checkbox/checkbox_hero.png and b/docs/components/assets/checkbox/checkbox_hero.png differ
diff --git a/docs/components/assets/chips/assist_anatomy.png b/docs/components/assets/chips/assist_anatomy.png
new file mode 100644
index 00000000000..73845a35aab
Binary files /dev/null and b/docs/components/assets/chips/assist_anatomy.png differ
diff --git a/docs/components/assets/chips/filter_anatomy.png b/docs/components/assets/chips/filter_anatomy.png
new file mode 100644
index 00000000000..b8068d47019
Binary files /dev/null and b/docs/components/assets/chips/filter_anatomy.png differ
diff --git a/docs/components/assets/chips/input_anatomy.png b/docs/components/assets/chips/input_anatomy.png
new file mode 100644
index 00000000000..5286ce128ef
Binary files /dev/null and b/docs/components/assets/chips/input_anatomy.png differ
diff --git a/docs/components/assets/chips/suggestion_anatomy.png b/docs/components/assets/chips/suggestion_anatomy.png
new file mode 100644
index 00000000000..199fc6fa4c9
Binary files /dev/null and b/docs/components/assets/chips/suggestion_anatomy.png differ
diff --git a/docs/components/assets/datepicker/datepickers_types.png b/docs/components/assets/datepicker/datepickers_types.png
new file mode 100644
index 00000000000..1c8892fb61f
Binary files /dev/null and b/docs/components/assets/datepicker/datepickers_types.png differ
diff --git a/docs/components/assets/datepicker/dockeddatepicker.png b/docs/components/assets/datepicker/dockeddatepicker.png
new file mode 100644
index 00000000000..ba4552d64fb
Binary files /dev/null and b/docs/components/assets/datepicker/dockeddatepicker.png differ
diff --git a/docs/components/assets/datepicker/dockeddatepicker_anatomy1.png b/docs/components/assets/datepicker/dockeddatepicker_anatomy1.png
new file mode 100644
index 00000000000..ffa46d2fda3
Binary files /dev/null and b/docs/components/assets/datepicker/dockeddatepicker_anatomy1.png differ
diff --git a/docs/components/assets/datepicker/dockeddatepicker_anatomy2.png b/docs/components/assets/datepicker/dockeddatepicker_anatomy2.png
new file mode 100644
index 00000000000..fb661725a59
Binary files /dev/null and b/docs/components/assets/datepicker/dockeddatepicker_anatomy2.png differ
diff --git a/docs/components/assets/datepicker/modaldateinput.png b/docs/components/assets/datepicker/modaldateinput.png
new file mode 100644
index 00000000000..0bdf54bf19b
Binary files /dev/null and b/docs/components/assets/datepicker/modaldateinput.png differ
diff --git a/docs/components/assets/datepicker/modaldateinput_anatomy.png b/docs/components/assets/datepicker/modaldateinput_anatomy.png
new file mode 100644
index 00000000000..d556417edc5
Binary files /dev/null and b/docs/components/assets/datepicker/modaldateinput_anatomy.png differ
diff --git a/docs/components/assets/datepicker/modaldatepicker.png b/docs/components/assets/datepicker/modaldatepicker.png
new file mode 100644
index 00000000000..fc84701917d
Binary files /dev/null and b/docs/components/assets/datepicker/modaldatepicker.png differ
diff --git a/docs/components/assets/datepicker/modaldatepicker_anatomy1.png b/docs/components/assets/datepicker/modaldatepicker_anatomy1.png
new file mode 100644
index 00000000000..1c03ea0f6ad
Binary files /dev/null and b/docs/components/assets/datepicker/modaldatepicker_anatomy1.png differ
diff --git a/docs/components/assets/datepicker/modaldatepicker_anatomy2.png b/docs/components/assets/datepicker/modaldatepicker_anatomy2.png
new file mode 100644
index 00000000000..1d177fa66d1
Binary files /dev/null and b/docs/components/assets/datepicker/modaldatepicker_anatomy2.png differ
diff --git a/docs/components/assets/datepicker/modaldatepicker_anatomy3.png b/docs/components/assets/datepicker/modaldatepicker_anatomy3.png
new file mode 100644
index 00000000000..52b856f3fd3
Binary files /dev/null and b/docs/components/assets/datepicker/modaldatepicker_anatomy3.png differ
diff --git a/docs/components/assets/dialogs/basic_dialogs_anatomy.png b/docs/components/assets/dialogs/basic_dialogs_anatomy.png
new file mode 100644
index 00000000000..0e2215f88d7
Binary files /dev/null and b/docs/components/assets/dialogs/basic_dialogs_anatomy.png differ
diff --git a/docs/components/assets/dialogs/dialogs_anatomy.png b/docs/components/assets/dialogs/dialogs_anatomy.png
deleted file mode 100644
index c336ee469cb..00000000000
Binary files a/docs/components/assets/dialogs/dialogs_anatomy.png and /dev/null differ
diff --git a/docs/components/assets/dialogs/dialogs_hero.png b/docs/components/assets/dialogs/dialogs_hero.png
deleted file mode 100644
index 957f85ff80d..00000000000
Binary files a/docs/components/assets/dialogs/dialogs_hero.png and /dev/null differ
diff --git a/docs/components/assets/dialogs/dialogs_types.png b/docs/components/assets/dialogs/dialogs_types.png
deleted file mode 100644
index e6583d265f7..00000000000
Binary files a/docs/components/assets/dialogs/dialogs_types.png and /dev/null differ
diff --git a/docs/components/assets/dialogs/dialogs_types_hero.png b/docs/components/assets/dialogs/dialogs_types_hero.png
new file mode 100644
index 00000000000..4994174aeeb
Binary files /dev/null and b/docs/components/assets/dialogs/dialogs_types_hero.png differ
diff --git a/docs/components/assets/dialogs/full-screen_dialogs_anatomy.png b/docs/components/assets/dialogs/full-screen_dialogs_anatomy.png
new file mode 100644
index 00000000000..8958cf0fcd0
Binary files /dev/null and b/docs/components/assets/dialogs/full-screen_dialogs_anatomy.png differ
diff --git a/docs/components/assets/dividers/divider_anatomy.png b/docs/components/assets/dividers/divider_anatomy.png
new file mode 100644
index 00000000000..c0960241ab5
Binary files /dev/null and b/docs/components/assets/dividers/divider_anatomy.png differ
diff --git a/docs/components/assets/dividers/divider_full_width_type.png b/docs/components/assets/dividers/divider_full_width_type.png
new file mode 100644
index 00000000000..af1c45d2fbe
Binary files /dev/null and b/docs/components/assets/dividers/divider_full_width_type.png differ
diff --git a/docs/components/assets/dividers/divider_fullwidth.png b/docs/components/assets/dividers/divider_fullwidth.png
new file mode 100644
index 00000000000..53b8eef0d4c
Binary files /dev/null and b/docs/components/assets/dividers/divider_fullwidth.png differ
diff --git a/docs/components/assets/dividers/divider_hero.png b/docs/components/assets/dividers/divider_hero.png
deleted file mode 100644
index e7976bf9788..00000000000
Binary files a/docs/components/assets/dividers/divider_hero.png and /dev/null differ
diff --git a/docs/components/assets/dividers/divider_inset.png b/docs/components/assets/dividers/divider_inset.png
new file mode 100644
index 00000000000..051c074abeb
Binary files /dev/null and b/docs/components/assets/dividers/divider_inset.png differ
diff --git a/docs/components/assets/dividers/divider_vertical.png b/docs/components/assets/dividers/divider_vertical.png
new file mode 100644
index 00000000000..a54a01f7df2
Binary files /dev/null and b/docs/components/assets/dividers/divider_vertical.png differ
diff --git a/docs/components/assets/dockedtoolbar/docked_toolbar.png b/docs/components/assets/dockedtoolbar/docked_toolbar.png
new file mode 100644
index 00000000000..f74076f1fd8
Binary files /dev/null and b/docs/components/assets/dockedtoolbar/docked_toolbar.png differ
diff --git a/docs/components/assets/dockedtoolbar/docked_toolbar_anatomy.png b/docs/components/assets/dockedtoolbar/docked_toolbar_anatomy.png
new file mode 100644
index 00000000000..83ebccbd4d7
Binary files /dev/null and b/docs/components/assets/dockedtoolbar/docked_toolbar_anatomy.png differ
diff --git a/docs/components/assets/dockedtoolbar/docked_toolbar_theming.png b/docs/components/assets/dockedtoolbar/docked_toolbar_theming.png
new file mode 100644
index 00000000000..c740e7b1783
Binary files /dev/null and b/docs/components/assets/dockedtoolbar/docked_toolbar_theming.png differ
diff --git a/docs/components/assets/fabmenu/fabmenu_anatomy.png b/docs/components/assets/fabmenu/fabmenu_anatomy.png
new file mode 100644
index 00000000000..4c4ca353605
Binary files /dev/null and b/docs/components/assets/fabmenu/fabmenu_anatomy.png differ
diff --git a/docs/components/assets/fabmenu/fabmenu_expressive.png b/docs/components/assets/fabmenu/fabmenu_expressive.png
new file mode 100644
index 00000000000..276460e55dc
Binary files /dev/null and b/docs/components/assets/fabmenu/fabmenu_expressive.png differ
diff --git a/docs/components/assets/fabmenu/fabmenu_hero.png b/docs/components/assets/fabmenu/fabmenu_hero.png
new file mode 100644
index 00000000000..537256746a5
Binary files /dev/null and b/docs/components/assets/fabmenu/fabmenu_hero.png differ
diff --git a/docs/components/assets/fabmenu/fabmenu_type.png b/docs/components/assets/fabmenu/fabmenu_type.png
new file mode 100644
index 00000000000..6a7973a6986
Binary files /dev/null and b/docs/components/assets/fabmenu/fabmenu_type.png differ
diff --git a/docs/components/assets/fabs/EFAB_anatomy.png b/docs/components/assets/fabs/EFAB_anatomy.png
new file mode 100644
index 00000000000..2e934ef8479
Binary files /dev/null and b/docs/components/assets/fabs/EFAB_anatomy.png differ
diff --git a/docs/components/assets/fabs/EFAB_expressive.png b/docs/components/assets/fabs/EFAB_expressive.png
new file mode 100644
index 00000000000..18922d7dd0f
Binary files /dev/null and b/docs/components/assets/fabs/EFAB_expressive.png differ
diff --git a/docs/components/assets/fabs/EFAB_hero.png b/docs/components/assets/fabs/EFAB_hero.png
new file mode 100644
index 00000000000..136bed06c7c
Binary files /dev/null and b/docs/components/assets/fabs/EFAB_hero.png differ
diff --git a/docs/components/assets/fabs/EFAB_sizes.png b/docs/components/assets/fabs/EFAB_sizes.png
new file mode 100644
index 00000000000..e80a69a8d1b
Binary files /dev/null and b/docs/components/assets/fabs/EFAB_sizes.png differ
diff --git a/docs/components/assets/fabs/EFAB_types.png b/docs/components/assets/fabs/EFAB_types.png
new file mode 100644
index 00000000000..e38b9a3bbcc
Binary files /dev/null and b/docs/components/assets/fabs/EFAB_types.png differ
diff --git a/docs/components/assets/fabs/FAB_3sizes.png b/docs/components/assets/fabs/FAB_3sizes.png
new file mode 100644
index 00000000000..a90cb291489
Binary files /dev/null and b/docs/components/assets/fabs/FAB_3sizes.png differ
diff --git a/docs/components/assets/fabs/FAB_anatomy.png b/docs/components/assets/fabs/FAB_anatomy.png
new file mode 100644
index 00000000000..1df4987c952
Binary files /dev/null and b/docs/components/assets/fabs/FAB_anatomy.png differ
diff --git a/docs/components/assets/fabs/FAB_anatomy-long.png b/docs/components/assets/fabs/FAB_anatomy_long.png
similarity index 100%
rename from docs/components/assets/fabs/FAB_anatomy-long.png
rename to docs/components/assets/fabs/FAB_anatomy_long.png
diff --git a/docs/components/assets/fabs/FAB_expressive.png b/docs/components/assets/fabs/FAB_expressive.png
new file mode 100644
index 00000000000..b1ff5b4dba2
Binary files /dev/null and b/docs/components/assets/fabs/FAB_expressive.png differ
diff --git a/docs/components/assets/fabs/FAB_large.png b/docs/components/assets/fabs/FAB_large.png
new file mode 100644
index 00000000000..0042d57f0f2
Binary files /dev/null and b/docs/components/assets/fabs/FAB_large.png differ
diff --git a/docs/components/assets/fabs/FAB_medium.png b/docs/components/assets/fabs/FAB_medium.png
new file mode 100644
index 00000000000..7c1bd4a027d
Binary files /dev/null and b/docs/components/assets/fabs/FAB_medium.png differ
diff --git a/docs/components/assets/fabs/FAB_types.png b/docs/components/assets/fabs/FAB_types.png
index 92d47f1f8a5..109f6a35d59 100644
Binary files a/docs/components/assets/fabs/FAB_types.png and b/docs/components/assets/fabs/FAB_types.png differ
diff --git a/docs/components/assets/fabs/FABs_sizes.png b/docs/components/assets/fabs/FABs_sizes.png
new file mode 100644
index 00000000000..a90cb291489
Binary files /dev/null and b/docs/components/assets/fabs/FABs_sizes.png differ
diff --git a/docs/components/assets/fabs/extended-FAB_anatomy-long.png b/docs/components/assets/fabs/extendedFAB_anatomy_long.png
similarity index 100%
rename from docs/components/assets/fabs/extended-FAB_anatomy-long.png
rename to docs/components/assets/fabs/extendedFAB_anatomy_long.png
diff --git a/docs/components/assets/fabs/fab_large.png b/docs/components/assets/fabs/fab_large.png
deleted file mode 100644
index 897269d3e84..00000000000
Binary files a/docs/components/assets/fabs/fab_large.png and /dev/null differ
diff --git a/docs/components/assets/fabs/largeFAB_anatomy-long.png b/docs/components/assets/fabs/largeFAB_anatomy_long.png
similarity index 100%
rename from docs/components/assets/fabs/largeFAB_anatomy-long.png
rename to docs/components/assets/fabs/largeFAB_anatomy_long.png
diff --git a/docs/components/assets/fabs/miniFAB_anatomy-long.png b/docs/components/assets/fabs/miniFAB_anatomy_long.png
similarity index 100%
rename from docs/components/assets/fabs/miniFAB_anatomy-long.png
rename to docs/components/assets/fabs/miniFAB_anatomy_long.png
diff --git a/docs/components/assets/floatingtoolbar/ftbanatomy.png b/docs/components/assets/floatingtoolbar/ftbanatomy.png
new file mode 100644
index 00000000000..18a3202159c
Binary files /dev/null and b/docs/components/assets/floatingtoolbar/ftbanatomy.png differ
diff --git a/docs/components/assets/floatingtoolbar/ftbhorizontal.png b/docs/components/assets/floatingtoolbar/ftbhorizontal.png
new file mode 100644
index 00000000000..21da38cac63
Binary files /dev/null and b/docs/components/assets/floatingtoolbar/ftbhorizontal.png differ
diff --git a/docs/components/assets/floatingtoolbar/ftbtheming.png b/docs/components/assets/floatingtoolbar/ftbtheming.png
new file mode 100644
index 00000000000..cfa7f03ae75
Binary files /dev/null and b/docs/components/assets/floatingtoolbar/ftbtheming.png differ
diff --git a/docs/components/assets/floatingtoolbar/ftbvertical.png b/docs/components/assets/floatingtoolbar/ftbvertical.png
new file mode 100644
index 00000000000..9b27807a1fb
Binary files /dev/null and b/docs/components/assets/floatingtoolbar/ftbvertical.png differ
diff --git a/docs/components/assets/lists/list_hero.png b/docs/components/assets/lists/list_hero.png
new file mode 100644
index 00000000000..b09a3994f23
Binary files /dev/null and b/docs/components/assets/lists/list_hero.png differ
diff --git a/docs/components/assets/lists/lists_anatomy.png b/docs/components/assets/lists/lists_anatomy.png
new file mode 100644
index 00000000000..52d789f328d
Binary files /dev/null and b/docs/components/assets/lists/lists_anatomy.png differ
diff --git a/docs/components/assets/lists/lists_hero.png b/docs/components/assets/lists/lists_hero.png
new file mode 100644
index 00000000000..b09a3994f23
Binary files /dev/null and b/docs/components/assets/lists/lists_hero.png differ
diff --git a/docs/components/assets/lists/lists_sizes.png b/docs/components/assets/lists/lists_sizes.png
new file mode 100644
index 00000000000..0ffa3e42ae2
Binary files /dev/null and b/docs/components/assets/lists/lists_sizes.png differ
diff --git a/docs/components/assets/navigationdrawer/navigation-drawer-anatomy.png b/docs/components/assets/navigationdrawer/navigation-drawer-anatomy.png
deleted file mode 100644
index 4f34335a550..00000000000
Binary files a/docs/components/assets/navigationdrawer/navigation-drawer-anatomy.png and /dev/null differ
diff --git a/docs/components/assets/navigationdrawer/navigation_drawer_anatomy.png b/docs/components/assets/navigationdrawer/navigation_drawer_anatomy.png
new file mode 100644
index 00000000000..e3c5bb9dead
Binary files /dev/null and b/docs/components/assets/navigationdrawer/navigation_drawer_anatomy.png differ
diff --git a/docs/components/assets/navigationdrawer/navigation_drawer_overview.png b/docs/components/assets/navigationdrawer/navigation_drawer_overview.png
new file mode 100644
index 00000000000..452e8002e16
Binary files /dev/null and b/docs/components/assets/navigationdrawer/navigation_drawer_overview.png differ
diff --git a/docs/components/assets/navigationrail/collapsed_nav_rail.png b/docs/components/assets/navigationrail/collapsed_nav_rail.png
new file mode 100644
index 00000000000..2b818e386ea
Binary files /dev/null and b/docs/components/assets/navigationrail/collapsed_nav_rail.png differ
diff --git a/docs/components/assets/navigationrail/expanded_nav_rail.png b/docs/components/assets/navigationrail/expanded_nav_rail.png
new file mode 100644
index 00000000000..d147e71246b
Binary files /dev/null and b/docs/components/assets/navigationrail/expanded_nav_rail.png differ
diff --git a/docs/components/assets/progressindicator/Progress_Indicator_Overview.mp4 b/docs/components/assets/progressindicator/Progress_Indicator_Overview.mp4
new file mode 100644
index 00000000000..f8adbced1e7
Binary files /dev/null and b/docs/components/assets/progressindicator/Progress_Indicator_Overview.mp4 differ
diff --git a/docs/components/assets/progressindicator/determinate_indeterminate.mp4 b/docs/components/assets/progressindicator/determinate_indeterminate.mp4
new file mode 100644
index 00000000000..c5c65511fdc
Binary files /dev/null and b/docs/components/assets/progressindicator/determinate_indeterminate.mp4 differ
diff --git a/docs/components/assets/progressindicator/progressindicators_anatomy.png b/docs/components/assets/progressindicator/progressindicators_anatomy.png
new file mode 100644
index 00000000000..4c72ba91883
Binary files /dev/null and b/docs/components/assets/progressindicator/progressindicators_anatomy.png differ
diff --git a/docs/components/assets/progressindicator/progressindicators_types.png b/docs/components/assets/progressindicator/progressindicators_types.png
new file mode 100644
index 00000000000..ae757e6add9
Binary files /dev/null and b/docs/components/assets/progressindicator/progressindicators_types.png differ
diff --git a/docs/components/assets/radiobutton/radiobutton.png b/docs/components/assets/radiobutton/radiobutton.png
deleted file mode 100644
index b79c09f39ca..00000000000
Binary files a/docs/components/assets/radiobutton/radiobutton.png and /dev/null differ
diff --git a/docs/components/assets/radiobutton/radiobutton_anatomy.png b/docs/components/assets/radiobutton/radiobutton_anatomy.png
new file mode 100644
index 00000000000..eb3b039e910
Binary files /dev/null and b/docs/components/assets/radiobutton/radiobutton_anatomy.png differ
diff --git a/docs/components/assets/radiobutton/radiobutton_hero.png b/docs/components/assets/radiobutton/radiobutton_hero.png
new file mode 100644
index 00000000000..cef615f3789
Binary files /dev/null and b/docs/components/assets/radiobutton/radiobutton_hero.png differ
diff --git a/docs/components/assets/search/search-bar-anatomy.png b/docs/components/assets/search/search_bar_anatomy.png
similarity index 100%
rename from docs/components/assets/search/search-bar-anatomy.png
rename to docs/components/assets/search/search_bar_anatomy.png
diff --git a/docs/components/assets/search/search_hero.png b/docs/components/assets/search/search_hero.png
new file mode 100644
index 00000000000..c4393b1fee4
Binary files /dev/null and b/docs/components/assets/search/search_hero.png differ
diff --git a/docs/components/assets/search/search-view-anatomy.png b/docs/components/assets/search/search_view_anatomy.png
similarity index 100%
rename from docs/components/assets/search/search-view-anatomy.png
rename to docs/components/assets/search/search_view_anatomy.png
diff --git a/docs/components/assets/sidesheet/modal_sidesheet_anatomy.png b/docs/components/assets/sidesheet/modal_sidesheet_anatomy.png
new file mode 100644
index 00000000000..5f49e9833f1
Binary files /dev/null and b/docs/components/assets/sidesheet/modal_sidesheet_anatomy.png differ
diff --git a/docs/components/assets/sidesheet/sidesheet_hero.png b/docs/components/assets/sidesheet/sidesheet_hero.png
new file mode 100644
index 00000000000..aea84062b79
Binary files /dev/null and b/docs/components/assets/sidesheet/sidesheet_hero.png differ
diff --git a/docs/components/assets/sidesheet/standard_sidesheet_anatomy.png b/docs/components/assets/sidesheet/standard_sidesheet_anatomy.png
new file mode 100644
index 00000000000..a3c1b26bd4a
Binary files /dev/null and b/docs/components/assets/sidesheet/standard_sidesheet_anatomy.png differ
diff --git a/docs/components/assets/snackbar/snackbar_anatomy.png b/docs/components/assets/snackbar/snackbar_anatomy.png
index 95409d4ead7..abab713a58a 100644
Binary files a/docs/components/assets/snackbar/snackbar_anatomy.png and b/docs/components/assets/snackbar/snackbar_anatomy.png differ
diff --git a/docs/components/assets/snackbar/snackbar_overview.png b/docs/components/assets/snackbar/snackbar_overview.png
new file mode 100644
index 00000000000..5e6857c0145
Binary files /dev/null and b/docs/components/assets/snackbar/snackbar_overview.png differ
diff --git a/docs/components/assets/switch/switch_anatomy.png b/docs/components/assets/switch/switch_anatomy.png
index 39cc16ae2c2..25caef2b5c7 100644
Binary files a/docs/components/assets/switch/switch_anatomy.png and b/docs/components/assets/switch/switch_anatomy.png differ
diff --git a/docs/components/assets/switch/switch_hero.png b/docs/components/assets/switch/switch_hero.png
index ab95ffc123b..90e25af4e9a 100644
Binary files a/docs/components/assets/switch/switch_hero.png and b/docs/components/assets/switch/switch_hero.png differ
diff --git a/docs/components/assets/tabs/Tabs_types.png b/docs/components/assets/tabs/Tabs_types.png
new file mode 100644
index 00000000000..f68d626a5a4
Binary files /dev/null and b/docs/components/assets/tabs/Tabs_types.png differ
diff --git a/docs/components/assets/tabs/primary_tabs_anatomy.png b/docs/components/assets/tabs/primary_tabs_anatomy.png
new file mode 100644
index 00000000000..b2b37f0a6e5
Binary files /dev/null and b/docs/components/assets/tabs/primary_tabs_anatomy.png differ
diff --git a/docs/components/assets/tabs/secondary_tabs_anatomy.png b/docs/components/assets/tabs/secondary_tabs_anatomy.png
new file mode 100644
index 00000000000..21013aa8172
Binary files /dev/null and b/docs/components/assets/tabs/secondary_tabs_anatomy.png differ
diff --git a/docs/components/assets/tabs/tabs-anatomy.png b/docs/components/assets/tabs/tabs-anatomy.png
deleted file mode 100644
index 3531eaf2025..00000000000
Binary files a/docs/components/assets/tabs/tabs-anatomy.png and /dev/null differ
diff --git a/docs/components/assets/textfields/text_field_hero.png b/docs/components/assets/textfields/text_field_hero.png
new file mode 100644
index 00000000000..e96843fbaa4
Binary files /dev/null and b/docs/components/assets/textfields/text_field_hero.png differ
diff --git a/docs/components/assets/textfields/textfields_filled_anatomy.png b/docs/components/assets/textfields/textfields_filled_anatomy.png
index 09d29714d56..a1f281a7e77 100644
Binary files a/docs/components/assets/textfields/textfields_filled_anatomy.png and b/docs/components/assets/textfields/textfields_filled_anatomy.png differ
diff --git a/docs/components/assets/textfields/textfields_outlined_anatomy.png b/docs/components/assets/textfields/textfields_outlined_anatomy.png
index 2cd20ee2ffa..f24d9b49fcd 100644
Binary files a/docs/components/assets/textfields/textfields_outlined_anatomy.png and b/docs/components/assets/textfields/textfields_outlined_anatomy.png differ
diff --git a/docs/components/assets/timepicker/timepicker_anatomy.png b/docs/components/assets/timepicker/timepicker_anatomy.png
deleted file mode 100644
index 5d431b5776a..00000000000
Binary files a/docs/components/assets/timepicker/timepicker_anatomy.png and /dev/null differ
diff --git a/docs/components/assets/timepicker/timepicker_type_dial.png b/docs/components/assets/timepicker/timepicker_type_dial.png
new file mode 100644
index 00000000000..dfcd3cb091e
Binary files /dev/null and b/docs/components/assets/timepicker/timepicker_type_dial.png differ
diff --git a/docs/components/assets/timepicker/timepicker_type_input.png b/docs/components/assets/timepicker/timepicker_type_input.png
new file mode 100644
index 00000000000..2094ea3cf3f
Binary files /dev/null and b/docs/components/assets/timepicker/timepicker_type_input.png differ
diff --git a/docs/components/assets/timepicker/timepicker_types.png b/docs/components/assets/timepicker/timepicker_types.png
new file mode 100644
index 00000000000..f2ae7feca17
Binary files /dev/null and b/docs/components/assets/timepicker/timepicker_types.png differ
diff --git a/docs/components/assets/timepicker/timepickerdial_anatomy.png b/docs/components/assets/timepicker/timepickerdial_anatomy.png
new file mode 100644
index 00000000000..672d27fa5b0
Binary files /dev/null and b/docs/components/assets/timepicker/timepickerdial_anatomy.png differ
diff --git a/docs/components/assets/timepicker/timepickerinput_anatomy.png b/docs/components/assets/timepicker/timepickerinput_anatomy.png
new file mode 100644
index 00000000000..fade2639a55
Binary files /dev/null and b/docs/components/assets/timepicker/timepickerinput_anatomy.png differ
diff --git a/docs/getting-started.md b/docs/getting-started.md
index f2c362caf4f..0222b0bcf0a 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -5,7 +5,7 @@ section: docs
path: /docs/getting-started/
-->
-# Getting started with Material Components for Android
+# Getting started with Material components for Android
## 1. Migration guidance
@@ -55,6 +55,9 @@ to find the latest version of the library.
**Note:** In order to use the new `Material3` themes and component styles, you
should depend on version `1.5.0` or later.
+**Note:** In order to use the new `Material3Expressive` themes and component
+styles, you should depend on version `1.14.0` or later.
+
### New Namespace and AndroidX
If your app currently depends on the original Design Support Library, you can
@@ -71,17 +74,37 @@ the `com.android.support:design:28.0.0` dependency.
**Note:** You should not use the `com.android.support` and
`com.google.android.material` dependencies in your app at the same time.
+### Non-Transitive R Classes (referencing library resources programmatically)
+
+Starting with version `1.13.0-alpha12`, the Material library is built with AGP
+8.7.3 (or later) and `android.nonTransitiveRClass=true`, meaning
+[R classes are no longer transitive](https://developer.android.com/build/optimize-your-build#use-non-transitive-r-classes)
+and resources must be fully qualified with their library path when used
+programmatically.
+
+For example, since `colorPrimary` is defined in the AppCompat library, you must
+refer to it as `androidx.appcompat.R.attr.colorPrimary` as opposed to
+`com.google.android.material.R.attr.colorPrimary` or `R.attr.colorPrimary`.
+
+For a Material defined resource like `colorOnPrimary`, you must refer to it as
+`com.google.android.material.R.attr.colorOnPrimary`.
+
+To opt out of this new behavior, set `android.nonTransitiveRClass=false` in your
+`gradle.properties` file. Then you can access any resource without a fully
+qualified path (i.e., simply `R..`).
+
+**Note:** This is relevant for all types of library resources, not just
+attribute references.
+
## 3. Android SDK compilation
In order to use the latest versions of Material Components for Android and the
AndroidX Jetpack libraries, you will have to install the latest version of
Android Studio and update your app to meet the following requirements:
-- `compileSdkVersion` -> `34` or later (see the
- [Android 14 app migration guide](https://developer.android.com/about/versions/14/migration))
-- `minSdkVersion` -> `19` or later (see this
- [AndroidX blog post](https://android-developers.googleblog.com/2023/10/androidx-minsdkversion-19.html)
- for more info)
+- `compileSdkVersion` -> `35` or later (see the
+ [Android 15 app migration guide](https://developer.android.com/about/versions/15/migration))
+- `minSdkVersion` -> `21` or later
## 4. Java 8 compilation
@@ -92,13 +115,13 @@ for more information on Java 8 support and how to enable it for your app.
## 5. Gradle, AGP, and Android Studio
-When using MDC-Android version `1.7.0` and above, you will need to make sure
+When using MDC-Android version `1.13.0` and above, you will need to make sure
your project is built with the following minimum requirements, in order to
support the latest build features such as XML `macro`:
-- [Gradle version 7.3.3](https://developer.android.com/studio/releases/gradle-plugin#updating-gradle)
-- [Android Gradle Plugin (AGP) version 7.2.0](https://developer.android.com/studio/releases/gradle-plugin#updating-gradle)
-- [Android Studio Chipmunk, version 2021.2.1](https://developer.android.com/studio/releases/gradle-plugin#android_gradle_plugin_and_android_studio_compatibility)
+- [Gradle version 8.9](https://developer.android.com/studio/releases/gradle-plugin#updating-gradle)
+- [Android Gradle Plugin (AGP) version 8.7.3](https://developer.android.com/studio/releases/gradle-plugin#updating-gradle)
+- [Android Studio Ladybug, version 2024.2.1](https://developer.android.com/studio/releases/gradle-plugin#android_gradle_plugin_and_android_studio_compatibility)
## 6. `AppCompatActivity`
@@ -127,6 +150,31 @@ to your theme. See the
[**AppCompat or MaterialComponents themes**](#appcompat-or-materialcomponents-themes)
section for more details.
+### **`Material3Expressive` themes**
+
+**Note:** You must depend on library version `1.14.0-alpha01` or later to use
+`Theme.Material3Expressive.*` themes, which are required for
+`Widget.Material3Expressive.*` component styles.
+
+Here are the `Material3Expressive` themes you can use to get the latest
+component styles and theme-level attributes, as well as their `Material3`
+equivalents when applicable.
+
+`Material3Expressive` | `Material3`
+-------------------------------------------------------------- | -----------
+`Theme.Material3Expressive.Light` | `Theme.Material3.Light`
+`Theme.Material3Expressive.Light.NoActionBar` | `Theme.Material3.Light.NoActionBar`
+`Theme.Material3Expressive.Dark` | `Theme.Material3.Dark`
+`Theme.Material3Expressive.Dark.NoActionBar` | `Theme.Material3.Dark.NoActionBar`
+`Theme.Material3Expressive.DayNight` | `Theme.Material3.DayNight`
+`Theme.Material3Expressive.DayNight.NoActionBar` | `Theme.Material3.DayNight.NoActionBar`
+`Theme.Material3Expressive.DynamicColors.Light` | `Theme.Material3.DynamicColors.Light`
+`Theme.Material3Expressive.DynamicColors.Light.NoActionBar` | `Theme.Material3.DynamicColors.Light.NoActionBar`
+`Theme.Material3Expressive.DynamicColors.Dark` | `Theme.Material3.DynamicColors.Dark`
+`Theme.Material3Expressive.DynamicColors.Dark.NoActionBar` | `Theme.Material3.DynamicColors.Dark.NoActionBar`
+`Theme.Material3Expressive.DynamicColors.DayNight` | `Theme.Material3.DynamicColors.DayNight`
+`Theme.Material3Expressive.DynamicColors.DayNight.NoActionBar` | `Theme.Material3.DynamicColors.DayNight.NoActionBar`
+
### **`Material3` themes**
Here are the `Material3` themes you can use to get the latest component styles
diff --git a/docs/theming/Motion.md b/docs/theming/Motion.md
index 28e5e49f159..e8bac504675 100644
--- a/docs/theming/Motion.md
+++ b/docs/theming/Motion.md
@@ -9,11 +9,15 @@ path: /theming/motion/
# Motion
-Material motion is a system to help create stylized and consistent animations across an app. Provided in the library are semantic easing and duration theme attributes, components that use themed motion for their built-in animations, and a set of transitions for navigational events or custom animations.
-
-Before you can use the motion library, you need to add a dependency on the
-Material Components for Android library (version `1.2.0` or later). For more
-information, go to the
+Material motion is a system to help create stylized and consistent animations
+across an app. Provided in the library are semantic easing and duration theme
+attributes, semantic spring theme attributes, components that use themed motion
+for their built-in animations, and a set of transitions for
+navigational events or custom animations.
+
+The easing and duration motion system is available in version `1.6.0` or later.
+The physics motion system is available in version `1.13.0` or later. For more
+information, see the
[Getting started](https://github.com/material-components/material-components-android/tree/master/docs/getting-started.md)
page.
@@ -22,11 +26,96 @@ page.
## Theming
-The Material motion system is backed by a limited number of easing and duration slots. These are the building blocks for creating any Material-styled animation. These slots are implemented as theme attributes, similar to color or shape attributes, and are used by all components in the library to create a unified motion feel.
+The Material motion system is backed by a set of easing and duration slots and
+a set of spring slots. These are the building blocks for creating any
+Material-styled animation. These slots are implemented as theme attributes,
+similar to color or shape attributes. They are used by components in the
+library to create a unified motion feel and can be used by custom animations
+to make motion feel cohesive across an entire app.
+
+### Springs
+
+The spring (or physics) motion system is a set of six opinionated spring
+attributes intended to be used with the [Dynamic Animation AndroidX library](https://developer.android.com/develop/ui/views/animations/spring-animation#add-support-library).
+A spring attribute is configured as a style made up of a damping and stiffness
+value (see [MaterialSpring styleable](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/motion/res/values/attrs.xml)
+for available properties and [spring styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/motion/res/values/styles.xml)
+for examples). The damping ratio describes how rapidly spring oscillations
+decay. Stiffness defines the strength of the spring. Learn more about how
+spring animations work [here](https://developer.android.com/develop/ui/views/animations/spring-animation).
+
+The spring system provides springs in three speeds - fast, slow, and default.
+A speed is chosen based on the size of the component being animated or the
+distance covered. Small component animations like switches should use the fast
+spring, full screen animations or transitions should use the slow spring, and
+everything in between should use the default spring.
+
+Additionally, for each speed there are two types of springs - spatial and
+effects. Spatial springs are used for animations that move something on
+screen - like the x & y position of a View. Effects springs are used to animate
+properties such as color or opacity where the property's value should not be
+overshot (e.g. a background's alpha shouldn't bounce or oscillate above 100%).
+
+This makes for a total of six spring attributes:
+
+Attribute | Default value | Description
+-------------- | ------------------------ | ---------------------------------
+**?attr/motionSpringFastSpatial** | `damping: 0.9, stiffness: 1400` | Spring for small components like switches and buttons.
+**?attr/motionSpringFastEffects** | `damping: 1, stiffness: 3800` | Spring for small component effects like color and opacity.
+**?attr/motionSpringSlowSpatial** | `damping: 0.9, stiffness: 300` | Spring for full screen animations.
+**?attr/motionSpringSlowEffects** | `damping: 1, stiffness: 800` | Spring for full screen animation effects like color and opacity.
+**?attr/motionSpringDefaultSpatial** | `damping: 0.9, stiffness: 700` | Spring for animations that partially cover the screen like a bottom sheet or nav drawer.
+**?attr/motionSpringDefaultEffects** | `damping: 1, stiffness: 1600` | Spring for animation effects that partially cover the screen.
+
+When building spring animations, a speed should be chosen based on the
+animation's size or distance covered. Then, a spatial or effects type should be
+chosen depending on the property being animated. For example, if animating a
+button's shape and color when pressed, use two springs: a
+`motionSpringFastSpatial` spring to animate the button's shape/size and a
+`motionSpringFastEffects` spring to animate the button's color.
+
+Spring attributes can be customized (or "themed") by overriding their value to
+your own [MaterialSpring](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/motion/res/values/attrs.xml)
+style.
+
+#### Custom animations using the spring motion system
+
+To create a spring animation, you'll need to declare a dependency on the
+Dynamic Animation AndroidX library. Follow instructions for including the
+library and creating a spring animation
+[here](https://developer.android.com/develop/ui/views/animations/spring-animation#add-support-library).
+
+With your configured `SpringAnimation`, use
+[MotionUtils.resolveThemeSpring()](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/motion/MotionUtils.java)
+to resolve a spring attribute from your theme into a SpringForce object. Then,
+use the resolved object to configure your SpringAnimation's SpringForce.
+
+```kt
+val defaultSpatialSpring = MotionUtils.resolveThemeSpringForce(
+ /* context= */ this,
+ /* attrResId= */ com.google.android.material.R.attr.motionSpringDefaultSpatial
+)
+SpringAnimation(box, DynamicAnimation.TRANSLATION_Y, 400f).apply {
+ spring.apply {
+ dampingRatio = defaultSpatialSpring.dampingRatio
+ stiffness = defaultSpatialSpring.stiffness
+ }
+ start()
+}
+```
+
+### Curves (easing & duration)
-### Easing
+Easing (aka interpolator) and duration theme attributes make up the curve
+motion system. Easing is a curve which determines how long it takes an object
+to start and stop moving. Duration determines the overall time of the animation.
+These are paired together to define how motion moves and feels. Learn about
+suggested pairings by reading through Material's
+[Easing and duration guidance](https://m3.material.io/styles/motion/easing-and-duration/applying-easing-and-duration).
-Easing theme attributes define a set of curves that are used as [Interpolators](https://developer.android.com/reference/androidx/core/animation/Interpolator).
+Material's curve system includes seven easing attributes
+([interpolators](https://developer.android.com/reference/androidx/core/animation/Interpolator))
+:
Attribute | Default value | Description
-------------- | ------------------------ | ---------------------------------
@@ -38,7 +127,10 @@ Attribute | Default value | Description
**?attr/motionEasingEmphasizedAccelerateInterpolator** | `cubic-bezier: 0.3, 0, 0.8, 0.15` | Easing used for common, M3-styled animations that exit the screen.
**?attr/motionEasingLinearInterpolator** | `cubic-bezier: 0, 0, 1, 1` | Easing for simple, non-stylized motion.
-To customize an easing value, override any of the attributes in your app’s theme to your own interpolator resource.
+By default, these attribute values are set to interpolators that feel cohesive
+when used together in an app. However, they can be overridden (or "themed") to
+reflect your app's unique style by setting their values to your own interpolator
+resource from your app's theme.
```xml
```
-**Note:** `AppCompat` must be used to handle backwards compatibility for sdk <
-16. If you don't need to support devices < 16 and aren't using AppCompat, you
-should set `android:fontFamily` instead of `fontFamily` or `app:fontFamily`.
-
## Downloadable fonts
Android O and Android Support Library 26 add support for [Downloadable
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 00000000000..355898b9a8f
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,99 @@
+[versions]
+androidGradlePlugin = "8.7.3"
+
+# Main library dependencies.
+# Versions defined for :lib are required to follow library versioning - stable
+# releases of the library can only use stable dependencies
+androidXActivity = "1.8.0"
+androidXActivityCompose = "1.10.1"
+androidXAnnotation = "1.2.0"
+androidXAnnotationExperimental = "1.0.0"
+androidXAppCompat = "1.7.0"
+androidXCardView = "1.0.0"
+androidXConstraintLayout = "2.1.0"
+androidXCoordinatorLayout = "1.1.0"
+androidXCore = "1.16.0"
+androidXComposeMaterialIconsCore = "1.7.8"
+androidXComposeMaterialIconsExtended = "1.7.8"
+androidXComposeMaterial3 = "1.4.0-alpha14"
+androidXDrawerLayout = "1.1.1"
+androidXDynamicAnimation = "1.1.0"
+androidXEspresso = "3.1.0"
+androidXFragment = "1.2.5"
+androidXGridLayout = "1.0.0"
+androidXGraphicsShapes = "1.0.1"
+androidXLifecycle = "2.0.0"
+androidXMultidex = "2.0.1"
+androidXPreference = "1.1.1"
+androidXRecyclerView = "1.2.1"
+androidXRecyclerViewSelection = "1.0.0"
+androidXResourceInspectionAnnotation = "1.0.1"
+androidXResourceInspectionProcessor = "1.0.1"
+androidXTestCore = "1.4.0"
+androidXTransition = "1.5.0"
+androidXVectorDrawable = "1.1.0"
+androidXViewPager2 = "1.0.0"
+androidXWindow = "1.0.0"
+dagger = "2.51.1"
+dexmaker = "1.2"
+errorProneAnnotations = "2.15.0"
+glide = "4.16.0"
+guava = "33.3.1-android"
+junit = "4.13.2"
+kotlinBom = "1.8.22"
+mockitoCore = "2.25.0"
+robolectric = "4.13"
+truth = "0.45"
+
+[libraries]
+android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
+androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "androidXActivity" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidXActivityCompose" }
+androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidXAnnotation" }
+androidx-annotation-experimental = { group = "androidx.annotation", name = "annotation-experimental", version.ref = "androidXAnnotationExperimental" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidXAppCompat" }
+androidx-cardview = { group = "androidx.cardview", name = "cardview", version.ref = "androidXCardView" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidXConstraintLayout" }
+androidx-coordinatorlayout = { group = "androidx.coordinatorlayout", name = "coordinatorlayout", version.ref = "androidXCoordinatorLayout" }
+androidx-core = { group = "androidx.core", name = "core", version.ref = "androidXCore" }
+androidx-compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core", version.ref = "androidXComposeMaterialIconsCore" }
+androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "androidXComposeMaterialIconsExtended" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidXComposeMaterial3" }
+androidx-drawerlayout = { group = "androidx.drawerlayout", name = "drawerlayout", version.ref = "androidXDrawerLayout" }
+androidx-dynamicanimation = { group = "androidx.dynamicanimation", name = "dynamicanimation", version.ref = "androidXDynamicAnimation" }
+androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "androidXFragment" }
+androidx-gridlayout = { group = "androidx.gridlayout", name = "gridlayout", version.ref = "androidXGridLayout" }
+androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime", version.ref = "androidXLifecycle" }
+androidx-multidex = { group = "androidx.multidex", name= "multidex", version.ref = "androidXMultidex" }
+androidx-preference = { group = "androidx.preference", name = "preference", version.ref = "androidXPreference" }
+androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidXRecyclerView" }
+androidx-transition = { group = "androidx.transition", name = "transition", version.ref = "androidXTransition" }
+androidx-vectordrawable = { group = "androidx.vectordrawable", name= "vectordrawable", version.ref = "androidXVectorDrawable" }
+androidx-recyclerview-selection = { group = "androidx.recyclerview", name = "recyclerview-selection", version.ref = "androidXRecyclerViewSelection" }
+androidx-resourceinspection-annotation = { group = "androidx.resourceinspection", name = "resourceinspection-annotation", version.ref = "androidXResourceInspectionAnnotation" }
+androidx-resourceinspection-processor = { group = "androidx.resourceinspection", name = "resourceinspection-processor", version.ref = "androidXResourceInspectionProcessor" }
+androidx-viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "androidXViewPager2" }
+androidx-graphics-shapes = { group = "androidx.graphics", name = "graphics-shapes", version.ref = "androidXGraphicsShapes" }
+androidx-espresso-accessibility = { group = "androidx.test.espresso", name = "espresso-accessibility", version.ref = "androidXEspresso" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidXEspresso" }
+androidx-espresso-contrib = { group = "androidx.test.espresso", name = "espresso-contrib", version.ref = "androidXEspresso" }
+androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidXTestCore" }
+androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidXTestCore" }
+androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidXTestCore" }
+androidx-window = { group = "androidx.window", name = "window", version.ref = "androidXWindow" }
+androidx-window-java = { group = "androidx.window", name = "window-java", version.ref = "androidXWindow" }
+dagger = { group = "com.google.dagger", name = "dagger", version.ref = "dagger" }
+dagger-android = { group = "com.google.dagger", name = "dagger-android", version.ref = "dagger" }
+dagger-android-processor = { group = "com.google.dagger", name = "dagger-android-processor", version.ref = "dagger" }
+dagger-android-support = { group = "com.google.dagger", name = "dagger-android-support", version.ref = "dagger" }
+dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", version.ref = "dagger" }
+dexmaker = { group = "com.google.dexmaker", name = "dexmaker", version.ref = "dexmaker" }
+dexmaker-mokito = { group = "com.google.dexmaker", name = "dexmaker-mockito", version.ref = "dexmaker" }
+errorprone-annotations = { group = "com.google.errorprone", name = "error_prone_annotations", version.ref = "errorProneAnnotations" }
+glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
+guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+kotlin-bom = { group = "org.jetbrains.kotlin", name = "kotlin-bom", version.ref = "kotlinBom" }
+mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockitoCore" }
+robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
+truth = { group = "com.google.truth", name = "truth", version.ref = "truth" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7f93135c49b..d64cd491770 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a8e0c38ef97..171d8761b27 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/lib/build.gradle b/lib/build.gradle
index a07cf980700..d9961002e60 100644
--- a/lib/build.gradle
+++ b/lib/build.gradle
@@ -1,4 +1,4 @@
-import org.gradle.internal.os.OperatingSystem
+import javax.inject.Inject
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
@@ -6,38 +6,37 @@ apply plugin: 'maven-publish'
version = mdcLibraryVersion
dependencies {
- api compatibility("activity")
- api compatibility("annotation")
- api compatibility("appcompat")
- api compatibility("cardview")
- api compatibility("coordinatorlayout")
- api compatibility("constraintlayout")
- api compatibility("core")
- api compatibility("drawerlayout")
- api compatibility("dynamicanimation")
- api compatibility("experimental")
- api compatibility("fragment")
- api compatibility("lifecycleRuntime")
- api compatibility("recyclerview")
- api compatibility("resourceInspectionAnnotation")
- api compatibility("transition")
- api compatibility("vectordrawable")
- api compatibility("viewpager2")
- api compatibility("graphicsShapes")
-
- annotationProcessor compatibility("resourceInspectionProcessor")
+ api libs.androidx.activity
+ api libs.androidx.annotation
+ api libs.androidx.annotation.experimental
+ api libs.androidx.appcompat
+ api libs.androidx.cardview
+ api libs.androidx.coordinatorlayout
+ api libs.androidx.constraintlayout
+ api libs.androidx.core
+ api libs.androidx.drawerlayout
+ api libs.androidx.dynamicanimation
+ api libs.androidx.fragment
+ api libs.androidx.lifecycle.runtime
+ api libs.androidx.recyclerview
+ api libs.androidx.resourceinspection.annotation
+ api libs.androidx.transition
+ api libs.androidx.vectordrawable
+ api libs.androidx.viewpager2
+ api libs.androidx.graphics.shapes
+
+ annotationProcessor libs.androidx.resourceinspection.processor
// Align kotlin versions
- implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22"))
-
- implementation "com.google.errorprone:error_prone_annotations:${errorproneVersion}"
-
- testImplementation "androidx.test:core:${testRunnerVersion}"
- testImplementation "androidx.test:runner:${testRunnerVersion}"
- testImplementation "junit:junit:4.13.2"
- testImplementation "com.google.truth:truth:${truthVersion}"
- testImplementation "org.mockito:mockito-core:${mockitoCoreVersion}"
- testImplementation "org.robolectric:robolectric:${robolectricVersion}"
+ implementation(platform(libs.kotlin.bom))
+
+ implementation libs.errorprone.annotations
+ testImplementation libs.androidx.test.core
+ testImplementation libs.androidx.test.runner
+ testImplementation libs.junit
+ testImplementation libs.truth
+ testImplementation libs.mockito.core
+ testImplementation libs.robolectric
}
def srcDirs = [
@@ -61,6 +60,7 @@ def srcDirs = [
'com/google/android/material/datepicker',
'com/google/android/material/dialog',
'com/google/android/material/divider',
+ 'com/google/android/material/dockedtoolbar',
'com/google/android/material/drawable',
'com/google/android/material/elevation',
'com/google/android/material/expandable',
@@ -75,6 +75,7 @@ def srcDirs = [
'com/google/android/material/motion',
'com/google/android/material/navigation',
'com/google/android/material/navigationrail',
+ 'com/google/android/material/overflow',
'com/google/android/material/progressindicator',
'com/google/android/material/radiobutton',
'com/google/android/material/resources',
@@ -100,6 +101,7 @@ def srcDirs = [
]
android {
+ namespace "com.google.android.material"
sourceSets {
main.manifest.srcFile 'java/com/google/android/material/AndroidManifest.xml'
main.java.srcDir 'java'
@@ -125,37 +127,9 @@ android {
defaultConfig {
minSdkVersion 21
}
-}
-
-task generateJavadocs(type: Javadoc) {
- if (project.hasProperty("online")) {
- options.addStringOption("toroot", "/")
- options.addStringOption("hdf", "android.whichdoc online")
- options.addStringOption("hdf", "dac")
- options.addBooleanOption("devsite", true)
- options.addBooleanOption("yamlV2", true)
- options.addStringOption("dac_libraryroot", "com/google/android/material")
- options.addStringOption("dac_dataname", "MATERIAL_DATA")
- }
- if (project.hasProperty("docletPathRoot")) {
- def docletPathRoot = project.property("docletPathRoot")
- def outputPath = project.hasProperty("outputPath") ? project.property("outputPath") : "doclava-out"
- def doclavaJar = project.getProperty("doclavaJar")
-
- source = android.sourceSets.main.java.source
- source = source.findAll { it.name.endsWith(".java") }
-
- title = null
- destinationDir = new File(outputPath)
- classpath = files("${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
- options.addStringOption("federate Android", "https://developer.android.com/")
- options.encoding = "UTF-8"
- options.doclet = "com.google.doclava.Doclava"
- options.docletpath = [
- file(doclavaJar),
- file(docletPathRoot + "/jsilver/v1_0_0/jsilver.jar")
- ]
+ publishing {
+ singleVariant("release")
}
}
@@ -165,144 +139,205 @@ task getVersion {
}
}
-task generateApiXml(type: Javadoc) {
- if (project.hasProperty("apiName")) {
- def jdiff = project.property("jdiffJar")
- def apiName = project.property("apiName")
- source = android.sourceSets.main.java.source
- source = source.findAll { it.name.endsWith(".java") }
- classpath = files("${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
- options.doclet = "jdiff.JDiff"
- options.addStringOption("subpackages", ".")
- options.addStringOption("apiname", apiName)
- options.docletpath = [
- file(jdiff),
- ]
-
- // Doclava does not understand -notimestamp option that is default since Gradle 6.0
- options.setNoTimestamp(false)
- }
-
- doLast {
- // Escape incorrect ampersands in API XML file
- if (OperatingSystem.current().isLinux()) {
- ["sed", "-i", "s/ & / \\& /g", "lib/${apiName}.xml"].execute()
- } else {
- ["sed", "-i", "''", "s/ & / \\& /g", "lib/${apiName}.xml"].execute()
- }
+task androidSourcesJar(type: Jar) {
+ archiveClassifier.set('sources')
+ from(android.sourceSets.main.java.srcDirs) {
+ // Needed because we have Java sources and resources in same directory
+ include '**/*.java'
+ includeEmptyDirs = false
}
}
-task generateJdiffReport(type: Javadoc) {
- if (project.hasProperty('oldApi')) {
- def outputPath = project.hasProperty('outputPath') ? project.property('outputPath') : 'diffs-out'
- def jdiff = project.property('jdiffjar')
- def xerces = project.property('xercesjar')
- def oldApi = project.property('oldApi')
- def newApi = project.property('newApi')
- def newApiDir = project.property('newApiDir')
- def oldApiDir = project.property('oldApiDir')
- destinationDir = new File(outputPath)
- source = android.sourceSets.main.java.source
- source = source.findAll { it.name.endsWith('.java') }
- classpath = files('${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar')
- options.doclet = 'jdiff.JDiff'
- options.addStringOption('subpackages', '.')
- options.addStringOption('newapidir', newApiDir)
- options.addStringOption('oldapidir', oldApiDir)
- options.addStringOption('oldapi', oldApi)
- options.addBooleanOption('verbose', true)
- options.addStringOption('newapi', newApi)
- // Doclava does not understand -notimestamp option that is default since Gradle 6.0
- options.setNoTimestamp(false)
- options.docletpath = [
- file(jdiff),
- file(xerces),
- ]
+publishing {
+ repositories {
+ maven {
+ url = "$mavenRepoUrl"
+ }
}
-}
-def R_CLASS_PATH = "build/generated/not_namespaced_r_class_sources/releaseUnitTest/processReleaseUnitTestResources/r/com/google/android/material/R.java"
-Attribute ARTIFACT_TYPE = Attribute.of("artifactType", String.class)
-afterEvaluate {
- [generateJavadocs, generateApiXml].forEach { task ->
- task.dependsOn(':lib:processReleaseUnitTestResources')
- task.source += R_CLASS_PATH
+ publications {
+ release(MavenPublication) {
+ artifact androidSourcesJar
+ groupId = 'com.google.android.material'
+ artifactId = 'material'
+ version project.version
+ pom {
+ name = 'Material Components for Android'
+ description = 'Material Components for Android is a static library ' +
+ 'that you can add to your Android application in order to use ' +
+ 'APIs that provide implementations of the Material Design specification. ' +
+ 'Compatible on devices running API 21 or later.'
+ url = 'https://github.com/material-components/material-components-android'
+ inceptionYear = '2015'
+ licenses {
+ license {
+ name = 'The Apache Software License, Version 2.0'
+ url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+ distribution = 'repo'
+ }
+ }
+ developers {
+ developer {
+ name = 'The Android Open Source Project'
+ }
+ }
+ scm {
+ connection = 'scm:git:https://github.com/material-components/material-components-android.git'
+ url = 'https://github.com/material-components/material-components-android'
+ }
+ }
- def releaseVariant = android.libraryVariants.find { it.name == 'release' }
- if (releaseVariant == null) {
- return
+ afterEvaluate {
+ from components.release
+ }
}
+ }
+}
- // Add transitive runtime dependencies to classpath
- task.classpath += releaseVariant.runtimeConfiguration.incoming.artifactView { aView ->
- aView.attributes.attribute(ARTIFACT_TYPE, "android-classes")
- }.files
+/**
+ * Generate library documentation.
+ *
+ * This task requires a Dackka jar to be run. This can be passed to the task
+ * by running the task from the command line and setting the --dackkaJar
+ * option.
+ *
+ * Example: ./gradlew generateDocumentation --dackkaJar=""
+ *
+ * The resulting documentation will be placed in `lib/build/docs` and contain
+ * documentation for both java and kotlin clients.
+ *
+ * TODO: b/149338266 - Dackka does not support links to resources in documentation.
+ * TODO: b/396171398 - inheritDoc results hav formatting issues with Dackka.
+ */
+tasks.register("generateDocumentation", DackkaRunner) {
+ group = "Publishing"
+ description = "Generate javadocs using dackka"
+ version = mdcLibraryVersion
+ config = layout.buildDirectory.file("resources/dackka_config.json").get().asFile
+ outputDirectory = layout.buildDirectory.dir("docs").get()
+ // Add all the files from the main source set to be documented by dackka
+ sourceDirectories.setFrom(
+ android.sourceSets.main.java.srcDirs.absolutePath
+ )
+ dependenciesClasspath.setFrom(
+ files("${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"),
+ )
+}
- // Add project and compile dependencies to classpath
- releaseVariant.javaCompileProvider.configure { javaCompileProvider ->
- task.classpath += releaseVariant.getCompileClasspath(null)
- task.classpath += task.project.files(javaCompileProvider.destinationDir)
- task.source += javaCompileProvider.source
- }
+/**
+ * A task that uses Dackka to generate javadoc.
+ */
+abstract class DackkaRunner extends DefaultTask {
+
+ private String dackkaJar;
+
+ /**
+ * Set the path to the Dackka jar to use by setting the --dackkaJar command
+ * line argument when running the gradle task used by this task.
+ *
+ * @param path the path to the dackka jar
+ */
+ @Option(option = "dackkaJar", description = "path to daccka jar")
+ void setDackkaJar(String path) {
+ this.dackkaJar = path
+ }
- // Doclava does not understand -notimestamp option that is default since Gradle 6.0
- task.options.setNoTimestamp(false)
+ @Input
+ String getDackkaJar() {
+ return dackkaJar;
}
-}
-task androidSourcesJar(type: Jar) {
- archiveClassifier.set('sources')
- from(android.sourceSets.main.java.srcDirs) {
- // Needed because we have Java sources and resources in same directory
- include '**/*.java'
- includeEmptyDirs = false
+ /**
+ * The Material library version of these docs.
+ *
+ * This should most likely match mdcLibraryVersion from the projects build
+ * file.
+ */
+ @Input
+ String version;
+
+ /** Source directories to be documented. */
+ @InputFiles
+ final ConfigurableFileCollection sourceDirectories = project.files()
+
+ @InputFiles
+ final ConfigurableFileCollection dependenciesClasspath = project.files();
+
+ /** A file that should be populated with the Dackka configuration. */
+ @OutputFile
+ abstract File config;
+
+ /** The output containing documentation generated by Dackka. */
+ @OutputDirectory
+ abstract Directory outputDirectory;
+
+ @Inject
+ abstract ExecOperations getExecOperations()
+
+ @TaskAction
+ void run() {
+ // Delete any previously generated documentation
+ outputDirectory.asFile.deleteDir()
+ // Populate the Dackka config json file with Material-specific properties
+ writeDackkaConfig()
+ // Run the Dackka jar and generate docs
+ getExecOperations().javaexec {
+ classpath(dackkaJar)
+ args(config.absolutePath)
+ }
}
-}
-afterEvaluate {
- publishing {
- repositories {
- maven {
- url = "$mavenRepoUrl"
- }
+ /** Populate the Dackka config file with project specific values. */
+ private void writeDackkaConfig() {
+ def configContents = """
+ {
+ "modeuleName": "Material Components",
+ "moduleVersion": "$version",
+ "outputDir": "${outputDirectory.asFile.absolutePath}",
+ "offlineMode": "true",
+ "noJdkLink": "true",
+ "sourceSets": [
+ {
+ "moduleDisplayName": "lib",
+ "analysisPlatform": "jvm",
+ "externalDocumentationLinks": [],
+ "sourceSetID": {
+ "scopeId": "com",
+ "sourceSetName": "main"
+ },
+ "sourceRoots": [${sourceDirectories.collect { '"' + it.absolutePath + '"' }.join(", ")}],
+ "samples": [],
+ "includes": [],
+ "classpath": [${dependenciesClasspath.collect { '"' + it.absolutePath + '"'}.join(", ")}],
+ "sourceLinks": [],
+ "documentedVisibilities": ["PUBLIC", "PROTECTED"]
+ }
+ ],
+ "pluginsConfiguration": [
+ {
+ "fqPluginName": "com.google.devsite.DevsitePlugin",
+ "serializationFormat": "JSON",
+ "values": "${getDevsitePluginConfigValues()}"
+ }
+ ]
}
+ """.trim();
- publications {
- release(MavenPublication) {
- from components.release
+ config.write(configContents)
+ }
- artifact androidSourcesJar
-
- groupId = 'com.google.android.material'
- artifactId = 'material'
- version project.version
- pom {
- name = 'Material Components for Android'
- description = 'Material Components for Android is a static library ' +
- 'that you can add to your Android application in order to use ' +
- 'APIs that provide implementations of the Material Design specification. ' +
- 'Compatible on devices running API 21 or later.'
- url = 'https://github.com/material-components/material-components-android'
- inceptionYear = '2015'
- licenses {
- license {
- name = 'The Apache Software License, Version 2.0'
- url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
- distribution = 'repo'
- }
- }
- developers {
- developer {
- name = 'The Android Open Source Project'
- }
- }
- scm {
- connection = 'scm:git:https://github.com/material-components/material-components-android.git'
- url = 'https://github.com/material-components/material-components-android'
- }
- }
- }
+ /** Returns the Dackka Devsite plugin configuration json string. */
+ private String getDevsitePluginConfigValues() {
+ return """
+ {
+ "docRootPath": "reference",
+ "projectPath": "com/google/android/material",
+ "excludedPackages": [ ".*excluded.*" ],
+ "javaDocsPath": "",
+ "kotlinDocsPath": "kotlin",
+ "baseSourceLink": "https://github.com/material-components/material-components-android/blob/$%7Bversion%7D/lib/java/%s",
+ "annotationsNotToDisplay": [ "java.lang.Override", "kotlin.ParameterName" ]
}
+ """.trim().replace("\n", "").replace('"', '\\\"')
}
}
diff --git a/lib/java/com/google/android/material/appbar/AppBarLayout.java b/lib/java/com/google/android/material/appbar/AppBarLayout.java
index f1d0f1bb071..f2a09cb2f7e 100644
--- a/lib/java/com/google/android/material/appbar/AppBarLayout.java
+++ b/lib/java/com/google/android/material/appbar/AppBarLayout.java
@@ -52,6 +52,7 @@
import androidx.annotation.ColorInt;
import androidx.annotation.Dimension;
import androidx.annotation.DrawableRes;
+import androidx.annotation.FloatRange;
import androidx.annotation.IdRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -77,10 +78,12 @@
import com.google.android.material.resources.MaterialResources;
import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.MaterialShapeUtils;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
/**
@@ -177,11 +180,30 @@ public interface OnOffsetChangedListener extends BaseOnOffsetChangedListener liftOnScrollTargetView;
- private final boolean hasLiftOnScrollColor;
@Nullable private ValueAnimator liftOnScrollColorAnimator;
@Nullable private AnimatorUpdateListener liftOnScrollColorUpdateListener;
private final List liftOnScrollListeners = new ArrayList<>();
+ private final LinkedHashSet liftProgressListeners =
+ new LinkedHashSet<>();
private final long liftOnScrollColorDuration;
private final TimeInterpolator liftOnScrollColorInterpolator;
private int[] tmpStatesArray;
+ @ColorInt private int backgroundOriginalColor;
@Nullable private Drawable statusBarForeground;
@Nullable private Integer statusBarForegroundOriginalColor;
@@ -248,31 +273,17 @@ public AppBarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int
ThemeEnforcement.obtainStyledAttributes(
context, attrs, R.styleable.AppBarLayout, defStyleAttr, DEF_STYLE_RES);
- setBackground(a.getDrawable(R.styleable.AppBarLayout_android_background));
-
- ColorStateList liftOnScrollColor =
+ liftOnScrollColor =
MaterialResources.getColorStateList(context, a, R.styleable.AppBarLayout_liftOnScrollColor);
- hasLiftOnScrollColor = liftOnScrollColor != null;
-
- ColorStateList originalBackgroundColor = DrawableUtils.getColorStateListOrNull(getBackground());
- if (originalBackgroundColor != null) {
- MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
- materialShapeDrawable.setFillColor(originalBackgroundColor);
- // If there is a lift on scroll color specified, we do not initialize the elevation overlay
- // and set the alpha to zero manually.
- if (liftOnScrollColor != null) {
- initializeLiftOnScrollWithColor(
- materialShapeDrawable, originalBackgroundColor, liftOnScrollColor);
- } else {
- initializeLiftOnScrollWithElevation(context, materialShapeDrawable);
- }
- }
- liftOnScrollColorDuration = MotionUtils.resolveThemeDuration(context,
- R.attr.motionDurationMedium2,
- getResources().getInteger(R.integer.app_bar_elevation_anim_duration));
- liftOnScrollColorInterpolator = MotionUtils.resolveThemeInterpolator(context,
- R.attr.motionEasingStandardInterpolator, AnimationUtils.LINEAR_INTERPOLATOR);
+ liftOnScrollColorDuration =
+ MotionUtils.resolveThemeDuration(
+ context,
+ R.attr.motionDurationMedium2,
+ getResources().getInteger(R.integer.app_bar_elevation_anim_duration));
+ liftOnScrollColorInterpolator =
+ MotionUtils.resolveThemeInterpolator(
+ context, R.attr.motionEasingStandardInterpolator, AnimationUtils.LINEAR_INTERPOLATOR);
if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
setExpanded(
@@ -286,6 +297,9 @@ public AppBarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int
this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0));
}
+ // Set the background drawable last to ensure that the background is updated for lift on scroll.
+ setBackground(a.getDrawable(R.styleable.AppBarLayout_android_background));
+
if (VERSION.SDK_INT >= VERSION_CODES.O) {
// In O+, we have these values set in the style. Since there is no defStyleAttr for
// AppBarLayout at the AppCompat level, check for these attributes here.
@@ -319,9 +333,41 @@ public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets)
});
}
+ private Drawable maybeCreateLiftOnScrollBackground(
+ @NonNull Context context, @NonNull Drawable originalBackground) {
+ MaterialShapeDrawable materialShapeDrawable =
+ maybeConvertToMaterialShapeDrawable(originalBackground);
+ if (materialShapeDrawable == null || materialShapeDrawable.getFillColor() == null) {
+ return originalBackground;
+ }
+ backgroundOriginalColor = materialShapeDrawable.getFillColor().getDefaultColor();
+ // If there is a lift on scroll color specified, we do not initialize the elevation overlay
+ // and set the alpha to zero manually.
+ if (liftOnScrollColor != null) {
+ initializeLiftOnScrollWithColor(materialShapeDrawable, liftOnScrollColor);
+ } else {
+ initializeLiftOnScrollWithElevation(context, materialShapeDrawable);
+ }
+ return materialShapeDrawable;
+ }
+
+ @Nullable
+ private MaterialShapeDrawable maybeConvertToMaterialShapeDrawable(Drawable originalBackground) {
+ if (originalBackground instanceof MaterialShapeDrawable) {
+ return (MaterialShapeDrawable) originalBackground;
+ }
+ ColorStateList originalBackgroundColor =
+ DrawableUtils.getColorStateListOrNull(originalBackground);
+ if (originalBackgroundColor == null) {
+ return null;
+ }
+ MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
+ materialShapeDrawable.setFillColor(originalBackgroundColor);
+ return materialShapeDrawable;
+ }
+
private void initializeLiftOnScrollWithColor(
MaterialShapeDrawable background,
- @NonNull ColorStateList originalBackgroundColor,
@NonNull ColorStateList liftOnScrollColor) {
Integer colorSurface = MaterialColors.getColorOrNull(getContext(), R.attr.colorSurface);
liftOnScrollColorUpdateListener =
@@ -329,14 +375,12 @@ private void initializeLiftOnScrollWithColor(
float liftProgress = (float) valueAnimator.getAnimatedValue();
int mixedColor =
MaterialColors.layer(
- originalBackgroundColor.getDefaultColor(),
- liftOnScrollColor.getDefaultColor(),
- liftProgress);
+ backgroundOriginalColor, liftOnScrollColor.getDefaultColor(), liftProgress);
background.setFillColor(ColorStateList.valueOf(mixedColor));
if (statusBarForeground != null
&& statusBarForegroundOriginalColor != null
&& statusBarForegroundOriginalColor.equals(colorSurface)) {
- DrawableCompat.setTint(statusBarForeground, mixedColor);
+ statusBarForeground.setTint(mixedColor);
}
if (!liftOnScrollListeners.isEmpty()) {
@@ -346,26 +390,33 @@ private void initializeLiftOnScrollWithColor(
}
}
}
- };
- setBackground(background);
+ if (!liftProgressListeners.isEmpty()) {
+ for (LiftOnScrollProgressListener liftProgressListener : liftProgressListeners) {
+ liftProgressListener.onUpdate(0, mixedColor, liftProgress);
+ }
+ }
+ };
}
private void initializeLiftOnScrollWithElevation(
Context context, MaterialShapeDrawable background) {
background.initializeElevationOverlay(context);
- liftOnScrollColorUpdateListener = valueAnimator -> {
- float elevation = (float) valueAnimator.getAnimatedValue();
- background.setElevation(elevation);
- if (statusBarForeground instanceof MaterialShapeDrawable) {
- ((MaterialShapeDrawable) statusBarForeground).setElevation(elevation);
- }
- for (LiftOnScrollListener liftOnScrollListener : liftOnScrollListeners) {
- liftOnScrollListener.onUpdate(elevation, background.getResolvedTintColor());
- }
- };
-
- setBackground(background);
+ liftOnScrollColorUpdateListener =
+ valueAnimator -> {
+ float elevation = (float) valueAnimator.getAnimatedValue();
+ background.setElevation(elevation);
+ if (statusBarForeground instanceof MaterialShapeDrawable) {
+ ((MaterialShapeDrawable) statusBarForeground).setElevation(elevation);
+ }
+ for (LiftOnScrollListener liftOnScrollListener : liftOnScrollListeners) {
+ liftOnScrollListener.onUpdate(elevation, background.getResolvedTintColor());
+ }
+ for (LiftOnScrollProgressListener liftProgressListener : liftProgressListeners) {
+ liftProgressListener.onUpdate(
+ elevation, background.getResolvedTintColor(), elevation / appBarElevation);
+ }
+ };
}
/**
@@ -411,21 +462,53 @@ public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
/**
* Add a {@link LiftOnScrollListener} that will be called when the lift on scroll elevation and
* background color of this {@link AppBarLayout} change.
+ *
+ * @deprecated Use {@link #addLiftOnScrollProgressListener(LiftOnScrollProgressListener)} instead.
*/
+ @Deprecated
public void addLiftOnScrollListener(@NonNull LiftOnScrollListener liftOnScrollListener) {
liftOnScrollListeners.add(liftOnScrollListener);
}
- /** Remove a previously added {@link LiftOnScrollListener}. */
+ /**
+ * Remove a previously added {@link LiftOnScrollListener}.
+ *
+ * @deprecated Use {@link #removeLiftOnScrollProgressListener(LiftOnScrollProgressListener)} instead.
+ */
+ @CanIgnoreReturnValue
+ @Deprecated
public boolean removeLiftOnScrollListener(@NonNull LiftOnScrollListener liftOnScrollListener) {
return liftOnScrollListeners.remove(liftOnScrollListener);
}
- /** Remove all previously added {@link LiftOnScrollListener}s. */
+ /**
+ * Remove all previously added {@link LiftOnScrollListener}s.
+ *
+ * @deprecated Use {@link #clearLiftOnScrollProgressListener()} instead.
+ */
+ @Deprecated
public void clearLiftOnScrollListener() {
liftOnScrollListeners.clear();
}
+ /**
+ * Add a {@link LiftOnScrollProgressListener} that will be called when the lift on scroll progress changes
+ */
+ public void addLiftOnScrollProgressListener(@NonNull LiftOnScrollProgressListener liftProgressListener) {
+ liftProgressListeners.add(liftProgressListener);
+ }
+
+ /** Remove a previously added {@link LiftOnScrollProgressListener}. */
+ @CanIgnoreReturnValue
+ public boolean removeLiftOnScrollProgressListener(@NonNull LiftOnScrollProgressListener liftProgressListener) {
+ return liftProgressListeners.remove(liftProgressListener);
+ }
+
+ /** Remove all previously added {@link LiftOnScrollProgressListener}s. */
+ public void clearLiftOnScrollProgressListener() {
+ liftProgressListeners.clear();
+ }
+
/**
* Set the drawable to use for the status bar foreground drawable. Providing null will disable the
* scrim functionality.
@@ -506,6 +589,11 @@ private Integer extractStatusBarForegroundColor() {
return null;
}
+ @Override
+ public void setBackground(Drawable background) {
+ super.setBackground(maybeCreateLiftOnScrollBackground(getContext(), background));
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
super.draw(canvas);
@@ -559,8 +647,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
case MeasureSpec.AT_MOST:
// For AT_MOST, we need to clamp our desired height with the max height
newHeight =
- clamp(
- getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec));
+ clamp(getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec));
break;
case MeasureSpec.UNSPECIFIED:
// For UNSPECIFIED we can use any height so just add the top inset
@@ -634,9 +721,10 @@ private void invalidateScrollRanges() {
// If there's a pending action, we should skip this step and respect the pending action.
SavedState savedState =
behavior == null
- || totalScrollRange == INVALID_SCROLL_RANGE
- || pendingAction != PENDING_ACTION_NONE
- ? null : behavior.saveScrollState(AbsSavedState.EMPTY_STATE, this);
+ || totalScrollRange == INVALID_SCROLL_RANGE
+ || pendingAction != PENDING_ACTION_NONE
+ ? null
+ : behavior.saveScrollState(AbsSavedState.EMPTY_STATE, this);
// Invalidate the scroll ranges
totalScrollRange = INVALID_SCROLL_RANGE;
downPreScrollRange = INVALID_SCROLL_RANGE;
@@ -924,17 +1012,22 @@ void onOffsetChanged(int offset) {
public final int getMinimumHeightForVisibleOverlappingContent() {
final int topInset = getTopInset();
final int minHeight = getMinimumHeight();
+
+ // If this layout has a min height, use it (doubled) or, if this doesn't clear the threshold,
+ // the min height.
if (minHeight != 0) {
- // If this layout has a min height, use it (doubled)
- return (minHeight * 2) + topInset;
+ int idealHeight = (minHeight * 2) + topInset;
+ return idealHeight < getHeight() ? idealHeight : minHeight + topInset;
}
- // Otherwise, we'll use twice the min height of our last child
+ // Otherwise, we'll ideally use twice the min height of our last child or, if this doesn't
+ // clear the threshold, the min height of our last child.
final int childCount = getChildCount();
final int lastChildMinHeight =
childCount >= 1 ? getChildAt(childCount - 1).getMinimumHeight() : 0;
if (lastChildMinHeight != 0) {
- return (lastChildMinHeight * 2) + topInset;
+ int idealHeight = (lastChildMinHeight * 2) + topInset;
+ return idealHeight < getHeight() ? idealHeight : lastChildMinHeight + topInset;
}
// If we reach here then we don't have a min height explicitly set. Instead we'll take a
@@ -1022,7 +1115,7 @@ boolean setLiftedState(boolean lifted, boolean force) {
this.lifted = lifted;
refreshDrawableState();
if (isLiftOnScrollCompatibleBackground()) {
- if (hasLiftOnScrollColor) {
+ if (liftOnScrollColor != null) {
// Only start the liftOnScrollColor based animation because the elevation based
// animation will happen via the lifted drawable state change and state list animator.
startLiftOnScrollColorAnimation(lifted ? 0 : 1, lifted ? 1 : 0);
@@ -1040,8 +1133,7 @@ private boolean isLiftOnScrollCompatibleBackground() {
return getBackground() instanceof MaterialShapeDrawable;
}
- private void startLiftOnScrollColorAnimation(
- float fromValue, float toValue) {
+ private void startLiftOnScrollColorAnimation(float fromValue, float toValue) {
if (liftOnScrollColorAnimator != null) {
liftOnScrollColorAnimator.cancel();
}
@@ -1095,6 +1187,17 @@ public void setLiftOnScrollTargetViewId(@IdRes int liftOnScrollTargetViewId) {
clearLiftOnScrollTargetView();
}
+ /** Sets the color of the {@link AppBarLayout} when it is fully lifted. */
+ public void setLiftOnScrollColor(@Nullable ColorStateList liftOnScrollColor) {
+ if (this.liftOnScrollColor != liftOnScrollColor) {
+ this.liftOnScrollColor = liftOnScrollColor;
+ // Force recreating the background drawable for the lift on scroll color change. The
+ // background could be potentially switched between liftOnScroll with color and liftOnScroll
+ // with elevation, based on whether the liftOnScroll color is null or not.
+ setBackground(getBackground());
+ }
+ }
+
/**
* Returns the id of the view that the {@link AppBarLayout} should use to determine whether it
* should be lifted.
@@ -1225,6 +1328,7 @@ public static class LayoutParams extends LinearLayout.LayoutParams {
})
@Retention(RetentionPolicy.SOURCE)
public @interface ScrollFlags {}
+
/**
* Disable scrolling on the view. This flag should not be combined with any of the other scroll
* flags.
@@ -1242,7 +1346,7 @@ public static class LayoutParams extends LinearLayout.LayoutParams {
* When exiting (scrolling off screen) the view will be scrolled until it is 'collapsed'. The
* collapsed height is defined by the view's minimum height.
*
- * @see ViewCompat#getMinimumHeight(View)
+ * @see View#getMinimumHeight()
* @see View#setMinimumHeight(int)
*/
public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 1 << 1;
@@ -1260,7 +1364,7 @@ public static class LayoutParams extends LinearLayout.LayoutParams {
* scroll range, the remainder of this view will be scrolled into view. The collapsed height is
* defined by the view's minimum height.
*
- * @see ViewCompat#getMinimumHeight(View)
+ * @see View#getMinimumHeight()
* @see View#setMinimumHeight(int)
*/
public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 1 << 3;
@@ -1296,8 +1400,8 @@ public static class LayoutParams extends LinearLayout.LayoutParams {
/**
* An effect that will "compress" this view as it hits the scroll ceiling (typically the top of
- * the screen). This is a parallax effect that masks this view and decreases its scroll ratio
- * in relation to the AppBarLayout's offset.
+ * the screen). This is a parallax effect that masks this view and decreases its scroll ratio in
+ * relation to the AppBarLayout's offset.
*/
public static final int SCROLL_EFFECT_COMPRESS = 1;
@@ -1396,9 +1500,7 @@ private ChildScrollEffect createScrollEffectFromInt(int scrollEffectInt) {
}
}
- /**
- * Get the scroll effect to be applied when the AppBarLayout's offset changes
- */
+ /** Get the scroll effect to be applied when the AppBarLayout's offset changes */
@Nullable
public ChildScrollEffect getScrollEffect() {
return scrollEffect;
@@ -1408,7 +1510,7 @@ public ChildScrollEffect getScrollEffect() {
* Set the scroll effect to be applied when the AppBarLayout's offset changes.
*
* @param scrollEffect An {@code AppBarLayoutChildScrollEffect} implementation. If null is
- * passed, the scroll effect will be cleared and no effect will be applied.
+ * passed, the scroll effect will be cleared and no effect will be applied.
*/
public void setScrollEffect(@Nullable ChildScrollEffect scrollEffect) {
this.scrollEffect = scrollEffect;
@@ -1417,9 +1519,9 @@ public void setScrollEffect(@Nullable ChildScrollEffect scrollEffect) {
/**
* Set the scroll effect to be applied when the AppBarLayout's offset changes.
*
- * @param scrollEffect An {@code AppBarLayoutChildScrollEffect} implementation. If
- * {@link #SCROLL_EFFECT_NONE} is passed, the scroll effect will be cleared and no
- * effect will be applied.
+ * @param scrollEffect An {@code AppBarLayoutChildScrollEffect} implementation. If {@link
+ * #SCROLL_EFFECT_NONE} is passed, the scroll effect will be cleared and no effect will be
+ * applied.
*/
public void setScrollEffect(@ScrollEffect int scrollEffect) {
this.scrollEffect = createScrollEffectFromInt(scrollEffect);
@@ -1816,7 +1918,7 @@ public boolean onLayoutChild(
// Keep fully expanded.
setHeaderTopBottomOffset(parent, abl, 0);
} else {
- // Not fully scrolled, restore the visible percetage of child layout.
+ // Not fully scrolled, restore the visible percentage of child layout.
View child = abl.getChildAt(savedState.firstVisibleChildIndex);
int offset = -child.getBottom();
if (savedState.firstVisibleChildAtMinimumHeight) {
@@ -1850,8 +1952,7 @@ public boolean onLayoutChild(
// We may have changed size, so let's constrain the top and bottom offset correctly,
// just in case we're out of the bounds
- setTopAndBottomOffset(
- clamp(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
+ setTopAndBottomOffset(clamp(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
// Update the AppBarLayout's drawable state for any elevation changes. This is needed so that
// the elevation is set in the first layout, so that we don't get a visual jump pre-N (due to
diff --git a/lib/java/com/google/android/material/appbar/CollapsingToolbarLayout.java b/lib/java/com/google/android/material/appbar/CollapsingToolbarLayout.java
index 6e5b95bb676..9d451aced91 100644
--- a/lib/java/com/google/android/material/appbar/CollapsingToolbarLayout.java
+++ b/lib/java/com/google/android/material/appbar/CollapsingToolbarLayout.java
@@ -53,7 +53,6 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleRes;
-import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.math.MathUtils;
import androidx.core.util.ObjectsCompat;
@@ -155,6 +154,27 @@ public class CollapsingToolbarLayout extends FrameLayout {
@Retention(RetentionPolicy.SOURCE)
public @interface TitleCollapseMode {}
+ /**
+ * The gravity of the collapsed title is based on the entire space of the CollapsedToolbarLayout.
+ */
+ private static final int COLLAPSED_TITLE_GRAVITY_ENTIRE_SPACE = 0;
+
+ /**
+ * The gravity of the collapsed title is based on the remaining space in the
+ * CollapsedToolbarLayout after accounting for other views such as the menu.
+ */
+ private static final int COLLAPSED_TITLE_GRAVITY_AVAILABLE_SPACE = 1;
+
+ /**
+ * The mode in which to calculate the gravity of the collapsed title.
+ *
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ @IntDef(value = {COLLAPSED_TITLE_GRAVITY_ENTIRE_SPACE, COLLAPSED_TITLE_GRAVITY_AVAILABLE_SPACE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CollapsedTitleGravityMode {}
+
private boolean refreshToolbar = true;
private int toolbarId;
@Nullable private ViewGroup toolbar;
@@ -165,12 +185,15 @@ public class CollapsingToolbarLayout extends FrameLayout {
private int expandedMarginTop;
private int expandedMarginEnd;
private int expandedMarginBottom;
+ private int expandedTitleSpacing;
private final Rect tmpRect = new Rect();
- @NonNull final CollapsingTextHelper collapsingTextHelper;
+ @NonNull final CollapsingTextHelper collapsingTitleHelper;
+ @NonNull final CollapsingTextHelper collapsingSubtitleHelper;
@NonNull final ElevationOverlayProvider elevationOverlayProvider;
private boolean collapsingTitleEnabled;
private boolean drawCollapsingTitle;
+ @CollapsedTitleGravityMode private final int collapsedTitleGravityMode;
@Nullable private Drawable contentScrim;
@Nullable Drawable statusBarScrim;
@@ -194,9 +217,12 @@ public class CollapsingToolbarLayout extends FrameLayout {
private int topInsetApplied = 0;
private boolean forceApplySystemWindowInsetTop;
- private int extraMultilineHeight = 0;
+ private int extraMultilineTitleHeight = 0;
+ private int extraMultilineSubtitleHeight = 0;
private boolean extraMultilineHeightEnabled;
+ private int extraHeightForTitles = 0;
+
public CollapsingToolbarLayout(@NonNull Context context) {
this(context, null);
}
@@ -205,35 +231,39 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet
this(context, attrs, R.attr.collapsingToolbarLayoutStyle);
}
- public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ public CollapsingToolbarLayout(
+ @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr);
// Ensure we are using the correctly themed context rather than the context that was passed in.
context = getContext();
screenOrientation = getResources().getConfiguration().orientation;
- collapsingTextHelper = new CollapsingTextHelper(this);
- collapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
- collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(false);
+ collapsingTitleHelper = new CollapsingTextHelper(this);
+ collapsingTitleHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
+ collapsingTitleHelper.setRtlTextDirectionHeuristicsEnabled(false);
elevationOverlayProvider = new ElevationOverlayProvider(context);
TypedArray a =
ThemeEnforcement.obtainStyledAttributes(
- context,
- attrs,
- R.styleable.CollapsingToolbarLayout,
- defStyleAttr,
- DEF_STYLE_RES);
+ context, attrs, R.styleable.CollapsingToolbarLayout, defStyleAttr, DEF_STYLE_RES);
- collapsingTextHelper.setExpandedTextGravity(
+ int titleExpandedGravity =
a.getInt(
R.styleable.CollapsingToolbarLayout_expandedTitleGravity,
- Gravity.START | Gravity.BOTTOM));
- collapsingTextHelper.setCollapsedTextGravity(
+ Gravity.START | Gravity.BOTTOM);
+ int titleCollapsedGravity =
a.getInt(
R.styleable.CollapsingToolbarLayout_collapsedTitleGravity,
- Gravity.START | Gravity.CENTER_VERTICAL));
+ Gravity.START | Gravity.CENTER_VERTICAL);
+ collapsedTitleGravityMode =
+ a.getInt(
+ R.styleable.CollapsingToolbarLayout_collapsedTitleGravityMode,
+ COLLAPSED_TITLE_GRAVITY_AVAILABLE_SPACE);
+
+ collapsingTitleHelper.setExpandedTextGravity(titleExpandedGravity);
+ collapsingTitleHelper.setCollapsedTextGravity(titleCollapsedGravity);
expandedMarginStart =
expandedMarginTop =
@@ -258,23 +288,27 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet
expandedMarginBottom =
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom, 0);
}
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleSpacing)) {
+ expandedTitleSpacing =
+ a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleSpacing, 0);
+ }
collapsingTitleEnabled = a.getBoolean(R.styleable.CollapsingToolbarLayout_titleEnabled, true);
setTitle(a.getText(R.styleable.CollapsingToolbarLayout_title));
// First load the default text appearances
- collapsingTextHelper.setExpandedTextAppearance(
+ collapsingTitleHelper.setExpandedTextAppearance(
R.style.TextAppearance_Design_CollapsingToolbar_Expanded);
- collapsingTextHelper.setCollapsedTextAppearance(
+ collapsingTitleHelper.setCollapsedTextAppearance(
androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Title);
// Now overlay any custom text appearances
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance)) {
- collapsingTextHelper.setExpandedTextAppearance(
+ collapsingTitleHelper.setExpandedTextAppearance(
a.getResourceId(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance, 0));
}
if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance)) {
- collapsingTextHelper.setCollapsedTextAppearance(
+ collapsingTitleHelper.setCollapsedTextAppearance(
a.getResourceId(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance, 0));
}
@@ -286,12 +320,12 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet
}
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextColor)) {
- collapsingTextHelper.setExpandedTextColor(
+ collapsingTitleHelper.setExpandedTextColor(
MaterialResources.getColorStateList(
context, a, R.styleable.CollapsingToolbarLayout_expandedTitleTextColor));
}
if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextColor)) {
- collapsingTextHelper.setCollapsedTextColor(
+ collapsingTitleHelper.setCollapsedTextColor(
MaterialResources.getColorStateList(
context, a, R.styleable.CollapsingToolbarLayout_collapsedTitleTextColor));
}
@@ -299,12 +333,59 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet
scrimVisibleHeightTrigger =
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_scrimVisibleHeightTrigger, -1);
- if (a.hasValue(R.styleable.CollapsingToolbarLayout_maxLines)) {
- collapsingTextHelper.setExpandedMaxLines(a.getInt(R.styleable.CollapsingToolbarLayout_maxLines, 1));
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_titleMaxLines)) {
+ collapsingTitleHelper.setExpandedMaxLines(
+ a.getInt(R.styleable.CollapsingToolbarLayout_titleMaxLines, 1));
+ } else if (a.hasValue(R.styleable.CollapsingToolbarLayout_maxLines)) {
+ collapsingTitleHelper.setExpandedMaxLines(
+ a.getInt(R.styleable.CollapsingToolbarLayout_maxLines, 1));
+ }
+
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator)) {
+ collapsingTitleHelper.setPositionInterpolator(
+ android.view.animation.AnimationUtils.loadInterpolator(
+ context,
+ a.getResourceId(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator, 0)));
+ }
+
+ collapsingSubtitleHelper = new CollapsingTextHelper(this);
+ collapsingSubtitleHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
+ collapsingSubtitleHelper.setRtlTextDirectionHeuristicsEnabled(false);
+
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_subtitle)) {
+ setSubtitle(a.getText(R.styleable.CollapsingToolbarLayout_subtitle));
}
+ collapsingSubtitleHelper.setExpandedTextGravity(titleExpandedGravity);
+ collapsingSubtitleHelper.setCollapsedTextGravity(titleCollapsedGravity);
+ collapsingSubtitleHelper.setExpandedTextAppearance(
+ androidx.appcompat.R.style.TextAppearance_AppCompat_Headline);
+ collapsingSubtitleHelper.setCollapsedTextAppearance(
+ androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Subtitle);
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextAppearance)) {
+ collapsingSubtitleHelper.setExpandedTextAppearance(
+ a.getResourceId(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextAppearance, 0));
+ }
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextAppearance)) {
+ collapsingSubtitleHelper.setCollapsedTextAppearance(
+ a.getResourceId(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextAppearance, 0));
+ }
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextColor)) {
+ collapsingSubtitleHelper.setExpandedTextColor(
+ MaterialResources.getColorStateList(
+ context, a, R.styleable.CollapsingToolbarLayout_expandedSubtitleTextColor));
+ }
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextColor)) {
+ collapsingSubtitleHelper.setCollapsedTextColor(
+ MaterialResources.getColorStateList(
+ context, a, R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextColor));
+ }
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_subtitleMaxLines)) {
+ collapsingSubtitleHelper.setExpandedMaxLines(
+ a.getInt(R.styleable.CollapsingToolbarLayout_subtitleMaxLines, 1));
+ }
if (a.hasValue(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator)) {
- collapsingTextHelper.setPositionInterpolator(
+ collapsingSubtitleHelper.setPositionInterpolator(
android.view.animation.AnimationUtils.loadInterpolator(
context,
a.getResourceId(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator, 0)));
@@ -374,7 +455,7 @@ protected void onAttachedToWindow() {
appBarLayout.addOnOffsetChangedListener(onOffsetChangedListener);
// We're attached, so lets request an inset dispatch
- ViewCompat.requestApplyInsets(this);
+ requestApplyInsets();
}
}
@@ -426,15 +507,17 @@ public void draw(@NonNull Canvas canvas) {
&& contentScrim != null
&& scrimAlpha > 0
&& isTitleCollapseFadeMode()
- && collapsingTextHelper.getExpansionFraction()
- < collapsingTextHelper.getFadeModeThresholdFraction()) {
+ && collapsingTitleHelper.getExpansionFraction()
+ < collapsingTitleHelper.getFadeModeThresholdFraction()) {
// Mask the expanded text with the contentScrim
int save = canvas.save();
canvas.clipRect(contentScrim.getBounds(), Op.DIFFERENCE);
- collapsingTextHelper.draw(canvas);
+ collapsingTitleHelper.draw(canvas);
+ collapsingSubtitleHelper.draw(canvas);
canvas.restoreToCount(save);
} else {
- collapsingTextHelper.draw(canvas);
+ collapsingTitleHelper.draw(canvas);
+ collapsingSubtitleHelper.draw(canvas);
}
}
@@ -452,7 +535,7 @@ && isTitleCollapseFadeMode()
@Override
protected void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- collapsingTextHelper.maybeUpdateFontWeightAdjustment(newConfig);
+ collapsingTitleHelper.maybeUpdateFontWeightAdjustment(newConfig);
// When the orientation changes with extra multiline height enabled and when collapsed, there
// can be an issue where the offset/scroll state is invalid due to the number of lines of text
@@ -461,7 +544,7 @@ protected void onConfigurationChanged(@NonNull Configuration newConfig) {
// fully collapsed prior to screen rotation.
if (screenOrientation != newConfig.orientation
&& extraMultilineHeightEnabled
- && collapsingTextHelper.getExpansionFraction() == 1f) {
+ && collapsingTitleHelper.getExpansionFraction() == 1f) {
ViewParent parent = getParent();
if (parent instanceof AppBarLayout) {
AppBarLayout appBarLayout = (AppBarLayout) parent;
@@ -616,22 +699,67 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
- if (extraMultilineHeightEnabled && collapsingTextHelper.getExpandedMaxLines() > 1) {
+ updateTitleFromToolbarIfNeeded();
+
+ if (collapsingTitleEnabled && !TextUtils.isEmpty(collapsingTitleHelper.getText())) {
+ final int originalHeight = getMeasuredHeight();
// Need to update title and bounds in order to calculate line count and text height.
- updateTitleFromToolbarIfNeeded();
- updateTextBounds(0, 0, getMeasuredWidth(), getMeasuredHeight(), /* forceRecalculate= */ true);
-
- int lineCount = collapsingTextHelper.getExpandedLineCount();
- if (lineCount > 1) {
- // Add extra height based on the amount of height beyond the first line of title text.
- int expandedTextHeight =
- Math.round(collapsingTextHelper.getExpandedTextFullSingleLineHeight());
- extraMultilineHeight = expandedTextHeight * (lineCount - 1);
- int newHeight = getMeasuredHeight() + extraMultilineHeight;
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ updateTextBounds(0, 0, getMeasuredWidth(), originalHeight, /* forceRecalculate= */ true);
+
+ // Calculates the extra height needed for the contents of the collapsing toolbar, if needed.
+ int expectedHeight =
+ (int)
+ (topInsetApplied
+ + expandedMarginTop
+ + collapsingTitleHelper.getExpandedTextFullSingleLineHeight()
+ + (TextUtils.isEmpty(collapsingSubtitleHelper.getText())
+ ? 0
+ : expandedTitleSpacing
+ + collapsingSubtitleHelper.getExpandedTextFullSingleLineHeight())
+ + expandedMarginBottom);
+ if (expectedHeight > originalHeight) {
+ extraHeightForTitles = expectedHeight - originalHeight;
} else {
- extraMultilineHeight = 0;
+ extraHeightForTitles = 0;
+ }
+
+ if (extraMultilineHeightEnabled) {
+ // Calculates the extra height needed for the multiline title, if needed.
+ if (collapsingTitleHelper.getExpandedMaxLines() > 1) {
+ int lineCount = collapsingTitleHelper.getExpandedLineCount();
+ if (lineCount > 1) {
+ // Add extra height based on the amount of height beyond the first line of title text.
+ int expandedTextHeight =
+ Math.round(collapsingTitleHelper.getExpandedTextFullSingleLineHeight());
+ extraMultilineTitleHeight = expandedTextHeight * (lineCount - 1);
+ } else {
+ extraMultilineTitleHeight = 0;
+ }
+ }
+ // Calculates the extra height needed for the multiline subtitle, if needed.
+ if (collapsingSubtitleHelper.getExpandedMaxLines() > 1) {
+ int lineCount = collapsingSubtitleHelper.getExpandedLineCount();
+ if (lineCount > 1) {
+ // Add extra height based on the amount of height beyond the first line of subtitle
+ // text.
+ int expandedTextHeight =
+ Math.round(collapsingSubtitleHelper.getExpandedTextFullSingleLineHeight());
+ extraMultilineSubtitleHeight = expandedTextHeight * (lineCount - 1);
+ } else {
+ extraMultilineSubtitleHeight = 0;
+ }
+ }
+ }
+
+ if (extraHeightForTitles + extraMultilineTitleHeight + extraMultilineSubtitleHeight > 0) {
+ heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(
+ originalHeight
+ + extraHeightForTitles
+ + extraMultilineTitleHeight
+ + extraMultilineSubtitleHeight,
+ MeasureSpec.EXACTLY);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@@ -669,7 +797,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
getViewOffsetHelper(getChildAt(i)).onViewLayout();
}
- updateTextBounds(left, top, right, bottom, /* forceRecalculate= */false);
+ updateTextBounds(left, top, right, bottom, /* forceRecalculate= */ false);
updateTitleFromToolbarIfNeeded();
@@ -696,23 +824,60 @@ private void updateTextBounds(
updateCollapsedBounds(isRtl);
// Update the expanded bounds
- collapsingTextHelper.setExpandedBounds(
- isRtl ? expandedMarginEnd : expandedMarginStart,
- tmpRect.top + expandedMarginTop,
- right - left - (isRtl ? expandedMarginStart : expandedMarginEnd),
- bottom - top - expandedMarginBottom);
-
- // Now recalculate using the new bounds
- collapsingTextHelper.recalculate(forceRecalculate);
+ final int titleBoundsLeft = isRtl ? expandedMarginEnd : expandedMarginStart;
+ final int titleBoundsTop = tmpRect.top + expandedMarginTop;
+ final int titleBoundsRight =
+ right - left - (isRtl ? expandedMarginStart : expandedMarginEnd);
+ final int titleBoundsBottom = bottom - top - expandedMarginBottom;
+ if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) {
+ collapsingTitleHelper.setExpandedBounds(
+ titleBoundsLeft, titleBoundsTop, titleBoundsRight, titleBoundsBottom);
+
+ // Now recalculate using the new bounds
+ collapsingTitleHelper.recalculate(forceRecalculate);
+ } else {
+ collapsingTitleHelper.setExpandedBounds(
+ titleBoundsLeft,
+ titleBoundsTop,
+ titleBoundsRight,
+ (int)
+ (titleBoundsBottom
+ - (collapsingSubtitleHelper.getExpandedTextFullSingleLineHeight()
+ + extraMultilineSubtitleHeight)
+ - expandedTitleSpacing),
+ /* alignBaselineAtBottom= */ false);
+ collapsingSubtitleHelper.setExpandedBounds(
+ titleBoundsLeft,
+ (int)
+ (titleBoundsTop
+ + (collapsingTitleHelper.getExpandedTextFullSingleLineHeight()
+ + extraMultilineTitleHeight)
+ + expandedTitleSpacing),
+ titleBoundsRight,
+ titleBoundsBottom,
+ /* alignBaselineAtBottom= */ false);
+
+ // Now recalculate using the new bounds
+ collapsingTitleHelper.recalculate(forceRecalculate);
+ collapsingSubtitleHelper.recalculate(forceRecalculate);
+ }
}
}
}
private void updateTitleFromToolbarIfNeeded() {
if (toolbar != null) {
- if (collapsingTitleEnabled && TextUtils.isEmpty(collapsingTextHelper.getText())) {
- // If we do not currently have a title, try and grab it from the Toolbar
- setTitle(getToolbarTitle(toolbar));
+ if (collapsingTitleEnabled) {
+ CharSequence title = getToolbarTitle(toolbar);
+ if (TextUtils.isEmpty(collapsingTitleHelper.getText()) && !TextUtils.isEmpty(title)) {
+ // If we do not currently have a title, try and grab it from the Toolbar
+ setTitle(title);
+ }
+ CharSequence subtitle = getToolbarSubtitle(toolbar);
+ if (TextUtils.isEmpty(collapsingSubtitleHelper.getText()) && !TextUtils.isEmpty(subtitle)) {
+ // If we do not currently have a subtitle, try and grab it from the Toolbar
+ setSubtitle(subtitle);
+ }
}
}
}
@@ -743,13 +908,56 @@ private void updateCollapsedBounds(boolean isRtl) {
titleMarginTop = 0;
titleMarginBottom = 0;
}
- collapsingTextHelper.setCollapsedBounds(
- tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart),
- tmpRect.top + maxOffset + titleMarginTop,
- tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd),
- tmpRect.bottom + maxOffset - titleMarginBottom);
+ final int titleBoundsLeft = tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart);
+ final int titleBoundsRight = tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd);
+ final int titleBoundsTop = tmpRect.top + maxOffset + titleMarginTop;
+ final int titleBoundsBottom = tmpRect.bottom + maxOffset - titleMarginBottom;
+ final int titleBoundsBottomWithSubtitle =
+ (int) (titleBoundsBottom - collapsingSubtitleHelper.getCollapsedFullSingleLineHeight());
+ final int subtitleBoundsTop = (int) (titleBoundsTop + collapsingTitleHelper.getCollapsedFullSingleLineHeight());
+
+ // Setting the valid collapsed bounds that text can be displayed in
+ if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) {
+ collapsingTitleHelper.setCollapsedBounds(
+ titleBoundsLeft, titleBoundsTop, titleBoundsRight, titleBoundsBottom);
+ } else {
+ collapsingTitleHelper.setCollapsedBounds(
+ titleBoundsLeft,
+ titleBoundsTop,
+ titleBoundsRight,
+ titleBoundsBottomWithSubtitle);
+ collapsingSubtitleHelper.setCollapsedBounds(
+ titleBoundsLeft,
+ subtitleBoundsTop,
+ titleBoundsRight,
+ titleBoundsBottom);
+ }
+
+ // If the collapsed title gravity should be using the whole collapsing toolbar layout instead of
+ // the dummy layout, we should set the collapsed bounds for offsets.
+ if (collapsedTitleGravityMode == COLLAPSED_TITLE_GRAVITY_ENTIRE_SPACE) {
+ DescendantOffsetUtils.getDescendantRect(this, this, tmpRect);
+ final int validTitleBoundsLeft = tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart);
+ final int validTitleBoundsRight = tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd);
+ if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) {
+ collapsingTitleHelper.setCollapsedBoundsForOffsets(
+ validTitleBoundsLeft, titleBoundsTop, validTitleBoundsRight, titleBoundsBottom);
+ } else {
+ collapsingTitleHelper.setCollapsedBoundsForOffsets(
+ validTitleBoundsLeft,
+ titleBoundsTop,
+ validTitleBoundsRight,
+ titleBoundsBottomWithSubtitle);
+ collapsingSubtitleHelper.setCollapsedBoundsForOffsets(
+ validTitleBoundsLeft,
+ subtitleBoundsTop,
+ validTitleBoundsRight,
+ titleBoundsBottom);
+ }
+ }
}
+ @Nullable
private static CharSequence getToolbarTitle(View view) {
if (view instanceof androidx.appcompat.widget.Toolbar) {
return ((androidx.appcompat.widget.Toolbar) view).getTitle();
@@ -760,6 +968,17 @@ private static CharSequence getToolbarTitle(View view) {
}
}
+ @Nullable
+ private static CharSequence getToolbarSubtitle(View view) {
+ if (view instanceof androidx.appcompat.widget.Toolbar) {
+ return ((androidx.appcompat.widget.Toolbar) view).getSubtitle();
+ } else if (view instanceof android.widget.Toolbar) {
+ return ((android.widget.Toolbar) view).getSubtitle();
+ } else {
+ return null;
+ }
+ }
+
private static int getHeightWithMargins(@NonNull final View view) {
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof MarginLayoutParams) {
@@ -787,7 +1006,7 @@ static ViewOffsetHelper getViewOffsetHelper(@NonNull View view) {
* @attr ref R.styleable#CollapsingToolbarLayout_title
*/
public void setTitle(@Nullable CharSequence title) {
- collapsingTextHelper.setText(title);
+ collapsingTitleHelper.setText(title);
updateContentDescriptionFromTitle();
}
@@ -799,7 +1018,29 @@ public void setTitle(@Nullable CharSequence title) {
*/
@Nullable
public CharSequence getTitle() {
- return collapsingTitleEnabled ? collapsingTextHelper.getText() : null;
+ return collapsingTitleEnabled ? collapsingTitleHelper.getText() : null;
+ }
+
+ /**
+ * Sets the subtitle to be displayed by this view, if enabled.
+ *
+ * @see #setTitleEnabled(boolean)
+ * @see #getSubtitle()
+ * @attr ref R.styleable#CollapsingToolbarLayout_subtitle
+ */
+ public void setSubtitle(@Nullable CharSequence subtitle) {
+ collapsingSubtitleHelper.setText(subtitle);
+ }
+
+ /**
+ * Returns the subtitle currently being displayed by this view. If the subtitle is not enabled,
+ * then this will return {@code null}.
+ *
+ * @attr ref R.styleable#CollapsingToolbarLayout_subtitle
+ */
+ @Nullable
+ public CharSequence getSubtitle() {
+ return collapsingTitleEnabled ? collapsingSubtitleHelper.getText() : null;
}
/**
@@ -812,7 +1053,8 @@ public void setTitleCollapseMode(@TitleCollapseMode int titleCollapseMode) {
this.titleCollapseMode = titleCollapseMode;
boolean fadeModeEnabled = isTitleCollapseFadeMode();
- collapsingTextHelper.setFadeModeEnabled(fadeModeEnabled);
+ collapsingTitleHelper.setFadeModeEnabled(fadeModeEnabled);
+ collapsingSubtitleHelper.setFadeModeEnabled(fadeModeEnabled);
ViewParent parent = getParent();
if (parent instanceof AppBarLayout) {
@@ -876,7 +1118,6 @@ public boolean isTitleEnabled() {
return collapsingTitleEnabled;
}
-
/**
* Set ellipsizing on the title text.
*
@@ -884,15 +1125,13 @@ public boolean isTitleEnabled() {
* @attr ref R.styleable#CollapsingToolbarLayout_titleTextEllipsize
*/
public void setTitleEllipsize(@NonNull TruncateAt ellipsize) {
- collapsingTextHelper.setTitleTextEllipsize(ellipsize);
+ collapsingTitleHelper.setTitleTextEllipsize(ellipsize);
}
- /**
- * Get ellipsizing currently applied on the title text.
- */
+ /** Get ellipsizing currently applied on the title text. */
@NonNull
public TruncateAt getTitleTextEllipsize() {
- return collapsingTextHelper.getTitleTextEllipsize();
+ return collapsingTitleHelper.getTitleTextEllipsize();
}
// Convert to supported TruncateAt values
@@ -1024,7 +1263,7 @@ public void setContentScrimColor(@ColorInt int color) {
* @see #getContentScrim()
*/
public void setContentScrimResource(@DrawableRes int resId) {
- setContentScrim(ContextCompat.getDrawable(getContext(), resId));
+ setContentScrim(getContext().getDrawable(resId));
}
/**
@@ -1082,8 +1321,8 @@ protected void drawableStateChanged() {
if (d != null && d.isStateful()) {
changed |= d.setState(state);
}
- if (collapsingTextHelper != null) {
- changed |= collapsingTextHelper.setState(state);
+ if (collapsingTitleHelper != null) {
+ changed |= collapsingTitleHelper.setState(state);
}
if (changed) {
@@ -1130,7 +1369,7 @@ public void setStatusBarScrimColor(@ColorInt int color) {
* @see #getStatusBarScrim()
*/
public void setStatusBarScrimResource(@DrawableRes int resId) {
- setStatusBarScrim(ContextCompat.getDrawable(getContext(), resId));
+ setStatusBarScrim(getContext().getDrawable(resId));
}
/**
@@ -1152,7 +1391,18 @@ public Drawable getStatusBarScrim() {
* com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance
*/
public void setCollapsedTitleTextAppearance(@StyleRes int resId) {
- collapsingTextHelper.setCollapsedTextAppearance(resId);
+ collapsingTitleHelper.setCollapsedTextAppearance(resId);
+ }
+
+ /**
+ * Sets the text color and size for the collapsed subtitle from the specified TextAppearance
+ * resource.
+ *
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedSubtitleTextAppearance
+ */
+ public void setCollapsedSubtitleTextAppearance(@StyleRes int resId) {
+ collapsingSubtitleHelper.setCollapsedTextAppearance(resId);
}
/**
@@ -1170,26 +1420,46 @@ public void setCollapsedTitleTextColor(@ColorInt int color) {
* @param colors ColorStateList containing the new text colors
*/
public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) {
- collapsingTextHelper.setCollapsedTextColor(colors);
+ collapsingTitleHelper.setCollapsedTextColor(colors);
}
/**
- * Sets the horizontal alignment of the collapsed title and the vertical gravity that will be used
- * when there is extra space in the collapsed bounds beyond what is required for the title itself.
+ * Sets the text color of the collapsed subtitle.
+ *
+ * @param color The new text color in ARGB format
+ */
+ public void setCollapsedSubtitleTextColor(@ColorInt int color) {
+ setCollapsedSubtitleTextColor(ColorStateList.valueOf(color));
+ }
+
+ /**
+ * Sets the text colors of the collapsed subtitle.
+ *
+ * @param colors ColorStateList containing the new text colors
+ */
+ public void setCollapsedSubtitleTextColor(@NonNull ColorStateList colors) {
+ collapsingSubtitleHelper.setCollapsedTextColor(colors);
+ }
+
+ /**
+ * Sets the horizontal alignment of the collapsed titles and the vertical gravity that will be
+ * used when there is extra space in the collapsed bounds beyond what is required for the title
+ * itself.
*
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
*/
public void setCollapsedTitleGravity(int gravity) {
- collapsingTextHelper.setCollapsedTextGravity(gravity);
+ collapsingTitleHelper.setCollapsedTextGravity(gravity);
+ collapsingSubtitleHelper.setCollapsedTextGravity(gravity);
}
/**
- * Returns the horizontal and vertical alignment for title when collapsed.
+ * Returns the horizontal and vertical alignment for titles when collapsed.
*
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
*/
public int getCollapsedTitleGravity() {
- return collapsingTextHelper.getCollapsedTextGravity();
+ return collapsingTitleHelper.getCollapsedTextGravity();
}
/**
@@ -1199,7 +1469,18 @@ public int getCollapsedTitleGravity() {
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance
*/
public void setExpandedTitleTextAppearance(@StyleRes int resId) {
- collapsingTextHelper.setExpandedTextAppearance(resId);
+ collapsingTitleHelper.setExpandedTextAppearance(resId);
+ }
+
+ /**
+ * Sets the text color and size for the expanded subtitle from the specified TextAppearance
+ * resource.
+ *
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedSubtitleTextAppearance
+ */
+ public void setExpandedSubtitleTextAppearance(@StyleRes int resId) {
+ collapsingSubtitleHelper.setExpandedTextAppearance(resId);
}
/**
@@ -1217,26 +1498,45 @@ public void setExpandedTitleColor(@ColorInt int color) {
* @param colors ColorStateList containing the new text colors
*/
public void setExpandedTitleTextColor(@NonNull ColorStateList colors) {
- collapsingTextHelper.setExpandedTextColor(colors);
+ collapsingTitleHelper.setExpandedTextColor(colors);
+ }
+
+ /**
+ * Sets the text color of the expanded subtitle.
+ *
+ * @param color The new text color in ARGB format
+ */
+ public void setExpandedSubtitleColor(@ColorInt int color) {
+ setExpandedSubtitleTextColor(ColorStateList.valueOf(color));
}
/**
- * Sets the horizontal alignment of the expanded title and the vertical gravity that will be used
+ * Sets the text colors of the expanded subtitle.
+ *
+ * @param colors ColorStateList containing the new text colors
+ */
+ public void setExpandedSubtitleTextColor(@NonNull ColorStateList colors) {
+ collapsingSubtitleHelper.setExpandedTextColor(colors);
+ }
+
+ /**
+ * Sets the horizontal alignment of the expanded titles and the vertical gravity that will be used
* when there is extra space in the expanded bounds beyond what is required for the title itself.
*
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
*/
public void setExpandedTitleGravity(int gravity) {
- collapsingTextHelper.setExpandedTextGravity(gravity);
+ collapsingTitleHelper.setExpandedTextGravity(gravity);
+ collapsingSubtitleHelper.setExpandedTextGravity(gravity);
}
/**
- * Returns the horizontal and vertical alignment for title when expanded.
+ * Returns the horizontal and vertical alignment for titles when expanded.
*
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
*/
public int getExpandedTitleGravity() {
- return collapsingTextHelper.getExpandedTextGravity();
+ return collapsingTitleHelper.getExpandedTextGravity();
}
/**
@@ -1245,12 +1545,26 @@ public int getExpandedTitleGravity() {
* @param textSize The text size of the expanded title.
*/
public void setExpandedTitleTextSize(float textSize) {
- collapsingTextHelper.setExpandedTextSize(textSize);
+ collapsingTitleHelper.setExpandedTextSize(textSize);
+ }
+
+ /**
+ * Sets the text size of the expanded subtitle.
+ *
+ * @param textSize The text size of the expanded subtitle.
+ */
+ public void setExpandedSubtitleTextSize(float textSize) {
+ collapsingSubtitleHelper.setExpandedTextSize(textSize);
}
/** Returns the text size of the expanded title. */
public float getExpandedTitleTextSize() {
- return collapsingTextHelper.getExpandedTextSize();
+ return collapsingTitleHelper.getExpandedTextSize();
+ }
+
+ /** Returns the text size of the expanded subtitle. */
+ public float getExpandedSubtitleTextSize() {
+ return collapsingSubtitleHelper.getExpandedTextSize();
}
/**
@@ -1259,12 +1573,26 @@ public float getExpandedTitleTextSize() {
* @param textSize The text size of the collapsed title.
*/
public void setCollapsedTitleTextSize(float textSize) {
- collapsingTextHelper.setCollapsedTextSize(textSize);
+ collapsingTitleHelper.setCollapsedTextSize(textSize);
+ }
+
+ /**
+ * Sets the text size of the collapsed subtitle.
+ *
+ * @param textSize The text size of the collapsed subtitle.
+ */
+ public void setCollapsedSubtitleTextSize(float textSize) {
+ collapsingSubtitleHelper.setCollapsedTextSize(textSize);
}
/** Returns the text size of the collapsed title. */
public float getCollapsedTitleTextSize() {
- return collapsingTextHelper.getCollapsedTextSize();
+ return collapsingTitleHelper.getCollapsedTextSize();
+ }
+
+ /** Returns the text size of the collapsed subtitle. */
+ public float getCollapsedSubtitleTextSize() {
+ return collapsingSubtitleHelper.getCollapsedTextSize();
}
/**
@@ -1273,13 +1601,28 @@ public float getCollapsedTitleTextSize() {
* @param typeface typeface to use, or {@code null} to use the default.
*/
public void setCollapsedTitleTypeface(@Nullable Typeface typeface) {
- collapsingTextHelper.setCollapsedTypeface(typeface);
+ collapsingTitleHelper.setCollapsedTypeface(typeface);
+ }
+
+ /**
+ * Set the typeface to use for the collapsed subtitle.
+ *
+ * @param typeface typeface to use, or {@code null} to use the default.
+ */
+ public void setCollapsedSubtitleTypeface(@Nullable Typeface typeface) {
+ collapsingSubtitleHelper.setCollapsedTypeface(typeface);
}
/** Returns the typeface used for the collapsed title. */
@NonNull
public Typeface getCollapsedTitleTypeface() {
- return collapsingTextHelper.getCollapsedTypeface();
+ return collapsingTitleHelper.getCollapsedTypeface();
+ }
+
+ /** Returns the typeface used for the collapsed subtitle. */
+ @NonNull
+ public Typeface getCollapsedSubtitleTypeface() {
+ return collapsingSubtitleHelper.getCollapsedTypeface();
}
/**
@@ -1288,13 +1631,28 @@ public Typeface getCollapsedTitleTypeface() {
* @param typeface typeface to use, or {@code null} to use the default.
*/
public void setExpandedTitleTypeface(@Nullable Typeface typeface) {
- collapsingTextHelper.setExpandedTypeface(typeface);
+ collapsingTitleHelper.setExpandedTypeface(typeface);
+ }
+
+ /**
+ * Set the typeface to use for the expanded subtitle.
+ *
+ * @param typeface typeface to use, or {@code null} to use the default.
+ */
+ public void setExpandedSubtitleTypeface(@Nullable Typeface typeface) {
+ collapsingSubtitleHelper.setExpandedTypeface(typeface);
}
/** Returns the typeface used for the expanded title. */
@NonNull
public Typeface getExpandedTitleTypeface() {
- return collapsingTextHelper.getExpandedTypeface();
+ return collapsingTitleHelper.getExpandedTypeface();
+ }
+
+ /** Returns the typeface used for the expanded subtitle. */
+ @NonNull
+ public Typeface getExpandedSubtitleTypeface() {
+ return collapsingSubtitleHelper.getExpandedTypeface();
}
/**
@@ -1321,7 +1679,8 @@ public void setExpandedTitleMargin(int start, int top, int end, int bottom) {
/**
* @return the starting expanded title margin in pixels
* @see #setExpandedTitleMarginStart(int)
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
*/
public int getExpandedTitleMarginStart() {
return expandedMarginStart;
@@ -1332,7 +1691,8 @@ public int getExpandedTitleMarginStart() {
*
* @param margin the starting title margin in pixels
* @see #getExpandedTitleMarginStart()
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
*/
public void setExpandedTitleMarginStart(int margin) {
expandedMarginStart = margin;
@@ -1342,7 +1702,8 @@ public void setExpandedTitleMarginStart(int margin) {
/**
* @return the top expanded title margin in pixels
* @see #setExpandedTitleMarginTop(int)
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
*/
public int getExpandedTitleMarginTop() {
return expandedMarginTop;
@@ -1353,7 +1714,8 @@ public int getExpandedTitleMarginTop() {
*
* @param margin the top title margin in pixels
* @see #getExpandedTitleMarginTop()
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
*/
public void setExpandedTitleMarginTop(int margin) {
expandedMarginTop = margin;
@@ -1363,7 +1725,8 @@ public void setExpandedTitleMarginTop(int margin) {
/**
* @return the ending expanded title margin in pixels
* @see #setExpandedTitleMarginEnd(int)
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
*/
public int getExpandedTitleMarginEnd() {
return expandedMarginEnd;
@@ -1374,7 +1737,8 @@ public int getExpandedTitleMarginEnd() {
*
* @param margin the ending title margin in pixels
* @see #getExpandedTitleMarginEnd()
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
*/
public void setExpandedTitleMarginEnd(int margin) {
expandedMarginEnd = margin;
@@ -1384,7 +1748,8 @@ public void setExpandedTitleMarginEnd(int margin) {
/**
* @return the bottom expanded title margin in pixels
* @see #setExpandedTitleMarginBottom(int)
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
*/
public int getExpandedTitleMarginBottom() {
return expandedMarginBottom;
@@ -1395,7 +1760,8 @@ public int getExpandedTitleMarginBottom() {
*
* @param margin the bottom title margin in pixels
* @see #getExpandedTitleMarginBottom()
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
*/
public void setExpandedTitleMarginBottom(int margin) {
expandedMarginBottom = margin;
@@ -1403,30 +1769,44 @@ public void setExpandedTitleMarginBottom(int margin) {
}
/**
- * Sets the maximum number of lines to display in the expanded state.
- * Experimental Feature.
+ * Returns the spacing between the expanded title and subtitle in pixels
+ *
+ * @see #setExpandedTitleSpacing(int)
+ * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleSpacing
*/
- @RestrictTo(LIBRARY_GROUP)
- public void setMaxLines(int maxLines) {
- collapsingTextHelper.setExpandedMaxLines(maxLines);
+ public int getExpandedTitleSpacing() {
+ return expandedTitleSpacing;
}
/**
- * Gets the maximum number of lines to display in the expanded state.
- * Experimental Feature.
+ * Sets the spacing between the expanded title and subtitle.
+ *
+ * @param titleSpacing the spacing between the expanded title and subtitle in pixels
+ * @see #getExpandedTitleSpacing()
+ * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleSpacing
*/
+ public void setExpandedTitleSpacing(int titleSpacing) {
+ expandedTitleSpacing = titleSpacing;
+ requestLayout();
+ }
+
+ /** Sets the maximum number of lines to display in the expanded state. Experimental Feature. */
+ @RestrictTo(LIBRARY_GROUP)
+ public void setMaxLines(int maxLines) {
+ collapsingTitleHelper.setExpandedMaxLines(maxLines);
+ collapsingSubtitleHelper.setExpandedMaxLines(maxLines);
+ }
+
+ /** Gets the maximum number of lines to display in the expanded state. Experimental Feature. */
@RestrictTo(LIBRARY_GROUP)
public int getMaxLines() {
- return collapsingTextHelper.getExpandedMaxLines();
+ return collapsingTitleHelper.getExpandedMaxLines();
}
- /**
- * Gets the current number of lines of the title text.
- * Experimental Feature.
- */
+ /** Gets the current number of lines of the title text. Experimental Feature. */
@RestrictTo(LIBRARY_GROUP)
public int getLineCount() {
- return collapsingTextHelper.getLineCount();
+ return collapsingTitleHelper.getLineCount();
}
/**
@@ -1436,14 +1816,14 @@ public int getLineCount() {
@RestrictTo(LIBRARY_GROUP)
@RequiresApi(VERSION_CODES.M)
public void setLineSpacingAdd(float spacingAdd) {
- collapsingTextHelper.setLineSpacingAdd(spacingAdd);
+ collapsingTitleHelper.setLineSpacingAdd(spacingAdd);
}
/** Gets the line spacing addition of the title text, or -1 if not set. Experimental Feature. */
@RestrictTo(LIBRARY_GROUP)
@RequiresApi(VERSION_CODES.M)
public float getLineSpacingAdd() {
- return collapsingTextHelper.getLineSpacingAdd();
+ return collapsingTitleHelper.getLineSpacingAdd();
}
/**
@@ -1453,14 +1833,14 @@ public float getLineSpacingAdd() {
@RestrictTo(LIBRARY_GROUP)
@RequiresApi(VERSION_CODES.M)
public void setLineSpacingMultiplier(@FloatRange(from = 0.0) float spacingMultiplier) {
- collapsingTextHelper.setLineSpacingMultiplier(spacingMultiplier);
+ collapsingTitleHelper.setLineSpacingMultiplier(spacingMultiplier);
}
/** Gets the line spacing multiplier of the title text, or -1 if not set. Experimental Feature. */
@RestrictTo(LIBRARY_GROUP)
@RequiresApi(VERSION_CODES.M)
public float getLineSpacingMultiplier() {
- return collapsingTextHelper.getLineSpacingMultiplier();
+ return collapsingTitleHelper.getLineSpacingMultiplier();
}
/**
@@ -1470,14 +1850,14 @@ public float getLineSpacingMultiplier() {
@RestrictTo(LIBRARY_GROUP)
@RequiresApi(VERSION_CODES.M)
public void setHyphenationFrequency(int hyphenationFrequency) {
- collapsingTextHelper.setHyphenationFrequency(hyphenationFrequency);
+ collapsingTitleHelper.setHyphenationFrequency(hyphenationFrequency);
}
/** Gets the hyphenation frequency of the title text, or -1 if not set. Experimental Feature. */
@RestrictTo(LIBRARY_GROUP)
@RequiresApi(VERSION_CODES.M)
public int getHyphenationFrequency() {
- return collapsingTextHelper.getHyphenationFrequency();
+ return collapsingTitleHelper.getHyphenationFrequency();
}
/**
@@ -1495,7 +1875,7 @@ public int getHyphenationFrequency() {
@RequiresApi(VERSION_CODES.M)
public void setStaticLayoutBuilderConfigurer(
@Nullable StaticLayoutBuilderConfigurer staticLayoutBuilderConfigurer) {
- collapsingTextHelper.setStaticLayoutBuilderConfigurer(staticLayoutBuilderConfigurer);
+ collapsingTitleHelper.setStaticLayoutBuilderConfigurer(staticLayoutBuilderConfigurer);
}
/**
@@ -1504,7 +1884,7 @@ public void setStaticLayoutBuilderConfigurer(
*/
@RestrictTo(LIBRARY_GROUP)
public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeuristicsEnabled) {
- collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(rtlTextDirectionHeuristicsEnabled);
+ collapsingTitleHelper.setRtlTextDirectionHeuristicsEnabled(rtlTextDirectionHeuristicsEnabled);
}
/**
@@ -1513,12 +1893,12 @@ public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeurist
*/
@RestrictTo(LIBRARY_GROUP)
public boolean isRtlTextDirectionHeuristicsEnabled() {
- return collapsingTextHelper.isRtlTextDirectionHeuristicsEnabled();
+ return collapsingTitleHelper.isRtlTextDirectionHeuristicsEnabled();
}
/**
- * Sets whether the top system window inset should be respected regardless of what the
- * {@code layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature.
+ * Sets whether the top system window inset should be respected regardless of what the {@code
+ * layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature.
*/
@RestrictTo(LIBRARY_GROUP)
public void setForceApplySystemWindowInsetTop(boolean forceApplySystemWindowInsetTop) {
@@ -1526,8 +1906,8 @@ public void setForceApplySystemWindowInsetTop(boolean forceApplySystemWindowInse
}
/**
- * Gets whether the top system window inset should be respected regardless of what the
- * {@code layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature.
+ * Gets whether the top system window inset should be respected regardless of what the {@code
+ * layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature.
*/
@RestrictTo(LIBRARY_GROUP)
public boolean isForceApplySystemWindowInsetTop() {
@@ -1580,7 +1960,11 @@ public void setScrimVisibleHeightTrigger(@IntRange(from = 0) final int height) {
public int getScrimVisibleHeightTrigger() {
if (scrimVisibleHeightTrigger >= 0) {
// If we have one explicitly set, return it
- return scrimVisibleHeightTrigger + topInsetApplied + extraMultilineHeight;
+ return scrimVisibleHeightTrigger
+ + topInsetApplied
+ + extraMultilineTitleHeight
+ + extraMultilineSubtitleHeight
+ + extraHeightForTitles;
}
// Otherwise we'll use the default computed value
@@ -1606,7 +1990,7 @@ public int getScrimVisibleHeightTrigger() {
* com.google.android.material.R.styleable#CollapsingToolbarLayout_titlePositionInterpolator
*/
public void setTitlePositionInterpolator(@Nullable TimeInterpolator interpolator) {
- collapsingTextHelper.setPositionInterpolator(interpolator);
+ collapsingTitleHelper.setPositionInterpolator(interpolator);
}
/**
@@ -1615,14 +1999,15 @@ public void setTitlePositionInterpolator(@Nullable TimeInterpolator interpolator
*/
@Nullable
public TimeInterpolator getTitlePositionInterpolator() {
- return collapsingTextHelper.getPositionInterpolator();
+ return collapsingTitleHelper.getPositionInterpolator();
}
/**
* Set the duration used for scrim visibility animations.
*
* @param duration the duration to use in milliseconds
- * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_scrimAnimationDuration
+ * @attr ref
+ * com.google.android.material.R.styleable#CollapsingToolbarLayout_scrimAnimationDuration
*/
public void setScrimAnimationDuration(@IntRange(from = 0) final long duration) {
scrimAnimationDuration = duration;
@@ -1823,10 +2208,16 @@ public void onOffsetChanged(AppBarLayout layout, int verticalOffset) {
int height = getHeight();
final int expandRange = height - getMinimumHeight() - insetTop;
final int scrimRange = height - getScrimVisibleHeightTrigger();
- collapsingTextHelper.setFadeModeStartFraction(
+ final int currentOffsetY = currentOffset + expandRange;
+ final float expansionFraction = Math.abs(verticalOffset) / (float) expandRange;
+ collapsingTitleHelper.setFadeModeStartFraction(
+ Math.min(1, (float) scrimRange / (float) expandRange));
+ collapsingTitleHelper.setCurrentOffsetY(currentOffsetY);
+ collapsingTitleHelper.setExpansionFraction(expansionFraction);
+ collapsingSubtitleHelper.setFadeModeStartFraction(
Math.min(1, (float) scrimRange / (float) expandRange));
- collapsingTextHelper.setCurrentOffsetY(currentOffset + expandRange);
- collapsingTextHelper.setExpansionFraction(Math.abs(verticalOffset) / (float) expandRange);
+ collapsingSubtitleHelper.setCurrentOffsetY(currentOffsetY);
+ collapsingSubtitleHelper.setExpansionFraction(expansionFraction);
}
}
diff --git a/lib/java/com/google/android/material/appbar/MaterialToolbar.java b/lib/java/com/google/android/material/appbar/MaterialToolbar.java
index 9971af44a6d..7dc96d6d62b 100644
--- a/lib/java/com/google/android/material/appbar/MaterialToolbar.java
+++ b/lib/java/com/google/android/material/appbar/MaterialToolbar.java
@@ -26,11 +26,9 @@
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
-import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.widget.Toolbar;
import android.util.AttributeSet;
import android.util.Pair;
-import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
@@ -38,7 +36,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.DrawableCompat;
-import androidx.core.view.ViewCompat;
import com.google.android.material.drawable.DrawableUtils;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.internal.ToolbarUtils;
@@ -93,7 +90,7 @@ public MaterialToolbar(@NonNull Context context) {
}
public MaterialToolbar(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, R.attr.toolbarStyle);
+ this(context, attrs, androidx.appcompat.R.attr.toolbarStyle);
}
public MaterialToolbar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
@@ -126,19 +123,6 @@ public MaterialToolbar(@NonNull Context context, @Nullable AttributeSet attrs, i
initBackground(context);
}
- @Override
- public void inflateMenu(int i) {
- // Pause dispatching item changes during inflation to improve performance.
- Menu menu = getMenu();
- if (menu instanceof MenuBuilder) {
- ((MenuBuilder) menu).stopDispatchingItemsChanged();
- }
- super.inflateMenu(i);
- if (menu instanceof MenuBuilder) {
- ((MenuBuilder) menu).startDispatchingItemsChanged();
- }
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -308,7 +292,7 @@ public void clearNavigationIconTint() {
Drawable navigationIcon = getNavigationIcon();
if (navigationIcon != null) {
Drawable wrappedNavigationIcon = DrawableCompat.wrap(navigationIcon.mutate());
- DrawableCompat.setTintList(wrappedNavigationIcon, null);
+ wrappedNavigationIcon.setTintList(null);
setNavigationIcon(navigationIcon);
}
}
@@ -383,7 +367,7 @@ private void initBackground(Context context) {
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
materialShapeDrawable.setFillColor(backgroundColorStateList);
materialShapeDrawable.initializeElevationOverlay(context);
- materialShapeDrawable.setElevation(ViewCompat.getElevation(this));
+ materialShapeDrawable.setElevation(getElevation());
setBackground(materialShapeDrawable);
}
}
@@ -392,7 +376,7 @@ private void initBackground(Context context) {
private Drawable maybeTintNavigationIcon(@Nullable Drawable navigationIcon) {
if (navigationIcon != null && navigationIconTint != null) {
Drawable wrappedNavigationIcon = DrawableCompat.wrap(navigationIcon.mutate());
- DrawableCompat.setTint(wrappedNavigationIcon, navigationIconTint);
+ wrappedNavigationIcon.setTint(navigationIconTint);
return wrappedNavigationIcon;
} else {
return navigationIcon;
diff --git a/lib/java/com/google/android/material/appbar/res-public/values/public.xml b/lib/java/com/google/android/material/appbar/res-public/values/public.xml
index b350134b0db..7a232eb3d3e 100644
--- a/lib/java/com/google/android/material/appbar/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/appbar/res-public/values/public.xml
@@ -30,10 +30,15 @@
+
+
+
+
+
@@ -74,6 +79,7 @@
+
@@ -84,6 +90,8 @@
+
+
diff --git a/lib/java/com/google/android/material/appbar/res/animator/m3_appbar_state_list_animator.xml b/lib/java/com/google/android/material/appbar/res/animator/m3_appbar_state_list_animator.xml
index b1805e1e40d..fb75622067f 100644
--- a/lib/java/com/google/android/material/appbar/res/animator/m3_appbar_state_list_animator.xml
+++ b/lib/java/com/google/android/material/appbar/res/animator/m3_appbar_state_list_animator.xml
@@ -23,7 +23,7 @@
@@ -31,7 +31,7 @@
@@ -39,7 +39,7 @@
diff --git a/lib/java/com/google/android/material/appbar/res/values/attrs.xml b/lib/java/com/google/android/material/appbar/res/values/attrs.xml
index 87dc89a9509..1090c1a2c37 100644
--- a/lib/java/com/google/android/material/appbar/res/values/attrs.xml
+++ b/lib/java/com/google/android/material/appbar/res/values/attrs.xml
@@ -134,27 +134,35 @@
-
-
-
-
-
+
+
+
+
+
+
@@ -168,6 +176,12 @@
+
+
+
+
@@ -237,6 +251,8 @@
+
+
@@ -244,8 +260,11 @@
-
+
+
+
+
@@ -256,6 +275,16 @@
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/appbar/res/values/dimens.xml b/lib/java/com/google/android/material/appbar/res/values/dimens.xml
index 4184e1d68e2..765fc32b69d 100644
--- a/lib/java/com/google/android/material/appbar/res/values/dimens.xml
+++ b/lib/java/com/google/android/material/appbar/res/values/dimens.xml
@@ -20,9 +20,11 @@
56dp
- @dimen/m3_comp_top_app_bar_small_container_height
- @dimen/m3_comp_top_app_bar_medium_container_height
- @dimen/m3_comp_top_app_bar_large_container_height
+ @dimen/m3_comp_app_bar_small_container_height
+ @dimen/m3_comp_app_bar_medium_container_height
+ 136dp
+ @dimen/m3_comp_app_bar_large_container_height
+ 152dp16dp16dp
diff --git a/lib/java/com/google/android/material/appbar/res/values/styles.xml b/lib/java/com/google/android/material/appbar/res/values/styles.xml
index 542011cc589..6521f222e58 100644
--- a/lib/java/com/google/android/material/appbar/res/values/styles.xml
+++ b/lib/java/com/google/android/material/appbar/res/values/styles.xml
@@ -16,14 +16,189 @@
-->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -58,7 +233,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/java/com/google/android/material/appbar/res/values/tokens.xml b/lib/java/com/google/android/material/appbar/res/values/tokens.xml
index 280cafb3d54..d271b8580f1 100644
--- a/lib/java/com/google/android/material/appbar/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/appbar/res/values/tokens.xml
@@ -15,38 +15,43 @@
~ limitations under the License.
-->
-
+
-
-
- ?attr/colorSurface
- ?attr/colorSurfaceContainer
- 64dp
- @dimen/m3_sys_elevation_level0
-
- ?attr/colorOnSurface
-
- ?attr/colorOnSurfaceVariant
-
- ?attr/textAppearanceTitleLarge
- ?attr/colorOnSurface
-
- @dimen/m3_sys_elevation_level2
-
-
-
- 112dp
-
- ?attr/textAppearanceHeadlineSmall
- ?attr/colorOnSurface
-
-
-
- 152dp
-
- ?attr/textAppearanceHeadlineMedium
- ?attr/colorOnSurface
+
+
+ ?attr/colorSurface
+ ?attr/colorSurfaceContainer
+ @dimen/m3_sys_elevation_level0
+ @dimen/m3_sys_elevation_level2
+ ?attr/colorOnSurface
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSurface
+ ?attr/colorOnSurfaceVariant
+
+
+ 64dp
+ ?attr/textAppearanceTitleLarge
+ ?attr/textAppearanceLabelMedium
+
+
+ 112dp
+ ?attr/textAppearanceHeadlineSmall
+
+
+ 152dp
+ ?attr/textAppearanceHeadlineMedium
+
+
+ 112dp
+ ?attr/textAppearanceHeadlineMedium
+ ?attr/textAppearanceLabelLarge
+
+
+ 120dp
+ ?attr/textAppearanceDisplaySmall
+ ?attr/textAppearanceTitleMedium
diff --git a/lib/java/com/google/android/material/badge/res/values-af/strings.xml b/lib/java/com/google/android/material/badge/res/values-af/strings.xml
index b3d74ce1051..d306357005a 100644
--- a/lib/java/com/google/android/material/badge/res/values-af/strings.xml
+++ b/lib/java/com/google/android/material/badge/res/values-af/strings.xml
@@ -1,6 +1,6 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
diff --git a/lib/java/com/google/android/material/bottomnavigation/res/values/tokens.xml b/lib/java/com/google/android/material/bottomnavigation/res/values/tokens.xml
index 02d8af864c0..a97967cf1b5 100644
--- a/lib/java/com/google/android/material/bottomnavigation/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/bottomnavigation/res/values/tokens.xml
@@ -15,50 +15,45 @@
~ limitations under the License.
-->
-
+
-
-
- ?attr/colorSurfaceContainer
-
- 80dp
- @dimen/m3_sys_elevation_level2
-
- ?attr/textAppearanceLabelMedium
- ?attr/colorOnSurface
- ?attr/colorOnSurfaceVariant
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- ?attr/colorOnSurface
-
- 24dp
- ?attr/colorOnSecondaryContainer
- ?attr/colorOnSurfaceVariant
- ?attr/colorOnSecondaryContainer
- ?attr/colorOnSurface
- ?attr/colorOnSecondaryContainer
- ?attr/colorOnSurface
- ?attr/colorOnSecondaryContainer
- ?attr/colorOnSurface
-
- ?attr/colorSecondaryContainer
- 32dp
- 64dp
-
-
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- @dimen/m3_sys_state_hover_state_layer_opacity
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- @dimen/m3_sys_state_focus_state_layer_opacity
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- @dimen/m3_sys_state_pressed_state_layer_opacity
+
+
+ @dimen/m3_sys_elevation_level2
+ ?attr/colorSurfaceContainer
+ ?attr/colorSecondaryContainer
+ ?attr/colorSecondary
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSecondaryContainer
+
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSecondaryContainer
+
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSecondaryContainer
+
+
+ 4dp
+ 24dp
+
+ 64dp
+
+
+
+ ?attr/textAppearanceLabelMedium
+ 40dp
+
+
+ ?attr/textAppearanceLabelMedium
+ 32dp
+ 56dp
+ 6dp
diff --git a/lib/java/com/google/android/material/bottomsheet/BottomSheetBehavior.java b/lib/java/com/google/android/material/bottomsheet/BottomSheetBehavior.java
index 8327c6f8887..51b90fdd98b 100644
--- a/lib/java/com/google/android/material/bottomsheet/BottomSheetBehavior.java
+++ b/lib/java/com/google/android/material/bottomsheet/BottomSheetBehavior.java
@@ -33,6 +33,7 @@
import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray;
@@ -64,11 +65,15 @@
import androidx.core.math.MathUtils;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.WindowInsetsCompat.Side;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import androidx.core.view.accessibility.AccessibilityViewCommand;
+import androidx.core.view.insets.GradientProtection;
+import androidx.core.view.insets.Protection;
import androidx.customview.view.AbsSavedState;
import androidx.customview.widget.ViewDragHelper;
+import com.google.android.material.color.MaterialColors;
import com.google.android.material.internal.ViewUtils;
import com.google.android.material.internal.ViewUtils.RelativePadding;
import com.google.android.material.motion.MaterialBackHandler;
@@ -226,7 +231,8 @@ void onLayout(@NonNull View bottomSheet) {}
private static final int NO_MAX_SIZE = -1;
- private static final int VIEW_INDEX_BOTTOM_SHEET = 0;
+ @VisibleForTesting
+ static final int VIEW_INDEX_BOTTOM_SHEET = 0;
private static final int INVALID_POSITION = -1;
@@ -328,6 +334,7 @@ void onLayout(@NonNull View bottomSheet) {}
@Nullable WeakReference viewRef;
@Nullable WeakReference accessibilityDelegateViewRef;
+ @Nullable WeakReference dragHandleViewRef;
@Nullable WeakReference nestedScrollingChildRef;
@@ -346,6 +353,10 @@ void onLayout(@NonNull View bottomSheet) {}
@VisibleForTesting
final SparseIntArray expandHalfwayActionIds = new SparseIntArray();
+ @VisibleForTesting
+ final SparseIntArray expandActionIds = new SparseIntArray();
+ @VisibleForTesting
+ final SparseIntArray collapseActionIds = new SparseIntArray();
public BottomSheetBehavior() {}
@@ -559,8 +570,7 @@ public boolean onLayoutChild(
if (materialShapeDrawable != null) {
child.setBackground(materialShapeDrawable);
// Use elevation attr if set on bottomsheet; otherwise, use elevation of child view.
- materialShapeDrawable.setElevation(
- elevation == -1 ? ViewCompat.getElevation(child) : elevation);
+ materialShapeDrawable.setElevation(elevation == -1 ? child.getElevation() : elevation);
} else if (backgroundTint != null) {
ViewCompat.setBackgroundTintList(child, backgroundTint);
}
@@ -649,10 +659,11 @@ public boolean onInterceptTouchEvent(
// Only intercept nested scrolling events here if the view not being moved by the
// ViewDragHelper.
if (state != STATE_SETTLING) {
- View scroll = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
- if (scroll != null && parent.isPointInChildBounds(scroll, initialX, initialY)) {
+ if (isTouchingScrollingChild(parent, initialX, initialY)) {
activePointerId = event.getPointerId(event.getActionIndex());
- touchingScrollingChild = true;
+ if (!isTouchingDragHandle(parent, initialX, initialY)) {
+ touchingScrollingChild = true;
+ }
}
}
ignoreEvents =
@@ -1217,7 +1228,7 @@ public boolean isDraggableOnNestedScroll() {
return draggableOnNestedScroll;
}
- /*
+ /**
* Sets the velocity threshold considered significant enough to trigger a slide
* to the next stable state.
*
@@ -1229,7 +1240,7 @@ public void setSignificantVelocityThreshold(int significantVelocityThreshold) {
this.significantVelocityThreshold = significantVelocityThreshold;
}
- /*
+ /**
* Returns the significant velocity threshold.
*
* @see #setSignificantVelocityThreshold(int)
@@ -1532,6 +1543,20 @@ private float calculateCornerInterpolation(
return 0;
}
+ private boolean isTouchingScrollingChild(
+ @NonNull CoordinatorLayout parent, int xCoordinate, int yCoordinate) {
+ View scrollingChild = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
+ return scrollingChild != null
+ && parent.isPointInChildBounds(scrollingChild, xCoordinate, yCoordinate);
+ }
+
+ private boolean isTouchingDragHandle(
+ @NonNull CoordinatorLayout parent, int xCoordinate, int yCoordinate) {
+ View dragHandleView = dragHandleViewRef != null ? dragHandleViewRef.get() : null;
+ return dragHandleView != null
+ && parent.isPointInChildBounds(dragHandleView, xCoordinate, yCoordinate);
+ }
+
private boolean isAtTopOfScreen() {
if (viewRef == null || viewRef.get() == null) {
return false;
@@ -1692,7 +1717,7 @@ View findScrollingChild(View view) {
if (view.getVisibility() != View.VISIBLE) {
return null;
}
- if (ViewCompat.isNestedScrollingEnabled(view)) {
+ if (view.isNestedScrollingEnabled()) {
return view;
}
if (view instanceof ViewGroup) {
@@ -1900,7 +1925,7 @@ public boolean tryCaptureView(@NonNull View child, int pointerId) {
return false;
}
}
- viewCapturedMillis = System.currentTimeMillis();
+ viewCapturedMillis = SystemClock.uptimeMillis();
return viewRef != null && viewRef.get() == child;
}
@@ -1930,7 +1955,7 @@ public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel)
targetState = STATE_EXPANDED;
} else {
int currentTop = releasedChild.getTop();
- long dragDurationMillis = System.currentTimeMillis() - viewCapturedMillis;
+ long dragDurationMillis = SystemClock.uptimeMillis() - viewCapturedMillis;
if (shouldSkipHalfExpandedStateWhenDragging()) {
float yPositionPercentage = currentTop * 100f / parentHeight;
@@ -2020,10 +2045,7 @@ public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel)
@Override
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
- return MathUtils.clamp(
- top,
- getExpandedOffset(),
- getViewVerticalDragRange(child));
+ return MathUtils.clamp(top, getExpandedOffset(), getViewVerticalDragRange(child));
}
@Override
@@ -2346,6 +2368,10 @@ private void updateImportantForAccessibility(boolean expanded) {
}
}
+ void setDragHandleView(@Nullable BottomSheetDragHandleView dragHandleView) {
+ dragHandleViewRef = dragHandleView != null ? new WeakReference<>(dragHandleView) : null;
+ }
+
void setAccessibilityDelegateView(@Nullable View accessibilityDelegateView) {
if (accessibilityDelegateView == null && accessibilityDelegateViewRef != null) {
clearAccessibilityAction(
@@ -2388,24 +2414,30 @@ private void updateAccessibilityActions(View view, int viewIndex) {
switch (state) {
case STATE_EXPANDED:
{
- int nextState = fitToContents ? STATE_COLLAPSED : STATE_HALF_EXPANDED;
- replaceAccessibilityActionForState(
- view, AccessibilityActionCompat.ACTION_COLLAPSE, nextState);
+ collapseActionIds.put(
+ viewIndex,
+ addAccessibilityActionForState(
+ view, R.string.bottomsheet_action_collapse, STATE_COLLAPSED));
break;
}
case STATE_HALF_EXPANDED:
{
- replaceAccessibilityActionForState(
- view, AccessibilityActionCompat.ACTION_COLLAPSE, STATE_COLLAPSED);
- replaceAccessibilityActionForState(
- view, AccessibilityActionCompat.ACTION_EXPAND, STATE_EXPANDED);
+ collapseActionIds.put(
+ viewIndex,
+ addAccessibilityActionForState(
+ view, R.string.bottomsheet_action_collapse, STATE_COLLAPSED));
+ expandActionIds.put(
+ viewIndex,
+ addAccessibilityActionForState(
+ view, R.string.bottomsheet_action_expand, STATE_EXPANDED));
break;
}
case STATE_COLLAPSED:
{
- int nextState = fitToContents ? STATE_EXPANDED : STATE_HALF_EXPANDED;
- replaceAccessibilityActionForState(
- view, AccessibilityActionCompat.ACTION_EXPAND, nextState);
+ expandActionIds.put(
+ viewIndex,
+ addAccessibilityActionForState(
+ view, R.string.bottomsheet_action_expand, STATE_EXPANDED));
break;
}
case STATE_HIDDEN:
@@ -2419,15 +2451,27 @@ private void clearAccessibilityAction(View view, int viewIndex) {
if (view == null) {
return;
}
+ ViewCompat.removeAccessibilityAction(view, AccessibilityNodeInfoCompat.ACTION_DISMISS);
ViewCompat.removeAccessibilityAction(view, AccessibilityNodeInfoCompat.ACTION_COLLAPSE);
ViewCompat.removeAccessibilityAction(view, AccessibilityNodeInfoCompat.ACTION_EXPAND);
- ViewCompat.removeAccessibilityAction(view, AccessibilityNodeInfoCompat.ACTION_DISMISS);
+
+ int expandActionId = expandActionIds.get(viewIndex, View.NO_ID);
+ if (expandActionId != View.NO_ID) {
+ ViewCompat.removeAccessibilityAction(view, expandActionId);
+ expandActionIds.delete(viewIndex);
+ }
int expandHalfwayActionId = expandHalfwayActionIds.get(viewIndex, View.NO_ID);
if (expandHalfwayActionId != View.NO_ID) {
ViewCompat.removeAccessibilityAction(view, expandHalfwayActionId);
expandHalfwayActionIds.delete(viewIndex);
}
+
+ int collapseActionId = collapseActionIds.get(viewIndex, View.NO_ID);
+ if (collapseActionId != View.NO_ID) {
+ ViewCompat.removeAccessibilityAction(view, collapseActionId);
+ collapseActionIds.delete(viewIndex);
+ }
}
private void replaceAccessibilityActionForState(
@@ -2453,4 +2497,21 @@ public boolean perform(@NonNull View view, @Nullable CommandArguments arguments)
}
};
}
+
+ /**
+ * Returns a default {@link GradientProtection} for use with BottomSheets.
+ *
+ * @throws IllegalArgumentException if {@code R.attr.colorSurfaceContainerLow} and {@code
+ * R.attr.colorSurface} are not set in the current theme.
+ */
+ @NonNull
+ public static Protection getDefaultBottomGradientProtection(@NonNull Context context) {
+ Integer color = MaterialColors.getColorOrNull(context, R.attr.colorSurfaceContainerLow);
+ if (color == null) {
+ color =
+ MaterialColors.getColor(
+ context, R.attr.colorSurface, BottomSheetBehavior.class.getSimpleName());
+ }
+ return new GradientProtection(Side.BOTTOM, color);
+ }
}
diff --git a/lib/java/com/google/android/material/bottomsheet/BottomSheetDialog.java b/lib/java/com/google/android/material/bottomsheet/BottomSheetDialog.java
index febebc1d2e7..a69485116bd 100644
--- a/lib/java/com/google/android/material/bottomsheet/BottomSheetDialog.java
+++ b/lib/java/com/google/android/material/bottomsheet/BottomSheetDialog.java
@@ -48,10 +48,13 @@
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.insets.Protection;
+import androidx.core.view.insets.ProtectionLayout;
import com.google.android.material.internal.EdgeToEdgeUtils;
import com.google.android.material.internal.ViewUtils;
import com.google.android.material.motion.MaterialBackOrchestrator;
import com.google.android.material.shape.MaterialShapeDrawable;
+import java.util.List;
/**
* Base class for {@link android.app.Dialog}s styled as a bottom sheet.
@@ -79,6 +82,8 @@ public class BottomSheetDialog extends AppCompatDialog {
private CoordinatorLayout coordinator;
private FrameLayout bottomSheet;
+ private ProtectionLayout protectionLayout;
+
boolean dismissWithAnimation;
boolean cancelable = true;
@@ -87,6 +92,7 @@ public class BottomSheetDialog extends AppCompatDialog {
private EdgeToEdgeCallback edgeToEdgeCallback;
private boolean edgeToEdgeEnabled;
@Nullable private MaterialBackOrchestrator backOrchestrator;
+ private List protectionsList;
public BottomSheetDialog(@NonNull Context context) {
this(context, 0);
@@ -215,8 +221,8 @@ public void onDetachedFromWindow() {
* or calling `dismiss()` from a `BottomSheetDialogFragment`, tapping outside a dialog, etc...
*
*
The default animation to dismiss this dialog is a fade-out transition through a
- * windowAnimation. Call {@link #setDismissWithAnimation(true)} if you want to utilize the
- * BottomSheet animation instead.
+ * windowAnimation. Call {@link #setDismissWithAnimation(boolean)} with `true`
+ * if you want to utilize the BottomSheet animation instead.
*
*
If this function is called from a swipe down interaction, or dismissWithAnimation is false,
* then keep the default behavior.
@@ -279,11 +285,29 @@ public boolean getEdgeToEdgeEnabled() {
return edgeToEdgeEnabled;
}
+ /**
+ * Set the {@link Protection}s applied to this BottomSheetDialog.
+ *
+ * @param protections the list of {@link Protection}s to apply. This value will override the
+ * existing Protections. An empty list will clear the Protections.
+ */
+ public void setProtections(@NonNull List protections) {
+ protectionsList = protections;
+ if (protectionLayout != null) {
+ protectionLayout.setProtections(protections);
+ protectionLayout.setVisibility(protections.isEmpty() ? View.GONE : View.VISIBLE);
+ }
+ }
+
/** Creates the container layout which must exist to find the behavior */
private FrameLayout ensureContainerAndBehavior() {
if (container == null) {
container =
(FrameLayout) View.inflate(getContext(), R.layout.design_bottom_sheet_dialog, null);
+ protectionLayout = (ProtectionLayout) container.findViewById(R.id.protection_layout);
+ if (protectionsList != null) {
+ setProtections(protectionsList);
+ }
coordinator = (CoordinatorLayout) container.findViewById(R.id.coordinator);
bottomSheet = (FrameLayout) container.findViewById(R.id.design_bottom_sheet);
@@ -306,7 +330,7 @@ private View wrapInBottomSheet(
if (edgeToEdgeEnabled) {
ViewCompat.setOnApplyWindowInsetsListener(
- bottomSheet,
+ container,
new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
@@ -453,7 +477,7 @@ private EdgeToEdgeCallback(
if (msd != null) {
backgroundTint = msd.getFillColor();
} else {
- backgroundTint = ViewCompat.getBackgroundTintList(bottomSheet);
+ backgroundTint = bottomSheet.getBackgroundTintList();
}
if (backgroundTint != null) {
diff --git a/lib/java/com/google/android/material/bottomsheet/BottomSheetDragHandleView.java b/lib/java/com/google/android/material/bottomsheet/BottomSheetDragHandleView.java
index a33f23ed16c..f6881226e4b 100644
--- a/lib/java/com/google/android/material/bottomsheet/BottomSheetDragHandleView.java
+++ b/lib/java/com/google/android/material/bottomsheet/BottomSheetDragHandleView.java
@@ -21,9 +21,18 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
import androidx.appcompat.widget.AppCompatImageView;
+import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
@@ -35,7 +44,7 @@
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityEventCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback;
@@ -44,27 +53,37 @@
* BottomSheetBehavior}. This view will automatically handle the accessibility interaction when the
* accessibility service is enabled. When you add a drag handle to a bottom sheet and the user
* enables the accessibility service, the drag handle will become important for accessibility and
- * clickable. Clicking the drag handle will toggle the bottom sheet between its collapsed and
- * expanded states.
+ * clickable. Clicking the drag handle will toggle the bottom sheet between its collapsed,
+ * half expanded, and expanded states.
*/
-public class BottomSheetDragHandleView extends AppCompatImageView
- implements AccessibilityStateChangeListener {
+public class BottomSheetDragHandleView extends AppCompatImageView implements
+ AccessibilityStateChangeListener {
private static final int DEF_STYLE_RES = R.style.Widget_Material3_BottomSheet_DragHandle;
@Nullable private final AccessibilityManager accessibilityManager;
@Nullable private BottomSheetBehavior> bottomSheetBehavior;
- private boolean accessibilityServiceEnabled;
- private boolean interactable;
+ private final GestureDetector gestureDetector;
+
private boolean clickToExpand;
+ /**
+ * Track whether clients have set their own touch or click listeners on the drag handle.
+ *
+ * Setting a custom touch or click listener will override the default behavior of cycling through
+ * bottom sheet states when tapped and dismissing the sheet when double tapped. Clients can
+ * restore this behavior by setting their touch and click listeners back to null.
+ */
+ private boolean hasTouchListener = false;
+ private boolean hasClickListener = false;
+
private final String clickToExpandActionLabel =
- getResources().getString(R.string.bottomsheet_action_expand);
+ getResources().getString(R.string.bottomsheet_action_expand_description);
+ private final String clickToHalfExpandActionLabel =
+ getResources().getString(R.string.bottomsheet_action_half_expand_description);
private final String clickToCollapseActionLabel =
- getResources().getString(R.string.bottomsheet_action_collapse);
- private final String clickFeedback =
- getResources().getString(R.string.bottomsheet_drag_handle_clicked);
+ getResources().getString(R.string.bottomsheet_action_collapse_description);
private final BottomSheetCallback bottomSheetCallback =
new BottomSheetCallback() {
@@ -78,6 +97,34 @@ public void onStateChanged(
public void onSlide(@NonNull View bottomSheet, float slideOffset) {}
};
+ /**
+ * A gesture listener that handles both single and double taps on the drag handle.
+ *
+ * Single taps cycle through the available states of the bottom sheet. A double tap hides
+ * the sheet.
+ */
+ private final OnGestureListener gestureListener = new SimpleOnGestureListener() {
+
+ @Override
+ public boolean onDown(@NonNull MotionEvent e) {
+ return isClickable();
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(@NonNull MotionEvent e) {
+ return expandOrCollapseBottomSheetIfPossible();
+ }
+
+ @Override
+ public boolean onDoubleTap(@NonNull MotionEvent e) {
+ if (bottomSheetBehavior != null && bottomSheetBehavior.isHideable()) {
+ bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
+ return true;
+ }
+ return super.onDoubleTap(e);
+ }
+ };
+
public BottomSheetDragHandleView(@NonNull Context context) {
this(context, /* attrs= */ null);
}
@@ -86,6 +133,7 @@ public BottomSheetDragHandleView(@NonNull Context context, @Nullable AttributeSe
this(context, attrs, R.attr.bottomSheetDragHandleStyle);
}
+ @SuppressLint("ClickableViewAccessibility") // Will be handled by accessibility delegate
public BottomSheetDragHandleView(
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr);
@@ -93,11 +141,12 @@ public BottomSheetDragHandleView(
// Override the provided context with the wrapped one to prevent it from being used.
context = getContext();
+ gestureDetector =
+ new GestureDetector(context, gestureListener, new Handler(Looper.getMainLooper()));
+
accessibilityManager =
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- updateInteractableState();
-
ViewCompat.setAccessibilityDelegate(
this,
new AccessibilityDelegateCompat() {
@@ -108,6 +157,39 @@ public void onPopulateAccessibilityEvent(View host, @NonNull AccessibilityEvent
expandOrCollapseBottomSheetIfPossible();
}
}
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(
+ @NonNull View host, @NonNull AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ if (!hasAttachedBehavior()) {
+ return;
+ }
+
+ CharSequence originalDescription = getContentDescription();
+ String stateName = null;
+
+ switch (bottomSheetBehavior.getState()) {
+ case BottomSheetBehavior.STATE_COLLAPSED:
+ stateName = getResources().getString(R.string.bottomsheet_state_collapsed);
+ break;
+ case BottomSheetBehavior.STATE_EXPANDED:
+ stateName = getResources().getString(R.string.bottomsheet_state_expanded);
+ break;
+ case BottomSheetBehavior.STATE_HALF_EXPANDED:
+ stateName = getResources().getString(R.string.bottomsheet_state_half_expanded);
+ break;
+ default: // fall out
+ }
+
+ if (!TextUtils.isEmpty(stateName)) {
+ CharSequence newDescription =
+ TextUtils.isEmpty(originalDescription)
+ ? stateName
+ : stateName + ". " + originalDescription;
+ info.setContentDescription(newDescription);
+ }
+ }
});
}
@@ -130,24 +212,49 @@ protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
+ @SuppressLint("ClickableViewAccessibility") // Will be handled by accessibility delegate
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (hasClickListener || hasTouchListener) {
+ // If clients have set their own click or touch listeners, do nothing.
+ return super.onTouchEvent(event);
+ }
+
+ return gestureDetector.onTouchEvent(event);
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public void setOnTouchListener(OnTouchListener l) {
+ hasTouchListener = l != null;
+ super.setOnTouchListener(l);
+ }
+
+ @Override
+ public void setOnClickListener(@Nullable OnClickListener l) {
+ hasClickListener = l != null;
+ super.setOnClickListener(l);
+ }
+
@Override
public void onAccessibilityStateChanged(boolean enabled) {
- accessibilityServiceEnabled = enabled;
- updateInteractableState();
+ // Do nothing.
}
private void setBottomSheetBehavior(@Nullable BottomSheetBehavior> behavior) {
if (bottomSheetBehavior != null) {
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallback);
bottomSheetBehavior.setAccessibilityDelegateView(null);
+ bottomSheetBehavior.setDragHandleView(null);
}
bottomSheetBehavior = behavior;
if (bottomSheetBehavior != null) {
bottomSheetBehavior.setAccessibilityDelegateView(this);
+ bottomSheetBehavior.setDragHandleView(this);
onBottomSheetStateChanged(bottomSheetBehavior.getState());
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback);
}
- updateInteractableState();
+ setClickable(hasAttachedBehavior());
}
private void onBottomSheetStateChanged(@BottomSheetBehavior.State int state) {
@@ -156,19 +263,29 @@ private void onBottomSheetStateChanged(@BottomSheetBehavior.State int state) {
} else if (state == BottomSheetBehavior.STATE_EXPANDED) {
clickToExpand = false;
} // Else keep the original settings
+ int nextState = getNextState();
+ String text = null;
+ switch (nextState) {
+ case BottomSheetBehavior.STATE_COLLAPSED:
+ text = clickToCollapseActionLabel;
+ break;
+ case BottomSheetBehavior.STATE_EXPANDED:
+ text = clickToExpandActionLabel;
+ break;
+ case BottomSheetBehavior.STATE_HALF_EXPANDED:
+ text = clickToHalfExpandActionLabel;
+ break;
+ default: // fall out
+ }
ViewCompat.replaceAccessibilityAction(
this,
AccessibilityActionCompat.ACTION_CLICK,
- clickToExpand ? clickToExpandActionLabel : clickToCollapseActionLabel,
+ text,
(v, args) -> expandOrCollapseBottomSheetIfPossible());
}
- private void updateInteractableState() {
- interactable = accessibilityServiceEnabled && bottomSheetBehavior != null;
- setImportantForAccessibility(bottomSheetBehavior != null
- ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
- : View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- setClickable(interactable);
+ private boolean hasAttachedBehavior() {
+ return bottomSheetBehavior != null;
}
/**
@@ -182,41 +299,47 @@ private void updateInteractableState() {
* the previous state was EXPANDED) or EXPANDED (when the previous state was COLLAPSED.)
*/
private boolean expandOrCollapseBottomSheetIfPossible() {
- if (!interactable) {
+ if (!hasAttachedBehavior()) {
return false;
}
- announceAccessibilityEvent(clickFeedback);
+ int nextState = getNextState();
+ if (nextState != -1) {
+ bottomSheetBehavior.setState(nextState);
+ }
+ return true;
+ }
+
+ private int getNextState() {
+ int nextState = -1;
+ if (!hasAttachedBehavior()) {
+ return nextState;
+ }
boolean canHalfExpand =
!bottomSheetBehavior.isFitToContents()
&& !bottomSheetBehavior.shouldSkipHalfExpandedStateWhenDragging();
int currentState = bottomSheetBehavior.getState();
- int nextState;
- if (currentState == BottomSheetBehavior.STATE_COLLAPSED) {
- nextState =
- canHalfExpand
- ? BottomSheetBehavior.STATE_HALF_EXPANDED
- : BottomSheetBehavior.STATE_EXPANDED;
- } else if (currentState == BottomSheetBehavior.STATE_EXPANDED) {
- nextState =
- canHalfExpand
- ? BottomSheetBehavior.STATE_HALF_EXPANDED
- : BottomSheetBehavior.STATE_COLLAPSED;
- } else {
- nextState =
- clickToExpand ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED;
- }
- bottomSheetBehavior.setState(nextState);
- return true;
- }
-
- private void announceAccessibilityEvent(String announcement) {
- if (accessibilityManager == null) {
- return;
+ switch (currentState) {
+ case BottomSheetBehavior.STATE_COLLAPSED:
+ nextState =
+ canHalfExpand
+ ? BottomSheetBehavior.STATE_HALF_EXPANDED
+ : BottomSheetBehavior.STATE_EXPANDED;
+ break;
+ case BottomSheetBehavior.STATE_EXPANDED:
+ nextState =
+ canHalfExpand
+ ? BottomSheetBehavior.STATE_HALF_EXPANDED
+ : BottomSheetBehavior.STATE_COLLAPSED;
+ break;
+ case BottomSheetBehavior.STATE_HALF_EXPANDED:
+ nextState =
+ clickToExpand
+ ? BottomSheetBehavior.STATE_EXPANDED
+ : BottomSheetBehavior.STATE_COLLAPSED;
+ break;
+ default: // fall out
}
- AccessibilityEvent announce =
- AccessibilityEvent.obtain(AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
- announce.getText().add(announcement);
- accessibilityManager.sendAccessibilityEvent(announce);
+ return nextState;
}
/**
@@ -244,4 +367,24 @@ private static View getParentView(View view) {
ViewParent parent = view.getParent();
return parent instanceof View ? (View) parent : null;
}
+
+ @Override
+ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
+ if (!isEnabled()) {
+ return super.onKeyDown(keyCode, event);
+ }
+
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ if (hasClickListener) {
+ return performClick();
+ }
+ return expandOrCollapseBottomSheetIfPossible();
+ default:
+ // Nothing to do in this case.
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
}
diff --git a/lib/java/com/google/android/material/bottomsheet/res/layout/design_bottom_sheet_dialog.xml b/lib/java/com/google/android/material/bottomsheet/res/layout/design_bottom_sheet_dialog.xml
index 111d6961d20..7fce8a90393 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/layout/design_bottom_sheet_dialog.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/layout/design_bottom_sheet_dialog.xml
@@ -48,4 +48,10 @@
+
+
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-af/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-af/strings.xml
index 46c18701209..c05e1da1018 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-af/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-af/strings.xml
@@ -1,6 +1,6 @@
Vou halfpad uit
+ Vou uit
+ Vou inSleephandvatsel
- Vou die onderste blad uit
- Vou die onderste blad in
- Het op sleephandvatsel gedubbeltik
+ Vou die onderste blad uit
+ Vou die onderste blad halfpad uit
+ Vou die onderste blad in
+ Ingevou
+ Halfpad uitgevou
+ Uitgevou
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-am/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-am/strings.xml
index d8a27954142..f08d89cc620 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-am/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-am/strings.xml
@@ -1,6 +1,6 @@
ግማሽ መንገድ ዘርጋ
+ ዘርጋ
+ ሰብስብመያዣ ይጎትቱ
- የግርጌ ሉሁን ይዘርጉ
- የግርጌ ሉሁን ይሰብስቡ
- መያዣ ይጎትቱ ሁለቴ መታ ተደርጓል
+ የግርጌ ሉሁን ይዘርጉ
+ የታች ሉሁን በግማሽ ይዘርጉ
+ የግርጌ ሉሁን ይሰብስቡ
+ ተሰብስቧል
+ በግማሽ ተዘርግቷል
+ ተዘርግቷል
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ar/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ar/strings.xml
index ee27ecf94d5..2f977fa7fa4 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ar/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ar/strings.xml
@@ -1,6 +1,6 @@
توسيع البطاقة السفلية
+ توسيع
+ تصغيرمقبض السحب
- توسيع البطاقة السفلية
- تصغير البطاقة السفلية
- تم النقر مرّتين على مقبض السحب.
+ توسيع البطاقة السفلية
+ توسيع البطاقة السفلية بشكل نصفي
+ تصغير البطاقة السفلية
+ مصغَّرة
+ مُوسَّعة نصفيًا
+ مُوسَّعة
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-as/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-as/strings.xml
index 4814acc8926..6ba2b93e45a 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-as/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-as/strings.xml
@@ -1,6 +1,6 @@
Expand halfway
+ Expand
+ CollapseDrag handle
- Expand the bottom sheet
- Collapse the bottom sheet
- Drag handle double-tapped
+ Expand the bottom sheet
+ Half expand the bottom sheet
+ Collapse the bottom sheet
+ Collapsed
+ Half expanded
+ Expanded
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-az/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-az/strings.xml
index 731927755ce..d879711a52a 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-az/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-az/strings.xml
@@ -1,6 +1,6 @@
Tam genişləndirin
+ Genişləndirin
+ YığcamlaşdırınDəstək
- Aşağıdakı vərəqi genişləndirin
- Aşağıdakı vərəqi yığcamlaşdırın
- Dəstəyə iki dəfə toxunun
+ Aşağıdakı vərəqi genişləndirin
+ Aşağıdakı vərəqi yarıya qədər genişləndirin
+ Aşağıdakı vərəqi yığcamlaşdırın
+ Yığcamlaşdırıldı
+ Yarıya qədər genişləndirildi
+ Genişləndirildi
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-b+es+419/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-b+es+419/strings.xml
index b059f0ea843..ad53efeeb9c 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-b+es+419/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-b+es+419/strings.xml
@@ -1,6 +1,6 @@
Expandir hasta la mitad
+ Expandir
+ ContraerControlador de arrastre
- Expandir la hoja inferior
- Contraer la hoja inferior
- El control de arrastre se presionó dos veces
+ Expandir la hoja inferior
+ Expandir a la mitad la hoja inferior
+ Contraer la hoja inferior
+ Contraída
+ Expandida a la mitad
+ Expandida
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-b+sr+Latn/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-b+sr+Latn/strings.xml
index 0047b775963..a636657bc8d 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-b+sr+Latn/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-b+sr+Latn/strings.xml
@@ -1,6 +1,6 @@
Proširite do pola
+ Proširite
+ SkupiteRučica za prevlačenje
- Proširite donju tabelu
- Skupite donju tabelu
- Identifikator za prevlačenje je dvaput dodirnut
+ Proširite donju tabelu
+ Proširite donju tabelu do pola
+ Skupite donju tabelu
+ Skupljeno
+ Prošireno do pola
+ Prošireno
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-be/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-be/strings.xml
index be1fa4bff9c..06fefabd9ab 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-be/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-be/strings.xml
@@ -1,6 +1,6 @@
Разгарнуць напалову
+ Разгарнуць
+ ЗгарнуцьМаркер перацягвання
- Разгарнуць ніжні аркуш
- Згарнуць ніжні аркуш
- Маркер перацягвання націснуты двойчы
+ Разгарнуць ніжні аркуш
+ Напалову разгарнуць ніжні аркуш
+ Згарнуць ніжні аркуш
+ Згорнута
+ Напалову разгорнута
+ Разгорнута
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-bg/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-bg/strings.xml
index 016e125231b..c75a0389006 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-bg/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-bg/strings.xml
@@ -1,6 +1,6 @@
Разгъване наполовина
+ Разгъване
+ СвиванеМанипулатор за преместване с плъзгане
- Разгъване на долния лист
- Свиване на долния лист
- Двукратно докосване на манипулатора за преместване с плъзгане
+ Разгъване на долния лист
+ Наполовина разгъване на долния лист
+ Свиване на долния лист
+ Свито
+ Наполовина разгънато
+ Разгънато
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-bn/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-bn/strings.xml
index 59f63d70c09..dc0d92ea24b 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-bn/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-bn/strings.xml
@@ -1,6 +1,6 @@
অর্ধেক প্রসারিত করুন
+ বড় করুন
+ আড়াল করুনটেনে আনার হ্যান্ডেল
- স্ক্রিনের নিচে অ্যাটাচ করা শিট বড় করুন
- স্ক্রিনের নিচে অ্যাটাচ করা শিট আড়াল করুন
- টেনে আনার হ্যান্ডেলে ডবল ট্যাপ করা হয়েছে
+ স্ক্রিনের নিচে অ্যাটাচ করা শিট বড় করুন
+ স্ক্রিনের নিচে অ্যাটাচ করা শিট অর্ধেকটা বড় করুন
+ স্ক্রিনের নিচে অ্যাটাচ করা শিট আড়াল করুন
+ আড়াল করা হয়েছে
+ অর্ধেকটা বড় করা হয়েছে
+ বড় করা হয়েছে
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-bs/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-bs/strings.xml
index 8df61f998d2..d1c657e799d 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-bs/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-bs/strings.xml
@@ -1,6 +1,6 @@
Proširivanje dopola
+ Proširivanje
+ SužavanjeRučica za prevlačenje
- Proširivanje donje tabele
- Sužavanje donje tabele
- Ručica za prevlačenje je dvaput dodirnuta
+ Proširivanje donje tabele
+ Djelimično proširivanje donje tabele
+ Sužavanje donje tabele
+ Suženo
+ Djelimično prošireno
+ Prošireno
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ca/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ca/strings.xml
index 8b71bd8b720..0b4a42c2a92 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ca/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ca/strings.xml
@@ -1,6 +1,6 @@
Desplega fins a la meitat
+ Desplega
+ ReplegaAnsa per arrossegar
- Desplega el full inferior
- Replega el full inferior
- Has fet doble toc a l\'ansa per arrossegar
+ Desplega el full inferior
+ Desplega el full inferior fins a la meitat
+ Replega el full inferior
+ Replegat
+ Desplegat fins a la meitat
+ Desplegat
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-cs/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-cs/strings.xml
index af651442891..2a9e9670f65 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-cs/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-cs/strings.xml
@@ -1,6 +1,6 @@
Rozbalit napůl
+ Rozbalit
+ SbalitÚchyt pro přetažení
- Rozbalit spodní tabulku
- Sbalit spodní tabulku
- Dvakrát jste klepli na úchyt pro přetažení
+ Rozbalit spodní panel
+ Rozbalit spodní panel na polovinu
+ Sbalit spodní panel
+ Sbaleno
+ Rozbaleno na polovinu
+ Rozbaleno
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-da/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-da/strings.xml
index bcc16c3dc35..616ea414f9a 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-da/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-da/strings.xml
@@ -1,6 +1,6 @@
Udvid halvdelen
+ Udvid
+ SkjulHåndtag
- Udvid feltet i bunden
- Skjul feltet i bunden
- Du har trykket to gange på håndtaget
+ Udvid feltet i bunden
+ Udvid feltet i bunden halvt
+ Skjul feltet i bunden
+ Skjult
+ Halvt udvidet
+ Udvidet
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-de/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-de/strings.xml
index f0a5ae274d7..f0a2149b669 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-de/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-de/strings.xml
@@ -1,6 +1,6 @@
Zur Hälfte maximieren
+ Maximieren
+ MinimierenZiehpunkt
- Ansicht am unteren Rand maximieren
- Ansicht am unteren Rand minimieren
- Auf Ziehpunkt doppelt getippt
+ Ansicht am unteren Rand maximieren
+ Ansicht am unteren Rand zur Hälfte maximieren
+ Ansicht am unteren Rand minimieren
+ Minimiert
+ Zur Hälfte maximiert
+ Maximiert
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-el/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-el/strings.xml
index e4d71050a6e..151a83a840c 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-el/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-el/strings.xml
@@ -1,6 +1,6 @@
Ανάπτυξη μέχρι τη μέση
+ Ανάπτυξη
+ ΣύμπτυξηΛαβή μεταφοράς
- Ανάπτυξη του φύλλου κάτω μέρους
- Σύμπτυξη του φύλλου κάτω μέρους
- Η λαβή μεταφοράς πατήθηκε δύο φορές
+ Ανάπτυξη του φύλλου κάτω μέρους
+ Μερική ανάπτυξη φύλλου κάτω μέρους
+ Σύμπτυξη του φύλλου κάτω μέρους
+ Συμπτύχθηκε
+ Αναπτύχθηκε μερικώς
+ Αναπτύχθηκε
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-en-rGB/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-en-rGB/strings.xml
index 4814acc8926..6ba2b93e45a 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-en-rGB/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-en-rGB/strings.xml
@@ -1,6 +1,6 @@
Expand halfway
+ Expand
+ CollapseDrag handle
- Expand the bottom sheet
- Collapse the bottom sheet
- Drag handle double-tapped
+ Expand the bottom sheet
+ Half expand the bottom sheet
+ Collapse the bottom sheet
+ Collapsed
+ Half expanded
+ Expanded
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-es-rUS/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-es-rUS/strings.xml
index b059f0ea843..ad53efeeb9c 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-es-rUS/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-es-rUS/strings.xml
@@ -1,6 +1,6 @@
Expandir hasta la mitad
+ Expandir
+ ContraerControlador de arrastre
- Expandir la hoja inferior
- Contraer la hoja inferior
- El control de arrastre se presionó dos veces
+ Expandir la hoja inferior
+ Expandir a la mitad la hoja inferior
+ Contraer la hoja inferior
+ Contraída
+ Expandida a la mitad
+ Expandida
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-es/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-es/strings.xml
index 7b30570a767..3781f334d76 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-es/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-es/strings.xml
@@ -1,6 +1,6 @@
Desplegar hasta la mitad
+ Mostrar
+ OcultarControlador de arrastre
- Mostrar la hoja inferior
- Ocultar la hoja inferior
- Controlador de arrastre tocado dos veces
+ Mostrar la hoja inferior
+ Mostrar parcialmente la hoja inferior
+ Ocultar la hoja inferior
+ Ocultada
+ Mostrada parcialmente
+ Mostrada
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-et/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-et/strings.xml
index 57a51425841..19a30589b0e 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-et/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-et/strings.xml
@@ -1,6 +1,6 @@
Laiendamine poolenisti
+ Laienda
+ AhendaLohistamispide
- Alumise lehe laiendamine
- Alumise lehe ahendamine
- Lohistamispidet topeltpuudutati
+ Alumise lehe laiendamine
+ Alumise lehe pooles ulatuses laiendamine
+ Alumise lehe ahendamine
+ Ahendatud
+ Pooles ulatuses laiendatud
+ Laiendatud
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-eu/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-eu/strings.xml
index d625ed7250f..e569f3a38c2 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-eu/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-eu/strings.xml
@@ -1,6 +1,6 @@
Zabaldu erdiraino
+ Zabaldu
+ TolestuArrastatzeko kontrol-puntua
- Zabaldu pantailaren behealdean ainguratutako orria
- Tolestu pantailaren behealdean ainguratutako orria
- Birritan sakatu da arrastatzeko kontrol-puntua
+ Zabaldu pantailaren behealdean ainguratutako orria
+ Zabaldu pantailaren behealdean ainguratutako orriaren erdia
+ Tolestu pantailaren behealdean ainguratutako orria
+ Tolestuta
+ Erdi zabalduta
+ Zabalduta
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-fa/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-fa/strings.xml
index a7160369cc4..7b9a3fe551a 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-fa/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-fa/strings.xml
@@ -1,6 +1,6 @@
گسترده کردن تا نیمه
+ ازهم باز کردن
+ جمع کردندستگیره کشاندن
- ازهم بازکردن برگ زیرین
- جمع کردن برگ زیرین
- روی دستگیره کشاندن دوضربه زده شد
+ ازهم باز کردن برگ زیرین
+ نیمه باز کردن برگ زیرین
+ جمع کردن برگ زیرین
+ جمع شد
+ تا نیمه باز شد
+ ازهم باز شد
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-fi/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-fi/strings.xml
index 7ff948bb823..b944eb09ef4 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-fi/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-fi/strings.xml
@@ -1,6 +1,6 @@
Laajenna puoliväliin
+ Laajenna
+ TiivistäVetokahva
- Laajenna alapaneeli
- Tiivistä alapaneeli
- Vetokahvaa kaksoisnapautettu
+ Laajenna alapaneeli
+ Laajenna alapaneeli puoliksi
+ Tiivistä alapaneeli
+ Tiivistetty
+ Puoliksi laajennettu
+ Laajennettu
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-fr-rCA/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-fr-rCA/strings.xml
index d2a011b0270..01050d8f9fd 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-fr-rCA/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-fr-rCA/strings.xml
@@ -1,6 +1,6 @@
Développer à moitié
+ Développer
+ RéduireFaire glisser la poignée
- Développer la zone de contenu dans le bas de l\'écran
- Réduire la zone de contenu dans le bas de l\'écran
- Poignée de déplacement touchée deux fois
+ Développer la zone de contenu dans le bas de l\'écran
+ Développer de moitié la zone de contenu dans le bas de l\'écran
+ Réduire la zone de contenu dans le bas de l\'écran
+ Réduite
+ Développée de moitié
+ Développée
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-fr/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-fr/strings.xml
index 61e9b453b9c..38624b05cd7 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-fr/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-fr/strings.xml
@@ -1,6 +1,6 @@
Développer en entier
+ Développer
+ RéduirePoignée de déplacement
- Développer la bottom sheet
- Réduire la bottom sheet
- A appuyé deux fois sur la poignée de déplacement
+ Développer la bottom sheet
+ Développer la bottom sheet à moitié
+ Réduire la bottom sheet
+ Réduite
+ Développée à moitié
+ Développée
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-gl/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-gl/strings.xml
index 39b976341e0..ff20eece1c3 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-gl/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-gl/strings.xml
@@ -1,6 +1,6 @@
Despregar ata a metade
+ Despregar
+ ContraerControlador de arrastre
- Desprega o panel inferior
- Contrae o panel inferior
- Controlador de arrastre tocado dúas veces
+ Despregar o panel inferior
+ Despregar o panel inferior ata a metade
+ Contraer o panel inferior
+ Contraído
+ Despregado ata a metade
+ Despregado
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-gu/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-gu/strings.xml
index aa1658978d5..fe9e76fab72 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-gu/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-gu/strings.xml
@@ -1,6 +1,6 @@
અડધે સુધી મોટું કરો
+ મોટું કરો
+ નાનું કરોઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ
- બોટમ શીટ મોટી કરો
- બોટમ શીટ નાની કરો
- ઑબ્જેક્ટ ખેંચવાના હૅન્ડલ પર બે વાર ટૅપ કર્યું
+ બોટમ શીટ મોટી કરો
+ બોટમ શીટ અડધે સુધી મોટી કરો
+ બોટમ શીટ નાની કરો
+ નાની કરી છે
+ અડધે સુધી મોટી કરી છે
+ મોટી કરી છે
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-hi/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-hi/strings.xml
index 5d91de7d3b2..f52e7ecbacb 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-hi/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-hi/strings.xml
@@ -1,6 +1,6 @@
पूरी तरह बड़ा करें
+ बड़ा करें
+ छोटा करेंखींचकर छोड़ने वाला हैंडल
- बॉटम शीट को बड़ा करें
- बॉटम शीट को छोटा करें
- खींचकर छोड़ने वाले हैंडल पर दो बार टैप किया गया
+ बॉटम शीट को बड़ा करें
+ बॉटम शीट को आधा खोलें
+ बॉटम शीट को छोटा करें
+ छोटा किया गया
+ आधा खोला गया
+ बड़ा किया गया
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-hr/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-hr/strings.xml
index 76055fa799c..bda349acd42 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-hr/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-hr/strings.xml
@@ -1,6 +1,6 @@
Proširi donju polovicu
+ Proširi
+ SažmiMarker za povlačenje
- Proširite donju tablicu
- Sažmite donju tablicu
- Dvaput dodirnut marker za povlačenje
+ Proširite donju tablicu
+ Djelomično proširite donju tablicu
+ Sažmite donju tablicu
+ Sažeto
+ Pola prošireno
+ Prošireno
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-hu/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-hu/strings.xml
index d105caf52d6..5501b6dc178 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-hu/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-hu/strings.xml
@@ -1,6 +1,6 @@
Kibontás félig
+ Kibontás
+ ÖsszecsukásCsúszka
- Az alsó lap kibontása
- Az alsó lap összecsukása
- Duplán koppintott a csúszkára
+ Az alsó lap kibontása
+ Az alsó lap kibontása félig
+ Az alsó lap összecsukása
+ Összecsukva
+ Félig kibontva
+ Kibontva
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-hy/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-hy/strings.xml
index fe1d77d4f72..cbe91f66d1e 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-hy/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-hy/strings.xml
@@ -1,6 +1,6 @@
Ծավալել կիսով չափ
+ Ծավալել
+ ԾալելՏեղափոխման նշիչ
- Ծավալել ներքևի էկրանը
- Ծալել ներքևի էկրանը
- Կրկնակի հպում տեղափոխման նշիչին
+ Ծավալել ներքևի էկրանը
+ Կիսով չափ ծավալել ներքևի էկրանը
+ Ծալել ներքևի էկրանը
+ Ծալված է
+ Կիսով չափ ծավալված է
+ Ծավալված է
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-in/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-in/strings.xml
index d9dc7219812..8d1083fb9b0 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-in/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-in/strings.xml
@@ -1,6 +1,6 @@
Luaskan setengah
+ Luaskan
+ CiutkanHandel geser
- Meluaskan sheet bawah
- Menciutkan sheet bawah
- Handel geser diketuk dua kali
+ Meluaskan panel bawah
+ Meluaskan sebagian panel bawah
+ Menciutkan panel bawah
+ Diciutkan
+ Diluaskan sebagian
+ Diluaskan
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-is/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-is/strings.xml
index cb4183840af..dcf798aec09 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-is/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-is/strings.xml
@@ -1,6 +1,6 @@
Stækka til hálfs
+ Stækka
+ MinnkaDragkló
- Stækka blað neðst
- Minnka blað neðst
- Ýtt tvisvar á dragkló
+ Stækka blað neðst
+ Stækka blað neðst til hálfs
+ Minnka blað neðst
+ Minnkað
+ Stækkað til hálfs
+ Stækkað
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-it/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-it/strings.xml
index b0ec9c20245..2566e2cc77d 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-it/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-it/strings.xml
@@ -1,6 +1,6 @@
Espandi a metà
+ Espandi
+ ComprimiPunto di trascinamento
- Espandi riquadro inferiore
- Comprimi riquadro inferiore
- Doppio tocco su punto di trascinamento
+ Espandi il riquadro inferiore
+ Espandi parzialmente il riquadro inferiore
+ Comprimi il riquadro inferiore
+ Compresso
+ Parzialmente espanso
+ Espanso
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-iw/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-iw/strings.xml
index d9453a01488..0235bd585d9 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-iw/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-iw/strings.xml
@@ -1,6 +1,6 @@
הרחבה עד האמצע
+ הרחבה
+ כיווץנקודת אחיזה לגרירה
- הרחבת הגיליון התחתון
- כיווץ הגיליון התחתון
- בוצעה הקשה כפולה על נקודת האחיזה לגרירה
+ הרחבת הגיליון התחתון
+ חצי הרחבה של הגיליון התחתון
+ כיווץ הגיליון התחתון
+ מכווץ
+ חצי הרחבה
+ מורחב
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ja/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ja/strings.xml
index b2e543a6ceb..f0deea580da 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ja/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ja/strings.xml
@@ -1,6 +1,6 @@
下半分を展開
+ 開く
+ 閉じるドラッグ ハンドル
- ボトムシートを開く
- ボトムシートを閉じる
- ドラッグ ハンドルをダブルタップしました
+ ボトムシートを開く
+ ボトムシートを半分開く
+ ボトムシートを閉じる
+ 閉じた状態です
+ 半分開いた状態です
+ 開いた状態です
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ka/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ka/strings.xml
index 105fdfc2711..aaf694b6db4 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ka/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ka/strings.xml
@@ -1,6 +1,6 @@
ნახევრამდე გაფართოება
+ გაშლა
+ ჩაკეცვასახელური ჩავლებისთვის
- ქვედა ფურცლის გაშლა
- ქვედა ფურცლის ჩაკეცვა
- ორმაგი შეხება ჩავლების სახელურზე
+ ქვედა ფურცლის გაშლა
+ ქვედა ფურცლის ნახევრად გაშლა
+ ქვედა ფურცლის ჩაკეცვა
+ ჩაკეცილი
+ ნახევრად გაშლილი
+ გაშლილი
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-kk/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-kk/strings.xml
index 807c72e5f23..7834ab19e53 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-kk/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-kk/strings.xml
@@ -1,6 +1,6 @@
Жартылай кеңейту
+ Жаю
+ ЖиюСүйрейтін тетік
- Төменгі парақшаны жаю
- Төменгі парақшаны жию
- Сүйрейтін тетік екі рет түртілді.
+ Төменгі парақшаны жаю
+ Төменгі парақшаны жартылай жаю
+ Төменгі парақшаны жию
+ Жиылды
+ Жартылай жайылды
+ Жайылды
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-km/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-km/strings.xml
index b94044870af..234e045ef5d 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-km/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-km/strings.xml
@@ -1,6 +1,6 @@
ពង្រីកពាក់កណ្ដាល
+ ពង្រីក
+ បង្រួមដងអូស
- ពង្រីកសន្លឹកខាងក្រោម
- បង្រួមសន្លឹកខាងក្រោម
- បានប៉ះដងអូសពីរដង
+ ពង្រីកសន្លឹកខាងក្រោម
+ ពង្រីកសន្លឹកខាងក្រោមពាក់កណ្ដាល
+ បង្រួមសន្លឹកខាងក្រោម
+ បានបង្រួម
+ បានពង្រីកពាក់កណ្ដាល
+ បានពង្រីក
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-kn/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-kn/strings.xml
index 9c701ce3263..a8f0ddd7b48 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-kn/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-kn/strings.xml
@@ -1,6 +1,6 @@
ಅರ್ಧದಷ್ಟು ವಿಸ್ತರಿಸಿ
+ ವಿಸ್ತೃತಗೊಳಿಸಿ
+ ಕುಗ್ಗಿಸಿಹ್ಯಾಂಡಲ್ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ
- ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ವಿಸ್ತರಿಸಿ
- ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ಕುಗ್ಗಿಸಿ
- ಹ್ಯಾಂಡಲ್ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ ಎಂಬುದನ್ನು ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಲಾಗಿದೆ
+ ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ
+ ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ಅರ್ಧದಷ್ಟು ವಿಸ್ತೃತಗೊಳಿಸಿ
+ ಕೆಳಭಾಗದ ಶೀಟ್ ಅನ್ನು ಕುಗ್ಗಿಸಿ
+ ಕುಗ್ಗಿಸಲಾಗಿದೆ
+ ಅರ್ಧದಷ್ಟು ವಿಸ್ತೃತಗೊಳಿಸಲಾಗಿದೆ
+ ವಿಸ್ತೃತಗೊಳಿಸಲಾಗಿದೆ
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ko/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ko/strings.xml
index fef54e384b4..54ca6954137 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ko/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ko/strings.xml
@@ -1,6 +1,6 @@
반만 펼치기
+ 펼치기
+ 접기드래그 핸들
- 하단 시트 펼치기
- 하단 시트 접기
- 두 번 탭한 드래그 핸들
+ 하단 시트 펼치기
+ 하단 시트 반만 펼치기
+ 하단 시트 접기
+ 접힘
+ 반만 펼쳐짐
+ 펼쳐짐
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ky/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ky/strings.xml
index a0654aef310..56dbb3f0288 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ky/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ky/strings.xml
@@ -1,6 +1,6 @@
Жарымын жайып көрсөтүү
+ Жайып көрсөтүү
+ ЖыйыштырууТизменин керектүү жерине сүйрөп баруу
- Ылдыйкы экранды жайып көрсөтүү
- Ылдыйкы экранды жыйыштыруу
- Эки жолу таптап, тизменин керектүү жерине сүйрөп баруу
+ Ылдыйкы экранды жайып көрсөтүү
+ Ылдыйкы экранды жарымына чейин жайып көрсөтүү
+ Ылдыйкы экранды жыйыштыруу
+ Жыйыштырылды
+ Жарымына чейин жайылып көрсөтүлдү
+ Жайылып көрсөтүлдү
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-lo/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-lo/strings.xml
index d408014b249..e2a597b7a49 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-lo/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-lo/strings.xml
@@ -1,6 +1,6 @@
ຂະຫຍາຍອອກເຄິ່ງໜຶ່ງ
+ ຂະຫຍາຍ
+ ຫຍໍ້ລົງບ່ອນຈັບລາກ
- ຂະຫຍາຍຊີດລຸ່ມສຸດ
- ຫຍໍ້ຊີດລຸ່ມສຸດລົງ
- ແຕະບ່ອນຈັບລາກສອງເທື່ອແລ້ວ
+ ຂະຫຍາຍຊີດລຸ່ມສຸດ
+ ຂະຫຍາຍຊີດລຸ່ມສຸດເຄິ່ງໜຶ່ງ
+ ຫຍໍ້ຊີດລຸ່ມສຸດລົງ
+ ຫຍໍ້ແລ້ວ
+ ຂະຫຍາຍເຄິ່ງໜຶ່ງແລ້ວ
+ ຂະຫຍາຍແລ້ວ
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-lt/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-lt/strings.xml
index 87ba910a611..835e3079988 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-lt/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-lt/strings.xml
@@ -1,6 +1,6 @@
Išskleisti iki pusės
+ Išskleisti
+ SutrauktiVilkimo rankenėlė
- Išskleisti apatinį lapą
- Sutraukti apatinį lapą
- Vilkimo rankenėlė dukart paliesta
+ Išskleisti apatinį lapą
+ Išskleisti apatinį lapą iki pusės
+ Sutraukti apatinį lapą
+ Sutraukta
+ Išskleista iki pusės
+ Išskleista
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-lv/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-lv/strings.xml
index 2547cd2e81f..7155c38c68c 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-lv/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-lv/strings.xml
@@ -1,6 +1,6 @@
Izvērst līdz pusei
+ Izvērst
+ SakļautVilkšanas turis
- Izvērst ekrāna apakšdaļas lapu
- Sakļaut ekrāna apakšdaļas lapu
- Tika veikts dubultskāriens uz vilkšanas tura
+ Izvērst ekrāna apakšdaļas lapu
+ Izvērst ekrāna apakšdaļas lapu līdz pusei
+ Sakļaut ekrāna apakšdaļas lapu
+ Sakļauts
+ Izvērsts līdz pusei
+ Izvērsts
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-mk/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-mk/strings.xml
index a839a9ca70c..0650c63ea90 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-mk/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-mk/strings.xml
@@ -1,6 +1,6 @@
Прошири до половина
+ Проширете
+ СоберетеРачка за влечење
- Проширете го долниот лист
- Соберете го долниот лист
- Рачката за влечење е допрена двапати
+ Проширете го долниот лист
+ Проширете го долниот лист до половина
+ Соберете го долниот лист
+ Собрано
+ Проширено до половина
+ Проширено
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ml/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ml/strings.xml
index c47eb02b389..d1c039b9f9b 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ml/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ml/strings.xml
@@ -1,6 +1,6 @@
മുഴുവനായി വികസിപ്പിക്കുക
+ വികസിപ്പിക്കുക
+ ചുരുക്കുകവലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ
- ബോട്ടം ഷീറ്റ് വികസിപ്പിക്കുക
- ബോട്ടം ഷീറ്റ് ചുരുക്കുക
- വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ ഡബിൾ ടാപ്പ് ചെയ്തു
+ ബോട്ടം ഷീറ്റ് വികസിപ്പിക്കുക
+ ബോട്ടം ഷീറ്റ് പകുതി വികസിപ്പിക്കുക
+ ബോട്ടം ഷീറ്റ് ചുരുക്കുക
+ ചുരുക്കി
+ പകുതി വികസിപ്പിക്കുക
+ വികസിപ്പിച്ചു
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-mn/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-mn/strings.xml
index 12b73f1110f..5d4a0d9888f 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-mn/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-mn/strings.xml
@@ -1,6 +1,6 @@
Хагас дэлгэнэ үү
+ Дэлгэх
+ ХураахЧирэх бариул
- Доод хүснэгтийг дэлгэх
- Доод хүснэгтийг хураах
- Чирэх бариулыг хоёр товших
+ Доод хүснэгтийг дэлгэх
+ Доод хүснэгтийг тал дэлгэх
+ Доод хүснэгтийг хураах
+ Хураасан
+ Тал дэлгэсэн
+ Дэлгэсэн
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-mr/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-mr/strings.xml
index c4dc8df4765..b437730ba03 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-mr/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-mr/strings.xml
@@ -1,6 +1,6 @@
पूर्णपणे विस्तृत करा
+ विस्तार करा
+ कोलॅप्स कराड्रॅग हॅंडल
- तळाशी असलेल्या शीटचा विस्तार करा
- तळाशी असलेली शीट कोलॅप्स करा
- ड्रॅग हॅंडलवर दोनदा टॅप केले आहे
+ तळाशी असलेल्या शीटचा विस्तार करा
+ तळाशी असलेल्या शीटचा अर्ध्यावर विस्तार करा
+ तळाशी असलेली शीट कोलॅप्स करा
+ कोलॅप्स केली आहे
+ अर्ध्यावर विस्तार केला आहे
+ विस्तार केला आहे
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ms/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ms/strings.xml
index ad19813d4c3..f81ffd56e25 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ms/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ms/strings.xml
@@ -1,6 +1,6 @@
Kembangkan helaian bawah
+ Kembangkan
+ KuncupkanPemegang seret
- Kembangkan helaian bawah
- Kuncupkan helaian bawah
- Pemegang seret diketik dua kali
+ Kembangkan helaian bawah
+ Kembangkan separa helaian bawah
+ Kuncupkan helaian bawah
+ Dikuncupkan
+ Dikembangkan separa
+ Dikembangkan
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-my/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-my/strings.xml
index 06e6703e693..e87b55dcd14 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-my/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-my/strings.xml
@@ -1,6 +1,6 @@
တစ်ဝက်ချဲ့ရန်
+ ချဲ့ရန်
+ ချုံ့ရန်ဖိဆွဲအထိန်း
- အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ချဲ့နိုင်သည်
- အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ပိတ်နိုင်သည်
- ဖိဆွဲအထိန်း နှစ်ချက်တို့ထားသည်
+ အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ချဲ့နိုင်သည်
+ အောက်ခြေအပိုဆောင်း စာမျက်နှာကို တစ်ဝက်ချဲ့နိုင်သည်
+ အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ချုံ့နိုင်သည်
+ ချုံ့ထားသည်
+ တစ်ဝက်ချဲ့ထားသည်
+ ချဲ့ထားသည်
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-nb/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-nb/strings.xml
index 52391bb578b..71b528bee79 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-nb/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-nb/strings.xml
@@ -1,6 +1,6 @@
Vis halve feltet nederst
+ Vis
+ SkjulHåndtak
- Vis feltet nederst
- Skjul feltet nederst
- Dobbelttrykket på håndtaket
+ Vis feltet nederst
+ Vis halvparten av feltet nederst
+ Skjul feltet nederst
+ Skjult
+ Vises halvveis
+ Vises
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ne/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ne/strings.xml
index 7d2075c505d..922fcd7dd20 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ne/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ne/strings.xml
@@ -1,6 +1,6 @@
आधा ठुलो पार्नुहोस्
+ एक्स्पान्ड गर्नुहोस्
+ कोल्याप्स गर्नुहोस्ड्र्याग ह्यान्डल
- पुछारको पाना एक्स्पान्ड गर्नुहोस्
- पुछारको पाना कोल्याप्स गर्नुहोस्
- ड्र्याग ह्यान्डलमा डबल ट्याप गरियो
+ पुछारको पाना एक्स्पान्ड गर्नुहोस्
+ पुछारको पाना आधा एक्स्पान्ड गर्नुहोस्
+ पुछारको पाना कोल्याप्स गर्नुहोस्
+ कोल्याप्स गरिएको छ
+ आधा एक्स्पान्ड गरिएको छ
+ एक्स्पान्ड गरिएको छ
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-nl/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-nl/strings.xml
index 31c198807ff..5a300796041 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-nl/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-nl/strings.xml
@@ -1,6 +1,6 @@
Half uitvouwen
+ Uitvouwen
+ SamenvouwenHandgreep voor slepen
- Het blad onderaan uitvouwen
- Het blad onderaan samenvouwen
- Dubbelgetikt op handgreep voor slepen
+ Het blad onderaan uitvouwen
+ Het blad onderaan half uitvouwen
+ Het blad onderaan samenvouwen
+ Samengevouwen
+ Half uitgevouwen
+ Uitgevouwen
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-or/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-or/strings.xml
index 4814acc8926..6ba2b93e45a 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-or/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-or/strings.xml
@@ -1,6 +1,6 @@
Expand halfway
+ Expand
+ CollapseDrag handle
- Expand the bottom sheet
- Collapse the bottom sheet
- Drag handle double-tapped
+ Expand the bottom sheet
+ Half expand the bottom sheet
+ Collapse the bottom sheet
+ Collapsed
+ Half expanded
+ Expanded
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-pa/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-pa/strings.xml
index 775ad514162..03ea8057637 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-pa/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-pa/strings.xml
@@ -1,6 +1,6 @@
ਅੱਧ ਤੱਕ ਵਿਸਤਾਰ ਕਰੋ
+ ਵਿਸਤਾਰ ਕਰੋ
+ ਸਮੇਟੋਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ
- ਹੇਠਲੀ ਸ਼ੀਟ ਦਾ ਵਿਸਤਾਰ ਕਰੋ
- ਹੇਠਲੀ ਸ਼ੀਟ ਨੂੰ ਸਮੇਟੋ
- ਘਸੀਟਣ ਵਾਲੇ ਹੈਂਡਲ \'ਤੇ ਡਬਲ-ਟੈਪ ਕੀਤਾ ਗਿਆ
+ ਹੇਠਲੀ ਸ਼ੀਟ ਦਾ ਵਿਸਤਾਰ ਕਰੋ
+ ਹੇਠਲੀ ਸ਼ੀਟ ਦਾ ਅੱਧ ਤੱਕ ਵਿਸਤਾਰ ਕਰੋ
+ ਹੇਠਲੀ ਸ਼ੀਟ ਨੂੰ ਸਮੇਟੋ
+ ਸਮੇਟਿਆ ਗਿਆ
+ ਅੱਧ ਤੱਕ ਵਿਸਤਾਰ ਕੀਤਾ ਗਿਆ
+ ਵਿਸਤਾਰ ਕੀਤਾ ਗਿਆ
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-pl/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-pl/strings.xml
index 8971ef95181..aea7e76641e 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-pl/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-pl/strings.xml
@@ -1,6 +1,6 @@
Rozwiń do połowy
+ Rozwiń
+ ZwińUchwyt do przeciągania
- Rozwiń planszę dolną
- Zwiń planszę dolną
- Dwukrotnie kliknięto uchwyt do przeciągania
+ Rozwiń planszę dolną
+ Rozwiń planszę dolną do połowy
+ Zwiń planszę dolną
+ Zwinięto
+ Rozwinięto do połowy
+ Rozwinięto
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-pt-rBR/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-pt-rBR/strings.xml
index a309dc903f5..9e663b03cb9 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-pt-rBR/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-pt-rBR/strings.xml
@@ -1,6 +1,6 @@
Expandir até a metade
+ Abrir
+ FecharAlça de arrastar
- Abrir a página inferior
- Fechar a página inferior
- Alça de arrastar tocada duas vezes
+ Abrir a página inferior
+ Abrir parcialmente a página inferior
+ Fechar a página inferior
+ Fechada
+ Aberta até a metade
+ Aberta
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-pt-rPT/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-pt-rPT/strings.xml
index 33c741f703e..1ab5ff34157 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-pt-rPT/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-pt-rPT/strings.xml
@@ -1,6 +1,6 @@
Expandir até metade
+ Expandir
+ ReduzirIndicador para arrastar
- Expanda a secção inferior
- Reduza a secção inferior
- Dois toques no indicador para arrastar
+ Expanda a secção inferior
+ Expanda a secção inferior até metade
+ Reduza a secção inferior
+ Reduzido
+ Expandido até metade
+ Expandido
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ro/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ro/strings.xml
index d5ba26b15a2..398925ef296 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ro/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ro/strings.xml
@@ -1,6 +1,6 @@
Extinde pe ecran complet
+ Extindeți
+ RestrângețiGhidaj de tragere
- Extindeți foaia din partea de jos
- Restrângeți foaia din partea de jos
- Ghidaj de tragere atins de două ori
+ Extindeți foaia din partea de jos
+ Extindeți pe jumătate foaia din partea de jos
+ Restrângeți foaia din partea de jos
+ Restrânsă
+ Extinsă pe jumătate
+ Extinsă
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ru/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ru/strings.xml
index 1e7f2ae1120..c3356669acf 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ru/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ru/strings.xml
@@ -1,6 +1,6 @@
Развернуть наполовину
+ Развернуть
+ СвернутьМаркер перемещения
- Развернуть нижний экран
- Свернуть нижний экран
- Двойное нажатие на маркер перемещения
+ Развернуть нижний экран
+ Развернуть нижний экран наполовину
+ Свернуть нижний экран
+ Свернуто
+ Развернуто наполовину
+ Развернуто
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-si/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-si/strings.xml
index 213d5f73ddb..d494c61cff6 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-si/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-si/strings.xml
@@ -1,6 +1,6 @@
අඩක් දිග හරින්න
+ දිග හරින්න
+ හකුළන්නහැඬලය අදින්න
- පහළම පත්රය දිග හරින්න
- පහළම පත්රය හකුළන්න
- ඇදීම් හැඬලය දෙවරක් තට්ටු කර ඇත
+ පහළම පත්රය දිග හරින්න
+ පහළ පත්රය අඩක් පුළුල් කරන්න
+ පහළම පත්රය හකුළන්න
+ හකුළන ලදි
+ අඩක් දිග හරින ලදි
+ දිග හරින ලදි
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-sk/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-sk/strings.xml
index 2bcd1157c89..9a18773c5b6 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-sk/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-sk/strings.xml
@@ -1,6 +1,6 @@
Rozbaliť napoly
+ Rozbaliť
+ ZbaliťPresúvadlo
- Rozbaliť dolný hárok
- Zbaliť dolný hárok
- Dvojité klepnutie na presúvadlo
+ Rozbaliť dolný hárok
+ Rozbaliť dolný hárok na polovicu
+ Zbaliť dolný hárok
+ Zbalené
+ Rozbalené na polovicu
+ Rozbalené
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-sl/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-sl/strings.xml
index 9a52fbdd2b9..4709f750aab 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-sl/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-sl/strings.xml
@@ -1,6 +1,6 @@
Razširjanje na pol višine
+ Razširitev
+ StrnitevRočica za vlečenje
- Razširitev razdelka na dnu zaslona
- Strnitev razdelka na dnu zaslona
- Dvakrat dotaknjena ročica za vlečenje
+ Razširitev razdelka na dnu zaslona
+ Polovična razširitev razdelka na dnu zaslona
+ Strnitev razdelka na dnu zaslona
+ Strnjeno
+ Polovično razširjeno
+ Razširjeno
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-sq/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-sq/strings.xml
index 34ee049a2d6..5572ce6e6ff 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-sq/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-sq/strings.xml
@@ -1,6 +1,6 @@
Zgjeroje deri në gjysmë
+ Zgjero
+ PalosDoreza e zvarritjes
- Zgjero fletën e poshtme
- Palos fletën e poshtme
- Te doreza e zvarritjes u trokit dy herë
+ Zgjero fletën e poshtme
+ Zgjero në gjysmë fletën e poshtme
+ Palos fletën e poshtme
+ Palosur
+ Zgjeruar në gjysmë
+ Zgjeruar
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-sr/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-sr/strings.xml
index 5015e82ab05..11e8df6fc83 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-sr/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-sr/strings.xml
@@ -1,6 +1,6 @@
Проширите до пола
+ Проширите
+ СкупитеРучица за превлачење
- Проширите доњу табелу
- Скупите доњу табелу
- Идентификатор за превлачење је двапут додирнут
+ Проширите доњу табелу
+ Проширите доњу табелу до пола
+ Скупите доњу табелу
+ Скупљено
+ Проширено до пола
+ Проширено
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-sv/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-sv/strings.xml
index 97f3bf02ad6..edf636c8fa5 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-sv/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-sv/strings.xml
@@ -1,6 +1,6 @@
Utöka till hälften
+ Utöka
+ KomprimeraHandtag
- Utöka arket på nedre delen av skärmen
- Komprimera arket på nedre delen av skärmen
- Du tryckte snabbt två gånger på handtaget
+ Utöka arket på nedre delen av skärmen
+ Utöka arket på nedre delen av skärmen halvvägs
+ Komprimera arket på nedre delen av skärmen
+ Komprimerat
+ Halvvägs utökat
+ Utökat
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-sw/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-sw/strings.xml
index b1705b4b507..3a4d6ad74ca 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-sw/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-sw/strings.xml
@@ -1,6 +1,6 @@
Panua nusu
+ Panua
+ KunjaAikoni ya buruta
- Panua safu ya chini
- Kunja safu ya chini
- Aikoni ya buruta imeguswa mara mbili
+ Panua safu ya chini
+ Panua nusu safu ya chini
+ Kunja safu ya chini
+ Imekunjwa
+ Imepanuliwa nusu
+ Imepanuliwa
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ta/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ta/strings.xml
index 3ae34ac41bd..a3c651d5c7f 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ta/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ta/strings.xml
@@ -1,6 +1,6 @@
பாதியளவு விரிவாக்கும்
+ விரிவாக்கும்
+ சுருக்கும்இழுப்பதற்கான ஹேண்டில்
- கீழ்ப்புறச் சீட்டை விரிவாக்கும்
- கீழ்ப்புறச் சீட்டைச் சுருக்கும்
- இழுப்பதற்கான ஹேண்டில் இருமுறை தட்டப்பட்டது
+ கீழ்ப்புறச் சீட்டை விரிவாக்கும்
+ கீழ்ப்புறச் சீட்டைப் பாதியளவு விரிவாக்கும்
+ கீழ்ப்புறச் சீட்டைச் சுருக்கும்
+ சுருக்கப்பட்டது
+ பாதியளவு விரிவாக்கப்பட்டது
+ விரிவாக்கப்பட்டது
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-te/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-te/strings.xml
index 0814c74dba0..42f3367682e 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-te/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-te/strings.xml
@@ -1,6 +1,6 @@
సగాన్ని విస్తరింపజేయండి
+ విస్తరించండి
+ కుదించండిలాగే హ్యాండిల్
- దిగువున ఉన్న షీట్ను విస్తరిస్తుంది
- దిగువున ఉన్న షీట్ను కుదిస్తుంది
- లాగే హ్యాండిల్ డబుల్-ట్యాప్ చేయబడింది
+ దిగువున ఉన్న షీట్ను విస్తరిస్తుంది
+ దిగువున ఉన్న షీట్ను సగం విస్తరిస్తుంది
+ దిగువున ఉన్న షీట్ను కుదిస్తుంది
+ కుదించబడింది
+ సగం విస్తరించబడింది
+ విస్తరించబడింది
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-th/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-th/strings.xml
index 5e1363e479a..39b4481e388 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-th/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-th/strings.xml
@@ -1,6 +1,6 @@
ขยายรายการครึ่งหนึ่ง
+ ขยาย
+ ยุบแฮนเดิลการลาก
- ขยาย Bottom Sheet
- ยุบ Bottom Sheet
- แตะแฮนเดิลการลากสองครั้ง
+ ขยาย Bottom Sheet
+ ขยาย Bottom Sheet ครึ่งหนึ่ง
+ ยุบ Bottom Sheet
+ ยุบแล้ว
+ ขยายครึ่งหนึ่งแล้ว
+ ขยายแล้ว
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-tl/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-tl/strings.xml
index f6c66077f33..4bf7d510462 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-tl/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-tl/strings.xml
@@ -1,6 +1,6 @@
I-expand hanggang gitna
+ I-expand
+ I-collapseI-drag ang handle
- I-expand ang bottom sheet
- I-collapse ang bottom sheet
- Na-double tap ang handle sa pag-drag
+ I-expand ang bottom sheet
+ I-expand nang kalahati ang bottom sheet
+ I-collapse ang bottom sheet
+ Na-collapse
+ Na-expand nang kalahati
+ Na-expand
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-tr/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-tr/strings.xml
index dbc6c4dae6f..b1c3ec427d9 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-tr/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-tr/strings.xml
@@ -1,6 +1,6 @@
Yarım genişlet
+ Genişlet
+ DaraltSürükleme tutamacı
- Alt sayfayı genişlet
- Alt sayfayı daralt
- Sürükleme tutamacına iki kez dokunuldu
+ Alt sayfayı genişlet
+ Alt sayfayı yarım genişlet
+ Alt sayfayı daralt
+ Daraltıldı
+ Yarım genişletildi
+ Genişletildi
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-uk/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-uk/strings.xml
index fdcc6f863a4..62f9c4cf19e 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-uk/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-uk/strings.xml
@@ -1,6 +1,6 @@
Розгорнути нижню половину
+ Розгорнути
+ ЗгорнутиМаркер переміщення
- Розгорнути нижній екран
- Згорнути нижній екран
- Маркер переміщення активовано подвійним дотиком
+ Розгорнути нижній екран
+ Розгорнути нижній екран наполовину
+ Згорнути нижній екран
+ Згорнуто
+ Розгорнуто наполовину
+ Розгорнуто
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-ur/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-ur/strings.xml
index 7d44aa395c1..f7c89a26711 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-ur/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-ur/strings.xml
@@ -1,6 +1,6 @@
نصف تک پھیلائیں
+ پھیلائیں
+ سکیڑیںگھسیٹنے کا ہینڈل
- نیچے کی شیٹ کو پھیلائیں
- نیچے کی شیٹ کو سکیڑیں
- گھسیٹنے کے ہینڈل کو دو بار تھپتھپائیں
+ نیچے کی شیٹ کو پھیلائیں
+ نیچے کی شیٹ کو آدھا پھیلائیں
+ نیچے کی شیٹ کو سکیڑیں
+ سکیڑی گئی
+ آدھا پھیلائی گئی
+ پھیلائی گئی
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-uz/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-uz/strings.xml
index d209a7c8e6c..c0c6edadd47 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-uz/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-uz/strings.xml
@@ -1,6 +1,6 @@
Yarmiga kengaytirish
+ Yoyish
+ YigʻishSurish dastagi
- Quyi ekranni kengaytirish
- Quyi ekranni yigʻish
- Surish dastagi ikki marta bosildi
+ Quyi ekranni yoyish
+ Quyi ekranni yarim yoyish
+ Quyi ekranni yigʻish
+ Yigʻilgan
+ Yarim yoyilgan
+ Yoyilgan
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-vi/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-vi/strings.xml
index bcbe8cb6811..391b1f4d4f5 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-vi/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-vi/strings.xml
@@ -1,6 +1,6 @@
Mở rộng một nửa
+ Mở rộng
+ Thu gọnNút kéo
- Mở rộng bảng dưới cùng
- Thu gọn bảng dưới cùng
- Đã nhấn đúp nút kéo
+ Mở rộng bảng dưới cùng
+ Mở rộng một nửa bảng dưới cùng
+ Thu gọn bảng dưới cùng
+ Đã thu gọn
+ Đã mở rộng một nửa
+ Đã mở rộng
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-zh-rCN/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-zh-rCN/strings.xml
index 7cde34f8382..f4e0e337527 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-zh-rCN/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-zh-rCN/strings.xml
@@ -1,6 +1,6 @@
展开到一半高度
+ 展开
+ 收起拖动手柄
- 展开底部动作条
- 收起底部动作条
- 拖动手柄被点按两次
+ 展开底部动作条
+ 将底部动作条展开一半
+ 收起底部动作条
+ 已收起
+ 已展开一半
+ 已展开
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-zh-rHK/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-zh-rHK/strings.xml
index 8ac0f01c049..681976d3ca7 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-zh-rHK/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-zh-rHK/strings.xml
@@ -1,6 +1,6 @@
展開一半
+ 打開
+ 閂埋拖曳控點
- 展開頁底面板
- 收合頁底面板
- 㩒咗兩下拖曳控點
+ 打開頁底面板
+ 將頁底面板打開一半
+ 閂埋頁底面板
+ 閂埋咗
+ 打開咗一半
+ 打開咗
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-zh-rTW/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-zh-rTW/strings.xml
index 3ecec23ff56..f4e0ec0db30 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-zh-rTW/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-zh-rTW/strings.xml
@@ -1,6 +1,6 @@
展開一半
+ 展開
+ 收合拖曳控點
- 展開底部功能表
- 收合底部功能表
- 已輕觸兩下拖曳控點
+ 展開底部功能表
+ 展開一半底部功能表
+ 收合底部功能表
+ 已收合
+ 已展開一半
+ 已展開
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values-zu/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values-zu/strings.xml
index 5f6b92d9bbf..e577ccdb8af 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values-zu/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values-zu/strings.xml
@@ -1,6 +1,6 @@
Nweba phakathi
+ Nweba
+ GoqaHudula isibambi
- Nweba ishidi eliphansi
- Goqa ishidi eliphansi
- Hudula isibambi esithephwe kabili
+ Nweba ishidi eliphansi
+ Nweba ingxenye yeshidi eliphansi
+ Goqa ishidi eliphansi
+ Kugoqiwe
+ Uhhafu unwetshiwe
+ Kunwetshiwe
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values/strings.xml b/lib/java/com/google/android/material/bottomsheet/res/values/strings.xml
index d15c9557188..7cc77b999da 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values/strings.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values/strings.xml
@@ -17,11 +17,19 @@
com.google.android.material.bottomsheet.BottomSheetBehavior
- Expand halfway
+ Expand
+ CollapseDrag handle
- Expand the bottom sheet
- Collapse the bottom sheet
- Drag handle double-tapped
+ Expand the bottom sheet
+ Half expand the bottom sheet
+ Collapse the bottom sheet
+
+ Collapsed
+ Half expanded
+ Expanded
diff --git a/lib/java/com/google/android/material/bottomsheet/res/values/styles.xml b/lib/java/com/google/android/material/bottomsheet/res/values/styles.xml
index f70373c10fc..5bc62cab399 100644
--- a/lib/java/com/google/android/material/bottomsheet/res/values/styles.xml
+++ b/lib/java/com/google/android/material/bottomsheet/res/values/styles.xml
@@ -29,9 +29,7 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -279,9 +745,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This is necessary as before API 24, it is not guaranteed that Views will ever be notified
* about their parent changing. Thus, we don't have a proper point to hook in and re-check {@link
* #isShown()} on parent changes that result from {@link
- * android.view.ViewGroup#attachViewToParent(View, int, LayoutParams)}, which *can* change our
- * effective visibility. So this method errs on the side of assuming visibility unless we can
- * conclusively prove otherwise (but may result in some false positives, if this view ends up
- * being attached to a non-visible hierarchy after being detached in a visible state).
+ * android.view.ViewGroup#attachViewToParent(View, int, ViewGroup.LayoutParams)}, which *can*
+ * change our effective visibility. So this method errs on the side of assuming visibility unless
+ * we can conclusively prove otherwise (but may result in some false positives, if this view
+ * ends up being attached to a non-visible hierarchy after being detached in a visible state).
*/
boolean isEffectivelyVisible() {
View current = this;
@@ -273,7 +274,11 @@ public int getContainerHeight() {
public void setIndicatorColor(@ColorInt int... indicatorColors) {
if (indicatorColors.length == 0) {
// Uses theme primary color for indicator by default. Indicator color cannot be empty.
- indicatorColors = new int[] {MaterialColors.getColor(getContext(), R.attr.colorPrimary, -1)};
+ indicatorColors =
+ new int[] {
+ MaterialColors.getColor(
+ getContext(), androidx.appcompat.R.attr.colorPrimary, -1)
+ };
}
if (!Arrays.equals(getIndicatorColor(), indicatorColors)) {
specs.indicatorColors = indicatorColors;
diff --git a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorAnimatorDelegate.java b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorAnimatorDelegate.java
index f4f5a5d07c9..ed3dbd23864 100644
--- a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorAnimatorDelegate.java
+++ b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorAnimatorDelegate.java
@@ -121,14 +121,21 @@ public void onAnimationRepeat(Animator animation) {
/** Updates the indicator's rotation based on current playtime. */
private void updateIndicatorRotation(int playtime) {
+ float morphFactorBase = morphFactorTarget - 1;
+ float morphFactorPerShape = morphFactor - morphFactorBase;
+ float timeFactorPerShape = (float) playtime / DURATION_PER_SHAPE_IN_MS;
+ if (timeFactorPerShape == 1f) {
+ // The animation on repeat is called before the playtime restart. So if playtime reaches the
+ // end, we take it as restarted as 0.
+ timeFactorPerShape = 0f;
+ }
// Initial rotation.
- indicatorState.rotationDegree = CONSTANT_ROTATION_PER_SHAPE_DEGREES * (morphFactorTarget - 1);
- // Constant rotation for the current shape.
- indicatorState.rotationDegree +=
- CONSTANT_ROTATION_PER_SHAPE_DEGREES * ((float) playtime / DURATION_PER_SHAPE_IN_MS);
- // Rotation driven by spring animation.
indicatorState.rotationDegree =
- (CONSTANT_ROTATION_PER_SHAPE_DEGREES + EXTRA_ROTATION_PER_SHAPE_DEGREES) * morphFactor;
+ (CONSTANT_ROTATION_PER_SHAPE_DEGREES + EXTRA_ROTATION_PER_SHAPE_DEGREES) * morphFactorBase;
+ // Constant rotation.
+ indicatorState.rotationDegree += CONSTANT_ROTATION_PER_SHAPE_DEGREES * timeFactorPerShape;
+ // Rotation driven by spring animation.
+ indicatorState.rotationDegree += EXTRA_ROTATION_PER_SHAPE_DEGREES * morphFactorPerShape;
indicatorState.rotationDegree %= 360;
}
diff --git a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawable.java b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawable.java
index 8d5efcc9598..1c5c407d19c 100644
--- a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawable.java
+++ b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawable.java
@@ -28,7 +28,6 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.drawable.DrawableCompat;
import com.google.android.material.progressindicator.AnimatorDurationScaleProvider;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -98,7 +97,7 @@ public void draw(@NonNull Canvas canvas) {
if (isSystemAnimatorDisabled() && staticDummyDrawable != null) {
staticDummyDrawable.setBounds(bounds);
- DrawableCompat.setTint(staticDummyDrawable, specs.indicatorColors[0]);
+ staticDummyDrawable.setTint(specs.indicatorColors[0]);
staticDummyDrawable.draw(canvas);
return;
}
diff --git a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawingDelegate.java b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawingDelegate.java
index e27e3e61d12..6b6761148ec 100644
--- a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawingDelegate.java
+++ b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorDrawingDelegate.java
@@ -32,6 +32,7 @@
import androidx.graphics.shapes.RoundedPolygon;
import androidx.graphics.shapes.Shapes_androidKt;
import com.google.android.material.color.MaterialColors;
+import com.google.android.material.math.MathUtils;
import com.google.android.material.shape.MaterialShapes;
class LoadingIndicatorDrawingDelegate {
@@ -116,9 +117,10 @@ void drawIndicator(
canvas.rotate(indicatorState.rotationDegree);
// Draws the shape morph.
indicatorPath.rewind();
+ int shapeMorphFraction = (int) Math.floor(indicatorState.morphFraction);
int fractionAmongAllShapes =
- (int) (indicatorState.morphFraction % INDETERMINATE_MORPH_SEQUENCE.length);
- float fractionPerShape = indicatorState.morphFraction % 1;
+ MathUtils.floorMod(shapeMorphFraction, INDETERMINATE_MORPH_SEQUENCE.length);
+ float fractionPerShape = indicatorState.morphFraction - shapeMorphFraction;
Shapes_androidKt.toPath(
INDETERMINATE_MORPH_SEQUENCE[fractionAmongAllShapes], fractionPerShape, indicatorPath);
// We need to apply the scaling to the path directly, instead of on the canvas, to avoid the
diff --git a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorSpec.java b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorSpec.java
index 85783b58e80..2193a58dc08 100644
--- a/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorSpec.java
+++ b/lib/java/com/google/android/material/loadingindicator/LoadingIndicatorSpec.java
@@ -85,7 +85,10 @@ public LoadingIndicatorSpec(
private void loadIndicatorColors(@NonNull Context context, @NonNull TypedArray typedArray) {
if (!typedArray.hasValue(R.styleable.LoadingIndicator_indicatorColor)) {
// Uses theme primary color for indicator if not provided in the attribute set.
- indicatorColors = new int[] {MaterialColors.getColor(context, R.attr.colorPrimary, -1)};
+ indicatorColors =
+ new int[] {
+ MaterialColors.getColor(context, androidx.appcompat.R.attr.colorPrimary, -1)
+ };
return;
}
diff --git a/lib/java/com/google/android/material/loadingindicator/res/values-af/strings.xml b/lib/java/com/google/android/material/loadingindicator/res/values-af/strings.xml
index 33727cfcc55..b144b9d999b 100644
--- a/lib/java/com/google/android/material/loadingindicator/res/values-af/strings.xml
+++ b/lib/java/com/google/android/material/loadingindicator/res/values-af/strings.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/lib/java/com/google/android/material/materialswitch/MaterialSwitch.java b/lib/java/com/google/android/material/materialswitch/MaterialSwitch.java
index 5b410d3fa90..cda1fd94a53 100644
--- a/lib/java/com/google/android/material/materialswitch/MaterialSwitch.java
+++ b/lib/java/com/google/android/material/materialswitch/MaterialSwitch.java
@@ -35,7 +35,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
-import androidx.core.graphics.drawable.DrawableCompat;
import com.google.android.material.drawable.DrawableUtils;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.internal.ViewUtils;
@@ -232,7 +231,7 @@ public int getThumbIconSize() {
*
* Subsequent calls to {@link #setThumbIconDrawable(Drawable)} will
* automatically mutate the drawable and apply the specified tint and tint
- * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
*
* @param tintList the tint to apply, may be {@code null} to clear tint
*
@@ -346,7 +345,7 @@ public Drawable getTrackDecorationDrawable() {
*
*
Subsequent calls to {@link #setTrackDecorationDrawable(Drawable)} will
* automatically mutate the drawable and apply the specified tint and tint
- * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
*
* @param tintList the tint to apply, may be {@code null} to clear tint
*
@@ -487,11 +486,9 @@ private static void setInterpolatedDrawableTintIfPossible(
return;
}
- DrawableCompat.setTint(
- drawable,
- blendARGB(
- tint.getColorForState(stateUnchecked, 0),
- tint.getColorForState(stateChecked, 0),
- thumbPosition));
+ drawable.setTint(blendARGB(
+ tint.getColorForState(stateUnchecked, 0),
+ tint.getColorForState(stateChecked, 0),
+ thumbPosition));
}
}
diff --git a/lib/java/com/google/android/material/materialswitch/res/values/tokens.xml b/lib/java/com/google/android/material/materialswitch/res/values/tokens.xml
index e43409a4a5d..abfde8a1be3 100644
--- a/lib/java/com/google/android/material/materialswitch/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/materialswitch/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/menu/res/values/styles.xml b/lib/java/com/google/android/material/menu/res/values/styles.xml
index eba586e30a8..0e01a6419d8 100644
--- a/lib/java/com/google/android/material/menu/res/values/styles.xml
+++ b/lib/java/com/google/android/material/menu/res/values/styles.xml
@@ -56,22 +56,22 @@
diff --git a/lib/java/com/google/android/material/menu/res/values/tokens.xml b/lib/java/com/google/android/material/menu/res/values/tokens.xml
index c6e19760da1..0b990c42795 100644
--- a/lib/java/com/google/android/material/menu/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/menu/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/motion/MaterialBackAnimationHelper.java b/lib/java/com/google/android/material/motion/MaterialBackAnimationHelper.java
index 4051843da61..7bb6068853f 100644
--- a/lib/java/com/google/android/material/motion/MaterialBackAnimationHelper.java
+++ b/lib/java/com/google/android/material/motion/MaterialBackAnimationHelper.java
@@ -21,12 +21,12 @@
import android.content.Context;
import android.util.Log;
import android.view.View;
+import android.view.animation.PathInterpolator;
import androidx.activity.BackEventCompat;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
-import androidx.core.view.animation.PathInterpolatorCompat;
/**
* Base helper class for views that support back handling, which assists with common animation
@@ -43,8 +43,7 @@ public abstract class MaterialBackAnimationHelper {
private static final int CANCEL_DURATION_DEFAULT = 100;
@NonNull
- private final TimeInterpolator progressInterpolator =
- PathInterpolatorCompat.create(0.1f, 0.1f, 0, 1);
+ private final TimeInterpolator progressInterpolator = new PathInterpolator(0.1f, 0.1f, 0, 1);
@NonNull protected final V view;
protected final int hideDurationMax;
diff --git a/lib/java/com/google/android/material/motion/MotionUtils.java b/lib/java/com/google/android/material/motion/MotionUtils.java
index e6b2aca6b9a..1dfbeb555a4 100644
--- a/lib/java/com/google/android/material/motion/MotionUtils.java
+++ b/lib/java/com/google/android/material/motion/MotionUtils.java
@@ -15,14 +15,19 @@
*/
package com.google.android.material.motion;
+import com.google.android.material.R;
+
import android.animation.TimeInterpolator;
import android.content.Context;
+import android.content.res.TypedArray;
import android.util.TypedValue;
import android.view.animation.AnimationUtils;
+import android.view.animation.PathInterpolator;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
+import androidx.annotation.StyleRes;
import androidx.core.graphics.PathParser;
-import androidx.core.view.animation.PathInterpolatorCompat;
+import androidx.dynamicanimation.animation.SpringForce;
import com.google.android.material.resources.MaterialAttributes;
/** A utility class for motion system functions. */
@@ -36,6 +41,47 @@ public class MotionUtils {
private MotionUtils() {}
+ /**
+ * Resolve a {@link SpringForce} object from a Material spring theme attribute.
+ *
+ * @param context the context from where the theme attribute will be resolved
+ * @param attrResId the {@code motionSpring*} theme attribute to resolve
+ * into a {@link SpringForce} object
+ * @param defStyleRes a {@code MaterialSpring} style to load if attrResId cannot be resolved
+ * @return a {@link SpringForce} object configured using the stiffness and damping from the
+ * resolved Material spring attribute
+ */
+ @NonNull
+ public static SpringForce resolveThemeSpringForce(
+ @NonNull Context context, @AttrRes int attrResId, @StyleRes int defStyleRes) {
+
+ TypedValue tv = MaterialAttributes.resolve(context, attrResId);
+ TypedArray a;
+ if (tv == null) {
+ a = context.obtainStyledAttributes(null, R.styleable.MaterialSpring, 0, defStyleRes);
+ } else {
+ a = context.obtainStyledAttributes(tv.resourceId, R.styleable.MaterialSpring);
+ }
+
+ SpringForce springForce = new SpringForce();
+ try {
+ float stiffness = a.getFloat(R.styleable.MaterialSpring_stiffness, Float.MIN_VALUE);
+ if (stiffness == Float.MIN_VALUE) {
+ throw new IllegalArgumentException("A MaterialSpring style must have stiffness value.");
+ }
+ float damping = a.getFloat(R.styleable.MaterialSpring_damping, Float.MIN_VALUE);
+ if (damping == Float.MIN_VALUE) {
+ throw new IllegalArgumentException("A MaterialSpring style must have a damping value.");
+ }
+
+ springForce.setStiffness(stiffness);
+ springForce.setDampingRatio(damping);
+ } finally {
+ a.recycle();
+ }
+ return springForce;
+ }
+
/**
* Resolve a duration from a material duration theme attribute.
*
@@ -100,10 +146,10 @@ private static TimeInterpolator getLegacyThemeInterpolator(String easingString)
float controlY1 = getLegacyControlPoint(controlPoints, 1);
float controlX2 = getLegacyControlPoint(controlPoints, 2);
float controlY2 = getLegacyControlPoint(controlPoints, 3);
- return PathInterpolatorCompat.create(controlX1, controlY1, controlX2, controlY2);
+ return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
} else if (isLegacyEasingType(easingString, EASING_TYPE_PATH)) {
String path = getLegacyEasingContent(easingString, EASING_TYPE_PATH);
- return PathInterpolatorCompat.create(PathParser.createPathFromPathData(path));
+ return new PathInterpolator(PathParser.createPathFromPathData(path));
} else {
throw new IllegalArgumentException("Invalid motion easing type: " + easingString);
}
diff --git a/lib/java/com/google/android/material/motion/res-public/values/public.xml b/lib/java/com/google/android/material/motion/res-public/values/public.xml
index 5f57d2b4869..ea6d97e4421 100644
--- a/lib/java/com/google/android/material/motion/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/motion/res-public/values/public.xml
@@ -15,7 +15,15 @@
~ limitations under the License.
-->
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/motion/res/values/attrs.xml b/lib/java/com/google/android/material/motion/res/values/attrs.xml
index f39991cc459..2293849d1b8 100644
--- a/lib/java/com/google/android/material/motion/res/values/attrs.xml
+++ b/lib/java/com/google/android/material/motion/res/values/attrs.xml
@@ -1,4 +1,5 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/motion/res/values/styles.xml b/lib/java/com/google/android/material/motion/res/values/styles.xml
new file mode 100644
index 00000000000..8bc8802e707
--- /dev/null
+++ b/lib/java/com/google/android/material/motion/res/values/styles.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/motion/res/values/tokens.xml b/lib/java/com/google/android/material/motion/res/values/tokens.xml
index f0739d6d6c2..c93fc413586 100644
--- a/lib/java/com/google/android/material/motion/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/motion/res/values/tokens.xml
@@ -15,11 +15,25 @@
~ limitations under the License.
-->
-
+
-
+
+
+ 0.9
+ 1400
+ 1
+ 3800
+ 0.9
+ 700
+ 1
+ 1600
+ 0.9
+ 300
+ 1
+ 800path(M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1)
diff --git a/lib/java/com/google/android/material/navigation/DividerMenuItem.java b/lib/java/com/google/android/material/navigation/DividerMenuItem.java
new file mode 100644
index 00000000000..8105166a3db
--- /dev/null
+++ b/lib/java/com/google/android/material/navigation/DividerMenuItem.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2024 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.navigation;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ActionProvider;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * An empty MenuItem that is used to represent a divider in the menu.
+ */
+class DividerMenuItem implements MenuItem {
+
+ @Override
+ public boolean collapseActionView() {
+ return false;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public ActionProvider getActionProvider() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public View getActionView() {
+ return null;
+ }
+
+ @Override
+ public char getAlphabeticShortcut() {
+ return 0;
+ }
+
+ @Override
+ public int getGroupId() {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public Drawable getIcon() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Intent getIntent() {
+ return null;
+ }
+
+ @Override
+ public int getItemId() {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public ContextMenuInfo getMenuInfo() {
+ return null;
+ }
+
+ @Override
+ public char getNumericShortcut() {
+ return 0;
+ }
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public SubMenu getSubMenu() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getTitle() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getTitleCondensed() {
+ return null;
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ @Override
+ public boolean isActionViewExpanded() {
+ return false;
+ }
+
+ @Override
+ public boolean isCheckable() {
+ return false;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return false;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setActionProvider(@Nullable ActionProvider actionProvider) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setActionView(@Nullable View view) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setActionView(int resId) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setCheckable(boolean checkable) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setChecked(boolean checked) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setEnabled(boolean enabled) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setIcon(@Nullable Drawable icon) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setIcon(int iconRes) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setIntent(@Nullable Intent intent) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setNumericShortcut(char numericChar) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setOnActionExpandListener(@Nullable OnActionExpandListener listener) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setOnMenuItemClickListener(
+ @Nullable OnMenuItemClickListener menuItemClickListener) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ return null;
+ }
+
+ @Override
+ public void setShowAsAction(int actionEnum) {
+
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setShowAsActionFlags(int actionEnum) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setTitle(int title) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setTitle(@Nullable CharSequence title) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setTitleCondensed(@Nullable CharSequence title) {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public MenuItem setVisible(boolean visible) {
+ return null;
+ }
+}
diff --git a/lib/java/com/google/android/material/navigation/NavigationBarDividerView.java b/lib/java/com/google/android/material/navigation/NavigationBarDividerView.java
new file mode 100644
index 00000000000..b47a911cd48
--- /dev/null
+++ b/lib/java/com/google/android/material/navigation/NavigationBarDividerView.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 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.navigation;
+
+import com.google.android.material.R;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import androidx.appcompat.view.menu.MenuItemImpl;
+import android.view.LayoutInflater;
+import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+/**
+ * Provides a view that will be used to render subheader items inside a {@link
+ * NavigationBarMenuView}.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class NavigationBarDividerView extends FrameLayout implements NavigationBarMenuItemView {
+
+ private boolean expanded;
+ boolean onlyShowWhenExpanded;
+ private boolean dividersEnabled;
+
+ NavigationBarDividerView(@NonNull Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.m3_navigation_menu_divider, this, true);
+ updateVisibility();
+ }
+
+ @Override
+ public void initialize(@NonNull MenuItemImpl menuItem, int i) {
+ updateVisibility();
+ }
+
+ @Override
+ @Nullable
+ public MenuItemImpl getItemData() {
+ return null;
+ }
+
+ @Override
+ public void setTitle(@Nullable CharSequence charSequence) {}
+
+ @Override
+ public void setEnabled(boolean enabled) {}
+
+ @Override
+ public void setCheckable(boolean checkable) {}
+
+ @Override
+ public void setChecked(boolean checked) {}
+
+ @Override
+ public void setShortcut(boolean showShortcut, char shortcutKey) {}
+
+ @Override
+ public void setIcon(@Nullable Drawable drawable) {}
+
+ @Override
+ public boolean prefersCondensedTitle() {
+ return false;
+ }
+
+ @Override
+ public boolean showsIcon() {
+ return false;
+ }
+
+ @Override
+ public void setExpanded(boolean expanded) {
+ this.expanded = expanded;
+ updateVisibility();
+ }
+
+ @Override
+ public boolean isExpanded() {
+ return this.expanded;
+ }
+
+ @Override
+ public void setOnlyShowWhenExpanded(boolean onlyShowWhenExpanded) {
+ this.onlyShowWhenExpanded = onlyShowWhenExpanded;
+ updateVisibility();
+ }
+
+ @Override
+ public boolean isOnlyVisibleWhenExpanded() {
+ return this.onlyShowWhenExpanded;
+ }
+
+ public void updateVisibility() {
+ setVisibility(dividersEnabled && (expanded || !onlyShowWhenExpanded) ? VISIBLE : GONE);
+ }
+
+ public void setDividersEnabled(boolean dividersEnabled) {
+ this.dividersEnabled = dividersEnabled;
+ updateVisibility();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
diff --git a/lib/java/com/google/android/material/navigation/NavigationBarItemView.java b/lib/java/com/google/android/material/navigation/NavigationBarItemView.java
index b76a8e9c5e2..6c27b45292b 100644
--- a/lib/java/com/google/android/material/navigation/NavigationBarItemView.java
+++ b/lib/java/com/google/android/material/navigation/NavigationBarItemView.java
@@ -30,6 +30,7 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
@@ -60,7 +61,6 @@
import androidx.annotation.Px;
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleRes;
-import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
@@ -166,6 +166,8 @@ public abstract class NavigationBarItemView extends FrameLayout
private boolean expanded = false;
private boolean onlyShowWhenExpanded = false;
private boolean measurePaddingFromBaseline = false;
+ private boolean scaleLabelSizeWithFont = false;
+ private Rect itemActiveIndicatorExpandedPadding = new Rect();
public NavigationBarItemView(@NonNull Context context) {
super(context);
@@ -208,20 +210,37 @@ public NavigationBarItemView(@NonNull Context context) {
if (icon.getVisibility() == VISIBLE) {
tryUpdateBadgeBounds(icon);
}
- // If item icon gravity is start, we want to update the active indicator width in a layout
- // change listener to keep the active indicator size up to date with the content width.
LayoutParams lp = (LayoutParams) innerContentContainer.getLayoutParams();
int newWidth = right - left + lp.rightMargin + lp.leftMargin;
+ int newHeight = bottom - top + lp.topMargin + lp.bottomMargin;
+ // If item icon gravity is start, we want to update the active indicator width in a layout
+ // change listener to keep the active indicator size up to date with the content width.
if (itemIconGravity == ITEM_ICON_GRAVITY_START
- && activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT
- && newWidth != activeIndicatorView.getMeasuredWidth()) {
+ && activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT) {
+
LayoutParams indicatorParams = (LayoutParams) activeIndicatorView.getLayoutParams();
- int minWidth =
- min(
- activeIndicatorDesiredWidth,
- getMeasuredWidth() - (activeIndicatorMarginHorizontal * 2));
- indicatorParams.width = max(newWidth, minWidth);
- activeIndicatorView.setLayoutParams(indicatorParams);
+ boolean layoutParamsChanged = false;
+ if (activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT
+ && activeIndicatorView.getMeasuredWidth() != newWidth) {
+ int minWidth =
+ min(
+ activeIndicatorDesiredWidth,
+ getMeasuredWidth() - (activeIndicatorMarginHorizontal * 2));
+ indicatorParams.width = max(newWidth, minWidth);
+ layoutParamsChanged = true;
+ }
+
+ // We expect the active indicator height to be larger than the height of the
+ // inner content due to having a min height, but if it is smaller (for example due to
+ // the text content changing to be multi-line) it should encompass that
+ if (activeIndicatorView.getMeasuredHeight() < newHeight) {
+ indicatorParams.height = newHeight;
+ layoutParamsChanged = true;
+ }
+
+ if (layoutParamsChanged) {
+ activeIndicatorView.setLayoutParams(indicatorParams);
+ }
}
});
}
@@ -373,7 +392,10 @@ private void addDefaultExpandedLabelGroupViews() {
}
private void updateItemIconGravity() {
- int sideMargin = 0;
+ int leftMargin = 0;
+ int rightMargin = 0;
+ int topMargin = 0;
+ int bottomMargin = 0;
int sidePadding = 0;
badgeFixedEdge = BadgeDrawable.BADGE_FIXED_EDGE_START;
int verticalLabelGroupVisibility = VISIBLE;
@@ -383,9 +405,10 @@ private void updateItemIconGravity() {
if (expandedLabelGroup.getParent() == null) {
addDefaultExpandedLabelGroupViews();
}
- sideMargin =
- getResources()
- .getDimensionPixelSize(R.dimen.m3_navigation_item_leading_trailing_space);
+ leftMargin = itemActiveIndicatorExpandedPadding.left;
+ rightMargin = itemActiveIndicatorExpandedPadding.right;
+ topMargin = itemActiveIndicatorExpandedPadding.top;
+ bottomMargin = itemActiveIndicatorExpandedPadding.bottom;
badgeFixedEdge = BadgeDrawable.BADGE_FIXED_EDGE_END;
sidePadding = activeIndicatorExpandedMarginHorizontal;
verticalLabelGroupVisibility = GONE;
@@ -398,8 +421,10 @@ private void updateItemIconGravity() {
contentContainerLp.gravity = itemGravity;
FrameLayout.LayoutParams innerContentLp =
(LayoutParams) innerContentContainer.getLayoutParams();
- innerContentLp.leftMargin = sideMargin;
- innerContentLp.rightMargin = sideMargin;
+ innerContentLp.leftMargin = leftMargin;
+ innerContentLp.rightMargin = rightMargin;
+ innerContentLp.topMargin = topMargin;
+ innerContentLp.bottomMargin = bottomMargin;
setPadding(sidePadding, 0, sidePadding, 0);
updateActiveIndicatorLayoutParams(getWidth());
@@ -563,8 +588,8 @@ private void setLayoutConfigurationIconAndLabel(
itemGravity);
setViewMarginAndGravity(
innerContentContainer,
- 0,
- 0,
+ itemIconGravity == ITEM_ICON_GRAVITY_TOP ? 0 : itemActiveIndicatorExpandedPadding.top,
+ itemIconGravity == ITEM_ICON_GRAVITY_TOP ? 0 : itemActiveIndicatorExpandedPadding.bottom,
itemIconGravity == ITEM_ICON_GRAVITY_TOP
? Gravity.CENTER
: Gravity.START | Gravity.CENTER_VERTICAL);
@@ -765,7 +790,7 @@ public void setIcon(@Nullable Drawable iconDrawable) {
DrawableCompat.wrap(state == null ? iconDrawable : state.newDrawable()).mutate();
wrappedIconDrawable = iconDrawable;
if (iconTint != null) {
- DrawableCompat.setTintList(wrappedIconDrawable, iconTint);
+ wrappedIconDrawable.setTintList(iconTint);
}
}
this.icon.setImageDrawable(iconDrawable);
@@ -784,7 +809,7 @@ public boolean showsIcon() {
public void setIconTintList(@Nullable ColorStateList tint) {
iconTint = tint;
if (itemData != null && wrappedIconDrawable != null) {
- DrawableCompat.setTintList(wrappedIconDrawable, iconTint);
+ wrappedIconDrawable.setTintList(iconTint);
wrappedIconDrawable.invalidateSelf();
}
}
@@ -819,12 +844,28 @@ public void setMeasureBottomPaddingFromLabelBaseline(boolean measurePaddingFromB
requestLayout();
}
+ public void setLabelFontScalingEnabled(boolean scaleLabelSizeWithFont) {
+ this.scaleLabelSizeWithFont = scaleLabelSizeWithFont;
+ setTextAppearanceActive(textAppearanceActive);
+ setTextAppearanceInactive(textAppearanceInactive);
+ setHorizontalTextAppearanceActive(horizontalTextAppearanceActive);
+ setHorizontalTextAppearanceInactive(horizontalTextAppearanceInactive);
+ }
+
+ private void setTextAppearanceForLabel(TextView label, int textAppearance) {
+ if (scaleLabelSizeWithFont) {
+ TextViewCompat.setTextAppearance(label, textAppearance);
+ } else {
+ setTextAppearanceWithoutFontScaling(label, textAppearance);
+ }
+ }
+
private void updateInactiveLabelTextAppearance(
@Nullable TextView smallLabel, @StyleRes int textAppearanceInactive) {
if (smallLabel == null) {
return;
}
- setTextAppearanceWithoutFontScaling(smallLabel, textAppearanceInactive);
+ setTextAppearanceForLabel(smallLabel, textAppearanceInactive);
calculateTextScaleFactors();
smallLabel.setMinimumHeight(
MaterialResources.getUnscaledLineHeight(
@@ -841,7 +882,7 @@ private void updateActiveLabelTextAppearance(
if (largeLabel == null) {
return;
}
- setTextAppearanceWithoutFontScaling(largeLabel, textAppearanceActive);
+ setTextAppearanceForLabel(largeLabel, textAppearanceActive);
calculateTextScaleFactors();
largeLabel.setMinimumHeight(
MaterialResources.getUnscaledLineHeight(
@@ -911,6 +952,37 @@ private static void setTextAppearanceWithoutFontScaling(
}
}
+ public void setLabelMaxLines(int labelMaxLines) {
+ smallLabel.setMaxLines(labelMaxLines);
+ largeLabel.setMaxLines(labelMaxLines);
+ expandedSmallLabel.setMaxLines(labelMaxLines);
+ expandedLargeLabel.setMaxLines(labelMaxLines);
+
+ // Due to b/316260445 that was fixed in V+, text with ellipses may be cut off when centered
+ // due to letter spacing being miscalculated for the ellipses character. We only center the text
+ // in the following scenarios:
+ // 1. API level is greater than 34, OR
+ // 2. The text is not cut off by an ellipses
+ if (VERSION.SDK_INT > VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ smallLabel.setGravity(Gravity.CENTER);
+ largeLabel.setGravity(Gravity.CENTER);
+ } else if (labelMaxLines > 1) {
+ // If not single-line, remove the ellipses and center. Removing the ellipses is an unfortunate
+ // tradeoff due to this bug. We do not want to remove the ellipses for single-line text
+ // because centering text is not useful (since the textview is centered already) and we
+ // would rather keep the ellipses.
+ smallLabel.setEllipsize(null);
+ largeLabel.setEllipsize(null);
+ smallLabel.setGravity(Gravity.CENTER);
+ largeLabel.setGravity(Gravity.CENTER);
+ } else {
+ smallLabel.setGravity(Gravity.CENTER_VERTICAL);
+ largeLabel.setGravity(Gravity.CENTER_VERTICAL);
+ }
+
+ requestLayout();
+ }
+
public void setTextColor(@Nullable ColorStateList color) {
textColor = color;
if (color != null) {
@@ -936,8 +1008,7 @@ private void calculateTextScaleFactors() {
}
public void setItemBackground(int background) {
- Drawable backgroundDrawable =
- background == 0 ? null : ContextCompat.getDrawable(getContext(), background);
+ Drawable backgroundDrawable = background == 0 ? null : getContext().getDrawable(background);
setItemBackground(backgroundDrawable);
}
@@ -1104,13 +1175,22 @@ public void setActiveIndicatorExpandedHeight(int height) {
updateActiveIndicatorLayoutParams(getWidth());
}
+ /**
+ * Set the padding of the active indicator when it is expanded to wrap its content.
+ *
+ * @param itemActiveIndicatorExpandedPadding the Rect containing the padding information
+ */
+ public void setActiveIndicatorExpandedPadding(@NonNull Rect itemActiveIndicatorExpandedPadding) {
+ this.itemActiveIndicatorExpandedPadding = itemActiveIndicatorExpandedPadding;
+ }
+
/**
* Update the active indicators width and height for the available width and label visibility
* mode.
*
* @param availableWidth The total width of this item layout.
*/
- private void updateActiveIndicatorLayoutParams(int availableWidth) {
+ public void updateActiveIndicatorLayoutParams(int availableWidth) {
// Set width to the min of either the desired indicator width or the available width minus
// a horizontal margin.
if (availableWidth <= 0 && getVisibility() == VISIBLE) {
@@ -1133,7 +1213,8 @@ private void updateActiveIndicatorLayoutParams(int availableWidth) {
} else {
newWidth = min(activeIndicatorExpandedDesiredWidth, adjustedAvailableWidth);
}
- newHeight = activeIndicatorExpandedDesiredHeight;
+ newHeight =
+ max(activeIndicatorExpandedDesiredHeight, innerContentContainer.getMeasuredHeight());
}
LayoutParams indicatorParams = (LayoutParams) activeIndicatorView.getLayoutParams();
// If the label visibility is unlabeled, make the active indicator's height equal to its
diff --git a/lib/java/com/google/android/material/navigation/NavigationBarMenuBuilder.java b/lib/java/com/google/android/material/navigation/NavigationBarMenuBuilder.java
index 0d04cd6644e..92339d6851f 100644
--- a/lib/java/com/google/android/material/navigation/NavigationBarMenuBuilder.java
+++ b/lib/java/com/google/android/material/navigation/NavigationBarMenuBuilder.java
@@ -48,8 +48,9 @@ public class NavigationBarMenuBuilder {
}
/**
- * Returns total number of items in the menu, including submenus and submenu items. For example,
- * a Menu with items {Item, SubMenu, SubMenuItem} would have a size of 3.
+ * Returns total number of items in the menu, including submenus, submenu items, and dividers. For
+ * example, a Menu with items {Item, Divider, SubMenu, SubMenuItem, Divider} would have a size of
+ * 5.
*/
public int size() {
return items.size();
@@ -103,8 +104,13 @@ public void refreshItems() {
visibleMainItemCount = 0;
for (int i = 0; i < menuBuilder.size(); i++) {
MenuItem item = menuBuilder.getItem(i);
- items.add(item);
if (item.hasSubMenu()) {
+ if (!items.isEmpty()
+ && !(items.get(items.size() - 1) instanceof DividerMenuItem)
+ && item.isVisible()) {
+ items.add(new DividerMenuItem());
+ }
+ items.add(item);
SubMenu subMenu = item.getSubMenu();
for (int j = 0; j < subMenu.size(); j++) {
MenuItem submenuItem = subMenu.getItem(j);
@@ -117,7 +123,9 @@ public void refreshItems() {
visibleContentItemCount++;
}
}
+ items.add(new DividerMenuItem());
} else {
+ items.add(item);
contentItemCount++;
if (item.isVisible()) {
visibleContentItemCount++;
@@ -125,5 +133,9 @@ public void refreshItems() {
}
}
}
+
+ if (!items.isEmpty() && items.get(items.size()-1) instanceof DividerMenuItem) {
+ items.remove(items.size()-1);
+ }
}
}
diff --git a/lib/java/com/google/android/material/navigation/NavigationBarMenuView.java b/lib/java/com/google/android/material/navigation/NavigationBarMenuView.java
index 6c7d1b2b073..a1ad151ed92 100644
--- a/lib/java/com/google/android/material/navigation/NavigationBarMenuView.java
+++ b/lib/java/com/google/android/material/navigation/NavigationBarMenuView.java
@@ -24,6 +24,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.view.menu.MenuBuilder;
@@ -122,6 +123,8 @@ public abstract class NavigationBarMenuView extends ViewGroup implements MenuVie
private NavigationBarPresenter presenter;
private NavigationBarMenuBuilder menu;
private boolean measurePaddingFromLabelBaseline;
+ private boolean scaleLabelWithFont;
+ private int labelMaxLines = 1;
private int itemPoolSize = 0;
private boolean expanded;
@@ -129,6 +132,8 @@ public abstract class NavigationBarMenuView extends ViewGroup implements MenuVie
private static final int DEFAULT_COLLAPSED_MAX_COUNT = 7;
private int collapsedMaxItemCount = DEFAULT_COLLAPSED_MAX_COUNT;
+ private boolean dividersEnabled = false;
+ private final Rect itemActiveIndicatorExpandedPadding = new Rect();
public NavigationBarMenuView(@NonNull Context context) {
super(context);
@@ -182,7 +187,7 @@ public void setCheckedItem(@NonNull MenuItem checkedItem) {
return;
}
// Unset the previous checked item
- if (this.checkedItem != null) {
+ if (this.checkedItem != null && this.checkedItem.isChecked()) {
this.checkedItem.setChecked(false);
}
checkedItem.setChecked(true);
@@ -502,6 +507,38 @@ public void setMeasurePaddingFromLabelBaseline(boolean measurePaddingFromLabelBa
}
}
+ public void setLabelFontScalingEnabled(boolean scaleLabelWithFont) {
+ this.scaleLabelWithFont = scaleLabelWithFont;
+ if (buttons != null) {
+ for (NavigationBarMenuItemView item : buttons) {
+ if (item instanceof NavigationBarItemView) {
+ ((NavigationBarItemView) item)
+ .setLabelFontScalingEnabled(scaleLabelWithFont);
+ }
+ }
+ }
+ }
+
+ public boolean getScaleLabelTextWithFont() {
+ return scaleLabelWithFont;
+ }
+
+ public void setLabelMaxLines(int labelMaxLines) {
+ this.labelMaxLines = labelMaxLines;
+ if (buttons != null) {
+ for (NavigationBarMenuItemView item : buttons) {
+ if (item instanceof NavigationBarItemView) {
+ ((NavigationBarItemView) item)
+ .setLabelMaxLines(labelMaxLines);
+ }
+ }
+ }
+ }
+
+ public int getLabelMaxLines() {
+ return labelMaxLines;
+ }
+
/**
* Get the distance between the item's active indicator container and the label.
*/
@@ -769,6 +806,30 @@ public void setItemActiveIndicatorExpandedMarginHorizontal(@Px int marginHorizon
}
}
+ /**
+ * Set the padding of the expanded active indicator wrapping the content.
+ *
+ * @param paddingLeft The left padding, in pixels.
+ * @param paddingTop The top padding, in pixels.
+ * @param paddingRight The right padding, in pixels.
+ * @param paddingBottom The bottom padding, in pixels.
+ */
+ public void setItemActiveIndicatorExpandedPadding(int paddingLeft, int paddingTop,
+ int paddingRight, int paddingBottom) {
+ itemActiveIndicatorExpandedPadding.left = paddingLeft;
+ itemActiveIndicatorExpandedPadding.top = paddingTop;
+ itemActiveIndicatorExpandedPadding.right = paddingRight;
+ itemActiveIndicatorExpandedPadding.bottom = paddingBottom;
+ if (buttons != null) {
+ for (NavigationBarMenuItemView item : buttons) {
+ if (item instanceof NavigationBarItemView) {
+ ((NavigationBarItemView) item)
+ .setActiveIndicatorExpandedPadding(itemActiveIndicatorExpandedPadding);
+ }
+ }
+ }
+ }
+
/**
* Get the {@link ShapeAppearanceModel} of the active indicator drawable.
*
@@ -1062,6 +1123,7 @@ private NavigationBarItemView createMenuItem(
presenter.setUpdateSuspended(false);
NavigationBarItemView child = getNewItem();
child.setShifting(shifting);
+ child.setLabelMaxLines(labelMaxLines);
child.setIconTintList(itemIconTint);
child.setIconSize(itemIconSize);
// Set the text color the default, then look for another text color in order of precedence.
@@ -1079,6 +1141,7 @@ private NavigationBarItemView createMenuItem(
child.setItemPaddingBottom(itemPaddingBottom);
}
child.setMeasureBottomPaddingFromLabelBaseline(measurePaddingFromLabelBaseline);
+ child.setLabelFontScalingEnabled(scaleLabelWithFont);
if (itemActiveIndicatorLabelPadding != NO_PADDING) {
child.setActiveIndicatorLabelPadding(itemActiveIndicatorLabelPadding);
}
@@ -1091,6 +1154,7 @@ private NavigationBarItemView createMenuItem(
child.setActiveIndicatorExpandedHeight(itemActiveIndicatorExpandedHeight);
child.setActiveIndicatorMarginHorizontal(itemActiveIndicatorMarginHorizontal);
child.setItemGravity(itemGravity);
+ child.setActiveIndicatorExpandedPadding(itemActiveIndicatorExpandedPadding);
child.setActiveIndicatorExpandedMarginHorizontal(itemActiveIndicatorExpandedMarginHorizontal);
child.setActiveIndicatorDrawable(createItemActiveIndicatorDrawable());
child.setActiveIndicatorResizeable(itemActiveIndicatorResizeable);
@@ -1150,7 +1214,12 @@ public void buildMenuView() {
for (int i = 0; i < menuSize; i++) {
MenuItem menuItem = menu.getItemAt(i);
NavigationBarMenuItemView child;
- if (menuItem.hasSubMenu()) {
+ if (menuItem instanceof DividerMenuItem) {
+ // Add a divider
+ child = new NavigationBarDividerView(getContext());
+ child.setOnlyShowWhenExpanded(true);
+ ((NavigationBarDividerView) child).setDividersEnabled(dividersEnabled);
+ } else if (menuItem.hasSubMenu()) {
if (nextSubheaderItemCount > 0) {
// We do not support submenus inside submenus. If there is still subheader items to be
// instantiated, we should not have another submenu.
@@ -1177,7 +1246,9 @@ public void buildMenuView() {
i, (MenuItemImpl) menuItem, shifting, collapsedItemsSoFar >= collapsedMaxItemCount);
collapsedItemsSoFar++;
}
- if (menuItem.isCheckable() && selectedItemPosition == NO_SELECTED_ITEM) {
+ if (!(menuItem instanceof DividerMenuItem)
+ && menuItem.isCheckable()
+ && selectedItemPosition == NO_SELECTED_ITEM) {
selectedItemPosition = i;
}
buttons[i] = child;
@@ -1192,11 +1263,19 @@ private boolean isMenuStructureSame() {
return false;
}
for (int i = 0; i < buttons.length; i++) {
- if (menu.getItemAt(i).hasSubMenu()
- ? buttons[i] instanceof NavigationBarItemView
- : buttons[i] instanceof NavigationBarSubheaderView) {
+ // If the menu item is a divider but the existing item is not a divider, return false
+ if (menu.getItemAt(i) instanceof DividerMenuItem
+ && !(buttons[i] instanceof NavigationBarDividerView)) {
return false;
}
+ boolean incorrectSubheaderType =
+ menu.getItemAt(i).hasSubMenu() && !(buttons[i] instanceof NavigationBarSubheaderView);
+ boolean incorrectItemType =
+ !menu.getItemAt(i).hasSubMenu() && !(buttons[i] instanceof NavigationBarItemView);
+ if (!(menu.getItemAt(i) instanceof DividerMenuItem)
+ && (incorrectSubheaderType || incorrectItemType)) {
+ return false;
+ }
}
return true;
}
@@ -1242,7 +1321,9 @@ public void updateMenuView() {
itemView.setItemGravity(itemGravity);
itemView.setShifting(shifting);
}
- buttons[i].initialize((MenuItemImpl) menu.getItemAt(i), 0);
+ if (menu.getItemAt(i) instanceof MenuItemImpl) {
+ buttons[i].initialize((MenuItemImpl) menu.getItemAt(i), 0);
+ }
presenter.setUpdateSuspended(false);
}
}
@@ -1255,6 +1336,20 @@ private NavigationBarItemView getNewItem() {
return item;
}
+ public void setSubmenuDividersEnabled(boolean dividersEnabled) {
+ if (this.dividersEnabled == dividersEnabled) {
+ return;
+ }
+ this.dividersEnabled = dividersEnabled;
+ if (buttons != null) {
+ for (NavigationBarMenuItemView itemView : buttons) {
+ if (itemView instanceof NavigationBarDividerView) {
+ ((NavigationBarDividerView) itemView).setDividersEnabled(dividersEnabled);
+ }
+ }
+ }
+ }
+
public void setCollapsedMaxItemCount(int collapsedMaxCount) {
this.collapsedMaxItemCount = collapsedMaxCount;
}
@@ -1416,4 +1511,14 @@ private void validateMenuItemId(int viewId) {
throw new IllegalArgumentException(viewId + " is not a valid view id");
}
}
+
+ public void updateActiveIndicator(int availableWidth) {
+ if (buttons != null) {
+ for (NavigationBarMenuItemView item : buttons) {
+ if (item instanceof NavigationBarItemView) {
+ ((NavigationBarItemView) item).updateActiveIndicatorLayoutParams(availableWidth);
+ }
+ }
+ }
+ }
}
diff --git a/lib/java/com/google/android/material/navigation/NavigationBarView.java b/lib/java/com/google/android/material/navigation/NavigationBarView.java
index d63ad267547..839478bd9d5 100644
--- a/lib/java/com/google/android/material/navigation/NavigationBarView.java
+++ b/lib/java/com/google/android/material/navigation/NavigationBarView.java
@@ -51,7 +51,6 @@
import androidx.annotation.Px;
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleRes;
-import androidx.core.graphics.drawable.DrawableCompat;
import androidx.customview.view.AbsSavedState;
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.drawable.DrawableUtils;
@@ -301,7 +300,7 @@ public NavigationBarView(
ColorStateList backgroundTint =
MaterialResources.getColorStateList(
context, attributes, R.styleable.NavigationBarView_backgroundTint);
- DrawableCompat.setTintList(getBackground().mutate(), backgroundTint);
+ getBackground().mutate().setTintList(backgroundTint);
setLabelVisibilityMode(
attributes.getInteger(
@@ -327,6 +326,12 @@ public NavigationBarView(
setMeasureBottomPaddingFromLabelBaseline(attributes.getBoolean(
R.styleable.NavigationBarView_measureBottomPaddingFromLabelBaseline, true));
+ setLabelFontScalingEnabled(
+ attributes.getBoolean(R.styleable.NavigationBarView_labelFontScalingEnabled, false));
+
+ setLabelMaxLines(
+ attributes.getInteger(R.styleable.NavigationBarView_labelMaxLines, 1));
+
int activeIndicatorStyleResId =
attributes.getResourceId(R.styleable.NavigationBarView_itemActiveIndicatorStyle, 0);
@@ -383,6 +388,29 @@ public NavigationBarView(
itemActiveIndicatorMarginHorizontal);
setItemActiveIndicatorExpandedMarginHorizontal(itemActiveIndicatorExpandedMarginHorizontal);
+ int activeIndicatorExpandedDefaultStartEndPadding = getResources()
+ .getDimensionPixelSize(R.dimen.m3_navigation_item_leading_trailing_space);
+ int activeIndicatorExpandedStartPadding =
+ activeIndicatorAttributes.getDimensionPixelOffset(
+ R.styleable.NavigationBarActiveIndicator_expandedActiveIndicatorPaddingStart,
+ activeIndicatorExpandedDefaultStartEndPadding);
+ int activeIndicatorExpandedEndPadding =
+ activeIndicatorAttributes.getDimensionPixelOffset(
+ R.styleable.NavigationBarActiveIndicator_expandedActiveIndicatorPaddingEnd,
+ activeIndicatorExpandedDefaultStartEndPadding);
+
+ setItemActiveIndicatorExpandedPadding(
+ getLayoutDirection() == LAYOUT_DIRECTION_RTL
+ ? activeIndicatorExpandedEndPadding : activeIndicatorExpandedStartPadding,
+ activeIndicatorAttributes.getDimensionPixelOffset(
+ R.styleable.NavigationBarActiveIndicator_expandedActiveIndicatorPaddingTop,
+ 0),
+ getLayoutDirection() == LAYOUT_DIRECTION_RTL
+ ? activeIndicatorExpandedStartPadding : activeIndicatorExpandedEndPadding,
+ activeIndicatorAttributes.getDimensionPixelOffset(
+ R.styleable.NavigationBarActiveIndicator_expandedActiveIndicatorPaddingBottom,
+ 0));
+
ColorStateList itemActiveIndicatorColor =
MaterialResources.getColorStateList(
context,
@@ -708,6 +736,34 @@ private void setMeasureBottomPaddingFromLabelBaseline(boolean measurePaddingFrom
menuView.setMeasurePaddingFromLabelBaseline(measurePaddingFromBaseline);
}
+ /**
+ * Sets whether or not the label text should scale with the system font size.
+ */
+ public void setLabelFontScalingEnabled(boolean labelFontScalingEnabled) {
+ menuView.setLabelFontScalingEnabled(labelFontScalingEnabled);
+ }
+
+ /**
+ * Returns whether or not the label text should scale with the system font size.
+ */
+ public boolean getScaleLabelTextWithFont() {
+ return menuView.getScaleLabelTextWithFont();
+ }
+
+ /**
+ * Set the max lines limit for the label text.
+ */
+ public void setLabelMaxLines(int labelMaxLines) {
+ menuView.setLabelMaxLines(labelMaxLines);
+ }
+
+ /**
+ * Returns the max lines limit for the label text.
+ */
+ public int getLabelMaxLines(int labelMaxLines) {
+ return menuView.getLabelMaxLines();
+ }
+
/**
* Set the distance between the active indicator container and the item's label.
*/
@@ -825,7 +881,7 @@ public void setItemActiveIndicatorMarginHorizontal(@Px int horizontalMargin) {
* @see #getItemGravity()
*/
public void setItemGravity(@ItemGravity int itemGravity) {
- if (menuView.getItemIconGravity() != itemGravity) {
+ if (menuView.getItemGravity() != itemGravity) {
menuView.setItemGravity(itemGravity);
presenter.updateMenuView(false);
}
@@ -907,6 +963,19 @@ public void setItemActiveIndicatorExpandedMarginHorizontal(@Px int horizontalMar
menuView.setItemActiveIndicatorExpandedMarginHorizontal(horizontalMargin);
}
+ /**
+ * Set the padding of the expanded active indicator wrapping the content.
+ *
+ * @param paddingLeft The left padding, in pixels.
+ * @param paddingTop The top padding, in pixels.
+ * @param paddingRight The right padding, in pixels.
+ * @param paddingBottom The bottom padding, in pixels.
+ */
+ public void setItemActiveIndicatorExpandedPadding(
+ @Px int paddingLeft, @Px int paddingTop, @Px int paddingRight, @Px int paddingBottom) {
+ menuView.setItemActiveIndicatorExpandedPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+ }
+
/**
* Get the {@link ShapeAppearanceModel} of the active indicator drawable.
*
diff --git a/lib/java/com/google/android/material/navigation/NavigationView.java b/lib/java/com/google/android/material/navigation/NavigationView.java
index 89a83328ad3..aaaaa23022c 100644
--- a/lib/java/com/google/android/material/navigation/NavigationView.java
+++ b/lib/java/com/google/android/material/navigation/NavigationView.java
@@ -66,7 +66,6 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
-import androidx.core.content.ContextCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.customview.view.AbsSavedState;
import androidx.drawerlayout.widget.DrawerLayout;
@@ -722,7 +721,7 @@ public Drawable getItemBackground() {
* @attr ref R.styleable#NavigationView_itemBackground
*/
public void setItemBackgroundResource(@DrawableRes int resId) {
- setItemBackground(ContextCompat.getDrawable(getContext(), resId));
+ setItemBackground(getContext().getDrawable(resId));
}
/**
diff --git a/lib/java/com/google/android/material/navigation/res-public/values/public.xml b/lib/java/com/google/android/material/navigation/res-public/values/public.xml
index 26219a99858..3e897ec0001 100644
--- a/lib/java/com/google/android/material/navigation/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/navigation/res-public/values/public.xml
@@ -66,5 +66,13 @@
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/navigation/res/layout/m3_navigation_menu_divider.xml b/lib/java/com/google/android/material/navigation/res/layout/m3_navigation_menu_divider.xml
new file mode 100644
index 00000000000..0eab193e9fe
--- /dev/null
+++ b/lib/java/com/google/android/material/navigation/res/layout/m3_navigation_menu_divider.xml
@@ -0,0 +1,25 @@
+
+
+
diff --git a/lib/java/com/google/android/material/navigation/res/layout/m3_navigation_menu_subheader.xml b/lib/java/com/google/android/material/navigation/res/layout/m3_navigation_menu_subheader.xml
index 12a62e0e2f3..f797a7644e4 100644
--- a/lib/java/com/google/android/material/navigation/res/layout/m3_navigation_menu_subheader.xml
+++ b/lib/java/com/google/android/material/navigation/res/layout/m3_navigation_menu_subheader.xml
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/m3_navigation_subheader_horizontal_padding"
android:paddingVertical="@dimen/m3_navigation_subheader_vertical_padding"
- android:layout_marginStart="@dimen/m3_navigation_subheader_horizontal_margin"
+ android:layout_marginStart="@dimen/m3_navigation_content_horizontal_margin"
android:layout_marginTop="@dimen/m3_navigation_subheader_top_margin"
android:gravity="center_vertical|start"
android:ellipsize="end"
diff --git a/lib/java/com/google/android/material/navigation/res/values/attrs.xml b/lib/java/com/google/android/material/navigation/res/values/attrs.xml
index 1080bd0c92a..d5208181a9a 100644
--- a/lib/java/com/google/android/material/navigation/res/values/attrs.xml
+++ b/lib/java/com/google/android/material/navigation/res/values/attrs.xml
@@ -118,6 +118,12 @@
+
+
+
+
+
+
@@ -145,6 +151,18 @@
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/navigation/res/values/dimens.xml b/lib/java/com/google/android/material/navigation/res/values/dimens.xml
index 50e91b6d3c3..2d901c08fcf 100644
--- a/lib/java/com/google/android/material/navigation/res/values/dimens.xml
+++ b/lib/java/com/google/android/material/navigation/res/values/dimens.xml
@@ -50,7 +50,9 @@
16dp8dp12dp
- 20dp
+ 20dp
+ 8dp
+ 3dp12sp14sp
diff --git a/lib/java/com/google/android/material/navigation/res/values/tokens.xml b/lib/java/com/google/android/material/navigation/res/values/tokens.xml
index 0f1f887e415..91921dc3b67 100644
--- a/lib/java/com/google/android/material/navigation/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/navigation/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/navigationrail/NavigationRailFrameLayout.java b/lib/java/com/google/android/material/navigationrail/NavigationRailFrameLayout.java
index b54a0997627..dea19af5633 100644
--- a/lib/java/com/google/android/material/navigationrail/NavigationRailFrameLayout.java
+++ b/lib/java/com/google/android/material/navigationrail/NavigationRailFrameLayout.java
@@ -55,6 +55,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int totalHeaderHeight = 0;
View menuView = getChildAt(0);
int menuHeightSpec = heightMeasureSpec;
+ int height = MeasureSpec.getSize(heightMeasureSpec);
// If there's more than one child, the header should be first
if (childCount > 1) {
@@ -64,7 +65,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
LayoutParams headerLp = (LayoutParams) headerView.getLayoutParams();
totalHeaderHeight =
headerView.getMeasuredHeight() + headerLp.bottomMargin + headerLp.topMargin;
- int maxMenuHeight = getMeasuredHeight() - totalHeaderHeight - paddingTop;
+ int maxMenuHeight = height - totalHeaderHeight - paddingTop;
// Measure menu
menuView = getChildAt(1);
@@ -77,7 +78,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
LayoutParams menuLp = (LayoutParams) menuView.getLayoutParams();
measureChild(menuView, widthMeasureSpec, menuHeightSpec);
int totalMenuHeight = menuView.getMeasuredHeight() + menuLp.bottomMargin + menuLp.topMargin;
- int totalHeight = max(getMeasuredHeight(), paddingTop + totalHeaderHeight + totalMenuHeight);
+ int totalHeight = max(height, paddingTop + totalHeaderHeight + totalMenuHeight);
setMeasuredDimension(getMeasuredWidth(), totalHeight);
}
diff --git a/lib/java/com/google/android/material/navigationrail/NavigationRailMenuView.java b/lib/java/com/google/android/material/navigationrail/NavigationRailMenuView.java
index f7d2df73ee7..3098bf38fb4 100644
--- a/lib/java/com/google/android/material/navigationrail/NavigationRailMenuView.java
+++ b/lib/java/com/google/android/material/navigationrail/NavigationRailMenuView.java
@@ -32,7 +32,6 @@
import androidx.annotation.RestrictTo;
import com.google.android.material.navigation.NavigationBarItemView;
import com.google.android.material.navigation.NavigationBarMenuView;
-import com.google.android.material.navigation.NavigationBarSubheaderView;
/** @hide For internal use only. */
@RestrictTo(LIBRARY_GROUP)
@@ -140,7 +139,7 @@ private int measureSharedChildHeights(
int totalHeight = 0;
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
- if (child instanceof NavigationBarSubheaderView) {
+ if (!(child instanceof NavigationBarItemView)) {
int subheaderHeight = measureChildHeight(child, widthMeasureSpec, subheaderHeightSpec);
maxHeight -= subheaderHeight;
totalHeight += subheaderHeight;
@@ -175,8 +174,8 @@ private int measureSharedChildHeights(
}
private int measureChildHeight(View child, int widthMeasureSpec, int heightMeasureSpec) {
+ child.measure(widthMeasureSpec, heightMeasureSpec);
if (child.getVisibility() != GONE) {
- child.measure(widthMeasureSpec, heightMeasureSpec);
return child.getMeasuredHeight();
}
diff --git a/lib/java/com/google/android/material/navigationrail/NavigationRailView.java b/lib/java/com/google/android/material/navigationrail/NavigationRailView.java
index 7107eec6d62..e32ff9f96f1 100644
--- a/lib/java/com/google/android/material/navigationrail/NavigationRailView.java
+++ b/lib/java/com/google/android/material/navigationrail/NavigationRailView.java
@@ -26,11 +26,13 @@
import static java.lang.Math.min;
import android.animation.TimeInterpolator;
+import android.annotation.SuppressLint;
import android.content.Context;
import androidx.appcompat.widget.TintTypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.PathInterpolator;
@@ -52,6 +54,7 @@
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.internal.ViewUtils;
import com.google.android.material.internal.ViewUtils.RelativePadding;
+import com.google.android.material.navigation.NavigationBarDividerView;
import com.google.android.material.navigation.NavigationBarItemView;
import com.google.android.material.navigation.NavigationBarView;
import com.google.android.material.resources.MaterialResources;
@@ -133,6 +136,7 @@ public class NavigationRailView extends NavigationBarView {
private final int minExpandedWidth;
private final int maxExpandedWidth;
private final boolean scrollingEnabled;
+ private boolean submenuDividersEnabled;
@Nullable private View headerView;
@Nullable private Boolean paddingTopSystemWindowInsets = null;
@Nullable private Boolean paddingBottomSystemWindowInsets = null;
@@ -169,22 +173,10 @@ public NavigationRailView(
// Ensure we are using the correctly themed context rather than the context that was passed in.
context = getContext();
- minExpandedWidth =
- getContext()
- .getResources()
- .getDimensionPixelSize(R.dimen.m3_navigation_rail_min_expanded_width);
- maxExpandedWidth =
- getContext()
- .getResources()
- .getDimensionPixelSize(R.dimen.m3_navigation_rail_max_expanded_width);
expandedItemSpacing =
getContext()
.getResources()
.getDimensionPixelSize(R.dimen.m3_navigation_rail_expanded_item_spacing);
- expandedItemMinHeight =
- getContext()
- .getResources()
- .getDimensionPixelSize(R.dimen.m3_navigation_rail_expanded_item_min_height);
expandedItemGravity = ITEM_GRAVITY_START_CENTER;
expandedIconGravity = ITEM_ICON_GRAVITY_START;
@@ -202,6 +194,7 @@ public NavigationRailView(
getResources().getDimensionPixelSize(R.dimen.mtrl_navigation_rail_margin));
scrollingEnabled =
attributes.getBoolean(R.styleable.NavigationRailView_scrollingEnabled, false);
+ setSubmenuDividersEnabled(attributes.getBoolean(R.styleable.NavigationRailView_submenuDividersEnabled, false));
addContentContainer();
@@ -213,11 +206,31 @@ public NavigationRailView(
setMenuGravity(
attributes.getInt(R.styleable.NavigationRailView_menuGravity, DEFAULT_MENU_GRAVITY));
- if (attributes.hasValue(R.styleable.NavigationRailView_itemMinHeight)) {
- setCollapsedItemMinimumHeight(
- attributes.getDimensionPixelSize(
- R.styleable.NavigationRailView_itemMinHeight, NO_ITEM_MINIMUM_HEIGHT));
+ int collapsedItemMinHeight = attributes.getDimensionPixelSize(
+ R.styleable.NavigationRailView_itemMinHeight, NO_ITEM_MINIMUM_HEIGHT);
+ int expandedItemMinHeight = attributes.getDimensionPixelSize(
+ R.styleable.NavigationRailView_itemMinHeight, NO_ITEM_MINIMUM_HEIGHT);
+
+ if (attributes.hasValue(R.styleable.NavigationRailView_collapsedItemMinHeight)) {
+ collapsedItemMinHeight = attributes.getDimensionPixelSize(
+ R.styleable.NavigationRailView_collapsedItemMinHeight, NO_ITEM_MINIMUM_HEIGHT);
+ }
+ if (attributes.hasValue(R.styleable.NavigationRailView_expandedItemMinHeight)) {
+ expandedItemMinHeight = attributes.getDimensionPixelSize(
+ R.styleable.NavigationRailView_expandedItemMinHeight, NO_ITEM_MINIMUM_HEIGHT);
}
+ setCollapsedItemMinimumHeight(collapsedItemMinHeight);
+ setExpandedItemMinimumHeight(expandedItemMinHeight);
+ minExpandedWidth = attributes.getDimensionPixelSize(
+ R.styleable.NavigationRailView_expandedMinWidth,
+ context
+ .getResources()
+ .getDimensionPixelSize(R.dimen.m3_navigation_rail_min_expanded_width));
+ maxExpandedWidth = attributes.getDimensionPixelSize(
+ R.styleable.NavigationRailView_expandedMaxWidth,
+ context
+ .getResources()
+ .getDimensionPixelSize(R.dimen.m3_navigation_rail_max_expanded_width));
if (attributes.hasValue(R.styleable.NavigationRailView_paddingTopSystemWindowInsets)) {
paddingTopSystemWindowInsets =
@@ -406,8 +419,9 @@ public WindowInsetsCompat onApplyWindowInsets(
@NonNull WindowInsetsCompat insets,
@NonNull RelativePadding initialPadding) {
// Apply the top, bottom, and start padding for a start edge aligned
- // NavigationRailView to dodge the system status and navigation bars
+ // NavigationRailView to dodge the system status/navigation bars and display cutouts
Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());
+ Insets displayCutoutInsets = insets.getInsets(WindowInsetsCompat.Type.displayCutout());
if (shouldApplyWindowInsetPadding(paddingTopSystemWindowInsets)) {
initialPadding.top += systemBarInsets.top;
}
@@ -415,8 +429,11 @@ public WindowInsetsCompat onApplyWindowInsets(
initialPadding.bottom += systemBarInsets.bottom;
}
if (shouldApplyWindowInsetPadding(paddingStartSystemWindowInsets)) {
- initialPadding.start +=
- ViewUtils.isLayoutRtl(view) ? systemBarInsets.right : systemBarInsets.left;
+ if (ViewUtils.isLayoutRtl(view)) {
+ initialPadding.start += max(systemBarInsets.right, displayCutoutInsets.right);
+ } else {
+ initialPadding.start += max(systemBarInsets.left, displayCutoutInsets.left);
+ }
}
initialPadding.applyToView(view);
return insets;
@@ -439,7 +456,7 @@ private int getMaxChildWidth() {
int maxChildWidth = 0;
for (int i = 0; i < childCount; i++) {
View child = getNavigationRailMenuView().getChildAt(i);
- if (child.getVisibility() != GONE) {
+ if (child.getVisibility() != GONE && !(child instanceof NavigationBarDividerView)) {
maxChildWidth = max(maxChildWidth, child.getMeasuredWidth());
}
}
@@ -457,6 +474,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}
// Measure properly with the max child width
minWidthSpec = makeExpandedWidthMeasureSpec(widthMeasureSpec, getMaxChildWidth());
+ // If the active indicator is match_parent, it should be notified to update its width
+ if (getItemActiveIndicatorExpandedWidth() == MATCH_PARENT) {
+ getNavigationRailMenuView().updateActiveIndicator(MeasureSpec.getSize(minWidthSpec));
+ }
}
super.onMeasure(minWidthSpec, heightMeasureSpec);
// If the content container is measured to be less than the measured height of the nav rail,
@@ -568,6 +589,50 @@ public void setCollapsedItemMinimumHeight(@Px int minHeight) {
}
}
+ /**
+ * Gets the minimum height of a navigation rail menu item when the navigation rail is collapsed.
+ */
+ public int getCollapsedItemMinimumHeight() {
+ return collapsedItemMinHeight;
+ }
+
+ /**
+ * Sets the minimum height of a navigation rail menu item when the navigation rail is expanded.
+ *
+ * @param minHeight the min height of the item when the nav rail is collapsed
+ */
+ public void setExpandedItemMinimumHeight(@Px int minHeight) {
+ expandedItemMinHeight = minHeight;
+ if (expanded) {
+ ((NavigationRailMenuView) getMenuView()).setItemMinimumHeight(minHeight);
+ }
+ }
+
+ /**
+ * Gets the minimum height of a navigation rail menu item when the navigation rail is expanded.
+ */
+ public int getExpandedItemMinimumHeight() {
+ return expandedItemMinHeight;
+ }
+
+ /**
+ * Set whether or not to enable the dividers which go between each subgroup in the menu.
+ */
+ public void setSubmenuDividersEnabled(boolean submenuDividersEnabled) {
+ if (this.submenuDividersEnabled == submenuDividersEnabled) {
+ return;
+ }
+ this.submenuDividersEnabled = submenuDividersEnabled;
+ getNavigationRailMenuView().setSubmenuDividersEnabled(submenuDividersEnabled);
+ }
+
+ /**
+ * Get whether or not to enable the dividers which go between each subgroup in the menu.
+ */
+ public boolean getSubmenuDividersEnabled() {
+ return submenuDividersEnabled;
+ }
+
/**
* Set the padding in between the navigation rail menu items.
*/
@@ -679,4 +744,12 @@ private void addContentContainer() {
public boolean shouldAddMenuView() {
return true;
}
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
+ super.onTouchEvent(event);
+ // Consume all events to avoid views under the NavigationRailView from receiving touch events.
+ return true;
+ }
}
diff --git a/lib/java/com/google/android/material/navigationrail/res-public/values/public.xml b/lib/java/com/google/android/material/navigationrail/res-public/values/public.xml
index e592eb6b94d..c868d2d2803 100644
--- a/lib/java/com/google/android/material/navigationrail/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/navigationrail/res-public/values/public.xml
@@ -18,12 +18,16 @@
+
+
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_icon_tint.xml b/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_icon_tint.xml
index d5fd948cb8c..c43fcc06fd6 100644
--- a/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_icon_tint.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_icon_tint.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
-
-
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_label_tint.xml b/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_label_tint.xml
index 1018aaa840c..9c054f2cff4 100644
--- a/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_label_tint.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_item_with_indicator_label_tint.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
-
-
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_ripple_color_selector.xml b/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_ripple_color_selector.xml
index a64bfbc252a..58005b8c3f8 100644
--- a/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_ripple_color_selector.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/color/m3_navigation_rail_ripple_color_selector.xml
@@ -17,14 +17,26 @@
-
-
-
+
+
+
-
-
-
+
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_icon_tint.xml b/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_icon_tint.xml
new file mode 100644
index 00000000000..78bef7837a0
--- /dev/null
+++ b/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_icon_tint.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_label_tint.xml b/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_label_tint.xml
new file mode 100644
index 00000000000..0c6b43684b1
--- /dev/null
+++ b/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_label_tint.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_ripple_tint.xml b/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_ripple_tint.xml
new file mode 100644
index 00000000000..8d6ba766149
--- /dev/null
+++ b/lib/java/com/google/android/material/navigationrail/res/color/m3expressive_nav_rail_item_ripple_tint.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml b/lib/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml
index a407643f3bc..30a44a2c8de 100644
--- a/lib/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml
@@ -72,7 +72,7 @@
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:ellipsize="end"
- android:maxLines="1"
+ android:gravity="center_vertical"
android:paddingStart="@dimen/m3_navigation_rail_label_padding_horizontal"
android:paddingEnd="@dimen/m3_navigation_rail_label_padding_horizontal"
android:textSize="@dimen/mtrl_navigation_rail_text_size" />
@@ -81,8 +81,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
+ android:gravity="center_vertical"
android:ellipsize="end"
- android:maxLines="1"
android:textSize="@dimen/mtrl_navigation_rail_active_text_size"
android:paddingStart="@dimen/m3_navigation_rail_label_padding_horizontal"
android:paddingEnd="@dimen/m3_navigation_rail_label_padding_horizontal"
diff --git a/lib/java/com/google/android/material/navigationrail/res/values/attrs.xml b/lib/java/com/google/android/material/navigationrail/res/values/attrs.xml
index ef396190adf..82e83ee8c47 100644
--- a/lib/java/com/google/android/material/navigationrail/res/values/attrs.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/values/attrs.xml
@@ -19,9 +19,19 @@
-
+
+
+
+
+
+
+
+
+
@@ -61,6 +71,8 @@
+
+
diff --git a/lib/java/com/google/android/material/navigationrail/res/values/dimens.xml b/lib/java/com/google/android/material/navigationrail/res/values/dimens.xml
index 8e158f81ef9..3c21836c2ee 100644
--- a/lib/java/com/google/android/material/navigationrail/res/values/dimens.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/values/dimens.xml
@@ -26,16 +26,16 @@
14dp16dp
- @dimen/m3_comp_navigation_rail_container_elevation
- @dimen/m3_comp_navigation_rail_icon_size
- @dimen/m3_comp_navigation_rail_container_width
+ @dimen/m3_comp_nav_rail_collapsed_container_elevation
+ @dimen/m3_comp_nav_rail_item_icon_size
+ @dimen/m3_comp_nav_rail_collapsed_narrow_container_width60dp4dp12dp
- 20dp12dp
- @dimen/m3_comp_navigation_rail_active_indicator_width
- @dimen/m3_comp_navigation_rail_active_indicator_height
+ 20dp
+ @dimen/m3_comp_nav_rail_item_vertical_active_indicator_width
+ @dimen/m3_comp_nav_rail_item_vertical_active_indicator_height4dp2dp4dp
@@ -47,6 +47,14 @@
220dp360dp0dp
- 56dp
+
+ @dimen/m3_comp_nav_rail_item_container_height
+ @dimen/m3_comp_nav_rail_collapsed_container_width
+ @dimen/m3_comp_nav_rail_collapsed_top_space
+ @dimen/m3_comp_nav_rail_item_header_space_minimum
+ @dimen/m3_comp_nav_rail_item_container_vertical_space
+ @dimen/m3_comp_nav_rail_collapsed_item_vertical_space
+
+ 20dp
diff --git a/lib/java/com/google/android/material/navigationrail/res/values/styles.xml b/lib/java/com/google/android/material/navigationrail/res/values/styles.xml
index b93b31a3268..3cf74f3ec11 100644
--- a/lib/java/com/google/android/material/navigationrail/res/values/styles.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/values/styles.xml
@@ -16,6 +16,99 @@
-->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -68,46 +161,4 @@
-
-
-
-
-
-
-
-
-
diff --git a/lib/java/com/google/android/material/navigationrail/res/values/tokens.xml b/lib/java/com/google/android/material/navigationrail/res/values/tokens.xml
index 7cfc86d3c34..e9ac547f9fa 100644
--- a/lib/java/com/google/android/material/navigationrail/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/navigationrail/res/values/tokens.xml
@@ -15,40 +15,66 @@
~ limitations under the License.
-->
-
+
-
-
- ?attr/colorSurface
-
- 80dp
- @dimen/m3_sys_elevation_level0
-
- ?attr/textAppearanceLabelMedium
- ?attr/colorOnSurface
- ?attr/colorOnSurfaceVariant
-
- 24dp
- ?attr/colorOnSecondaryContainer
- ?attr/colorOnSurfaceVariant
-
- ?attr/colorSecondaryContainer
- 32dp
-
- 56dp
-
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- @dimen/m3_sys_state_hover_state_layer_opacity
-
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- @dimen/m3_sys_state_focus_state_layer_opacity
-
- ?attr/colorOnSurface
- ?attr/colorOnSurface
- @dimen/m3_sys_state_pressed_state_layer_opacity
+
+
+ ?attr/colorSecondaryContainer
+ ?attr/colorSecondary
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+
+ ?attr/colorOnSecondaryContainer
+ @dimen/m3_sys_state_hover_state_layer_opacity
+ ?attr/colorOnSecondaryContainer
+
+ ?attr/colorOnSecondaryContainer
+ @dimen/m3_sys_state_focus_state_layer_opacity
+ ?attr/colorOnSecondaryContainer
+
+ ?attr/colorOnSecondaryContainer
+ @dimen/m3_sys_state_pressed_state_layer_opacity
+ ?attr/colorOnSecondaryContainer
+
+
+ 96dp
+ 80dp
+ @dimen/m3_sys_elevation_level0
+
+ ?attr/colorSurface
+ 4dp
+ 44dp
+
+
+ 220dp
+ 360dp
+
+
+
+ 24dp
+
+ 64dp
+ 56dp
+ 6dp
+ 40dp
+
+
+ 32dp
+ 56dp
+ ?attr/textAppearanceLabelMedium
+ 4dp
+ 16dp
+ 16dp
+
+
+ ?attr/textAppearanceLabelLarge
+ 56dp
+ 16dp
+ 16dp
+ 8dp
diff --git a/lib/java/com/google/android/material/overflow/OverflowLinearLayout.java b/lib/java/com/google/android/material/overflow/OverflowLinearLayout.java
new file mode 100644
index 00000000000..6ca7cb549a1
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/OverflowLinearLayout.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2025 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.overflow;
+
+import com.google.android.material.R;
+
+import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import androidx.appcompat.widget.PopupMenu;
+import androidx.appcompat.widget.TintTypedArray;
+import androidx.appcompat.widget.TooltipCompat;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.button.MaterialButton;
+import com.google.android.material.button.MaterialButtonGroup.OverflowUtils;
+import com.google.android.material.internal.ThemeEnforcement;
+import com.google.android.material.resources.MaterialAttributes;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Provides an implementation of an overflow linear layout.
+ *
+ *
The OverflowLinearLayout will automatically hide/show its children depending on the current
+ * available screen space and/or the max size of its parent layout. If there is not enough space to
+ * show all children, the ones that do not fit will be put in an overflow menu, and an overflow
+ * button will be automatically added as the last child of the layout.
+ *
+ *
Note: if you'd like to hide/show children independently from this layout's decisions, you'll
+ * need to add/remove the desired view(s), instead of changing their visibility, as the
+ * OverflowLinearLayout will determine the final visibility value of its children.
+ *
+ *
The OverflowLinearLayout is commonly used with the {@link
+ * com.google.android.material.floatingtoolbar.FloatingToolbarLayout} and the {@link
+ * com.google.android.material.dockedtoolbar.DockedToolbarLayout}.
+ */
+public class OverflowLinearLayout extends LinearLayout {
+
+ private static final int DEF_STYLE_RES = R.style.Widget_Material3_OverflowLinearLayout;
+
+ @NonNull private final MaterialButton overflowButton;
+ private boolean overflowButtonAdded = false;
+
+ private final Set overflowViews = new LinkedHashSet<>();
+
+ public OverflowLinearLayout(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public OverflowLinearLayout(@NonNull Context context, @Nullable AttributeSet attributeSet) {
+ this(context, attributeSet, R.attr.overflowLinearLayoutStyle);
+ }
+
+ public OverflowLinearLayout(
+ @NonNull Context context, @Nullable AttributeSet attributeSet, int defStyleAttr) {
+ super(wrap(context, attributeSet, defStyleAttr, DEF_STYLE_RES), attributeSet, defStyleAttr);
+ // Ensure we are using the correctly themed context rather than the context that was passed in.
+ context = getContext();
+
+ TintTypedArray attributes =
+ ThemeEnforcement.obtainTintedStyledAttributes(
+ context, attributeSet, R.styleable.OverflowLinearLayout, defStyleAttr, DEF_STYLE_RES);
+
+ Drawable overflowButtonDrawable =
+ attributes.getDrawable(R.styleable.OverflowLinearLayout_overflowButtonIcon);
+
+ attributes.recycle();
+
+ // Configurations of the overflow button.
+ overflowButton =
+ (MaterialButton)
+ LayoutInflater.from(context)
+ .inflate(R.layout.m3_overflow_linear_layout_overflow_button, this, false);
+ TooltipCompat.setTooltipText(overflowButton, getResources().getString(R.string.m3_overflow_linear_layout_button_tooltip_text));
+ setOverflowButtonIcon(overflowButtonDrawable);
+ if (overflowButton.getContentDescription() == null) {
+ overflowButton.setContentDescription(
+ context.getString(R.string.m3_overflow_linear_layout_button_content_description));
+ }
+ int overflowMenuStyle =
+ MaterialAttributes.resolveOrThrow(this, R.attr.overflowLinearLayoutPopupMenuStyle);
+ PopupMenu popupMenu;
+ if (VERSION.SDK_INT > VERSION_CODES.LOLLIPOP) {
+ popupMenu = new PopupMenu(getContext(), overflowButton, Gravity.CENTER, 0, overflowMenuStyle);
+ } else {
+ popupMenu = new PopupMenu(getContext(), overflowButton, Gravity.CENTER);
+ }
+ int overflowItemIconPadding =
+ context
+ .getResources()
+ .getDimensionPixelOffset(R.dimen.m3_overflow_item_icon_horizontal_padding);
+ overflowButton.setOnClickListener(
+ v -> handleOverflowButtonClick(popupMenu, overflowItemIconPadding));
+ }
+
+ /** Whether the OverflowLinearLayout currently has items overflowed. */
+ public boolean isOverflowed() {
+ return !overflowViews.isEmpty();
+ }
+
+ /** Returns the current set of overflowed views. */
+ @NonNull
+ public Set getOverflowedViews() {
+ return overflowViews;
+ }
+
+ /**
+ * Sets the icon to show for the overflow button.
+ *
+ * @param icon Drawable to use for the overflow button's icon.
+ * @attr ref com.google.android.material.R.styleable#OverflowLinearLayout_overflowButtonIcon
+ * @see #setOverflowButtonIconResource(int)
+ * @see #getOverflowButtonIcon()
+ */
+ public void setOverflowButtonIcon(@Nullable Drawable icon) {
+ overflowButton.setIcon(icon);
+ }
+
+ /**
+ * Sets the icon to show for the overflow button.
+ *
+ * @param iconResourceId drawable resource ID to use for the overflow button's icon.
+ * @attr ref com.google.android.material.R.styleable#OverflowLinearLayout_overflowButtonIcon
+ * @see #setOverflowButtonIcon(Drawable)
+ * @see #getOverflowButtonIcon()
+ */
+ public void setOverflowButtonIconResource(@DrawableRes int iconResourceId) {
+ overflowButton.setIconResource(iconResourceId);
+ }
+
+ /**
+ * Returns the icon shown for the overflow button, if present.
+ *
+ * @return the overflow button icon, if present.
+ * @attr ref com.google.android.material.R.styleable#OverflowLinearLayout_overflowButtonIcon
+ * @see #setOverflowButtonIcon(Drawable)
+ * @see #setOverflowButtonIconResource(int)
+ */
+ @Nullable
+ public Drawable getOverflowButtonIcon() {
+ return overflowButton.getIcon();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ boolean isHorizontal = getOrientation() == HORIZONTAL;
+ int childCountWithoutOverflowButton =
+ overflowButtonAdded ? getChildCount() - 1 : getChildCount();
+ int atMostSize =
+ isHorizontal
+ ? MeasureSpec.getSize(widthMeasureSpec)
+ : MeasureSpec.getSize(heightMeasureSpec);
+ int childrenSize = 0;
+ int overflowButtonSize =
+ getOverflowButtonSize(isHorizontal, overflowButton, widthMeasureSpec, heightMeasureSpec);
+ overflowButton.setVisibility(GONE);
+ overflowViews.clear();
+ boolean shouldShowOverflow = false;
+
+ for (int childIndex = 0; childIndex < childCountWithoutOverflowButton; childIndex++) {
+ View child = getChildAt(childIndex);
+ child.setVisibility(VISIBLE);
+ int childSize = getChildSize(isHorizontal, child, widthMeasureSpec, heightMeasureSpec);
+
+ if (childrenSize + childSize + overflowButtonSize > atMostSize) {
+ // Add views to be overflowed here in case overflow happens so that we don't have to loop
+ // over the children again. Here we're also accounting for the overflow button size, to make
+ // sure it'll fit in the layout we might have to remove extra buttons.
+ overflowViews.add(child);
+ }
+ // Overflow actually happens if adding this child makes it go beyond the atMostSize.
+ if (childrenSize + childSize > atMostSize) {
+ shouldShowOverflow = true;
+ int removedIndex = childIndex + 1;
+ // Finish looping through the children and adding remaining overflowed views.
+ while (removedIndex < childCountWithoutOverflowButton) {
+ overflowViews.add(getChildAt(removedIndex));
+ removedIndex++;
+ }
+ break;
+ } else {
+ childrenSize += childSize;
+ }
+ }
+
+ if (shouldShowOverflow) {
+ for (View view : overflowViews) {
+ view.setVisibility(GONE);
+ }
+ if (!overflowButtonAdded) {
+ // Add overflow button here so it's the last button of the layout.
+ addView(overflowButton);
+ overflowButtonAdded = true;
+ }
+ overflowButton.setVisibility(VISIBLE);
+ } else {
+ overflowButton.setVisibility(GONE);
+ // Make sure overflowViews is empty.
+ overflowViews.clear();
+ }
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ private int getChildSize(
+ boolean isHorizontal, View child, int widthMeasureSpec, int heightMeasureSpec) {
+ measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ int childSize =
+ isHorizontal
+ ? (child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin)
+ : (child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ // Child measured size may be zero in some cases, like if its final size is being determined by
+ // layout weight, so use minimum size instead for such cases.
+ if (childSize == 0) {
+ childSize =
+ isHorizontal
+ ? (child.getMinimumWidth() + lp.leftMargin + lp.rightMargin)
+ : (child.getMinimumHeight() + lp.topMargin + lp.bottomMargin);
+ }
+ return childSize;
+ }
+
+ private int getOverflowButtonSize(
+ boolean isHorizontal, View button, int widthMeasureSpec, int heightMeasureSpec) {
+ measureChild(button, widthMeasureSpec, heightMeasureSpec);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+ return isHorizontal
+ ? (button.getMeasuredWidth() + lp.leftMargin + lp.rightMargin)
+ : (button.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ }
+
+ private void handleOverflowButtonClick(PopupMenu popupMenu, int overflowItemIconPadding) {
+ popupMenu.getMenu().clear();
+ popupMenu.setForceShowIcon(true);
+ // Set up each item of the overflow menu.
+ for (View view : overflowViews) {
+ OverflowLinearLayout.LayoutParams lp =
+ (OverflowLinearLayout.LayoutParams) view.getLayoutParams();
+
+ CharSequence text = OverflowUtils.getMenuItemText(view, lp.overflowText);
+ MenuItem item = popupMenu.getMenu().add(text);
+ Drawable icon = lp.overflowIcon;
+ if (icon != null) {
+ item.setIcon(
+ new InsetDrawable(icon, overflowItemIconPadding, 0, overflowItemIconPadding, 0));
+ }
+ if (view instanceof MaterialButton) {
+ MaterialButton button = (MaterialButton) view;
+ item.setCheckable(button.isCheckable());
+ item.setChecked(button.isChecked());
+ }
+ item.setEnabled(view.isEnabled());
+ item.setOnMenuItemClickListener(
+ menuItem -> {
+ view.performClick();
+ if (item.isCheckable()) {
+ item.setChecked(!item.isChecked());
+ }
+ return true;
+ });
+ }
+ popupMenu.show();
+ }
+
+ @Override
+ @NonNull
+ protected OverflowLinearLayout.LayoutParams generateDefaultLayoutParams() {
+ if (getOrientation() == HORIZONTAL) {
+ return new OverflowLinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ } else {
+ return new OverflowLinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ @Override
+ @NonNull
+ public LayoutParams generateLayoutParams(@Nullable AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ @NonNull
+ protected OverflowLinearLayout.LayoutParams generateLayoutParams(
+ @NonNull ViewGroup.LayoutParams p) {
+ if (p instanceof LayoutParams) {
+ return new OverflowLinearLayout.LayoutParams(p);
+ } else if (p instanceof LinearLayout.LayoutParams) {
+ return new OverflowLinearLayout.LayoutParams((LinearLayout.LayoutParams) p);
+ } else if (p instanceof MarginLayoutParams) {
+ return new OverflowLinearLayout.LayoutParams((MarginLayoutParams) p);
+ } else {
+ return new OverflowLinearLayout.LayoutParams(p);
+ }
+ }
+
+ @Override
+ protected boolean checkLayoutParams(@NonNull ViewGroup.LayoutParams p) {
+ return p instanceof OverflowLinearLayout.LayoutParams;
+ }
+
+ /** A {@link LinearLayout.LayoutParams} implementation for {@link OverflowLinearLayout}. */
+ public static class LayoutParams extends LinearLayout.LayoutParams {
+ @Nullable public Drawable overflowIcon = null;
+ @Nullable public CharSequence overflowText = null;
+
+ /**
+ * Creates a new set of layout parameters. The values are extracted from the supplied attributes
+ * set and context.
+ *
+ * @param context the application environment
+ * @param attrs the set of attributes from which to extract the layout parameters' values
+ */
+ public LayoutParams(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ TypedArray attributes =
+ context.obtainStyledAttributes(attrs, R.styleable.OverflowLinearLayout_Layout);
+
+ overflowIcon =
+ attributes.getDrawable(R.styleable.OverflowLinearLayout_Layout_layout_overflowIcon);
+ overflowText =
+ attributes.getText(R.styleable.OverflowLinearLayout_Layout_layout_overflowText);
+
+ attributes.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(int width, int height, float weight) {
+ super(width, height, weight);
+ }
+
+ /**
+ * Creates a new set of layout parameters with the specified width, height, weight, overflow
+ * icon and overflow text.
+ *
+ * @param width the width, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed size
+ * in pixels
+ * @param height the height, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed size
+ * in pixels
+ * @param weight the weight
+ * @param overflowIcon the overflow icon drawable
+ * @param overflowText the overflow text char sequence
+ */
+ public LayoutParams(
+ int width,
+ int height,
+ float weight,
+ @Nullable Drawable overflowIcon,
+ @Nullable CharSequence overflowText) {
+ super(width, height, weight);
+ this.overflowIcon = overflowIcon;
+ this.overflowText = overflowText;
+ }
+
+ public LayoutParams(@NonNull ViewGroup.LayoutParams p) {
+ super(p);
+ }
+
+ public LayoutParams(@NonNull MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(@NonNull LinearLayout.LayoutParams source) {
+ super(source);
+ }
+
+ /**
+ * Copy constructor. Clones the values of the source.
+ *
+ * @param source The layout params to copy from.
+ */
+ public LayoutParams(@NonNull LayoutParams source) {
+ super(source);
+ this.overflowText = source.overflowText;
+ this.overflowIcon = source.overflowIcon;
+ }
+ }
+}
diff --git a/lib/java/com/google/android/material/overflow/res-public/values/public.xml b/lib/java/com/google/android/material/overflow/res-public/values/public.xml
new file mode 100644
index 00000000000..aa21c815559
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res-public/values/public.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/overflow/res/layout/m3_overflow_linear_layout_overflow_button.xml b/lib/java/com/google/android/material/overflow/res/layout/m3_overflow_linear_layout_overflow_button.xml
new file mode 100644
index 00000000000..a30d43d0401
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/layout/m3_overflow_linear_layout_overflow_button.xml
@@ -0,0 +1,23 @@
+
+
+
diff --git a/lib/java/com/google/android/material/snackbar/res/values/strings.xml b/lib/java/com/google/android/material/overflow/res/values-af/strings.xml
similarity index 71%
rename from lib/java/com/google/android/material/snackbar/res/values/strings.xml
rename to lib/java/com/google/android/material/overflow/res/values-af/strings.xml
index 75b159a9518..43bda19b880 100644
--- a/lib/java/com/google/android/material/snackbar/res/values/strings.xml
+++ b/lib/java/com/google/android/material/overflow/res/values-af/strings.xml
@@ -1,6 +1,6 @@
-
-
- Alert
+
+ Oorloopkieslys
diff --git a/lib/java/com/google/android/material/overflow/res/values-am/strings.xml b/lib/java/com/google/android/material/overflow/res/values-am/strings.xml
new file mode 100644
index 00000000000..996f3f06d12
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-am/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ትርፍ ምናሌ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ar/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ar/strings.xml
new file mode 100644
index 00000000000..294203b8021
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ar/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ القائمة الكاملة
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-as/strings.xml b/lib/java/com/google/android/material/overflow/res/values-as/strings.xml
new file mode 100644
index 00000000000..b55f8d95204
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-as/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Overflow menu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-az/strings.xml b/lib/java/com/google/android/material/overflow/res/values-az/strings.xml
new file mode 100644
index 00000000000..68b4e2df459
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-az/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Əlavə menyu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-b+es+419/strings.xml b/lib/java/com/google/android/material/overflow/res/values-b+es+419/strings.xml
new file mode 100644
index 00000000000..21fb1d77666
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-b+es+419/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menú ampliado
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-b+sr+Latn/strings.xml b/lib/java/com/google/android/material/overflow/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 00000000000..86841f7f8ce
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Preklopni meni
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-be/strings.xml b/lib/java/com/google/android/material/overflow/res/values-be/strings.xml
new file mode 100644
index 00000000000..68704bc89e8
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-be/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Меню з пашырэннем
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-bg/strings.xml b/lib/java/com/google/android/material/overflow/res/values-bg/strings.xml
new file mode 100644
index 00000000000..a2ed1311f2d
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-bg/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Меню при препълване
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-bn/strings.xml b/lib/java/com/google/android/material/overflow/res/values-bn/strings.xml
new file mode 100644
index 00000000000..b7114517ea3
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-bn/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ওভারফ্লো মেনু
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-bs/strings.xml b/lib/java/com/google/android/material/overflow/res/values-bs/strings.xml
new file mode 100644
index 00000000000..86841f7f8ce
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-bs/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Preklopni meni
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ca/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ca/strings.xml
new file mode 100644
index 00000000000..dda5f98d28b
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ca/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menú de desbordament
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-cs/strings.xml b/lib/java/com/google/android/material/overflow/res/values-cs/strings.xml
new file mode 100644
index 00000000000..fe2320b97a2
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-cs/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Rozbalovací nabídka
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-da/strings.xml b/lib/java/com/google/android/material/overflow/res/values-da/strings.xml
new file mode 100644
index 00000000000..8a949a69833
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-da/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Prikmenu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-de/strings.xml b/lib/java/com/google/android/material/overflow/res/values-de/strings.xml
new file mode 100644
index 00000000000..9da8d1db6fd
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-de/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Dreipunkt-Menü
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-el/strings.xml b/lib/java/com/google/android/material/overflow/res/values-el/strings.xml
new file mode 100644
index 00000000000..da8166dd8c2
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-el/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Μενού υπερχείλισης
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-en-rGB/strings.xml b/lib/java/com/google/android/material/overflow/res/values-en-rGB/strings.xml
new file mode 100644
index 00000000000..b55f8d95204
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-en-rGB/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Overflow menu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-es-rUS/strings.xml b/lib/java/com/google/android/material/overflow/res/values-es-rUS/strings.xml
new file mode 100644
index 00000000000..21fb1d77666
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-es-rUS/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menú ampliado
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-es/strings.xml b/lib/java/com/google/android/material/overflow/res/values-es/strings.xml
new file mode 100644
index 00000000000..541c63774d5
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-es/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menú adicional
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-et/strings.xml b/lib/java/com/google/android/material/overflow/res/values-et/strings.xml
new file mode 100644
index 00000000000..ef4ea852457
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-et/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Ületäitemenüü
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-eu/strings.xml b/lib/java/com/google/android/material/overflow/res/values-eu/strings.xml
new file mode 100644
index 00000000000..6d0fe2fad88
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-eu/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Luzapen-menua
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-fa/strings.xml b/lib/java/com/google/android/material/overflow/res/values-fa/strings.xml
new file mode 100644
index 00000000000..78c05e8d511
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-fa/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ منو سرریز
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-fi/strings.xml b/lib/java/com/google/android/material/overflow/res/values-fi/strings.xml
new file mode 100644
index 00000000000..322854a4e1b
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-fi/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Ylivuotovalikko
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-fr-rCA/strings.xml b/lib/java/com/google/android/material/overflow/res/values-fr-rCA/strings.xml
new file mode 100644
index 00000000000..5163dc1b054
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-fr-rCA/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menu à développer
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-fr/strings.xml b/lib/java/com/google/android/material/overflow/res/values-fr/strings.xml
new file mode 100644
index 00000000000..5163dc1b054
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-fr/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menu à développer
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-gl/strings.xml b/lib/java/com/google/android/material/overflow/res/values-gl/strings.xml
new file mode 100644
index 00000000000..541c63774d5
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-gl/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menú adicional
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-gu/strings.xml b/lib/java/com/google/android/material/overflow/res/values-gu/strings.xml
new file mode 100644
index 00000000000..8832c9fb3cc
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-gu/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ઓવરફ્લો મેનૂ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-hi/strings.xml b/lib/java/com/google/android/material/overflow/res/values-hi/strings.xml
new file mode 100644
index 00000000000..f95cddadcda
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-hi/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ओवरफ़्लो मेन्यू
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-hr/strings.xml b/lib/java/com/google/android/material/overflow/res/values-hr/strings.xml
new file mode 100644
index 00000000000..ba29cdbb0b6
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-hr/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Dodatni izbornik
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-hu/strings.xml b/lib/java/com/google/android/material/overflow/res/values-hu/strings.xml
new file mode 100644
index 00000000000..bf648199966
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-hu/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ További elemeket tartalmazó menü
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-hy/strings.xml b/lib/java/com/google/android/material/overflow/res/values-hy/strings.xml
new file mode 100644
index 00000000000..18724dd292b
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-hy/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Լրացուցիչ ընտրացանկ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-in/strings.xml b/lib/java/com/google/android/material/overflow/res/values-in/strings.xml
new file mode 100644
index 00000000000..0a41e0054ad
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-in/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menu tambahan
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-is/strings.xml b/lib/java/com/google/android/material/overflow/res/values-is/strings.xml
new file mode 100644
index 00000000000..61695728601
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-is/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Aukavalmynd
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-it/strings.xml b/lib/java/com/google/android/material/overflow/res/values-it/strings.xml
new file mode 100644
index 00000000000..c6af716fc92
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-it/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menu extra
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-iw/strings.xml b/lib/java/com/google/android/material/overflow/res/values-iw/strings.xml
new file mode 100644
index 00000000000..78130000521
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-iw/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ אפשרויות נוספות
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ja/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ja/strings.xml
new file mode 100644
index 00000000000..9fbba270843
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ja/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ オーバーフロー メニュー
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ka/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ka/strings.xml
new file mode 100644
index 00000000000..0b2cf57a7df
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ka/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ გადავსების მენიუ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-kk/strings.xml b/lib/java/com/google/android/material/overflow/res/values-kk/strings.xml
new file mode 100644
index 00000000000..cbff5c3481b
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-kk/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Қосымша мәзір
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-km/strings.xml b/lib/java/com/google/android/material/overflow/res/values-km/strings.xml
new file mode 100644
index 00000000000..0f5bb590b0d
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-km/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ម៉ឺនុយបន្ថែម
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-kn/strings.xml b/lib/java/com/google/android/material/overflow/res/values-kn/strings.xml
new file mode 100644
index 00000000000..d9d0c4dc924
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-kn/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ಓವರ್ಫ್ಲೋ ಮೆನು
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ko/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ko/strings.xml
new file mode 100644
index 00000000000..d45323276fb
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ko/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ 더보기 메뉴
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ky/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ky/strings.xml
new file mode 100644
index 00000000000..c9de61378c5
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ky/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Кошумча меню
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-lo/strings.xml b/lib/java/com/google/android/material/overflow/res/values-lo/strings.xml
new file mode 100644
index 00000000000..567e10a384c
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-lo/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ເມນູລົ້ນ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-lt/strings.xml b/lib/java/com/google/android/material/overflow/res/values-lt/strings.xml
new file mode 100644
index 00000000000..efb21df26e2
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-lt/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Perpildymo meniu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-lv/strings.xml b/lib/java/com/google/android/material/overflow/res/values-lv/strings.xml
new file mode 100644
index 00000000000..d31eec48e30
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-lv/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Pārpildes izvēlne
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-mk/strings.xml b/lib/java/com/google/android/material/overflow/res/values-mk/strings.xml
new file mode 100644
index 00000000000..44b43510a7b
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-mk/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Проширено мени
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ml/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ml/strings.xml
new file mode 100644
index 00000000000..81ef8c13507
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ml/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ഓവർഫ്ലോ മെനു
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-mn/strings.xml b/lib/java/com/google/android/material/overflow/res/values-mn/strings.xml
new file mode 100644
index 00000000000..ea7da1e1053
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-mn/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Урт цэс
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-mr/strings.xml b/lib/java/com/google/android/material/overflow/res/values-mr/strings.xml
new file mode 100644
index 00000000000..968895abf03
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-mr/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ओव्हरफ्लो मेनू
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ms/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ms/strings.xml
new file mode 100644
index 00000000000..219daa983cd
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ms/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menu limpahan
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-my/strings.xml b/lib/java/com/google/android/material/overflow/res/values-my/strings.xml
new file mode 100644
index 00000000000..843b8fd6bee
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-my/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ မီနူးအပို
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-nb/strings.xml b/lib/java/com/google/android/material/overflow/res/values-nb/strings.xml
new file mode 100644
index 00000000000..11d2394285f
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-nb/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Overflytsmeny
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ne/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ne/strings.xml
new file mode 100644
index 00000000000..1fef655ad30
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ne/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ओभरफ्लो मेनु
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-nl/strings.xml b/lib/java/com/google/android/material/overflow/res/values-nl/strings.xml
new file mode 100644
index 00000000000..1a4e0e8e759
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-nl/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Overloopmenu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-or/strings.xml b/lib/java/com/google/android/material/overflow/res/values-or/strings.xml
new file mode 100644
index 00000000000..b55f8d95204
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-or/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Overflow menu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-pa/strings.xml b/lib/java/com/google/android/material/overflow/res/values-pa/strings.xml
new file mode 100644
index 00000000000..738b8dfaf27
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-pa/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ਓਵਰਫ਼ਲੋ ਮੀਨੂ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-pl/strings.xml b/lib/java/com/google/android/material/overflow/res/values-pl/strings.xml
new file mode 100644
index 00000000000..fbf5b9062c0
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-pl/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Rozszerzone menu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-pt-rBR/strings.xml b/lib/java/com/google/android/material/overflow/res/values-pt-rBR/strings.xml
new file mode 100644
index 00000000000..c80ead97033
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-pt-rBR/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menu flutuante
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-pt-rPT/strings.xml b/lib/java/com/google/android/material/overflow/res/values-pt-rPT/strings.xml
new file mode 100644
index 00000000000..9d3e0eeb059
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-pt-rPT/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menu adicional
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ro/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ro/strings.xml
new file mode 100644
index 00000000000..24d47809d5c
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ro/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Meniu suplimentar
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ru/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ru/strings.xml
new file mode 100644
index 00000000000..eb6272c4d43
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ru/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Дополнительное меню
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-si/strings.xml b/lib/java/com/google/android/material/overflow/res/values-si/strings.xml
new file mode 100644
index 00000000000..35eea686309
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-si/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ පිටාර මෙනුව
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-sk/strings.xml b/lib/java/com/google/android/material/overflow/res/values-sk/strings.xml
new file mode 100644
index 00000000000..44ba59cdd8d
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-sk/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Rozšírená ponuka
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-sl/strings.xml b/lib/java/com/google/android/material/overflow/res/values-sl/strings.xml
new file mode 100644
index 00000000000..6682f878bc8
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-sl/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Meni z dodatnimi elementi
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-sq/strings.xml b/lib/java/com/google/android/material/overflow/res/values-sq/strings.xml
new file mode 100644
index 00000000000..fc29f83c954
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-sq/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menyja e tejkalimit
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-sr/strings.xml b/lib/java/com/google/android/material/overflow/res/values-sr/strings.xml
new file mode 100644
index 00000000000..d206969dbce
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-sr/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Преклопни мени
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-sv/strings.xml b/lib/java/com/google/android/material/overflow/res/values-sv/strings.xml
new file mode 100644
index 00000000000..2a0385dc664
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-sv/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Fler menyalternativ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-sw/strings.xml b/lib/java/com/google/android/material/overflow/res/values-sw/strings.xml
new file mode 100644
index 00000000000..cb0724af32f
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-sw/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Menyu ya vipengee vya ziada
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ta/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ta/strings.xml
new file mode 100644
index 00000000000..37384970d5d
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ta/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ கூடுதல் விருப்பங்கள்
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-te/strings.xml b/lib/java/com/google/android/material/overflow/res/values-te/strings.xml
new file mode 100644
index 00000000000..aa5e0cc68fe
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-te/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ ఓవర్ ఫ్లో మెనూ
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-th/strings.xml b/lib/java/com/google/android/material/overflow/res/values-th/strings.xml
new file mode 100644
index 00000000000..236d1598275
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-th/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ เมนูรายการเพิ่มเติม
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-tl/strings.xml b/lib/java/com/google/android/material/overflow/res/values-tl/strings.xml
new file mode 100644
index 00000000000..b55f8d95204
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-tl/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Overflow menu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-tr/strings.xml b/lib/java/com/google/android/material/overflow/res/values-tr/strings.xml
new file mode 100644
index 00000000000..4d0a1e099ac
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-tr/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Taşma menüsü
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-uk/strings.xml b/lib/java/com/google/android/material/overflow/res/values-uk/strings.xml
new file mode 100644
index 00000000000..b351653323b
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-uk/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Додаткове меню
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-ur/strings.xml b/lib/java/com/google/android/material/overflow/res/values-ur/strings.xml
new file mode 100644
index 00000000000..cb139192b7e
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-ur/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ اوورفلو مینیو
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-uz/strings.xml b/lib/java/com/google/android/material/overflow/res/values-uz/strings.xml
new file mode 100644
index 00000000000..3134eb2da48
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-uz/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Kengaytirilgan menyu
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-vi/strings.xml b/lib/java/com/google/android/material/overflow/res/values-vi/strings.xml
new file mode 100644
index 00000000000..71d366fcae8
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-vi/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Trình đơn mục bổ sung
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-zh-rCN/strings.xml b/lib/java/com/google/android/material/overflow/res/values-zh-rCN/strings.xml
new file mode 100644
index 00000000000..d0fb707b39c
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-zh-rCN/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ 菜单
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-zh-rHK/strings.xml b/lib/java/com/google/android/material/overflow/res/values-zh-rHK/strings.xml
new file mode 100644
index 00000000000..5ebd47c7737
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-zh-rHK/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ 展開式選單
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-zh-rTW/strings.xml b/lib/java/com/google/android/material/overflow/res/values-zh-rTW/strings.xml
new file mode 100644
index 00000000000..422ab252c42
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-zh-rTW/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ 溢位選單
+
diff --git a/lib/java/com/google/android/material/overflow/res/values-zu/strings.xml b/lib/java/com/google/android/material/overflow/res/values-zu/strings.xml
new file mode 100644
index 00000000000..df78a82d8a5
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values-zu/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Imenyu yakho konke
+
diff --git a/lib/java/com/google/android/material/overflow/res/values/attrs.xml b/lib/java/com/google/android/material/overflow/res/values/attrs.xml
new file mode 100644
index 00000000000..ca0e01414d4
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values/attrs.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/overflow/res/values/dimens.xml b/lib/java/com/google/android/material/overflow/res/values/dimens.xml
new file mode 100644
index 00000000000..de530bc04d3
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values/dimens.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ 4dp
+
diff --git a/lib/java/com/google/android/material/overflow/res/values/strings.xml b/lib/java/com/google/android/material/overflow/res/values/strings.xml
new file mode 100644
index 00000000000..d58e75087d7
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values/strings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+ Overflow menu
+
+
+ More options
+
+
diff --git a/lib/java/com/google/android/material/overflow/res/values/styles.xml b/lib/java/com/google/android/material/overflow/res/values/styles.xml
new file mode 100644
index 00000000000..59698c3188d
--- /dev/null
+++ b/lib/java/com/google/android/material/overflow/res/values/styles.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/progressindicator/BaseProgressIndicator.java b/lib/java/com/google/android/material/progressindicator/BaseProgressIndicator.java
index 158686115b3..a2481e11bc5 100644
--- a/lib/java/com/google/android/material/progressindicator/BaseProgressIndicator.java
+++ b/lib/java/com/google/android/material/progressindicator/BaseProgressIndicator.java
@@ -29,6 +29,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ProgressBar;
import androidx.annotation.AttrRes;
@@ -471,10 +472,10 @@ && getWindowVisibility() == View.VISIBLE
*
This is necessary as before API 24, it is not guaranteed that Views will ever be notified
* about their parent changing. Thus, we don't have a proper point to hook in and re-check {@link
* #isShown()} on parent changes that result from {@link
- * android.view.ViewGroup#attachViewToParent(View, int, LayoutParams)}, which *can* change our
- * effective visibility. So this method errs on the side of assuming visibility unless we can
- * conclusively prove otherwise (but may result in some false positives, if this view ends up
- * being attached to a non-visible hierarchy after being detached in a visible state).
+ * android.view.ViewGroup#attachViewToParent(View, int, ViewGroup.LayoutParams)}, which *can*
+ * change our effective visibility. So this method errs on the side of assuming visibility unless
+ * we can conclusively prove otherwise (but may result in some false positives, if this view
+ * ends up being attached to a non-visible hierarchy after being detached in a visible state).
*/
boolean isEffectivelyVisible() {
View current = this;
@@ -588,7 +589,11 @@ public int[] getIndicatorColor() {
public void setIndicatorColor(@ColorInt int... indicatorColors) {
if (indicatorColors.length == 0) {
// Uses theme primary color for indicator by default. Indicator color cannot be empty.
- indicatorColors = new int[] {MaterialColors.getColor(getContext(), R.attr.colorPrimary, -1)};
+ indicatorColors =
+ new int[] {
+ MaterialColors.getColor(
+ getContext(), androidx.appcompat.R.attr.colorPrimary, -1)
+ };
}
if (!Arrays.equals(getIndicatorColor(), indicatorColors)) {
spec.indicatorColors = indicatorColors;
@@ -646,7 +651,36 @@ public int getTrackCornerRadius() {
*/
public void setTrackCornerRadius(@Px int trackCornerRadius) {
if (spec.trackCornerRadius != trackCornerRadius) {
- spec.trackCornerRadius = Math.round(min(trackCornerRadius, spec.trackThickness / 2f));
+ spec.trackCornerRadius = min(trackCornerRadius, spec.trackThickness / 2);
+ spec.useRelativeTrackCornerRadius = false;
+ invalidate();
+ }
+ }
+
+ /**
+ * Returns the relative radius of the rounded corner for the indicator and track in pixels.
+ *
+ * @see #setTrackCornerRadiusFraction(float)
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#BaseProgressIndicator_trackCornerRadius
+ */
+ public float getTrackCornerRadiusFraction() {
+ return spec.trackCornerRadiusFraction;
+ }
+
+ /**
+ * Sets the radius of the rounded corner for the indicator and track in fraction of track
+ * thickness.
+ *
+ * @param fraction The fraction of corner radius to track thickness.
+ * @see #getTrackCornerRadiusFraction()
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#BaseProgressIndicator_trackCornerRadius
+ */
+ public void setTrackCornerRadiusFraction(@FloatRange(from = 0f, to = 0.5f) float fraction) {
+ if (spec.trackCornerRadiusFraction != fraction) {
+ spec.trackCornerRadiusFraction = min(fraction, 0.5f);
+ spec.useRelativeTrackCornerRadius = true;
invalidate();
}
}
diff --git a/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java b/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java
index 7c17a85285b..d633a7e2416 100644
--- a/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java
+++ b/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java
@@ -54,6 +54,18 @@ public abstract class BaseProgressIndicatorSpec {
*/
@Px public int trackCornerRadius;
+ /**
+ * The fraction of the track thickness to be used as the corner radius. And the stroke ROUND cap
+ * is used to prevent artifacts like (b/319309456), when 0.5f is specified.
+ */
+ public float trackCornerRadiusFraction;
+
+ /**
+ * When this is true, the {#link trackCornerRadiusFraction} takes effect. Otherwise, the {@link
+ * trackCornerRadius} takes effect.
+ */
+ public boolean useRelativeTrackCornerRadius;
+
/**
* The color array used in the indicator. In determinate mode, only the first item will be used.
*/
@@ -112,11 +124,21 @@ protected BaseProgressIndicatorSpec(
trackThickness =
getDimensionPixelSize(
context, a, R.styleable.BaseProgressIndicator_trackThickness, defaultIndicatorSize);
- trackCornerRadius =
- min(
- getDimensionPixelSize(
- context, a, R.styleable.BaseProgressIndicator_trackCornerRadius, 0),
- Math.round(trackThickness / 2f));
+ TypedValue trackCornerRadiusValue =
+ a.peekValue(R.styleable.BaseProgressIndicator_trackCornerRadius);
+ if (trackCornerRadiusValue != null) {
+ if (trackCornerRadiusValue.type == TypedValue.TYPE_DIMENSION) {
+ trackCornerRadius =
+ min(
+ TypedValue.complexToDimensionPixelSize(
+ trackCornerRadiusValue.data, a.getResources().getDisplayMetrics()),
+ trackThickness / 2);
+ useRelativeTrackCornerRadius = false;
+ } else if (trackCornerRadiusValue.type == TypedValue.TYPE_FRACTION) {
+ trackCornerRadiusFraction = min(trackCornerRadiusValue.getFraction(1.0f, 1.0f), 0.5f);
+ useRelativeTrackCornerRadius = true;
+ }
+ }
showAnimationBehavior =
a.getInt(
R.styleable.BaseProgressIndicator_showAnimationBehavior,
@@ -160,7 +182,10 @@ protected BaseProgressIndicatorSpec(
private void loadIndicatorColors(@NonNull Context context, @NonNull TypedArray typedArray) {
if (!typedArray.hasValue(R.styleable.BaseProgressIndicator_indicatorColor)) {
// Uses theme primary color for indicator if not provided in the attribute set.
- indicatorColors = new int[] {MaterialColors.getColor(context, R.attr.colorPrimary, -1)};
+ indicatorColors =
+ new int[] {
+ MaterialColors.getColor(context, androidx.appcompat.R.attr.colorPrimary, -1)
+ };
return;
}
@@ -222,6 +247,27 @@ public boolean hasWavyEffect(boolean isDeterminate) {
&& ((!isDeterminate && wavelengthIndeterminate > 0)
|| (isDeterminate && wavelengthDeterminate > 0));
}
+
+ /**
+ * Returns the track corner radius in pixels.
+ *
+ *
If {@link #useRelativeTrackCornerRadius} is true, the track corner radius is calculated
+ * using the track thickness and the track corner radius fraction. Otherwise, the track corner
+ * radius is returned directly.
+ */
+ public int getTrackCornerRadiusInPx() {
+ return useRelativeTrackCornerRadius
+ ? (int) (trackThickness * trackCornerRadiusFraction)
+ : trackCornerRadius;
+ }
+
+ /**
+ * Returns true if the stroke ROUND cap should be used to prevent artifacts like (b/319309456),
+ * when fully rounded corners are specified.
+ */
+ public boolean useStrokeCap() {
+ return useRelativeTrackCornerRadius && trackCornerRadiusFraction == 0.5f;
+ }
@CallSuper
void validateSpec() {
diff --git a/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java b/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java
index 737fe4e3b3d..6a2e16d897c 100644
--- a/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java
+++ b/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java
@@ -58,8 +58,6 @@ final class CircularDrawingDelegate extends DrawingDelegate 0) {
+ if (!spec.useStrokeCap() && displayedCornerRadius > 0) {
paint.setStyle(Style.FILL);
drawRoundedBlock(canvas, paint, endPoints.first, blockWidth, displayedTrackThickness);
drawRoundedBlock(canvas, paint, endPoints.second, blockWidth, displayedTrackThickness);
diff --git a/lib/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java b/lib/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java
index 70f06bc689d..42b2f959754 100644
--- a/lib/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java
+++ b/lib/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java
@@ -25,6 +25,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.SystemClock;
import android.util.Property;
import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
@@ -467,7 +468,7 @@ float getPhaseFraction() {
? baseSpec.wavelengthDeterminate
: baseSpec.wavelengthIndeterminate;
int cycleInMs = (int) (1000f * wavelength / baseSpec.waveSpeed * durationScale);
- phaseFraction = (float) (System.currentTimeMillis() % cycleInMs) / cycleInMs;
+ phaseFraction = (float) (SystemClock.uptimeMillis() % cycleInMs) / cycleInMs;
if (phaseFraction < 0f) {
phaseFraction = (phaseFraction % 1) + 1f;
}
diff --git a/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java b/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java
index 827f47e786e..531b4cf1480 100644
--- a/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java
+++ b/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java
@@ -31,7 +31,6 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.drawable.DrawableCompat;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import com.google.android.material.progressindicator.DrawingDelegate.ActiveIndicator;
@@ -189,7 +188,7 @@ public void draw(@NonNull Canvas canvas) {
if (isSystemAnimatorDisabled() && staticDummyDrawable != null) {
staticDummyDrawable.setBounds(getBounds());
- DrawableCompat.setTint(staticDummyDrawable, baseSpec.indicatorColors[0]);
+ staticDummyDrawable.setTint(baseSpec.indicatorColors[0]);
staticDummyDrawable.draw(canvas);
return;
}
diff --git a/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java b/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java
index b03a5091b08..8262572ba28 100644
--- a/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java
+++ b/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java
@@ -51,10 +51,10 @@ final class LinearDrawingDelegate extends DrawingDelegate= endBlockCenterX) {
- // Draws the start rounded block clipped by the end rounded block.
+
+ if (startPx == 0
+ && endBlockCenterX + endCornerRadius < startBlockCenterX + startCornerRadius) {
drawRoundedBlock(
- canvas, paint, endPoints.first, endPoints.second, blockWidth, displayedTrackThickness);
+ canvas,
+ paint,
+ endPoints.first,
+ startBlockWidth,
+ displayedTrackThickness,
+ startCornerRadius,
+ endPoints.second,
+ endBlockWidth,
+ displayedTrackThickness,
+ endCornerRadius,
+ true);
+ } else if (startBlockCenterX - startCornerRadius > endBlockCenterX - endCornerRadius) {
+ drawRoundedBlock(
+ canvas,
+ paint,
+ endPoints.second,
+ endBlockWidth,
+ displayedTrackThickness,
+ endCornerRadius,
+ endPoints.first,
+ startBlockWidth,
+ displayedTrackThickness,
+ startCornerRadius,
+ false);
} else {
// Draws the path with ROUND cap if the corner radius is half of the track
// thickness.
paint.setStyle(Style.STROKE);
- paint.setStrokeCap(useStrokeCap ? Cap.ROUND : Cap.BUTT);
+ paint.setStrokeCap(spec.useStrokeCap() ? Cap.ROUND : Cap.BUTT);
// If start rounded block is on the left of end rounded block, draws the path with the
// start and end rounded blocks.
@@ -293,14 +339,26 @@ private void drawLine(
phaseFraction);
canvas.drawPath(displayedActivePath, paint);
}
- if (!useStrokeCap && displayedCornerRadius > 0) {
- if (startBlockCenterX > 0) {
+ if (!spec.useStrokeCap()) {
+ if (startBlockCenterX > 0 && startCornerRadius > 0) {
// Draws the start rounded block.
- drawRoundedBlock(canvas, paint, endPoints.first, blockWidth, displayedTrackThickness);
+ drawRoundedBlock(
+ canvas,
+ paint,
+ endPoints.first,
+ startBlockWidth,
+ displayedTrackThickness,
+ startCornerRadius);
}
- if (endBlockCenterX < trackLength) {
+ if (endBlockCenterX < trackLength && endCornerRadius > 0) {
// Draws the end rounded block.
- drawRoundedBlock(canvas, paint, endPoints.second, blockWidth, displayedTrackThickness);
+ drawRoundedBlock(
+ canvas,
+ paint,
+ endPoints.second,
+ endBlockWidth,
+ displayedTrackThickness,
+ endCornerRadius);
}
}
}
@@ -315,56 +373,104 @@ void drawStopIndicator(
@IntRange(from = 0, to = 255) int drawableAlpha) {
int paintColor = MaterialColors.compositeARGBWithAlpha(color, drawableAlpha);
drawingDeterminateIndicator = false;
- if (spec.trackStopIndicatorSize > 0 && paintColor != Color.TRANSPARENT) {
+ int trackStopIndicatorSize = spec.getActualTrackStopIndicatorSize();
+ if (trackStopIndicatorSize > 0 && paintColor != Color.TRANSPARENT) {
// Draws the stop indicator at the end of the track if needed.
paint.setStyle(Style.FILL);
paint.setColor(paintColor);
+ float stopIndicatorCenterX =
+ spec.trackStopIndicatorPadding != null
+ ? spec.trackStopIndicatorPadding.floatValue() + spec.trackStopIndicatorSize / 2f
+ : displayedTrackThickness / 2;
drawRoundedBlock(
canvas,
paint,
new PathPoint(
- new float[] {trackLength / 2 - displayedTrackThickness / 2, 0}, new float[] {1, 0}),
- spec.trackStopIndicatorSize,
- spec.trackStopIndicatorSize);
+ new float[] {trackLength / 2 - stopIndicatorCenterX, 0}, new float[] {1, 0}),
+ trackStopIndicatorSize,
+ trackStopIndicatorSize,
+ displayedCornerRadius * trackStopIndicatorSize / displayedTrackThickness);
}
}
+ /** Draws a single rounded block for one of the track ends. */
private void drawRoundedBlock(
@NonNull Canvas canvas,
@NonNull Paint paint,
@NonNull PathPoint drawCenter,
- float markWidth,
- float markHeight) {
- drawRoundedBlock(canvas, paint, drawCenter, null, markWidth, markHeight);
+ float drawWidth,
+ float drawHeight,
+ float drawCornerSize) {
+ drawRoundedBlock(
+ canvas, paint, drawCenter, drawWidth, drawHeight, drawCornerSize, null, 0, 0, 0, false);
}
+ /** Drawas the merged rounded block when two track ends are collapsed. */
private void drawRoundedBlock(
@NonNull Canvas canvas,
@NonNull Paint paint,
@NonNull PathPoint drawCenter,
+ float drawWidth,
+ float drawHeight,
+ float drawCornerSize,
@Nullable PathPoint clipCenter,
- float markWidth,
- float markHeight) {
- markHeight = min(markHeight, displayedTrackThickness);
- float markCornerSize = markHeight * displayedCornerRadius / displayedTrackThickness;
- markCornerSize = min(markWidth / 2, markCornerSize);
- RectF roundedBlock =
- new RectF(-markWidth / 2f, -markHeight / 2f, markWidth / 2f, markHeight / 2f);
+ float clipWidth,
+ float clipHeight,
+ float clipCornerSize,
+ boolean clipRight) {
+ drawHeight = min(drawHeight, displayedTrackThickness);
+ RectF drawRect = new RectF(-drawWidth / 2f, -drawHeight / 2f, drawWidth / 2f, drawHeight / 2f);
paint.setStyle(Style.FILL);
canvas.save();
+ // Clipping!
if (clipCenter != null) {
- // Clipping!
+ clipHeight = min(clipHeight, displayedTrackThickness);
+ clipCornerSize = min(clipWidth / 2, clipCornerSize * clipHeight / displayedTrackThickness);
+ RectF patchRect = new RectF();
+ if (clipRight) {
+ float leftEdgeDiff =
+ (clipCenter.posVec[0] - clipCornerSize) - (drawCenter.posVec[0] - drawCornerSize);
+ if (leftEdgeDiff > 0) {
+ // Clip block is too small. Expand it to include the left edge of the draw block.
+ clipCenter.translate(-leftEdgeDiff / 2, 0);
+ clipWidth += leftEdgeDiff;
+ }
+ // Draw the patch rectangle to fill the gap from the draw block center to its right edge.
+ patchRect.set(0, -drawHeight / 2f, drawWidth / 2f, drawHeight / 2f);
+ } else {
+ float rightEdgeDiff =
+ (clipCenter.posVec[0] + clipCornerSize) - (drawCenter.posVec[0] + drawCornerSize);
+ if (rightEdgeDiff < 0) {
+ // Clip block is too small. Expand it to include the right edge of the draw block.
+ clipCenter.translate(-rightEdgeDiff / 2, 0);
+ clipWidth -= rightEdgeDiff;
+ }
+ // Draw the patch rectangle to fill the gap from the draw block center to its left edge.
+ patchRect.set(-drawWidth / 2f, -drawHeight / 2f, 0, drawHeight / 2f);
+ }
+ RectF clipRect =
+ new RectF(-clipWidth / 2f, -clipHeight / 2f, clipWidth / 2f, clipHeight / 2f);
canvas.translate(clipCenter.posVec[0], clipCenter.posVec[1]);
canvas.rotate(vectorToCanvasRotation(clipCenter.tanVec));
Path clipPath = new Path();
- clipPath.addRoundRect(roundedBlock, markCornerSize, markCornerSize, Direction.CCW);
+ clipPath.addRoundRect(clipRect, clipCornerSize, clipCornerSize, Direction.CCW);
canvas.clipPath(clipPath);
+ // Manually restore to the original canvas transform.
canvas.rotate(-vectorToCanvasRotation(clipCenter.tanVec));
canvas.translate(-clipCenter.posVec[0], -clipCenter.posVec[1]);
+ // Transform to the draw block center and rotation.
+ canvas.translate(drawCenter.posVec[0], drawCenter.posVec[1]);
+ canvas.rotate(vectorToCanvasRotation(drawCenter.tanVec));
+ canvas.drawRect(patchRect, paint);
+ // Draw the draw block.
+ canvas.drawRoundRect(drawRect, drawCornerSize, drawCornerSize, paint);
+ } else {
+ // Transform to the draw block center and rotation.
+ canvas.translate(drawCenter.posVec[0], drawCenter.posVec[1]);
+ canvas.rotate(vectorToCanvasRotation(drawCenter.tanVec));
+ // Draw the draw block.
+ canvas.drawRoundRect(drawRect, drawCornerSize, drawCornerSize, paint);
}
- canvas.translate(drawCenter.posVec[0], drawCenter.posVec[1]);
- canvas.rotate(vectorToCanvasRotation(drawCenter.tanVec));
- canvas.drawRoundRect(roundedBlock, markCornerSize, markCornerSize, paint);
canvas.restore();
}
diff --git a/lib/java/com/google/android/material/progressindicator/LinearProgressIndicator.java b/lib/java/com/google/android/material/progressindicator/LinearProgressIndicator.java
index 173bd152d75..d02155693c3 100644
--- a/lib/java/com/google/android/material/progressindicator/LinearProgressIndicator.java
+++ b/lib/java/com/google/android/material/progressindicator/LinearProgressIndicator.java
@@ -33,6 +33,7 @@
import androidx.annotation.RestrictTo.Scope;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* This class implements the linear type progress indicators.
@@ -57,8 +58,7 @@
* developer guidance and design guidelines.
*/
-public class LinearProgressIndicator
- extends BaseProgressIndicator {
+public class LinearProgressIndicator extends BaseProgressIndicator {
public static final int DEF_STYLE_RES = R.style.Widget_MaterialComponents_LinearProgressIndicator;
/**
@@ -121,11 +121,11 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
int contentHeight = h - (getPaddingTop() + getPaddingBottom());
Drawable drawable = getIndeterminateDrawable();
if (drawable != null) {
- drawable.setBounds(/*left=*/ 0, /*top=*/ 0, contentWidth, contentHeight);
+ drawable.setBounds(/* left= */ 0, /* top= */ 0, contentWidth, contentHeight);
}
drawable = getProgressDrawable();
if (drawable != null) {
- drawable.setBounds(/*left=*/ 0, /*top=*/ 0, contentWidth, contentHeight);
+ drawable.setBounds(/* left= */ 0, /* top= */ 0, contentWidth, contentHeight);
}
}
@@ -168,6 +168,59 @@ public void setTrackCornerRadius(int trackCornerRadius) {
invalidate();
}
+ /**
+ * Returns the radius of the rounded inner corner for the indicator and track in pixels.
+ *
+ * @see #setTrackInnerCornerRadius(int)
+ * @see #setTrackInnerCornerRadiusFraction(int)
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#LinearProgressIndicator_trackInnerCornerRadius
+ */
+ @Px
+ public int getTrackInnerCornerRadius() {
+ return spec.trackInnerCornerRadius;
+ }
+
+ /**
+ * Sets the radius of the rounded inner corner for the indicator and track in pixels.
+ *
+ * @param trackInnerCornerRadius The new corner radius in pixels.
+ * @see #setTrackInnerCornerRadiusFraction(float)
+ * @see #getTrackInnerCornerRadius()
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#LinearProgressIndicator_trackInnerCornerRadius
+ */
+ public void setTrackInnerCornerRadius(@Px int trackInnerCornerRadius) {
+ if (spec.trackInnerCornerRadius != trackInnerCornerRadius) {
+ spec.trackInnerCornerRadius =
+ Math.round(min(trackInnerCornerRadius, spec.trackThickness / 2f));
+ spec.useRelativeTrackInnerCornerRadius = false;
+ spec.hasInnerCornerRadius = true;
+ spec.validateSpec();
+ invalidate();
+ }
+ }
+
+ /**
+ * Sets the radius of the rounded inner corner for the indicator and track in fraction of the
+ * track thickness.
+ *
+ * @param trackInnerCornerRadiusFraction The new corner radius in fraction of the track thickness.
+ * @see #setTrackInnerCornerRadius(int)
+ * @see #getTrackInnerCornerRadius()
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#LinearProgressIndicator_trackInnerCornerRadius
+ */
+ public void setTrackInnerCornerRadiusFraction(float trackInnerCornerRadiusFraction) {
+ if (spec.trackInnerCornerRadiusFraction != trackInnerCornerRadiusFraction) {
+ spec.trackInnerCornerRadiusFraction = min(trackInnerCornerRadiusFraction, 0.5f);
+ spec.useRelativeTrackInnerCornerRadius = true;
+ spec.hasInnerCornerRadius = true;
+ spec.validateSpec();
+ invalidate();
+ }
+ }
+
/**
* Returns the size of the stop indicator at the end of the track in pixels.
*
@@ -190,12 +243,39 @@ public int getTrackStopIndicatorSize() {
*/
public void setTrackStopIndicatorSize(@Px int trackStopIndicatorSize) {
if (spec.trackStopIndicatorSize != trackStopIndicatorSize) {
- spec.trackStopIndicatorSize = min(trackStopIndicatorSize, spec.trackThickness);
+ spec.trackStopIndicatorSize = trackStopIndicatorSize;
spec.validateSpec();
invalidate();
}
}
+ /**
+ * Returns the padding of the stop indicator at the end of the track in pixels.
+ *
+ * @see #setTrackStopIndicatorPadding(int)
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#LinearProgressIndicator_trackStopIndicatorPadding
+ */
+ @Nullable
+ public Integer getTrackStopIndicatorPadding() {
+ return spec.trackStopIndicatorPadding;
+ }
+
+ /**
+ * Sets the padding of the stop indicator at the end of the track in pixels.
+ *
+ * @param trackStopIndicatorPadding The new stop indicator padding in pixels.
+ * @see #getTrackStopIndicatorPadding()
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#LinearProgressIndicator_trackStopIndicatorPadding
+ */
+ public void setTrackStopIndicatorPadding(@Nullable Integer trackStopIndicatorPadding) {
+ if (!Objects.equals(spec.trackStopIndicatorPadding, trackStopIndicatorPadding)) {
+ spec.trackStopIndicatorPadding = trackStopIndicatorPadding;
+ invalidate();
+ }
+ }
+
/**
* Returns the type of indeterminate animation of this progress indicator.
*
diff --git a/lib/java/com/google/android/material/progressindicator/LinearProgressIndicatorSpec.java b/lib/java/com/google/android/material/progressindicator/LinearProgressIndicatorSpec.java
index 512243f1cda..89852773819 100644
--- a/lib/java/com/google/android/material/progressindicator/LinearProgressIndicatorSpec.java
+++ b/lib/java/com/google/android/material/progressindicator/LinearProgressIndicatorSpec.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.util.TypedValue;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -47,9 +48,17 @@ public final class LinearProgressIndicatorSpec extends BaseProgressIndicatorSpec
boolean drawHorizontallyInverse;
- /** The size of the stop indicator at the end of the track. */
+ /** The desired size of the stop indicator at the end of the track. */
@Px public int trackStopIndicatorSize;
+ /** The padding of the stop indicator at the end of the track. */
+ @Nullable public Integer trackStopIndicatorPadding;
+
+ @Px public int trackInnerCornerRadius;
+ public float trackInnerCornerRadiusFraction;
+ public boolean useRelativeTrackInnerCornerRadius;
+ public boolean hasInnerCornerRadius;
+
/**
* Instantiates the spec for {@link LinearProgressIndicator}.
*
@@ -93,9 +102,29 @@ public LinearProgressIndicatorSpec(
R.styleable.LinearProgressIndicator_indicatorDirectionLinear,
LinearProgressIndicator.INDICATOR_DIRECTION_LEFT_TO_RIGHT);
trackStopIndicatorSize =
- min(
- a.getDimensionPixelSize(R.styleable.LinearProgressIndicator_trackStopIndicatorSize, 0),
- trackThickness);
+ a.getDimensionPixelSize(R.styleable.LinearProgressIndicator_trackStopIndicatorSize, 0);
+ if (a.hasValue(R.styleable.LinearProgressIndicator_trackStopIndicatorPadding)) {
+ trackStopIndicatorPadding =
+ a.getDimensionPixelSize(R.styleable.LinearProgressIndicator_trackStopIndicatorPadding, 0);
+ }
+ TypedValue trackInnerCornerRadiusValue =
+ a.peekValue(R.styleable.LinearProgressIndicator_trackInnerCornerRadius);
+ if (trackInnerCornerRadiusValue != null) {
+ if (trackInnerCornerRadiusValue.type == TypedValue.TYPE_DIMENSION) {
+ trackInnerCornerRadius =
+ min(
+ TypedValue.complexToDimensionPixelSize(
+ trackInnerCornerRadiusValue.data, a.getResources().getDisplayMetrics()),
+ trackThickness / 2);
+ useRelativeTrackInnerCornerRadius = false;
+ hasInnerCornerRadius = true;
+ } else if (trackInnerCornerRadiusValue.type == TypedValue.TYPE_FRACTION) {
+ trackInnerCornerRadiusFraction =
+ min(trackInnerCornerRadiusValue.getFraction(1.0f, 1.0f), 0.5f);
+ useRelativeTrackInnerCornerRadius = true;
+ hasInnerCornerRadius = true;
+ }
+ }
a.recycle();
validateSpec();
@@ -104,6 +133,24 @@ public LinearProgressIndicatorSpec(
indicatorDirection == LinearProgressIndicator.INDICATOR_DIRECTION_RIGHT_TO_LEFT;
}
+ public int getTrackInnerCornerRadiusInPx() {
+ return !hasInnerCornerRadius
+ ? getTrackCornerRadiusInPx()
+ : useRelativeTrackInnerCornerRadius
+ ? (int) (trackThickness * trackInnerCornerRadiusFraction)
+ : trackInnerCornerRadius;
+ }
+
+ @Px
+ int getActualTrackStopIndicatorSize() {
+ return min(trackStopIndicatorSize, trackThickness);
+ }
+
+ @Override
+ public boolean useStrokeCap() {
+ return super.useStrokeCap() && getTrackInnerCornerRadiusInPx() == getTrackCornerRadiusInPx();
+ }
+
@Override
void validateSpec() {
super.validateSpec();
@@ -113,7 +160,9 @@ void validateSpec() {
}
if (indeterminateAnimationType
== LinearProgressIndicator.INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS) {
- if (trackCornerRadius > 0 && indicatorTrackGapSize == 0) {
+ if ((getTrackCornerRadiusInPx() > 0
+ || (hasInnerCornerRadius && getTrackInnerCornerRadiusInPx() > 0))
+ && indicatorTrackGapSize == 0) {
// Throws an exception if trying to use the cornered indicator/track with contiguous
// indeterminate animation type without gap.
throw new IllegalArgumentException(
diff --git a/lib/java/com/google/android/material/progressindicator/res-public/values/public.xml b/lib/java/com/google/android/material/progressindicator/res-public/values/public.xml
index 7deb9e55c8f..107eba44766 100644
--- a/lib/java/com/google/android/material/progressindicator/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/progressindicator/res-public/values/public.xml
@@ -36,6 +36,8 @@
+
+
diff --git a/lib/java/com/google/android/material/progressindicator/res/values/attrs.xml b/lib/java/com/google/android/material/progressindicator/res/values/attrs.xml
index 69e1e364a86..94eef038219 100644
--- a/lib/java/com/google/android/material/progressindicator/res/values/attrs.xml
+++ b/lib/java/com/google/android/material/progressindicator/res/values/attrs.xml
@@ -24,9 +24,9 @@
-
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/progressindicator/res/values/styles.xml b/lib/java/com/google/android/material/progressindicator/res/values/styles.xml
index 4130c6c5d03..7bd992941b2 100644
--- a/lib/java/com/google/android/material/progressindicator/res/values/styles.xml
+++ b/lib/java/com/google/android/material/progressindicator/res/values/styles.xml
@@ -15,37 +15,65 @@
~ limitations under the License.
-->
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
@@ -79,40 +107,39 @@
0dp
-
-
-
-
-
-
+
+
-
diff --git a/lib/java/com/google/android/material/progressindicator/res/values/tokens.xml b/lib/java/com/google/android/material/progressindicator/res/values/tokens.xml
index fc72cc865ef..0c1c106aa17 100644
--- a/lib/java/com/google/android/material/progressindicator/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/progressindicator/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
@@ -30,12 +30,18 @@
4dp4dp4dp
+ 3dp
+ 40dp
+ 20dp40dp
+ 48dp4dp4dp
+ 1.6dp
+ 15dp
diff --git a/lib/java/com/google/android/material/radiobutton/MaterialRadioButton.java b/lib/java/com/google/android/material/radiobutton/MaterialRadioButton.java
index 05d39788eae..be78a1cf227 100644
--- a/lib/java/com/google/android/material/radiobutton/MaterialRadioButton.java
+++ b/lib/java/com/google/android/material/radiobutton/MaterialRadioButton.java
@@ -64,7 +64,7 @@ public MaterialRadioButton(@NonNull Context context) {
}
public MaterialRadioButton(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, R.attr.radioButtonStyle);
+ this(context, attrs, androidx.appcompat.R.attr.radioButtonStyle);
}
public MaterialRadioButton(
@@ -122,7 +122,8 @@ public boolean isUseMaterialThemeColors() {
private ColorStateList getMaterialThemeColorsTintList() {
if (materialThemeColorsTintList == null) {
- int colorControlActivated = MaterialColors.getColor(this, R.attr.colorControlActivated);
+ int colorControlActivated =
+ MaterialColors.getColor(this, androidx.appcompat.R.attr.colorControlActivated);
int colorOnSurface = MaterialColors.getColor(this, R.attr.colorOnSurface);
int colorSurface = MaterialColors.getColor(this, R.attr.colorSurface);
diff --git a/lib/java/com/google/android/material/radiobutton/res/values/tokens.xml b/lib/java/com/google/android/material/radiobutton/res/values/tokens.xml
index dce8ea1610f..8d0bef8d084 100644
--- a/lib/java/com/google/android/material/radiobutton/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/radiobutton/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/resources/MaterialResources.java b/lib/java/com/google/android/material/resources/MaterialResources.java
index 4f8b4a68959..f0804b99dd8 100644
--- a/lib/java/com/google/android/material/resources/MaterialResources.java
+++ b/lib/java/com/google/android/material/resources/MaterialResources.java
@@ -200,9 +200,12 @@ public static int getUnscaledTextSize(
return defValue;
}
- TypedArray a = context.obtainStyledAttributes(textAppearance, R.styleable.TextAppearance);
+ TypedArray a =
+ context.obtainStyledAttributes(
+ textAppearance, androidx.appcompat.R.styleable.TextAppearance);
TypedValue v = new TypedValue();
- boolean available = a.getValue(R.styleable.TextAppearance_android_textSize, v);
+ boolean available =
+ a.getValue(androidx.appcompat.R.styleable.TextAppearance_android_textSize, v);
a.recycle();
if (!available) {
diff --git a/lib/java/com/google/android/material/resources/TextAppearance.java b/lib/java/com/google/android/material/resources/TextAppearance.java
index e19dbf21fa1..b96f5ddba26 100644
--- a/lib/java/com/google/android/material/resources/TextAppearance.java
+++ b/lib/java/com/google/android/material/resources/TextAppearance.java
@@ -86,34 +86,53 @@ public class TextAppearance {
/** Parses the given TextAppearance style resource. */
public TextAppearance(@NonNull Context context, @StyleRes int id) {
- TypedArray a = context.obtainStyledAttributes(id, R.styleable.TextAppearance);
+ TypedArray a =
+ context.obtainStyledAttributes(id, androidx.appcompat.R.styleable.TextAppearance);
- setTextSize(a.getDimension(R.styleable.TextAppearance_android_textSize, 0f));
+ setTextSize(
+ a.getDimension(
+ androidx.appcompat.R.styleable.TextAppearance_android_textSize, 0f));
setTextColor(
MaterialResources.getColorStateList(
- context, a, R.styleable.TextAppearance_android_textColor));
+ context, a, androidx.appcompat.R.styleable.TextAppearance_android_textColor));
textColorHint =
MaterialResources.getColorStateList(
- context, a, R.styleable.TextAppearance_android_textColorHint);
+ context,
+ a,
+ androidx.appcompat.R.styleable.TextAppearance_android_textColorHint);
textColorLink =
MaterialResources.getColorStateList(
- context, a, R.styleable.TextAppearance_android_textColorLink);
- textStyle = a.getInt(R.styleable.TextAppearance_android_textStyle, Typeface.NORMAL);
- typeface = a.getInt(R.styleable.TextAppearance_android_typeface, TYPEFACE_SANS);
+ context,
+ a,
+ androidx.appcompat.R.styleable.TextAppearance_android_textColorLink);
+ textStyle =
+ a.getInt(
+ androidx.appcompat.R.styleable.TextAppearance_android_textStyle,
+ Typeface.NORMAL);
+ typeface =
+ a.getInt(
+ androidx.appcompat.R.styleable.TextAppearance_android_typeface,
+ TYPEFACE_SANS);
int fontFamilyIndex =
MaterialResources.getIndexWithValue(
a,
- R.styleable.TextAppearance_fontFamily,
- R.styleable.TextAppearance_android_fontFamily);
+ androidx.appcompat.R.styleable.TextAppearance_fontFamily,
+ androidx.appcompat.R.styleable.TextAppearance_android_fontFamily);
fontFamilyResourceId = a.getResourceId(fontFamilyIndex, 0);
fontFamily = a.getString(fontFamilyIndex);
- textAllCaps = a.getBoolean(R.styleable.TextAppearance_textAllCaps, false);
+ textAllCaps =
+ a.getBoolean(androidx.appcompat.R.styleable.TextAppearance_textAllCaps, false);
shadowColor =
MaterialResources.getColorStateList(
- context, a, R.styleable.TextAppearance_android_shadowColor);
- shadowDx = a.getFloat(R.styleable.TextAppearance_android_shadowDx, 0);
- shadowDy = a.getFloat(R.styleable.TextAppearance_android_shadowDy, 0);
- shadowRadius = a.getFloat(R.styleable.TextAppearance_android_shadowRadius, 0);
+ context,
+ a,
+ androidx.appcompat.R.styleable.TextAppearance_android_shadowColor);
+ shadowDx =
+ a.getFloat(androidx.appcompat.R.styleable.TextAppearance_android_shadowDx, 0);
+ shadowDy =
+ a.getFloat(androidx.appcompat.R.styleable.TextAppearance_android_shadowDy, 0);
+ shadowRadius =
+ a.getFloat(androidx.appcompat.R.styleable.TextAppearance_android_shadowRadius, 0);
a.recycle();
diff --git a/lib/java/com/google/android/material/resources/res-public/values/public.xml b/lib/java/com/google/android/material/resources/res-public/values/public.xml
index 35ac2e43467..6ef5b310a9b 100644
--- a/lib/java/com/google/android/material/resources/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/resources/res-public/values/public.xml
@@ -44,4 +44,9 @@
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/resources/res/values-af/strings.xml b/lib/java/com/google/android/material/resources/res/values-af/strings.xml
index 611c423943a..9f1da9477c8 100644
--- a/lib/java/com/google/android/material/resources/res/values-af/strings.xml
+++ b/lib/java/com/google/android/material/resources/res/values-af/strings.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/lib/java/com/google/android/material/resources/res/values-v28/tokens.xml b/lib/java/com/google/android/material/resources/res/values-v28/tokens.xml
index dbeb8470dbb..3871c4ddd34 100644
--- a/lib/java/com/google/android/material/resources/res/values-v28/tokens.xml
+++ b/lib/java/com/google/android/material/resources/res/values-v28/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/resources/res/values-vi/strings.xml b/lib/java/com/google/android/material/resources/res/values-vi/strings.xml
index 80515822926..333e310abe9 100644
--- a/lib/java/com/google/android/material/resources/res/values-vi/strings.xml
+++ b/lib/java/com/google/android/material/resources/res/values-vi/strings.xml
@@ -1,6 +1,6 @@
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/resources/res/values/standard_toolbar_tokens.xml b/lib/java/com/google/android/material/resources/res/values/standard_toolbar_tokens.xml
new file mode 100644
index 00000000000..43dd7cc4fd5
--- /dev/null
+++ b/lib/java/com/google/android/material/resources/res/values/standard_toolbar_tokens.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+ ?attr/colorSurfaceContainer
+ ?attr/colorSurfaceContainer
+ ?attr/colorSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+
+ ?attr/colorOnSurface
+ 0.38
+ ?attr/colorOnSurface
+ 0.38
+
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+ ?attr/colorOnSurfaceVariant
+ ?attr/colorOnSecondaryContainer
+
+
diff --git a/lib/java/com/google/android/material/resources/res/values/tokens.xml b/lib/java/com/google/android/material/resources/res/values/tokens.xml
index 6e82bf6284c..b5f067f2f3b 100644
--- a/lib/java/com/google/android/material/resources/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/resources/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/resources/res/values/vibrant_toolbar_tokens.xml b/lib/java/com/google/android/material/resources/res/values/vibrant_toolbar_tokens.xml
new file mode 100644
index 00000000000..3756f4069c7
--- /dev/null
+++ b/lib/java/com/google/android/material/resources/res/values/vibrant_toolbar_tokens.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+ ?attr/colorPrimaryContainer
+ ?attr/colorPrimaryContainer
+ ?attr/colorSurfaceContainer
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+
+
+ ?attr/colorOnSurface
+ 0.38
+ ?attr/colorOnSurface
+ 0.38
+
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+ ?attr/colorOnPrimaryContainer
+ ?attr/colorOnSurface
+
+
diff --git a/lib/java/com/google/android/material/ripple/RippleDrawableCompat.java b/lib/java/com/google/android/material/ripple/RippleDrawableCompat.java
index 2c66b02c245..7a1e6bc19cd 100644
--- a/lib/java/com/google/android/material/ripple/RippleDrawableCompat.java
+++ b/lib/java/com/google/android/material/ripple/RippleDrawableCompat.java
@@ -185,8 +185,7 @@ public int getOpacity() {
}
/**
- * A {@link ConstantState} for {@link Ripple}
- *
+ * A {@link ConstantState} for {@link RippleDrawableCompat}
*/
static final class RippleDrawableCompatState extends ConstantState {
diff --git a/lib/java/com/google/android/material/ripple/RippleUtils.java b/lib/java/com/google/android/material/ripple/RippleUtils.java
index c807aaca130..a5be8e128af 100644
--- a/lib/java/com/google/android/material/ripple/RippleUtils.java
+++ b/lib/java/com/google/android/material/ripple/RippleUtils.java
@@ -16,8 +16,6 @@
package com.google.android.material.ripple;
-import com.google.android.material.R;
-
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -229,7 +227,9 @@ private static Drawable createOvalRipple(@NonNull Context context, @Px int paddi
new InsetDrawable(maskDrawable, padding, padding, padding, padding);
return new RippleDrawable(
MaterialColors.getColorStateList(
- context, R.attr.colorControlHighlight, ColorStateList.valueOf(Color.TRANSPARENT)),
+ context,
+ androidx.appcompat.R.attr.colorControlHighlight,
+ ColorStateList.valueOf(Color.TRANSPARENT)),
null,
maskWithPaddings);
}
diff --git a/lib/java/com/google/android/material/search/SearchBar.java b/lib/java/com/google/android/material/search/SearchBar.java
index 6b9bc0404f4..21f2daf7fe2 100644
--- a/lib/java/com/google/android/material/search/SearchBar.java
+++ b/lib/java/com/google/android/material/search/SearchBar.java
@@ -20,6 +20,7 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
+import static java.lang.Math.max;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
@@ -35,17 +36,18 @@
import android.os.Parcel;
import android.os.Parcelable;
import androidx.appcompat.content.res.AppCompatResources;
-import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.widget.ActionMenuView;
import androidx.appcompat.widget.Toolbar;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.EditText;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.ColorInt;
@@ -55,18 +57,20 @@
import androidx.annotation.MenuRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.Px;
import androidx.annotation.RestrictTo;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.graphics.drawable.DrawableCompat;
-import androidx.core.view.ViewCompat;
import androidx.core.widget.TextViewCompat;
import androidx.customview.view.AbsSavedState;
import com.google.android.material.appbar.AppBarLayout;
+import com.google.android.material.appbar.AppBarLayout.LiftOnScrollProgressListener;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.internal.ToolbarUtils;
+import com.google.android.material.resources.MaterialResources;
import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.MaterialShapeUtils;
import com.google.android.material.shape.ShapeAppearanceModel;
@@ -133,6 +137,12 @@ public class SearchBar extends Toolbar {
private static final String NAMESPACE_APP = "http://schemas.android.com/apk/res-auto";
private final TextView textView;
+ private final TextView placeholderTextView;
+ private final FrameLayout textViewContainer;
+ private final int backgroundColor;
+
+ private boolean liftOnScroll;
+ @Nullable private final ColorStateList liftOnScrollColor;
private final boolean layoutInflated;
private final boolean defaultMarginsEnabled;
private final SearchBarAnimationHelper searchBarAnimationHelper;
@@ -145,6 +155,29 @@ public class SearchBar extends Toolbar {
private int menuResId = -1;
private boolean defaultScrollFlagsEnabled;
private MaterialShapeDrawable backgroundShape;
+ private boolean textCentered;
+ private int maxWidth;
+ private final boolean adaptiveMaxWidthEnabled;
+ private final int adaptiveMaxWidthParentBreakpoint;
+ @Nullable private ActionMenuView menuView;
+ @Nullable private ImageButton navIconButton;
+
+ // The percentage of the available width that the SearchBar should be at after the specified
+ // breakpoint in the measure pass.
+ private static final float ADAPTIVE_MAX_WIDTH_PERCENT_AFTER_BREAKPOINT = 0.5f;
+
+ private final LiftOnScrollProgressListener liftColorListener =
+ new LiftOnScrollProgressListener() {
+
+ @Override
+ public void onUpdate(float elevation, int appBarLayoutColor, float progress) {
+ if (liftOnScrollColor != null) {
+ int mixedColor =
+ MaterialColors.layer(backgroundColor, liftOnScrollColor.getDefaultColor(), progress);
+ backgroundShape.setFillColor(ColorStateList.valueOf(mixedColor));
+ }
+ }
+ };
public SearchBar(@NonNull Context context) {
this(context, null);
@@ -160,6 +193,8 @@ public SearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int def
context = getContext();
validateAttributes(attrs);
+ adaptiveMaxWidthParentBreakpoint =
+ getResources().getDimensionPixelSize(R.dimen.m3_searchbar_parent_width_breakpoint);
defaultNavigationIcon =
AppCompatResources.getDrawable(context, getDefaultNavigationIconResource());
searchBarAnimationHelper = new SearchBarAnimationHelper();
@@ -170,7 +205,9 @@ public SearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int def
ShapeAppearanceModel shapeAppearanceModel =
ShapeAppearanceModel.builder(context, attrs, defStyleAttr, DEF_STYLE_RES).build();
- int backgroundColor = a.getColor(R.styleable.SearchBar_backgroundTint, 0);
+ backgroundColor = a.getColor(R.styleable.SearchBar_backgroundTint, 0);
+ liftOnScrollColor =
+ MaterialResources.getColorStateList(context, a, R.styleable.SearchBar_liftOnScrollColor);
float elevation = a.getDimension(R.styleable.SearchBar_elevation, 0);
defaultMarginsEnabled = a.getBoolean(R.styleable.SearchBar_defaultMarginsEnabled, true);
defaultScrollFlagsEnabled = a.getBoolean(R.styleable.SearchBar_defaultScrollFlagsEnabled, true);
@@ -186,6 +223,10 @@ public SearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int def
String hint = a.getString(R.styleable.SearchBar_android_hint);
float strokeWidth = a.getDimension(R.styleable.SearchBar_strokeWidth, -1);
int strokeColor = a.getColor(R.styleable.SearchBar_strokeColor, Color.TRANSPARENT);
+ textCentered = a.getBoolean(R.styleable.SearchBar_textCentered, false);
+ liftOnScroll = a.getBoolean(R.styleable.SearchBar_liftOnScroll, false);
+ maxWidth = a.getDimensionPixelSize(R.styleable.SearchBar_android_maxWidth, -1);
+ adaptiveMaxWidthEnabled = a.getBoolean(R.styleable.SearchBar_adaptiveMaxWidthEnabled, false);
a.recycle();
@@ -199,12 +240,18 @@ public SearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int def
layoutInflated = true;
textView = findViewById(R.id.open_search_bar_text_view);
+ placeholderTextView = findViewById(R.id.open_search_bar_placeholder_text_view);
+ textViewContainer = findViewById(R.id.open_search_bar_text_view_container);
- ViewCompat.setElevation(this, elevation);
+ setElevation(elevation);
initTextView(textAppearanceResId, text, hint);
initBackground(shapeAppearanceModel, backgroundColor, elevation, strokeWidth, strokeColor);
}
+ void setPlaceholderText(String string) {
+ placeholderTextView.setText(string);
+ }
+
private void validateAttributes(@Nullable AttributeSet attributeSet) {
if (attributeSet == null) {
return;
@@ -219,6 +266,18 @@ private void validateAttributes(@Nullable AttributeSet attributeSet) {
}
}
+ @Nullable
+ private AppBarLayout getAppBarLayoutParentIfExists() {
+ ViewParent v = getParent();
+ while (v != null) {
+ if (v instanceof AppBarLayout) {
+ return (AppBarLayout) v;
+ }
+ v = v.getParent();
+ }
+ return null;
+ }
+
private void initNavigationIcon() {
// If no navigation icon, set up the default one; otherwise, re-set it for tinting if needed.
setNavigationIcon(getNavigationIcon() == null ? defaultNavigationIcon : getNavigationIcon());
@@ -232,13 +291,11 @@ private void initNavigationIcon() {
private void initTextView(@StyleRes int textAppearanceResId, String text, String hint) {
if (textAppearanceResId != -1) {
TextViewCompat.setTextAppearance(textView, textAppearanceResId);
+ TextViewCompat.setTextAppearance(placeholderTextView, textAppearanceResId);
}
setText(text);
setHint(hint);
- if (getNavigationIcon() == null) {
- ((MarginLayoutParams) textView.getLayoutParams()).setMarginStart(getResources()
- .getDimensionPixelSize(R.dimen.m3_searchbar_text_margin_start_no_navigation_icon));
- }
+ setTextCentered(textCentered);
}
private void initBackground(
@@ -254,7 +311,8 @@ private void initBackground(
backgroundShape.setStroke(strokeWidth, strokeColor);
}
- int rippleColor = MaterialColors.getColor(this, R.attr.colorControlHighlight);
+ int rippleColor =
+ MaterialColors.getColor(this, androidx.appcompat.R.attr.colorControlHighlight);
Drawable background;
backgroundShape.setFillColor(ColorStateList.valueOf(backgroundColor));
background =
@@ -335,7 +393,7 @@ private Drawable maybeTintNavigationIcon(@Nullable Drawable navigationIcon) {
}
Drawable wrappedNavigationIcon = DrawableCompat.wrap(navigationIcon.mutate());
- DrawableCompat.setTint(wrappedNavigationIcon, navigationIconColor);
+ wrappedNavigationIcon.setTint(navigationIconColor);
return wrappedNavigationIcon;
}
@@ -364,20 +422,23 @@ private void setNavigationIconDecorative(boolean decorative) {
@Override
public void inflateMenu(@MenuRes int resId) {
- // Pause dispatching item changes during inflation to improve performance.
- Menu menu = getMenu();
- if (menu instanceof MenuBuilder) {
- ((MenuBuilder) menu).stopDispatchingItemsChanged();
- }
super.inflateMenu(resId);
this.menuResId = resId;
- if (menu instanceof MenuBuilder) {
- ((MenuBuilder) menu).startDispatchingItemsChanged();
- }
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int availableWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int measureMode = MeasureSpec.getMode(widthMeasureSpec);
+ if (maxWidth >= 0 && availableWidth > maxWidth) {
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, measureMode);
+ } else if (adaptiveMaxWidthEnabled && availableWidth > adaptiveMaxWidthParentBreakpoint) {
+ int newWidth =
+ max(
+ adaptiveMaxWidthParentBreakpoint,
+ Math.round(ADAPTIVE_MAX_WIDTH_PERCENT_AFTER_BREAKPOINT * availableWidth));
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth, measureMode);
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureCenterView(widthMeasureSpec, heightMeasureSpec);
@@ -387,8 +448,55 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- layoutCenterView();
+ if (centerView != null) {
+ layoutViewInCenter(centerView);
+ }
setHandwritingBoundsInsets();
+ if (textView != null) {
+ // If the text is centered, we need to re-layout the textview in the center explicitly instead
+ // of using View gravities because a custom center view in the Toolbar will cause the textview
+ // to be pushed to the side. In this case, we want the textview to be still centered on top of
+ // any center views.
+ if (textCentered) {
+ // Make sure textview does not overlap with any toolbar views (nav icon, menu) or
+ // padding/insets
+ layoutTextViewCenterAvoidToolbarViewsAndPadding();
+ }
+ }
+ }
+
+ /**
+ * Sets whether the {@link SearchBar} lifts when a parent {@link AppBarLayout} lifts on scroll.
+ */
+ public void setLiftOnScroll(boolean liftOnScroll) {
+ this.liftOnScroll = liftOnScroll;
+ if (liftOnScroll) {
+ addLiftOnScrollProgressListener();
+ } else {
+ removeLiftOnScrollProgressListener();
+ }
+ }
+
+ /**
+ * Returns whether or not the {@link SearchBar} lifts when a parent {@link AppBarLayout} lifts
+ * on scroll.
+ */
+ public boolean isLiftOnScroll() {
+ return liftOnScroll;
+ }
+
+ private void addLiftOnScrollProgressListener() {
+ AppBarLayout appBarLayout = getAppBarLayoutParentIfExists();
+ if (appBarLayout != null && liftOnScrollColor != null) {
+ appBarLayout.addLiftOnScrollProgressListener(liftColorListener);
+ }
+ }
+
+ private void removeLiftOnScrollProgressListener() {
+ AppBarLayout appBarLayout = getAppBarLayoutParentIfExists();
+ if (appBarLayout != null) {
+ appBarLayout.removeLiftOnScrollProgressListener(liftColorListener);
+ }
}
@Override
@@ -398,6 +506,15 @@ protected void onAttachedToWindow() {
MaterialShapeUtils.setParentAbsoluteElevation(this, backgroundShape);
setDefaultMargins();
setOrClearDefaultScrollFlags();
+ if (liftOnScroll) {
+ addLiftOnScrollProgressListener();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ removeLiftOnScrollProgressListener();
}
/**
@@ -476,20 +593,98 @@ private void measureCenterView(int widthMeasureSpec, int heightMeasureSpec) {
}
}
- private void layoutCenterView() {
- if (centerView == null) {
+ @Nullable
+ private ActionMenuView findOrGetMenuView() {
+ if (menuView == null) {
+ menuView = ToolbarUtils.getActionMenuView(this);
+ }
+ return menuView;
+ }
+
+ @Nullable
+ private ImageButton findOrGetNavView() {
+ if (navIconButton == null) {
+ navIconButton = ToolbarUtils.getNavigationIconButton(this);
+ }
+ return navIconButton;
+ }
+
+ private void layoutTextViewCenterAvoidToolbarViewsAndPadding() {
+ int textViewContainerLeft = getMeasuredWidth() / 2 - textViewContainer.getMeasuredWidth() / 2;
+ int textViewContainerRight = textViewContainerLeft + textViewContainer.getMeasuredWidth();
+ int textViewContainerTop = getMeasuredHeight() / 2 - textViewContainer.getMeasuredHeight() / 2;
+ int textViewContainerBottom = textViewContainerTop + textViewContainer.getMeasuredHeight();
+ boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ View menuView = findOrGetMenuView();
+ View navIconButton = findOrGetNavView();
+
+ int textViewLeft = textViewContainer.getMeasuredWidth() / 2 - textView.getMeasuredWidth() / 2;
+ int textViewRight = textViewLeft + textView.getMeasuredWidth();
+
+ // left and right refer to the textview's left and right coordinates within the searchbar
+ int left = textViewLeft + textViewContainerLeft;
+ int right = textViewContainerLeft + textViewRight;
+
+ View leftView = isRtl ? menuView : navIconButton;
+ View rightView = isRtl ? navIconButton : menuView;
+ int leftShift = 0;
+ int rightShift = 0;
+ if (leftView != null) {
+ leftShift = max(leftView.getRight() - left, 0);
+ }
+ left += leftShift;
+ right += leftShift;
+ if (rightView != null) {
+ rightShift = max(right - rightView.getLeft(), 0);
+ }
+ left -= rightShift;
+ right -= rightShift;
+ // Make sure to not lay out the view inside the SearchBar padding. paddingLeftAdded and
+ // paddingRightAdded will never be non-zero at the same time, as Toolbar.measure has already
+ // measured the children accounting for padding.
+ int paddingLeftShift = max(getPaddingLeft() - left, getContentInsetLeft() - left);
+ int paddingRightShift =
+ max(
+ right - (getMeasuredWidth() - getPaddingRight()),
+ right - (getMeasuredWidth() - getContentInsetRight()));
+ paddingLeftShift = max(paddingLeftShift, 0);
+ paddingRightShift = max(paddingRightShift, 0);
+
+ int totalShift = leftShift - rightShift + paddingLeftShift - paddingRightShift;
+ // Center the textViewContainer and shift over textViewContainer by the amount that textView
+ // needs to be shifted over; this shifts both the container and the textView, which is necessary so the textView doesn't get
+ // laid outside of the container.
+ textViewContainer.layout(
+ textViewContainerLeft + totalShift,
+ textViewContainerTop,
+ textViewContainerRight + totalShift,
+ textViewContainerBottom);
+ }
+
+ /**
+ * Lays out the given view in the center of the {@link SearchBar}.
+ *
+ * @param view The view to layout in the center.
+ */
+ private void layoutViewInCenter(View view) {
+ if (view == null) {
return;
}
- int centerViewWidth = centerView.getMeasuredWidth();
- int left = getMeasuredWidth() / 2 - centerViewWidth / 2;
- int right = left + centerViewWidth;
+ int viewWidth = view.getMeasuredWidth();
+ int left = getMeasuredWidth() / 2 - viewWidth / 2;
+ int right = left + viewWidth;
- int centerViewHeight = centerView.getMeasuredHeight();
- int top = getMeasuredHeight() / 2 - centerViewHeight / 2;
- int bottom = top + centerViewHeight;
+ int viewHeight = view.getMeasuredHeight();
+ int top = getMeasuredHeight() / 2 - viewHeight / 2;
+ int bottom = top + viewHeight;
- layoutChild(centerView, left, top, right, bottom);
+ layoutChild(
+ view,
+ left,
+ top,
+ right,
+ bottom);
}
private void layoutChild(View child, int left, int top, int right, int bottom) {
@@ -543,6 +738,10 @@ public void setCenterView(@Nullable View view) {
}
}
+ TextView getPlaceholderTextView() {
+ return placeholderTextView;
+ }
+
/** Returns the main {@link TextView} which can be used for hint and search text. */
@NonNull
public TextView getTextView() {
@@ -558,16 +757,42 @@ public CharSequence getText() {
/** Sets the text of main {@link TextView}. */
public void setText(@Nullable CharSequence text) {
textView.setText(text);
+ placeholderTextView.setText(text);
}
/** Sets the text of main {@link TextView}. */
public void setText(@StringRes int textResId) {
textView.setText(textResId);
+ placeholderTextView.setText(textResId);
+ }
+
+ /** Whether or not to center the text within the TextView. */
+ public void setTextCentered(boolean textCentered) {
+ this.textCentered = textCentered;
+ if (textView == null) {
+ return;
+ }
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) textView.getLayoutParams();
+ if (textCentered) {
+ lp.gravity = Gravity.CENTER_HORIZONTAL;
+ textView.setGravity(Gravity.CENTER_HORIZONTAL);
+ } else {
+ lp.gravity = Gravity.NO_GRAVITY;
+ textView.setGravity(Gravity.NO_GRAVITY);
+ }
+ textView.setLayoutParams(lp);
+ placeholderTextView.setLayoutParams(lp);
+ }
+
+ /** Whether or not the text is centered. */
+ public boolean getTextCentered() {
+ return textCentered;
}
/** Clears the text of main {@link TextView}. */
public void clearText() {
textView.setText("");
+ placeholderTextView.setText("");
}
/** Returns the hint of main {@link TextView}. */
@@ -801,7 +1026,21 @@ int getMenuResId() {
}
float getCompatElevation() {
- return backgroundShape != null ? backgroundShape.getElevation() : ViewCompat.getElevation(this);
+ return backgroundShape != null ? backgroundShape.getElevation() : getElevation();
+ }
+
+ /** Sets the max width of SearchBar in pixels. **/
+ public void setMaxWidth(@Px int maxWidth) {
+ if (this.maxWidth != maxWidth) {
+ this.maxWidth = maxWidth;
+ requestLayout();
+ }
+ }
+
+ /** Get the max width of SearchBar in pixels. **/
+ @Px
+ public int getMaxWidth() {
+ return maxWidth;
}
/** Behavior that sets up the scroll-away mode for an {@link SearchBar}. */
diff --git a/lib/java/com/google/android/material/search/SearchBarAnimationHelper.java b/lib/java/com/google/android/material/search/SearchBarAnimationHelper.java
index 65dfdd899fd..7f05385b3fc 100644
--- a/lib/java/com/google/android/material/search/SearchBarAnimationHelper.java
+++ b/lib/java/com/google/android/material/search/SearchBarAnimationHelper.java
@@ -27,7 +27,6 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.core.view.ViewCompat;
import com.google.android.material.animation.AnimatableView;
import com.google.android.material.animation.AnimationUtils;
import com.google.android.material.appbar.AppBarLayout;
@@ -350,7 +349,7 @@ private AnimatorUpdateListener getExpandedViewBackgroundUpdateListener(
MaterialShapeDrawable expandedViewBackground =
MaterialShapeDrawable.createWithElevationOverlay(expandedView.getContext());
expandedViewBackground.setCornerSize(searchBar.getCornerSize());
- expandedViewBackground.setElevation(ViewCompat.getElevation(searchBar));
+ expandedViewBackground.setElevation(searchBar.getElevation());
return valueAnimator -> {
expandedViewBackground.setInterpolation(1 - valueAnimator.getAnimatedFraction());
diff --git a/lib/java/com/google/android/material/search/SearchView.java b/lib/java/com/google/android/material/search/SearchView.java
index 660005911c1..f37ac75455b 100644
--- a/lib/java/com/google/android/material/search/SearchView.java
+++ b/lib/java/com/google/android/material/search/SearchView.java
@@ -47,6 +47,7 @@
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
+import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.activity.BackEventCompat;
import androidx.annotation.ColorInt;
@@ -60,6 +61,7 @@
import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.core.graphics.Insets;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
@@ -143,6 +145,7 @@ public class SearchView extends FrameLayout
final MaterialToolbar toolbar;
final Toolbar dummyToolbar;
final TextView searchPrefix;
+ final LinearLayout textContainer;
final EditText editText;
final ImageButton clearButton;
final View divider;
@@ -214,6 +217,7 @@ public SearchView(@NonNull Context context, @Nullable AttributeSet attrs, int de
toolbar = findViewById(R.id.open_search_view_toolbar);
dummyToolbar = findViewById(R.id.open_search_view_dummy_toolbar);
searchPrefix = findViewById(R.id.open_search_view_search_prefix);
+ textContainer = findViewById(R.id.open_search_view_text_container);
editText = findViewById(R.id.open_search_view_edit_text);
clearButton = findViewById(R.id.open_search_view_clear_button);
divider = findViewById(R.id.open_search_view_divider);
@@ -231,6 +235,10 @@ public SearchView(@NonNull Context context, @Nullable AttributeSet attrs, int de
setUpClearButton();
setUpContentOnTouchListener();
setUpInsetListeners();
+
+ // Necessary to enable keyboard navigation to the searchview contents due to toolbar being a
+ // keyboard navigation cluster from API 26+
+ setToolbarTouchscreenBlocksFocus(false);
}
@Override
@@ -284,6 +292,9 @@ public void startBackProgress(@NonNull BackEventCompat backEvent) {
if (isHiddenOrHiding() || searchBar == null) {
return;
}
+ if (searchBar != null) {
+ searchBar.setPlaceholderText(editText.getText().toString());
+ }
searchViewAnimationHelper.startBackProgress(backEvent);
}
@@ -471,7 +482,7 @@ private void updateNavigationIconIfNeeded() {
DrawableCompat.wrap(
AppCompatResources.getDrawable(getContext(), navigationIcon).mutate());
if (toolbar.getNavigationIconTint() != null) {
- DrawableCompat.setTint(navigationIconDrawable, toolbar.getNavigationIconTint());
+ navigationIconDrawable.setTint(toolbar.getNavigationIconTint());
}
DrawableCompat.setLayoutDirection(navigationIconDrawable, getLayoutDirection());
toolbar.setNavigationIcon(
@@ -508,9 +519,13 @@ private void setUpToolbarInsetListener() {
boolean isRtl = ViewUtils.isLayoutRtl(toolbar);
int paddingLeft = isRtl ? initialPadding.end : initialPadding.start;
int paddingRight = isRtl ? initialPadding.start : initialPadding.end;
- toolbar.setPadding(
- paddingLeft + insets.getSystemWindowInsetLeft(), initialPadding.top,
- paddingRight + insets.getSystemWindowInsetRight(), initialPadding.bottom);
+ Insets systemBarCutoutInsets =
+ insets.getInsets(
+ WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
+ paddingLeft += systemBarCutoutInsets.left;
+ paddingRight += systemBarCutoutInsets.right;
+
+ toolbar.setPadding(paddingLeft, initialPadding.top, paddingRight, initialPadding.bottom);
return insets;
});
}
@@ -523,7 +538,11 @@ private void setUpStatusBarSpacerInsetListener() {
ViewCompat.setOnApplyWindowInsetsListener(
statusBarSpacer,
(v, insets) -> {
- int systemWindowInsetTop = insets.getSystemWindowInsetTop();
+ int systemWindowInsetTop =
+ insets.getInsets(
+ WindowInsetsCompat.Type.systemBars()
+ | WindowInsetsCompat.Type.displayCutout())
+ .top;
setUpStatusBarSpacer(systemWindowInsetTop);
if (!statusBarSpacerEnabledOverride) {
setStatusBarSpacerEnabledInternal(systemWindowInsetTop > 0);
@@ -539,8 +558,11 @@ private void setUpDividerInsetListener() {
ViewCompat.setOnApplyWindowInsetsListener(
divider,
(v, insets) -> {
- layoutParams.leftMargin = leftMargin + insets.getSystemWindowInsetLeft();
- layoutParams.rightMargin = rightMargin + insets.getSystemWindowInsetRight();
+ Insets systemBarCutoutInsets =
+ insets.getInsets(
+ WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
+ layoutParams.leftMargin = leftMargin + systemBarCutoutInsets.left;
+ layoutParams.rightMargin = rightMargin + systemBarCutoutInsets.right;
return insets;
});
}
@@ -878,7 +900,12 @@ public void hide() {
|| currentTransitionState.equals(TransitionState.HIDING)) {
return;
}
- searchViewAnimationHelper.hide();
+ if (searchBar != null && searchBar.isAttachedToWindow()) {
+ searchBar.setPlaceholderText(editText.getText().toString());
+ searchBar.post(searchViewAnimationHelper::hide);
+ } else {
+ searchViewAnimationHelper.hide();
+ }
}
/** Updates the visibility of the {@link SearchView} without an animation. */
@@ -963,9 +990,9 @@ public void setModalForAccessibility(boolean isSearchViewModal) {
}
/**
- * Sets the 'touchscreenBlocksFocus' attribute of the nested toolbar. The attribute defaults to
- * 'true' for API level 26+. We need to set it to 'false' if keyboard navigation is needed for the
- * search results.
+ * Sets the 'touchscreenBlocksFocus' attribute of the nested toolbar. This is set to 'false' by
+ * default, which allows keyboard navigation between the search view toolbar and the search
+ * results.
*/
public void setToolbarTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
toolbar.setTouchscreenBlocksFocus(touchscreenBlocksFocus);
diff --git a/lib/java/com/google/android/material/search/SearchViewAnimationHelper.java b/lib/java/com/google/android/material/search/SearchViewAnimationHelper.java
index 17d1b68b2d2..225f2e3ffc4 100644
--- a/lib/java/com/google/android/material/search/SearchViewAnimationHelper.java
+++ b/lib/java/com/google/android/material/search/SearchViewAnimationHelper.java
@@ -30,12 +30,15 @@
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable;
import androidx.appcompat.widget.ActionMenuView;
import androidx.appcompat.widget.Toolbar;
+import android.text.TextUtils;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewParent;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
+import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.activity.BackEventCompat;
import androidx.annotation.NonNull;
@@ -92,6 +95,7 @@ class SearchViewAnimationHelper {
private final FrameLayout toolbarContainer;
private final Toolbar toolbar;
private final Toolbar dummyToolbar;
+ private final LinearLayout textContainer;
private final TextView searchPrefix;
private final EditText editText;
private final ImageButton clearButton;
@@ -116,6 +120,7 @@ class SearchViewAnimationHelper {
this.clearButton = searchView.clearButton;
this.divider = searchView.divider;
this.contentContainer = searchView.contentContainer;
+ this.textContainer = searchView.textContainer;
backHelper = new MaterialMainContainerBackHelper(rootView);
}
@@ -286,7 +291,8 @@ private AnimatorSet getExpandCollapseAnimatorSet(boolean show) {
getDummyToolbarAnimator(show),
getActionMenuViewsAlphaAnimator(show),
getEditTextAnimator(show),
- getSearchPrefixAnimator(show));
+ getSearchPrefixAnimator(show),
+ getTextAnimator(show));
animatorSet.addListener(
new AnimatorListenerAdapter() {
@Override
@@ -297,6 +303,15 @@ public void onAnimationStart(Animator animation) {
@Override
public void onAnimationEnd(Animator animation) {
setContentViewsAlpha(show ? 1 : 0);
+ // Reset edittext and searchbar textview alphas after the animations are finished since
+ // the visibilities for searchview and searchbar have been set accordingly.
+ editText.setAlpha(1);
+ if (searchBar != null) {
+ searchBar.getTextView().setAlpha(1);
+ }
+ // Reset clip bounds so it can react to the screen or layout changes.
+ editText.setClipBounds(null);
+
// After expanding or collapsing, we should reset the clip bounds so it can react to the
// screen or layout changes. Otherwise it will result in wrong clipping on the layout.
rootView.resetClipBoundsAndCornerRadii();
@@ -429,17 +444,19 @@ private AnimatorSet getButtonsTranslationAnimator(boolean show) {
}
private void addBackButtonTranslationAnimatorIfNeeded(AnimatorSet animatorSet) {
- ImageButton backButton = ToolbarUtils.getNavigationIconButton(toolbar);
- if (backButton == null) {
+ ImageButton searchViewBackButton = ToolbarUtils.getNavigationIconButton(toolbar);
+ if (searchViewBackButton == null) {
return;
}
+ ImageButton searchBarBackButton = ToolbarUtils.getNavigationIconButton(searchBar);
ValueAnimator backButtonAnimatorX =
- ValueAnimator.ofFloat(getFromTranslationXStart(backButton), 0);
- backButtonAnimatorX.addUpdateListener(MultiViewUpdateListener.translationXListener(backButton));
+ ValueAnimator.ofFloat(
+ getTranslationXBetweenViews(searchBarBackButton, searchViewBackButton), 0);
+ backButtonAnimatorX.addUpdateListener(MultiViewUpdateListener.translationXListener(searchViewBackButton));
ValueAnimator backButtonAnimatorY = ValueAnimator.ofFloat(getFromTranslationY(), 0);
- backButtonAnimatorY.addUpdateListener(MultiViewUpdateListener.translationYListener(backButton));
+ backButtonAnimatorY.addUpdateListener(MultiViewUpdateListener.translationYListener(searchViewBackButton));
animatorSet.playTogether(backButtonAnimatorX, backButtonAnimatorY);
}
@@ -454,11 +471,25 @@ private void addBackButtonProgressAnimatorIfNeeded(AnimatorSet animatorSet) {
if (searchView.isAnimatedNavigationIcon()) {
addDrawerArrowDrawableAnimatorIfNeeded(animatorSet, drawable);
addFadeThroughDrawableAnimatorIfNeeded(animatorSet, drawable);
+ addBackButtonAnimatorIfNeeded(animatorSet, backButton);
} else {
setFullDrawableProgressIfNeeded(drawable);
}
}
+ private void addBackButtonAnimatorIfNeeded(AnimatorSet animatorSet, ImageButton backButton) {
+ // If there's no navigation icon on the search bar, we should set the alpha for the button
+ // itself instead of the drawables since the button background has a ripple.
+ if (searchBar == null || searchBar.getNavigationIcon() != null) {
+ return;
+ }
+
+ ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addUpdateListener(
+ animation -> backButton.setAlpha((Float) animation.getAnimatedValue()));
+ animatorSet.playTogether(animator);
+ }
+
private void addDrawerArrowDrawableAnimatorIfNeeded(AnimatorSet animatorSet, Drawable drawable) {
if (drawable instanceof DrawerArrowDrawable) {
DrawerArrowDrawable drawerArrowDrawable = (DrawerArrowDrawable) drawable;
@@ -489,29 +520,39 @@ private void setFullDrawableProgressIfNeeded(Drawable drawable) {
}
private void addActionMenuViewAnimatorIfNeeded(AnimatorSet animatorSet) {
- ActionMenuView actionMenuView = ToolbarUtils.getActionMenuView(toolbar);
- if (actionMenuView == null) {
+ ActionMenuView searchViewActionMenuView = ToolbarUtils.getActionMenuView(toolbar);
+ if (searchViewActionMenuView == null) {
return;
}
+ ActionMenuView searchBarActionMenuView = ToolbarUtils.getActionMenuView(searchBar);
ValueAnimator actionMenuViewAnimatorX =
- ValueAnimator.ofFloat(getFromTranslationXEnd(actionMenuView), 0);
+ ValueAnimator.ofFloat(
+ getTranslationXBetweenViews(searchBarActionMenuView, searchViewActionMenuView), 0);
actionMenuViewAnimatorX.addUpdateListener(
- MultiViewUpdateListener.translationXListener(actionMenuView));
+ MultiViewUpdateListener.translationXListener(searchViewActionMenuView));
ValueAnimator actionMenuViewAnimatorY = ValueAnimator.ofFloat(getFromTranslationY(), 0);
actionMenuViewAnimatorY.addUpdateListener(
- MultiViewUpdateListener.translationYListener(actionMenuView));
+ MultiViewUpdateListener.translationYListener(searchViewActionMenuView));
animatorSet.playTogether(actionMenuViewAnimatorX, actionMenuViewAnimatorY);
}
private Animator getDummyToolbarAnimator(boolean show) {
- return getTranslationAnimator(show, false, dummyToolbar);
+ return getTranslationAnimator(
+ show,
+ dummyToolbar,
+ getFromTranslationXEnd(dummyToolbar),
+ getFromTranslationY());
}
private Animator getHeaderContainerAnimator(boolean show) {
- return getTranslationAnimator(show, false, headerContainer);
+ return getTranslationAnimator(
+ show,
+ headerContainer,
+ getFromTranslationXEnd(headerContainer),
+ getFromTranslationY());
}
private Animator getActionMenuViewsAlphaAnimator(boolean show) {
@@ -531,11 +572,88 @@ private Animator getActionMenuViewsAlphaAnimator(boolean show) {
}
private Animator getSearchPrefixAnimator(boolean show) {
- return getTranslationAnimator(show, true, searchPrefix);
+ return getTranslationAnimatorForText(show, searchPrefix);
}
private Animator getEditTextAnimator(boolean show) {
- return getTranslationAnimator(show, true, editText);
+ return getTranslationAnimatorForText(show, editText);
+ }
+
+ private AnimatorSet getTextAnimator(boolean show) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ addTextFadeAnimatorIfNeeded(animatorSet);
+ addEditTextClipAnimator(animatorSet);
+ animatorSet.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS);
+ animatorSet.setInterpolator(
+ ReversableAnimatedValueInterpolator.of(show, AnimationUtils.LINEAR_INTERPOLATOR));
+ return animatorSet;
+ }
+
+ private void addEditTextClipAnimator(AnimatorSet animatorSet) {
+ // We only want to add a clip animation if the edittext and searchbar text is the same, which
+ // means it is translating instead of fading.
+ if (searchBar == null || !TextUtils.equals(editText.getText(), searchBar.getText())) {
+ return;
+ }
+ Rect editTextClipBounds =
+ new Rect(0, 0, editText.getWidth(), editText.getHeight());
+ ValueAnimator animator =
+ ValueAnimator.ofInt(
+ searchBar.getTextView().getWidth(), editText.getWidth());
+ animator.addUpdateListener(
+ animation -> {
+ editTextClipBounds.right = (int) animation.getAnimatedValue();
+ editText.setClipBounds(editTextClipBounds);
+ });
+ animatorSet.playTogether(animator);
+ }
+
+ private void addTextFadeAnimatorIfNeeded(AnimatorSet animatorSet) {
+ if (searchBar == null || TextUtils.equals(editText.getText(), searchBar.getText())) {
+ return;
+ }
+ // If the searchbar text is not equal to the searchview edittext, we want to fade out the
+ // edittext and fade in the searchbar text
+ ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addUpdateListener(
+ animation -> {
+ editText.setAlpha((Float) animation.getAnimatedValue());
+ searchBar.getTextView().setAlpha(1 - (Float) animation.getAnimatedValue());
+ });
+ animatorSet.playTogether(animator);
+ }
+
+ private Animator getTranslationAnimatorForText(boolean show, View v) {
+ TextView textView = searchBar.getPlaceholderTextView();
+ // If the placeholder text is empty, we animate to the searchbar textview instead.
+ // Or if we're showing the searchview, we always animate from the searchbar textview, not
+ // from the placeholder text.
+ if (TextUtils.isEmpty(textView.getText()) || show) {
+ textView = searchBar.getTextView();
+ }
+ int startX =
+ getViewLeftFromSearchViewParent(textView) - (v.getLeft() + textContainer.getLeft());
+ return getTranslationAnimator(show, v, startX, getFromTranslationY());
+ }
+
+ private int getViewLeftFromSearchViewParent(@NonNull View v) {
+ int left = v.getLeft();
+ ViewParent viewParent = v.getParent();
+ while (viewParent instanceof View && viewParent != searchView.getParent()) {
+ left += ((View) viewParent).getLeft();
+ viewParent = viewParent.getParent();
+ }
+ return left;
+ }
+
+ private int getViewTopFromSearchViewParent(@NonNull View v) {
+ int top = v.getTop();
+ ViewParent viewParent = v.getParent();
+ while (viewParent instanceof View && viewParent != searchView.getParent()) {
+ top += ((View) viewParent).getTop();
+ viewParent = viewParent.getParent();
+ }
+ return top;
}
private Animator getContentAnimator(boolean show) {
@@ -581,12 +699,11 @@ private Animator getContentScaleAnimator(boolean show) {
return animatorScale;
}
- private Animator getTranslationAnimator(boolean show, boolean anchoredToStart, View view) {
- int startX = anchoredToStart ? getFromTranslationXStart(view) : getFromTranslationXEnd(view);
+ private Animator getTranslationAnimator(boolean show, View view, int startX, int startY) {
ValueAnimator animatorX = ValueAnimator.ofFloat(startX, 0);
animatorX.addUpdateListener(MultiViewUpdateListener.translationXListener(view));
- ValueAnimator animatorY = ValueAnimator.ofFloat(getFromTranslationY(), 0);
+ ValueAnimator animatorY = ValueAnimator.ofFloat(startY, 0);
animatorY.addUpdateListener(MultiViewUpdateListener.translationYListener(view));
AnimatorSet animatorSet = new AnimatorSet();
@@ -597,24 +714,39 @@ private Animator getTranslationAnimator(boolean show, boolean anchoredToStart, V
return animatorSet;
}
- private int getFromTranslationXStart(View view) {
- int marginStart = ((MarginLayoutParams) view.getLayoutParams()).getMarginStart();
- int paddingStart = searchBar.getPaddingStart();
- return ViewUtils.isLayoutRtl(searchBar)
- ? searchBar.getWidth() - searchBar.getRight() + marginStart - paddingStart
- : searchBar.getLeft() - marginStart + paddingStart;
+ private int getTranslationXBetweenViews(
+ @Nullable View searchBarSubView, @NonNull View searchViewSubView) {
+ // If there is no equivalent for the SearchView subview in the SearchBar, we return the
+ // translation between the SearchBar and the start of the SearchView subview
+ if (searchBarSubView == null) {
+ int marginStart = ((MarginLayoutParams) searchViewSubView.getLayoutParams()).getMarginStart();
+ int paddingStart = searchBar.getPaddingStart();
+ int searchBarLeft = getViewLeftFromSearchViewParent(searchBar);
+ return ViewUtils.isLayoutRtl(searchBar)
+ ? searchBarLeft
+ + searchBar.getWidth()
+ + marginStart
+ - paddingStart
+ - searchView.getRight()
+ : (searchBarLeft - marginStart + paddingStart);
+ }
+ return getViewLeftFromSearchViewParent(searchBarSubView)
+ - getViewLeftFromSearchViewParent(searchViewSubView);
}
private int getFromTranslationXEnd(View view) {
int marginEnd = ((MarginLayoutParams) view.getLayoutParams()).getMarginEnd();
+ int viewLeft = getViewLeftFromSearchViewParent(searchBar);
return ViewUtils.isLayoutRtl(searchBar)
- ? searchBar.getLeft() - marginEnd
- : searchBar.getRight() - searchView.getWidth() + marginEnd;
+ ? viewLeft - marginEnd
+ : viewLeft + searchBar.getWidth() + marginEnd - searchView.getWidth();
}
private int getFromTranslationY() {
- int toolbarMiddleY = (toolbarContainer.getTop() + toolbarContainer.getBottom()) / 2;
- int searchBarMiddleY = (searchBar.getTop() + searchBar.getBottom()) / 2;
+ int toolbarMiddleY = toolbarContainer.getTop() + toolbarContainer.getHeight() / 2;
+ int searchBarMiddleY =
+ getViewTopFromSearchViewParent(searchBar)
+ + searchBar.getHeight() / 2;
return searchBarMiddleY - toolbarMiddleY;
}
diff --git a/lib/java/com/google/android/material/search/res-public/values/public.xml b/lib/java/com/google/android/material/search/res-public/values/public.xml
index 7fe602eb5f5..d74d9b3ec21 100644
--- a/lib/java/com/google/android/material/search/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/search/res-public/values/public.xml
@@ -20,11 +20,15 @@
+
+
+
+
@@ -38,4 +42,10 @@
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml b/lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml
index dda3fd94b6d..8843dd65baa 100644
--- a/lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml
+++ b/lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml
@@ -16,10 +16,24 @@
-->
-
+
+
+
diff --git a/lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml b/lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml
index cdd5133062b..b26b71776da 100644
--- a/lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml
+++ b/lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml
@@ -74,6 +74,7 @@
app:navigationContentDescription="@string/searchview_navigation_content_description">
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/search/res/values/dimens.xml b/lib/java/com/google/android/material/search/res/values/dimens.xml
index 18327fe3ed7..1c089e4f0fe 100644
--- a/lib/java/com/google/android/material/search/res/values/dimens.xml
+++ b/lib/java/com/google/android/material/search/res/values/dimens.xml
@@ -28,4 +28,6 @@
@dimen/m3_comp_search_view_container_elevation1dp
+ 312dp
+
diff --git a/lib/java/com/google/android/material/search/res/values/styles.xml b/lib/java/com/google/android/material/search/res/values/styles.xml
index 72d92800e03..b28e46c426f 100644
--- a/lib/java/com/google/android/material/search/res/values/styles.xml
+++ b/lib/java/com/google/android/material/search/res/values/styles.xml
@@ -14,7 +14,70 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ 0dp
+ 4dp
+ 8dp
+ 12dp
+ 16dp
+ 20dp
+ 28dp
+ 32dp
+ 48dp
diff --git a/lib/java/com/google/android/material/sidesheet/SheetDialog.java b/lib/java/com/google/android/material/sidesheet/SheetDialog.java
index 38f5c95f020..a2a728c0f77 100644
--- a/lib/java/com/google/android/material/sidesheet/SheetDialog.java
+++ b/lib/java/com/google/android/material/sidesheet/SheetDialog.java
@@ -41,6 +41,7 @@
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.google.android.material.motion.MaterialBackOrchestrator;
import com.google.android.material.sidesheet.Sheet.StableSheetState;
@@ -56,6 +57,7 @@ abstract class SheetDialog extends AppCompatDialog {
@Nullable private Sheet behavior;
@Nullable private FrameLayout container;
+ @Nullable private CoordinatorLayout coordinator;
@Nullable private FrameLayout sheet;
boolean dismissWithAnimation;
@@ -63,6 +65,7 @@ abstract class SheetDialog extends AppCompatDialog {
boolean cancelable = true;
private boolean canceledOnTouchOutside = true;
private boolean canceledOnTouchOutsideSet;
+ private boolean fitsSystemWindows = true;
@Nullable private MaterialBackOrchestrator backOrchestrator;
@@ -122,6 +125,19 @@ public void setCancelable(boolean cancelable) {
}
}
+ private void updateFitsSystemWindows() {
+ if (container != null) {
+ container.setFitsSystemWindows(fitsSystemWindows);
+ }
+ if (coordinator != null) {
+ coordinator.setFitsSystemWindows(fitsSystemWindows);
+ }
+ Window window = getWindow();
+ if (window != null) {
+ WindowCompat.setDecorFitsSystemWindows(window, fitsSystemWindows);
+ }
+ }
+
private void updateListeningForBackCallbacks() {
if (backOrchestrator == null) {
return;
@@ -145,6 +161,7 @@ protected void onStart() {
public void onAttachedToWindow() {
super.onAttachedToWindow();
maybeUpdateWindowAnimationsBasedOnLayoutDirection();
+ updateFitsSystemWindows();
updateListeningForBackCallbacks();
}
@@ -206,6 +223,19 @@ public boolean isDismissWithSheetAnimationEnabled() {
return dismissWithAnimation;
}
+ /**
+ * Sets whether or not this sheet dialog should account for system screen decorations
+ * such as the status bar and inset its content. Default is true.
+ *
+ *
This will forward the {@code fitsSystemWindows} to the sheet dialog's child views via
+ * {@link View#setFitsSystemWindows(boolean)}, as well as the sheet dialog's window via
+ * {@link WindowCompat#setDecorFitsSystemWindows(Window, boolean)}.
+ */
+ public void setFitsSystemWindows(boolean fitsSystemWindows) {
+ this.fitsSystemWindows = fitsSystemWindows;
+ updateFitsSystemWindows();
+ }
+
/** Creates the container layout which must exist to find the behavior */
private void ensureContainerAndBehavior() {
if (container == null) {
@@ -247,7 +277,7 @@ Sheet getBehavior() {
private View wrapInSheet(
int layoutResId, @Nullable View view, @Nullable ViewGroup.LayoutParams params) {
ensureContainerAndBehavior();
- CoordinatorLayout coordinator = getContainer().findViewById(COORDINATOR_LAYOUT_ID);
+ coordinator = getContainer().findViewById(COORDINATOR_LAYOUT_ID);
if (layoutResId != 0 && view == null) {
view = getLayoutInflater().inflate(layoutResId, coordinator, false);
diff --git a/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java b/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java
index d425690449b..73e4e6b44f5 100644
--- a/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java
+++ b/lib/java/com/google/android/material/sidesheet/SideSheetBehavior.java
@@ -365,8 +365,7 @@ public boolean onLayoutChild(
if (materialShapeDrawable != null) {
child.setBackground(materialShapeDrawable);
// Use elevation attr if set on side sheet; otherwise, use elevation of child view.
- materialShapeDrawable.setElevation(
- elevation == -1 ? ViewCompat.getElevation(child) : elevation);
+ materialShapeDrawable.setElevation(elevation == -1 ? child.getElevation() : elevation);
} else if (backgroundTint != null) {
ViewCompat.setBackgroundTintList(child, backgroundTint);
}
diff --git a/lib/java/com/google/android/material/sidesheet/res/values-af/strings.xml b/lib/java/com/google/android/material/sidesheet/res/values-af/strings.xml
index e78e48fc4b5..659dee05df6 100644
--- a/lib/java/com/google/android/material/sidesheet/res/values-af/strings.xml
+++ b/lib/java/com/google/android/material/sidesheet/res/values-af/strings.xml
@@ -1,6 +1,6 @@
end
- @dimen/m3_side_sheet_standard_elevation
+ @dimen/m3_side_sheet_standard_elevation@style/ShapeAppearance.M3.Comp.Sheet.Side.Docked.Container.Shape
@@ -45,7 +45,7 @@
explicitly on the modal side sheet View. -->
@dimen/m3_side_sheet_widthmatch_parent
- @dimen/m3_side_sheet_modal_elevation
+ @dimen/m3_side_sheet_modal_elevation@macro/m3_comp_sheet_side_docked_modal_container_color@macro/m3_comp_sheet_side_docked_modal_container_shape
diff --git a/lib/java/com/google/android/material/sidesheet/res/values/tokens.xml b/lib/java/com/google/android/material/sidesheet/res/values/tokens.xml
index 11c37485482..97388c1ffe1 100644
--- a/lib/java/com/google/android/material/sidesheet/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/sidesheet/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/slider/BaseSlider.java b/lib/java/com/google/android/material/slider/BaseSlider.java
index 87607b55b66..bf455830157 100644
--- a/lib/java/com/google/android/material/slider/BaseSlider.java
+++ b/lib/java/com/google/android/material/slider/BaseSlider.java
@@ -29,6 +29,9 @@
import static com.google.android.material.slider.LabelFormatter.LABEL_WITHIN_BOUNDS;
import static com.google.android.material.slider.SliderOrientation.HORIZONTAL;
import static com.google.android.material.slider.SliderOrientation.VERTICAL;
+import static com.google.android.material.slider.TickVisibilityMode.TICK_VISIBILITY_AUTO_HIDE;
+import static com.google.android.material.slider.TickVisibilityMode.TICK_VISIBILITY_AUTO_LIMIT;
+import static com.google.android.material.slider.TickVisibilityMode.TICK_VISIBILITY_HIDDEN;
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
import static java.lang.Float.compare;
import static java.lang.Math.abs;
@@ -64,6 +67,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import androidx.appcompat.content.res.AppCompatResources;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
@@ -71,15 +75,16 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.ViewOverlay;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.SeekBar;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DimenRes;
-import androidx.annotation.Dimension;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
@@ -96,9 +101,9 @@
import com.google.android.material.drawable.DrawableUtils;
import com.google.android.material.internal.DescendantOffsetUtils;
import com.google.android.material.internal.ThemeEnforcement;
-import com.google.android.material.internal.ViewOverlayImpl;
import com.google.android.material.internal.ViewUtils;
import com.google.android.material.motion.MotionUtils;
+import com.google.android.material.resources.MaterialAttributes;
import com.google.android.material.resources.MaterialResources;
import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.ShapeAppearanceModel;
@@ -157,8 +162,10 @@
* discrete mode. This is a short hand for setting both the {@code tickColorActive} and {@code
* tickColorInactive} to the same thing. This takes precedence over {@code tickColorActive}
* and {@code tickColorInactive}.
- *
{@code tickVisible}: Whether to show the tick marks. Only used when the slider is in
- * discrete mode.
+ *
{@code tickVisible} (deprecated, use {@code tickVisibilityMode} instead): Whether to
+ * show the tick marks. Only used when the slider is in discrete mode.
+ *
{@code tickVisibilityMode}: Mode to specify the visibility of tick marks. Only used when
+ * the slider is in discrete mode.
*
{@code trackColorActive}: The color of the active part of the track.
*
{@code trackColorInactive}: The color of the inactive part of the track.
*
{@code trackColor}: The color of the whole track. This is a short hand for setting both the
@@ -210,6 +217,7 @@
* @attr ref com.google.android.material.R.styleable#Slider_tickColorActive
* @attr ref com.google.android.material.R.styleable#Slider_tickColorInactive
* @attr ref com.google.android.material.R.styleable#Slider_tickVisible
+ * @attr ref com.google.android.material.R.styleable#Slider_tickVisibilityMode
* @attr ref com.google.android.material.R.styleable#Slider_trackColor
* @attr ref com.google.android.material.R.styleable#Slider_trackColorActive
* @attr ref com.google.android.material.R.styleable#Slider_trackColorInactive
@@ -240,8 +248,6 @@ abstract class BaseSlider<
+ " stepSize(%s)";
private static final String EXCEPTION_ILLEGAL_VALUE_FROM =
"valueFrom(%s) must be smaller than valueTo(%s)";
- private static final String EXCEPTION_ILLEGAL_VALUE_TO =
- "valueTo(%s) must be greater than valueFrom(%s)";
private static final String EXCEPTION_ILLEGAL_STEP_SIZE =
"The stepSize(%s) must be 0, or a factor of the valueFrom(%s)-valueTo(%s) range";
private static final String EXCEPTION_ILLEGAL_MIN_SEPARATION =
@@ -251,6 +257,8 @@ abstract class BaseSlider<
private static final String EXCEPTION_ILLEGAL_MIN_SEPARATION_STEP_SIZE =
"minSeparation(%s) must be greater or equal and a multiple of stepSize(%s) when using"
+ " stepSize(%s)";
+ private static final String EXCEPTION_ILLEGAL_CONTINUOUS_MODE_TICK_COUNT =
+ "The continuousModeTickCount(%s) must be greater than or equal to 0";
private static final String WARNING_FLOATING_POINT_ERROR =
"Floating point value used for %s(%s). Using floats can have rounding errors which may"
+ " result in incorrect values. Instead, consider using integers with a custom"
@@ -265,6 +273,7 @@ abstract class BaseSlider<
private static final double THRESHOLD = .0001;
private static final float THUMB_WIDTH_PRESSED_RATIO = .5f;
private static final int TRACK_CORNER_SIZE_UNSET = -1;
+ private static final float TOUCH_SLOP_RATIO = .8f;
static final int DEF_STYLE_RES = R.style.Widget_MaterialComponents_Slider;
static final int UNIT_VALUE = 1;
@@ -279,8 +288,14 @@ abstract class BaseSlider<
private static final int LABEL_ANIMATION_EXIT_EASING_ATTR =
R.attr.motionEasingEmphasizedAccelerateInterpolator;
- @Dimension(unit = Dimension.DP)
- private static final int MIN_TOUCH_TARGET_DP = 48;
+ private static final float TOP_LABEL_PIVOT_X = 0.5f;
+ private static final float TOP_LABEL_PIVOT_Y = 1.2f;
+
+ private static final float LEFT_LABEL_PIVOT_X = 1.2f;
+ private static final float LEFT_LABEL_PIVOT_Y = 0.5f;
+
+ private static final float RIGHT_LABEL_PIVOT_X = -0.2f;
+ private static final float RIGHT_LABEL_PIVOT_Y = 0.5f;
@NonNull private final Paint inactiveTrackPaint;
@NonNull private final Paint activeTrackPaint;
@@ -329,15 +344,22 @@ abstract class BaseSlider<
private int trackStopIndicatorSize;
private int trackCornerSize;
private int trackInsideCornerSize;
+ private boolean centered = false;
@Nullable private Drawable trackIconActiveStart;
+ private boolean trackIconActiveStartMutated = false;
@Nullable private Drawable trackIconActiveEnd;
+ private boolean trackIconActiveEndMutated = false;
@Nullable private ColorStateList trackIconActiveColor;
@Nullable private Drawable trackIconInactiveStart;
+ private boolean trackIconInactiveStartMutated = false;
@Nullable private Drawable trackIconInactiveEnd;
+ private boolean trackIconInactiveEndMutated = false;
@Nullable private ColorStateList trackIconInactiveColor;
@Px private int trackIconSize;
+ @Px private int trackIconPadding;
private int labelPadding;
- private float touchDownX;
+ private float touchDownAxis1;
+ private float touchDownAxis2;
private MotionEvent lastEvent;
private LabelFormatter formatter;
private boolean thumbIsPressed = false;
@@ -351,8 +373,9 @@ abstract class BaseSlider<
// The index of the currently focused thumb.
private int focusedThumbIdx = -1;
private float stepSize = 0.0f;
+ private int continuousModeTickCount = 0;
private float[] ticksCoordinates;
- private boolean tickVisible = true;
+ private int tickVisibilityMode;
private int tickActiveRadius;
private int tickInactiveRadius;
private int trackWidth;
@@ -368,7 +391,8 @@ abstract class BaseSlider<
@NonNull private final Path trackPath = new Path();
@NonNull private final RectF activeTrackRect = new RectF();
- @NonNull private final RectF inactiveTrackRect = new RectF();
+ @NonNull private final RectF inactiveTrackLeftRect = new RectF();
+ @NonNull private final RectF inactiveTrackRightRect = new RectF();
@NonNull private final RectF cornerRect = new RectF();
@NonNull private final Rect labelRect = new Rect();
@NonNull private final RectF iconRectF = new RectF();
@@ -504,6 +528,8 @@ private void loadResources(@NonNull Resources resources) {
minTickSpacing = resources.getDimensionPixelSize(R.dimen.mtrl_slider_tick_min_spacing);
labelPadding = resources.getDimensionPixelSize(R.dimen.mtrl_slider_label_padding);
+
+ trackIconPadding = resources.getDimensionPixelOffset(R.dimen.m3_slider_track_icon_padding);
}
private void processAttributes(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -519,10 +545,11 @@ private void processAttributes(Context context, AttributeSet attrs, int defStyle
valueFrom = a.getFloat(R.styleable.Slider_android_valueFrom, 0.0f);
valueTo = a.getFloat(R.styleable.Slider_android_valueTo, 1.0f);
setValues(valueFrom);
+ setCentered(a.getBoolean(R.styleable.Slider_centered, false));
stepSize = a.getFloat(R.styleable.Slider_android_stepSize, 0.0f);
+ continuousModeTickCount = a.getInt(R.styleable.Slider_continuousModeTickCount, 0);
- float defaultMinTouchTargetSize =
- (float) Math.ceil(ViewUtils.dpToPx(getContext(), MIN_TOUCH_TARGET_DP));
+ float defaultMinTouchTargetSize = MaterialAttributes.resolveMinimumAccessibleTouchTarget(context);
minTouchTargetSize =
(int)
Math.ceil(
@@ -566,7 +593,11 @@ private void processAttributes(Context context, AttributeSet attrs, int defStyle
? haloColor
: AppCompatResources.getColorStateList(context, R.color.material_slider_halo_color));
- tickVisible = a.getBoolean(R.styleable.Slider_tickVisible, true);
+ tickVisibilityMode =
+ a.hasValue(R.styleable.Slider_tickVisibilityMode)
+ ? a.getInt(R.styleable.Slider_tickVisibilityMode, -1)
+ : convertToTickVisibilityMode(a.getBoolean(R.styleable.Slider_tickVisible, true));
+
boolean hasTickColor = a.hasValue(R.styleable.Slider_tickColor);
int tickColorInactiveRes =
hasTickColor ? R.styleable.Slider_tickColor : R.styleable.Slider_tickColorInactive;
@@ -653,20 +684,6 @@ private boolean maybeIncreaseTrackSidePadding() {
return true;
}
- private void validateValueFrom() {
- if (valueFrom >= valueTo) {
- throw new IllegalStateException(
- String.format(EXCEPTION_ILLEGAL_VALUE_FROM, valueFrom, valueTo));
- }
- }
-
- private void validateValueTo() {
- if (valueTo <= valueFrom) {
- throw new IllegalStateException(
- String.format(EXCEPTION_ILLEGAL_VALUE_TO, valueTo, valueFrom));
- }
- }
-
private boolean valueLandsOnTick(float value) {
// Check that the value is a multiple of stepSize given the offset of valueFrom.
double result =
@@ -695,6 +712,11 @@ private void validateStepSize() {
}
private void validateValues() {
+ if (valueFrom >= valueTo) {
+ throw new IllegalStateException(
+ String.format(EXCEPTION_ILLEGAL_VALUE_FROM, valueFrom, valueTo));
+ }
+
for (Float value : values) {
if (value < valueFrom || value > valueTo) {
throw new IllegalStateException(
@@ -748,10 +770,8 @@ private void warnAboutFloatingPointError() {
private void validateConfigurationIfDirty() {
if (dirtyConfig) {
- validateValueFrom();
- validateValueTo();
- validateStepSize();
validateValues();
+ validateStepSize();
validateMinSeparation();
warnAboutFloatingPointError();
dirtyConfig = false;
@@ -961,6 +981,40 @@ public void setStepSize(float stepSize) {
}
}
+ /**
+ * Returns the tick count used in continuous mode.
+ *
+ * @see #setContinuousModeTickCount(int)
+ * @attr ref com.google.android.material.R.styleable#Slider_continuousModeTickCount
+ */
+ public int getContinuousModeTickCount() {
+ return continuousModeTickCount;
+ }
+
+ /**
+ * Sets the number of ticks to display in continuous mode. Default is 0.
+ *
+ *
This allows for showing purely visual ticks in continuous mode.
+ *
+ *
Setting this value to a negative value will result in an {@link IllegalArgumentException}.
+ *
+ * @param continuousModeTickCount The number of ticks that must be drawn in continuous mode count
+ * @throws IllegalArgumentException If the continuous mode tick count is less than 0
+ * @see #getContinuousModeTickCount()
+ * @attr ref com.google.android.material.R.styleable#Slider_continuousModeTickCount
+ */
+ public void setContinuousModeTickCount(int continuousModeTickCount) {
+ if (continuousModeTickCount < 0) {
+ throw new IllegalArgumentException(
+ String.format(EXCEPTION_ILLEGAL_CONTINUOUS_MODE_TICK_COUNT, continuousModeTickCount));
+ }
+ if (this.continuousModeTickCount != continuousModeTickCount) {
+ this.continuousModeTickCount = continuousModeTickCount;
+ dirtyConfig = true;
+ postInvalidate();
+ }
+ }
+
/**
* Sets the custom thumb drawable which will be used for all value positions. Note that the custom
* drawable provided will be resized to match the thumb radius set by {@link #setThumbRadius(int)}
@@ -1473,7 +1527,7 @@ public int getLabelBehavior() {
public void setLabelBehavior(@LabelBehavior int labelBehavior) {
if (this.labelBehavior != labelBehavior) {
this.labelBehavior = labelBehavior;
- requestLayout();
+ updateWidgetLayout(true);
}
}
@@ -1607,7 +1661,7 @@ private boolean maybeIncreaseWidgetThickness() {
}
private void updateRotationMatrix() {
- float pivot = widgetThickness / 2f;
+ float pivot = calculateTrackCenter();
rotationMatrix.reset();
rotationMatrix.setRotate(90, pivot, pivot);
}
@@ -1768,11 +1822,19 @@ public void setTickInactiveTintList(@NonNull ColorStateList tickColor) {
/**
* Returns whether the tick marks are visible. Only used when the slider is in discrete mode.
*
- * @see #setTickVisible(boolean)
* @attr ref com.google.android.material.R.styleable#Slider_tickVisible
*/
public boolean isTickVisible() {
- return tickVisible;
+ switch (tickVisibilityMode) {
+ case TICK_VISIBILITY_AUTO_LIMIT:
+ return true;
+ case TICK_VISIBILITY_AUTO_HIDE:
+ return getDesiredTickCount() <= getMaxTickCount();
+ case TICK_VISIBILITY_HIDDEN:
+ return false;
+ default:
+ throw new IllegalStateException("Unexpected tickVisibilityMode: " + tickVisibilityMode);
+ }
}
/**
@@ -1780,10 +1842,38 @@ public boolean isTickVisible() {
*
* @param tickVisible The visibility of tick marks.
* @attr ref com.google.android.material.R.styleable#Slider_tickVisible
+ * @deprecated Use {@link #setTickVisibilityMode(int)} instead.
*/
+ @Deprecated
public void setTickVisible(boolean tickVisible) {
- if (this.tickVisible != tickVisible) {
- this.tickVisible = tickVisible;
+ setTickVisibilityMode(convertToTickVisibilityMode(tickVisible));
+ }
+
+ @TickVisibilityMode
+ private int convertToTickVisibilityMode(boolean tickVisible) {
+ return tickVisible ? TICK_VISIBILITY_AUTO_LIMIT : TICK_VISIBILITY_HIDDEN;
+ }
+
+ /**
+ * Returns the current tick visibility mode.
+ *
+ * @see #setTickVisibilityMode(int)
+ * @attr ref com.google.android.material.R.styleable#Slider_tickVisibilityMode
+ */
+ @TickVisibilityMode
+ public int getTickVisibilityMode() {
+ return tickVisibilityMode;
+ }
+
+ /**
+ * Sets the tick visibility mode. Only used when the slider is in discrete mode.
+ *
+ * @see #getTickVisibilityMode()
+ * @attr ref com.google.android.material.R.styleable#Slider_tickVisibilityMode
+ */
+ public void setTickVisibilityMode(@TickVisibilityMode int tickVisibilityMode) {
+ if (this.tickVisibilityMode != tickVisibilityMode) {
+ this.tickVisibilityMode = tickVisibilityMode;
postInvalidate();
}
}
@@ -1991,13 +2081,29 @@ public void setTrackInsideCornerSize(@Px int cornerSize) {
* @see #getTrackIconActiveStart()
*/
public void setTrackIconActiveStart(@Nullable Drawable icon) {
- if (this.trackIconActiveStart == icon) {
+ if (icon == trackIconActiveStart) {
return;
}
- this.trackIconActiveStart = icon;
+
+ trackIconActiveStart = icon;
+ trackIconActiveStartMutated = false;
+ updateTrackIconActiveStart();
invalidate();
}
+ private void updateTrackIconActiveStart() {
+ if (trackIconActiveStart != null) {
+ if (!trackIconActiveStartMutated && trackIconActiveColor != null) {
+ trackIconActiveStart = DrawableCompat.wrap(trackIconActiveStart).mutate();
+ trackIconActiveStartMutated = true;
+ }
+
+ if (trackIconActiveStartMutated) {
+ trackIconActiveStart.setTintList(trackIconActiveColor);
+ }
+ }
+ }
+
/**
* Sets the active track start icon.
*
@@ -2036,13 +2142,29 @@ public Drawable getTrackIconActiveStart() {
* @see #getTrackIconActiveEnd()
*/
public void setTrackIconActiveEnd(@Nullable Drawable icon) {
- if (this.trackIconActiveEnd == icon) {
+ if (icon == trackIconActiveEnd) {
return;
}
- this.trackIconActiveEnd = icon;
+
+ trackIconActiveEnd = icon;
+ trackIconActiveEndMutated = false;
+ updateTrackIconActiveEnd();
invalidate();
}
+ private void updateTrackIconActiveEnd() {
+ if (trackIconActiveEnd != null) {
+ if (!trackIconActiveEndMutated && trackIconActiveColor != null) {
+ trackIconActiveEnd = DrawableCompat.wrap(trackIconActiveEnd).mutate();
+ trackIconActiveEndMutated = true;
+ }
+
+ if (trackIconActiveEndMutated) {
+ trackIconActiveEnd.setTintList(trackIconActiveColor);
+ }
+ }
+ }
+
/**
* Sets the active track end icon.
*
@@ -2106,10 +2228,13 @@ public int getTrackIconSize() {
* @see #getTrackIconActiveColor()
*/
public void setTrackIconActiveColor(@Nullable ColorStateList color) {
- if (this.trackIconActiveColor == color) {
+ if (color == trackIconActiveColor) {
return;
}
- this.trackIconActiveColor = color;
+
+ trackIconActiveColor = color;
+ updateTrackIconActiveStart();
+ updateTrackIconActiveEnd();
invalidate();
}
@@ -2134,13 +2259,29 @@ public ColorStateList getTrackIconActiveColor() {
* @see #getTrackIconInactiveStart()
*/
public void setTrackIconInactiveStart(@Nullable Drawable icon) {
- if (this.trackIconInactiveStart == icon) {
+ if (icon == trackIconInactiveStart) {
return;
}
- this.trackIconInactiveStart = icon;
+
+ trackIconInactiveStart = icon;
+ trackIconInactiveStartMutated = false;
+ updateTrackIconInactiveStart();
invalidate();
}
+ private void updateTrackIconInactiveStart() {
+ if (trackIconInactiveStart != null) {
+ if (!trackIconInactiveStartMutated && trackIconInactiveColor != null) {
+ trackIconInactiveStart = DrawableCompat.wrap(trackIconInactiveStart).mutate();
+ trackIconInactiveStartMutated = true;
+ }
+
+ if (trackIconInactiveStartMutated) {
+ trackIconInactiveStart.setTintList(trackIconInactiveColor);
+ }
+ }
+ }
+
/**
* Sets the inactive track start icon.
*
@@ -2179,13 +2320,29 @@ public Drawable getTrackIconInactiveStart() {
* @see #getTrackIconInactiveEnd()
*/
public void setTrackIconInactiveEnd(@Nullable Drawable icon) {
- if (this.trackIconInactiveEnd == icon) {
+ if (icon == trackIconInactiveEnd) {
return;
}
- this.trackIconInactiveEnd = icon;
+
+ trackIconInactiveEnd = icon;
+ trackIconInactiveEndMutated = false;
+ updateTrackIconInactiveEnd();
invalidate();
}
+ private void updateTrackIconInactiveEnd() {
+ if (trackIconInactiveEnd != null) {
+ if (!trackIconInactiveEndMutated && trackIconInactiveColor != null) {
+ trackIconInactiveEnd = DrawableCompat.wrap(trackIconInactiveEnd).mutate();
+ trackIconInactiveEndMutated = true;
+ }
+
+ if (trackIconInactiveEndMutated) {
+ trackIconInactiveEnd.setTintList(trackIconInactiveColor);
+ }
+ }
+ }
+
/**
* Sets the inactive track end icon.
*
@@ -2223,10 +2380,13 @@ public Drawable getTrackIconInactiveEnd() {
* @see #getTrackIconInactiveColor()
*/
public void setTrackIconInactiveColor(@Nullable ColorStateList color) {
- if (this.trackIconInactiveColor == color) {
+ if (color == trackIconInactiveColor) {
return;
}
- this.trackIconInactiveColor = color;
+
+ trackIconInactiveColor = color;
+ updateTrackIconInactiveStart();
+ updateTrackIconInactiveEnd();
invalidate();
}
@@ -2248,7 +2408,7 @@ protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
// When the visibility is set to VISIBLE, onDraw() is called again which adds or removes labels
// according to the setting.
if (visibility != VISIBLE) {
- ViewOverlayImpl contentViewOverlay = ViewUtils.getContentViewOverlay(this);
+ final ViewOverlay contentViewOverlay = getContentViewOverlay();
if (contentViewOverlay == null) {
return;
}
@@ -2258,6 +2418,12 @@ protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
}
}
+ @Nullable
+ private ViewOverlay getContentViewOverlay() {
+ final View contentView = ViewUtils.getContentView(this);
+ return contentView == null ? null : contentView.getOverlay();
+ }
+
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -2274,6 +2440,30 @@ public void setOrientation(@Orientation int orientation) {
updateWidgetLayout(true);
}
+ /**
+ * Sets the slider to be in centered configuration, meaning the starting value is positioned in
+ * the middle of the slider.
+ *
+ * @param isCentered boolean to use for the slider's centered configuration.
+ * @attr ref com.google.android.material.R.styleable#Slider_centered
+ * @see #isCentered()
+ */
+ public void setCentered(boolean isCentered) {
+ if (this.centered == isCentered) {
+ return;
+ }
+ this.centered = isCentered;
+
+ // if centered, the default value is at the center
+ if (isCentered) {
+ setValues((valueFrom + valueTo) / 2f);
+ } else {
+ setValues(valueFrom);
+ }
+
+ updateWidgetLayout(true);
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -2309,19 +2499,20 @@ protected void onDetachedFromWindow() {
}
private void detachLabelFromContentView(TooltipDrawable label) {
- ViewOverlayImpl contentViewOverlay = ViewUtils.getContentViewOverlay(this);
- if (contentViewOverlay != null) {
- contentViewOverlay.remove(label);
- label.detachView(ViewUtils.getContentView(this));
+ final View contentView = ViewUtils.getContentView(this);
+ if (contentView == null) {
+ return;
}
+
+ contentView.getOverlay().remove(label);
+ label.detachView(contentView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int labelSize = 0;
if (labelBehavior == LABEL_WITHIN_BOUNDS || shouldAlwaysShowLabel()) {
- labelSize =
- isVertical() ? labels.get(0).getIntrinsicWidth() : labels.get(0).getIntrinsicHeight();
+ labelSize = labels.get(0).getIntrinsicHeight();
}
int spec = MeasureSpec.makeMeasureSpec(widgetThickness + labelSize, MeasureSpec.EXACTLY);
if (isVertical()) {
@@ -2337,24 +2528,50 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
updateHaloHotspot();
}
- private void maybeCalculateTicksCoordinates() {
+ private void updateTicksCoordinates() {
+ validateConfigurationIfDirty();
+
+ // Continuous mode.
if (stepSize <= 0.0f) {
+ updateTicksCoordinates(continuousModeTickCount);
return;
}
- validateConfigurationIfDirty();
+ final int tickCount;
+ switch (tickVisibilityMode) {
+ case TICK_VISIBILITY_AUTO_LIMIT:
+ tickCount = min(getDesiredTickCount(), getMaxTickCount());
+ break;
+ case TICK_VISIBILITY_AUTO_HIDE:
+ int desiredTickCount = getDesiredTickCount();
+ tickCount = desiredTickCount <= getMaxTickCount() ? desiredTickCount : 0;
+ break;
+ case TICK_VISIBILITY_HIDDEN:
+ tickCount = 0;
+ break;
+ default:
+ throw new IllegalStateException("Unexpected tickVisibilityMode: " + tickVisibilityMode);
+ }
+
+ updateTicksCoordinates(tickCount);
+ }
+
+ private void updateTicksCoordinates(int tickCount) {
+ if (tickCount == 0) {
+ ticksCoordinates = null;
+ return;
+ }
- int tickCount = (int) ((valueTo - valueFrom) / stepSize + 1);
- // Limit the tickCount if they will be too dense.
- tickCount = min(tickCount, trackWidth / minTickSpacing + 1);
if (ticksCoordinates == null || ticksCoordinates.length != tickCount * 2) {
ticksCoordinates = new float[tickCount * 2];
}
float interval = trackWidth / (float) (tickCount - 1);
+ float trackCenterY = calculateTrackCenter();
+
for (int i = 0; i < tickCount * 2; i += 2) {
ticksCoordinates[i] = trackSidePadding + i / 2f * interval;
- ticksCoordinates[i + 1] = calculateTrackCenter();
+ ticksCoordinates[i + 1] = trackCenterY;
}
if (isVertical()) {
@@ -2362,12 +2579,20 @@ private void maybeCalculateTicksCoordinates() {
}
}
+ private int getDesiredTickCount() {
+ return (int) ((valueTo - valueFrom) / stepSize + 1);
+ }
+
+ private int getMaxTickCount() {
+ return trackWidth / minTickSpacing + 1;
+ }
+
private void updateTrackWidth(int width) {
// Update the visible track width.
trackWidth = max(width - trackSidePadding * 2, 0);
// Update the visible tick coordinates.
- maybeCalculateTicksCoordinates();
+ updateTicksCoordinates();
}
private void updateHaloHotspot() {
@@ -2381,12 +2606,8 @@ private void updateHaloHotspot() {
if (isVertical()) {
rotationMatrix.mapPoints(haloBounds);
}
- DrawableCompat.setHotspotBounds(
- background,
- (int) haloBounds[0],
- (int) haloBounds[1],
- (int) haloBounds[2],
- (int) haloBounds[3]);
+ background.setHotspotBounds(
+ (int) haloBounds[0], (int) haloBounds[1], (int) haloBounds[2], (int) haloBounds[3]);
}
}
}
@@ -2394,7 +2615,7 @@ private void updateHaloHotspot() {
private int calculateTrackCenter() {
return widgetThickness / 2
+ (labelBehavior == LABEL_WITHIN_BOUNDS || shouldAlwaysShowLabel()
- ? isVertical() ? labels.get(0).getIntrinsicWidth() : labels.get(0).getIntrinsicHeight()
+ ? labels.get(0).getIntrinsicHeight()
: 0);
}
@@ -2404,22 +2625,21 @@ protected void onDraw(@NonNull Canvas canvas) {
validateConfigurationIfDirty();
// Update the visible tick coordinates.
- maybeCalculateTicksCoordinates();
+ updateTicksCoordinates();
}
super.onDraw(canvas);
int yCenter = calculateTrackCenter();
- float first = values.get(0);
- float last = values.get(values.size() - 1);
- if (last < valueTo || (values.size() > 1 && first > valueFrom)) {
- drawInactiveTrack(canvas, trackWidth, yCenter);
- }
- if (last > valueFrom) {
- drawActiveTrack(canvas, trackWidth, yCenter);
+ drawInactiveTracks(canvas, trackWidth, yCenter);
+ drawActiveTracks(canvas, trackWidth, yCenter);
+
+ if (isRtl() || isVertical()) {
+ drawTrackIcons(canvas, activeTrackRect, inactiveTrackLeftRect);
+ } else {
+ drawTrackIcons(canvas, activeTrackRect, inactiveTrackRightRect);
}
- drawTrackIcons(canvas, activeTrackRect, inactiveTrackRect);
maybeDrawTicks(canvas);
maybeDrawStopIndicator(canvas, yCenter);
@@ -2443,32 +2663,55 @@ private float[] getActiveRange() {
float left = normalizeValue(values.size() == 1 ? valueFrom : min);
float right = normalizeValue(max);
- // In RTL we draw things in reverse, so swap the left and right range values
- return isRtl() || isVertical() ? new float[] {right, left} : new float[] {left, right};
+ // When centered, the active range is bound by the center.
+ if (isCentered()) {
+ left = min(.5f, right);
+ right = max(.5f, right);
+ }
+
+ // In RTL we draw things in reverse, so swap the left and right range values.
+ return !isCentered() && (isRtl() || isVertical())
+ ? new float[] {right, left}
+ : new float[] {left, right};
}
- private void drawInactiveTrack(@NonNull Canvas canvas, int width, int yCenter) {
+ private void drawInactiveTracks(@NonNull Canvas canvas, int width, int yCenter) {
float[] activeRange = getActiveRange();
- float right = trackSidePadding + activeRange[1] * width;
- if (right < trackSidePadding + width) {
- inactiveTrackRect.set(
- right + thumbTrackGapSize,
- yCenter - trackThickness / 2f,
- trackSidePadding + width + getTrackCornerSize(),
- yCenter + trackThickness / 2f);
- updateTrack(canvas, inactiveTrackPaint, inactiveTrackRect, FullCornerDirection.RIGHT);
- }
-
- // Also draw inactive track to the left if there is any
- float left = trackSidePadding + activeRange[0] * width;
- if (left > trackSidePadding) {
- inactiveTrackRect.set(
- trackSidePadding - getTrackCornerSize(),
- yCenter - trackThickness / 2f,
- left - thumbTrackGapSize,
- yCenter + trackThickness / 2f);
- updateTrack(canvas, inactiveTrackPaint, inactiveTrackRect, FullCornerDirection.LEFT);
+ float top = yCenter - trackThickness / 2f;
+ float bottom = yCenter + trackThickness / 2f;
+
+ drawInactiveTrackSection(
+ trackSidePadding - getTrackCornerSize(),
+ trackSidePadding + activeRange[0] * width - thumbTrackGapSize,
+ top,
+ bottom,
+ canvas,
+ inactiveTrackLeftRect,
+ FullCornerDirection.LEFT);
+ drawInactiveTrackSection(
+ trackSidePadding + activeRange[1] * width + thumbTrackGapSize,
+ trackSidePadding + width + getTrackCornerSize(),
+ top,
+ bottom,
+ canvas,
+ inactiveTrackRightRect,
+ FullCornerDirection.RIGHT);
+ }
+
+ private void drawInactiveTrackSection(
+ float from,
+ float to,
+ float top,
+ float bottom,
+ @NonNull Canvas canvas,
+ RectF rect,
+ FullCornerDirection direction) {
+ if (to - from > getTrackCornerSize() - thumbTrackGapSize) {
+ rect.set(from, top, to, bottom);
+ } else {
+ rect.setEmpty();
}
+ updateTrack(canvas, inactiveTrackPaint, rect, getTrackCornerSize(), direction);
}
/**
@@ -2483,13 +2726,17 @@ private float normalizeValue(float value) {
return normalized;
}
- private void drawActiveTrack(@NonNull Canvas canvas, int width, int yCenter) {
+ private void drawActiveTracks(@NonNull Canvas canvas, int width, int yCenter) {
float[] activeRange = getActiveRange();
float right = trackSidePadding + activeRange[1] * width;
float left = trackSidePadding + activeRange[0] * width;
+ if (left >= right) {
+ activeTrackRect.setEmpty();
+ return;
+ }
FullCornerDirection direction = FullCornerDirection.NONE;
- if (values.size() == 1) { // Only 1 thumb
+ if (values.size() == 1 && !isCentered()) { // Only 1 thumb
direction = isRtl() || isVertical() ? FullCornerDirection.RIGHT : FullCornerDirection.LEFT;
}
@@ -2506,18 +2753,25 @@ private void drawActiveTrack(@NonNull Canvas canvas, int width, int yCenter) {
}
}
+ int trackCornerSize = getTrackCornerSize();
switch (direction) {
case NONE:
- left += thumbTrackGapSize;
- right -= thumbTrackGapSize;
+ if (!isCentered()) {
+ left += thumbTrackGapSize;
+ right -= thumbTrackGapSize;
+ } else if (activeRange[1] == .5f) { // centered, active track ends at the center
+ left += thumbTrackGapSize;
+ } else if (activeRange[0] == .5f) { // centered, active track starts at the center
+ right -= thumbTrackGapSize;
+ }
break;
case LEFT:
- left -= getTrackCornerSize();
+ left -= trackCornerSize;
right -= thumbTrackGapSize;
break;
case RIGHT:
left += thumbTrackGapSize;
- right += getTrackCornerSize();
+ right += trackCornerSize;
break;
default:
// fall through
@@ -2525,55 +2779,82 @@ private void drawActiveTrack(@NonNull Canvas canvas, int width, int yCenter) {
// Nothing to draw if left is bigger than right.
if (left >= right) {
+ activeTrackRect.setEmpty();
continue;
}
activeTrackRect.set(
left, yCenter - trackThickness / 2f, right, yCenter + trackThickness / 2f);
- updateTrack(canvas, activeTrackPaint, activeTrackRect, direction);
+ updateTrack(canvas, activeTrackPaint, activeTrackRect, trackCornerSize, direction);
+ }
+ }
+
+ private float calculateStartTrackCornerSize(float trackCornerSize) {
+ if (values.isEmpty() || !hasGapBetweenThumbAndTrack()) {
+ return trackCornerSize;
+ }
+ int firstIdx = isRtl() || isVertical() ? values.size() - 1 : 0;
+ float currentX = valueToX(values.get(firstIdx)) - trackSidePadding;
+ if (currentX < trackCornerSize) {
+ return max(currentX, trackInsideCornerSize);
+ }
+ return trackCornerSize;
+ }
+
+ private float calculateEndTrackCornerSize(float trackCornerSize) {
+ if (values.isEmpty() || !hasGapBetweenThumbAndTrack()) {
+ return trackCornerSize;
}
+ int lastIdx = isRtl() || isVertical() ? 0 : values.size() - 1;
+ float currentX = valueToX(values.get(lastIdx)) - trackSidePadding;
+ if (currentX > trackWidth - trackCornerSize) {
+ return max(trackWidth - currentX, trackInsideCornerSize);
+ }
+ return trackCornerSize;
}
private void drawTrackIcons(
@NonNull Canvas canvas,
@NonNull RectF activeTrackBounds,
@NonNull RectF inactiveTrackBounds) {
+ if (!hasTrackIcons()) {
+ return;
+ }
+
if (values.size() > 1) {
Log.w(TAG, "Track icons can only be used when only 1 thumb is present.");
}
// draw track start icons
- calculateBoundsAndDrawTrackIcon(
- canvas, activeTrackBounds, trackIconActiveStart, trackIconActiveColor, true);
- calculateBoundsAndDrawTrackIcon(
- canvas, inactiveTrackBounds, trackIconInactiveStart, trackIconInactiveColor, true);
+ calculateBoundsAndDrawTrackIcon(canvas, activeTrackBounds, trackIconActiveStart, true);
+ calculateBoundsAndDrawTrackIcon(canvas, inactiveTrackBounds, trackIconInactiveStart, true);
// draw track end icons
- calculateBoundsAndDrawTrackIcon(
- canvas, activeTrackBounds, trackIconActiveEnd, trackIconActiveColor, false);
- calculateBoundsAndDrawTrackIcon(
- canvas, inactiveTrackBounds, trackIconInactiveEnd, trackIconInactiveColor, false);
+ calculateBoundsAndDrawTrackIcon(canvas, activeTrackBounds, trackIconActiveEnd, false);
+ calculateBoundsAndDrawTrackIcon(canvas, inactiveTrackBounds, trackIconInactiveEnd, false);
+ }
+
+ private boolean hasTrackIcons() {
+ return trackIconActiveStart != null
+ || trackIconActiveEnd != null
+ || trackIconInactiveStart != null
+ || trackIconInactiveEnd != null;
}
private void calculateBoundsAndDrawTrackIcon(
@NonNull Canvas canvas,
@NonNull RectF trackBounds,
@Nullable Drawable icon,
- @Nullable ColorStateList iconColor,
boolean isStart) {
if (icon != null) {
- calculateTrackIconBounds(trackBounds, iconRectF, trackIconSize, isStart);
+ calculateTrackIconBounds(trackBounds, iconRectF, trackIconSize, trackIconPadding, isStart);
if (!iconRectF.isEmpty()) {
- drawTrackIcon(canvas, iconRectF, icon, iconColor);
+ drawTrackIcon(canvas, iconRectF, icon);
}
}
}
private void drawTrackIcon(
- @NonNull Canvas canvas,
- @NonNull RectF iconBounds,
- @NonNull Drawable icon,
- @Nullable ColorStateList color) {
- DrawableCompat.setTintList(icon, color);
+ @NonNull Canvas canvas, @NonNull RectF iconBounds, @NonNull Drawable icon) {
if (isVertical()) {
rotationMatrix.mapRect(iconBounds);
}
@@ -2583,28 +2864,24 @@ private void drawTrackIcon(
}
private void calculateTrackIconBounds(
- @NonNull RectF trackBounds, @NonNull RectF iconBounds, @Px int iconSize, boolean isStart) {
- float iconPadding = getResources().getDimension(R.dimen.m3_slider_track_icon_padding);
- float iconLeft;
- if (isStart) {
- iconLeft =
- isRtl() || isVertical()
- ? trackBounds.right - iconSize - iconPadding
- : trackBounds.left + iconPadding;
- } else {
- iconLeft =
- isRtl() || isVertical()
+ @NonNull RectF trackBounds,
+ @NonNull RectF iconBounds,
+ @Px int iconSize,
+ @Px int iconPadding,
+ boolean isStart) {
+ if (trackBounds.right - trackBounds.left >= iconSize + 2 * iconPadding) {
+ float iconLeft =
+ (isStart ^ (isRtl() || isVertical()))
? trackBounds.left + iconPadding
- : trackBounds.right - iconSize - iconPadding;
- }
- float iconRight = iconLeft + iconSize;
- int iconTop = calculateTrackCenter() - iconSize / 2;
- if (trackBounds.left > iconLeft - iconPadding || trackBounds.right < iconRight + iconPadding) {
+ : trackBounds.right - iconPadding - iconSize;
+ float iconTop = calculateTrackCenter() - iconSize / 2f;
+ float iconRight = iconLeft + iconSize;
+ float iconBottom = iconTop + iconSize;
+ iconBounds.set(iconLeft, iconTop, iconRight, iconBottom);
+ } else {
// not enough space to draw icon
iconBounds.setEmpty();
- return;
}
- iconBounds.set(iconLeft, iconTop, iconRight, iconTop + iconSize);
}
private boolean hasGapBetweenThumbAndTrack() {
@@ -2620,9 +2897,13 @@ private enum FullCornerDirection {
}
private void updateTrack(
- Canvas canvas, Paint paint, RectF bounds, FullCornerDirection direction) {
- float leftCornerSize = getTrackCornerSize();
- float rightCornerSize = getTrackCornerSize();
+ Canvas canvas, Paint paint, RectF bounds, float cornerSize, FullCornerDirection direction) {
+ if (bounds.isEmpty()) {
+ return;
+ }
+
+ float leftCornerSize = calculateStartTrackCornerSize(cornerSize);
+ float rightCornerSize = calculateEndTrackCornerSize(cornerSize);
switch (direction) {
case BOTH:
break;
@@ -2703,7 +2984,7 @@ private float[] getCornerRadii(float leftSide, float rightSide) {
}
private void maybeDrawTicks(@NonNull Canvas canvas) {
- if (!tickVisible || stepSize <= 0.0f) {
+ if (ticksCoordinates == null || ticksCoordinates.length == 0) {
return;
}
@@ -2719,44 +3000,71 @@ private void maybeDrawTicks(@NonNull Canvas canvas) {
// Draw ticks on the left inactive track (if any).
if (leftActiveTickIndex > 0) {
- canvas.drawPoints(ticksCoordinates, 0, leftActiveTickIndex * 2, inactiveTicksPaint);
+ drawTicks(0, leftActiveTickIndex * 2, canvas, inactiveTicksPaint);
}
// Draw ticks on the active track (if any).
if (leftActiveTickIndex <= rightActiveTickIndex) {
- canvas.drawPoints(
- ticksCoordinates,
- leftActiveTickIndex * 2,
- (rightActiveTickIndex - leftActiveTickIndex + 1) * 2,
- activeTicksPaint);
+ drawTicks(leftActiveTickIndex * 2, (rightActiveTickIndex + 1) * 2, canvas, activeTicksPaint);
}
// Draw ticks on the right inactive track (if any).
if ((rightActiveTickIndex + 1) * 2 < ticksCoordinates.length) {
- canvas.drawPoints(
- ticksCoordinates,
- (rightActiveTickIndex + 1) * 2,
- ticksCoordinates.length - (rightActiveTickIndex + 1) * 2,
- inactiveTicksPaint);
+ drawTicks(
+ (rightActiveTickIndex + 1) * 2, ticksCoordinates.length, canvas, inactiveTicksPaint);
}
}
+ private void drawTicks(int from, int to, Canvas canvas, Paint paint) {
+ for (int i = from; i < to; i += 2) {
+ float coordinateToCheck = isVertical() ? ticksCoordinates[i + 1] : ticksCoordinates[i];
+ if (isOverlappingThumb(coordinateToCheck)
+ || (isCentered() && isOverlappingCenterGap(coordinateToCheck))) {
+ continue;
+ }
+ canvas.drawPoint(ticksCoordinates[i], ticksCoordinates[i + 1], paint);
+ }
+ }
+
+ private boolean isOverlappingThumb(float tickCoordinate) {
+ float threshold = thumbTrackGapSize + thumbWidth / 2f;
+ for (float value : values) {
+ float valueToX = valueToX(value);
+ return tickCoordinate >= valueToX - threshold && tickCoordinate <= valueToX + threshold;
+ }
+ return false;
+ }
+
+ private boolean isOverlappingCenterGap(float tickCoordinate) {
+ float threshold = thumbTrackGapSize + thumbWidth / 2f;
+ float trackCenter = (trackWidth + trackSidePadding * 2) / 2f;
+ return tickCoordinate >= trackCenter - threshold && tickCoordinate <= trackCenter + threshold;
+ }
+
private void maybeDrawStopIndicator(@NonNull Canvas canvas, int yCenter) {
- if (trackStopIndicatorSize <= 0) {
+ if (trackStopIndicatorSize <= 0 || values.isEmpty()) {
return;
}
// Draw stop indicator at the end of the track.
- if (!values.isEmpty() && values.get(values.size() - 1) < valueTo) {
+ if (values.get(values.size() - 1) < valueTo) {
drawStopIndicator(canvas, valueToX(valueTo), yCenter);
}
- // Multiple thumbs, inactive track may be visible at the start.
- if (values.size() > 1 && values.get(0) > valueFrom) {
+ // Centered, multiple thumbs, inactive track may be visible at the start.
+ if (isCentered() || (values.size() > 1 && values.get(0) > valueFrom)) {
drawStopIndicator(canvas, valueToX(valueFrom), yCenter);
}
}
private void drawStopIndicator(@NonNull Canvas canvas, float x, float y) {
+ // Prevent drawing indicator on the thumbs.
+ for (float value : values) {
+ float valueToX = valueToX(value);
+ float threshold = thumbTrackGapSize + thumbWidth / 2f;
+ if (x >= valueToX - threshold && x <= valueToX + threshold) {
+ return;
+ }
+ }
if (isVertical()) {
canvas.drawPoint(y, x, stopIndicatorPaint);
} else {
@@ -2790,7 +3098,7 @@ private void drawThumbDrawable(
@NonNull Canvas canvas, int width, int top, float value, @NonNull Drawable thumbDrawable) {
canvas.save();
if (isVertical()) {
- canvas.setMatrix(rotationMatrix);
+ canvas.concat(rotationMatrix);
}
canvas.translate(
trackSidePadding
@@ -2832,18 +3140,25 @@ public boolean onTouchEvent(@NonNull MotionEvent event) {
return false;
}
- float eventCoordinate = isVertical() ? event.getY() : event.getX();
- touchPosition = (eventCoordinate - trackSidePadding) / trackWidth;
+ float eventCoordinateAxis1 = isVertical() ? event.getY() : event.getX();
+ float eventCoordinateAxis2 = isVertical() ? event.getX() : event.getY();
+ touchPosition = (eventCoordinateAxis1 - trackSidePadding) / trackWidth;
touchPosition = max(0, touchPosition);
touchPosition = min(1, touchPosition);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- touchDownX = eventCoordinate;
+ touchDownAxis1 = eventCoordinateAxis1;
+ touchDownAxis2 = eventCoordinateAxis2;
// If we're inside a vertical scrolling container,
// we should start dragging in ACTION_MOVE
- if (isPotentialVerticalScroll(event)) {
+ if (!isVertical() && isPotentialVerticalScroll(event)) {
+ break;
+ }
+ // If we're inside a horizontal scrolling container,
+ // we should start dragging in ACTION_MOVE
+ if (isVertical() && isPotentialHorizontalScroll(event)) {
break;
}
@@ -2866,8 +3181,15 @@ public boolean onTouchEvent(@NonNull MotionEvent event) {
case MotionEvent.ACTION_MOVE:
if (!thumbIsPressed) {
// Check if we're trying to scroll vertically instead of dragging this Slider
- if (isPotentialVerticalScroll(event)
- && abs(eventCoordinate - touchDownX) < scaledTouchSlop) {
+ if (!isVertical()
+ && isPotentialVerticalScroll(event)
+ && abs(eventCoordinateAxis1 - touchDownAxis1) < scaledTouchSlop) {
+ return false;
+ }
+ // Check if we're trying to scroll horizontally instead of dragging this Slider
+ if (isVertical()
+ && isPotentialHorizontalScroll(event)
+ && abs(eventCoordinateAxis2 - touchDownAxis2) < scaledTouchSlop * TOUCH_SLOP_RATIO) {
return false;
}
getParent().requestDisallowInterceptTouchEvent(true);
@@ -3149,6 +3471,8 @@ private ValueAnimator createLabelAnimator(boolean enter) {
}
private void updateLabels() {
+ updateLabelPivots();
+
switch (labelBehavior) {
case LABEL_GONE:
ensureLabelsRemoved();
@@ -3173,6 +3497,29 @@ private void updateLabels() {
}
}
+ private void updateLabelPivots() {
+ // Set the pivot point so that the label pops up in the direction from the thumb.
+ final float labelPivotX;
+ final float labelPivotY;
+
+ final boolean isVertical = isVertical();
+ final boolean isRtl = isRtl();
+ if (isVertical && isRtl) {
+ labelPivotX = RIGHT_LABEL_PIVOT_X;
+ labelPivotY = RIGHT_LABEL_PIVOT_Y;
+ } else if (isVertical) {
+ labelPivotX = LEFT_LABEL_PIVOT_X;
+ labelPivotY = LEFT_LABEL_PIVOT_Y;
+ } else {
+ labelPivotX = TOP_LABEL_PIVOT_X;
+ labelPivotY = TOP_LABEL_PIVOT_Y;
+ }
+
+ for (TooltipDrawable label : labels) {
+ label.setPivots(labelPivotX, labelPivotY);
+ }
+ }
+
private boolean isSliderVisibleOnScreen() {
final Rect contentViewBounds = new Rect();
ViewUtils.getContentView(this).getHitRect(contentViewBounds);
@@ -3185,6 +3532,14 @@ private boolean isThisAndAncestorsVisible() {
return (VERSION.SDK_INT >= VERSION_CODES.N) ? thisAndAncestorsVisible : isShown();
}
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ // Setting visible to user to false prevents duplicate announcements by making only our virtual
+ // view accessible, not the parent container.
+ info.setVisibleToUser(false);
+ }
+
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
@@ -3203,7 +3558,11 @@ private void ensureLabelsRemoved() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- ViewOverlayImpl contentViewOverlay = ViewUtils.getContentViewOverlay(BaseSlider.this);
+ final ViewOverlay contentViewOverlay = getContentViewOverlay();
+ if (contentViewOverlay == null) {
+ return;
+ }
+
for (TooltipDrawable label : labels) {
contentViewOverlay.remove(label);
}
@@ -3256,7 +3615,12 @@ private String formatValue(float value) {
private void setValueForLabel(TooltipDrawable label, float value) {
label.setText(formatValue(value));
positionLabel(label, value);
- ViewUtils.getContentViewOverlay(this).add(label);
+ final ViewOverlay contentViewOverlay = getContentViewOverlay();
+ if (contentViewOverlay == null) {
+ return;
+ }
+
+ contentViewOverlay.add(label);
}
private void positionLabel(TooltipDrawable label, float value) {
@@ -3273,17 +3637,30 @@ private void positionLabel(TooltipDrawable label, float value) {
}
private void calculateLabelBounds(TooltipDrawable label, float value) {
- int left =
- trackSidePadding
- + (int) (normalizeValue(value) * trackWidth)
- - label.getIntrinsicWidth() / 2;
- int right = left + label.getIntrinsicWidth();
+ int left;
+ int right;
int bottom;
int top;
- if (isVertical() && !isRtl()) {
- top = calculateTrackCenter() + (labelPadding + thumbHeight / 2);
- bottom = top + label.getIntrinsicHeight();
+
+ if (isVertical()) {
+ left =
+ trackSidePadding
+ + (int) (normalizeValue(value) * trackWidth)
+ - label.getIntrinsicHeight() / 2;
+ right = left + label.getIntrinsicHeight();
+ if (isRtl()) {
+ bottom = calculateTrackCenter() - (labelPadding + thumbHeight / 2);
+ top = bottom - label.getIntrinsicWidth();
+ } else {
+ top = calculateTrackCenter() + (labelPadding + thumbHeight / 2);
+ bottom = top + label.getIntrinsicWidth();
+ }
} else {
+ left =
+ trackSidePadding
+ + (int) (normalizeValue(value) * trackWidth)
+ - label.getIntrinsicWidth() / 2;
+ right = left + label.getIntrinsicWidth();
bottom = calculateTrackCenter() - (labelPadding + thumbHeight / 2);
top = bottom - label.getIntrinsicHeight();
}
@@ -3315,6 +3692,20 @@ private boolean isInVerticalScrollingContainer() {
return false;
}
+ private boolean isInHorizontalScrollingContainer() {
+ ViewParent p = getParent();
+ while (p instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) p;
+ boolean canScrollHorizontally =
+ parent.canScrollHorizontally(1) || parent.canScrollHorizontally(-1);
+ if (canScrollHorizontally && parent.shouldDelayChildPressedState()) {
+ return true;
+ }
+ p = p.getParent();
+ }
+ return false;
+ }
+
private static boolean isMouseEvent(MotionEvent event) {
return event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
}
@@ -3323,6 +3714,10 @@ private boolean isPotentialVerticalScroll(MotionEvent event) {
return !isMouseEvent(event) && isInVerticalScrollingContainer();
}
+ private boolean isPotentialHorizontalScroll(MotionEvent event) {
+ return !isMouseEvent(event) && isInHorizontalScrollingContainer();
+ }
+
@SuppressWarnings("unchecked")
private void dispatchOnChangedProgrammatically() {
for (L listener : changeListeners) {
@@ -3483,10 +3878,14 @@ final boolean isRtl() {
return getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
- final boolean isVertical() {
+ public boolean isVertical() {
return widgetOrientation == VERTICAL;
}
+ public boolean isCentered() {
+ return centered;
+ }
+
/**
* Attempts to move focus to next or previous thumb independent of layout direction and
* returns whether the focused thumb changed. If focused thumb didn't change, we're at the view
@@ -3752,7 +4151,7 @@ void updateBoundsForVirtualViewId(int virtualViewId, Rect virtualViewBounds) {
virtualViewBounds.set((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom);
}
- private static class AccessibilityHelper extends ExploreByTouchHelper {
+ public static class AccessibilityHelper extends ExploreByTouchHelper {
private final BaseSlider, ?, ?> slider;
final Rect virtualViewBounds = new Rect();
@@ -3775,7 +4174,7 @@ protected int getVirtualViewAt(float x, float y) {
}
@Override
- protected void getVisibleVirtualViews(List virtualViewIds) {
+ protected void getVisibleVirtualViews(@NonNull List virtualViewIds) {
for (int i = 0; i < slider.getValues().size(); i++) {
virtualViewIds.add(i);
}
@@ -3783,7 +4182,7 @@ protected void getVisibleVirtualViews(List virtualViewIds) {
@Override
protected void onPopulateNodeForVirtualView(
- int virtualViewId, AccessibilityNodeInfoCompat info) {
+ int virtualViewId, @NonNull AccessibilityNodeInfoCompat info) {
info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS);
@@ -3824,7 +4223,13 @@ protected void onPopulateNodeForVirtualView(
if (values.size() > 1) {
verbalValueType = startOrEndDescription(virtualViewId);
}
- contentDescription.append(String.format(Locale.US, "%s, %s", verbalValueType, verbalValue));
+ CharSequence stateDescription = ViewCompat.getStateDescription(slider);
+ if (!TextUtils.isEmpty(stateDescription)) {
+ info.setStateDescription(stateDescription);
+ } else {
+ contentDescription.append(
+ String.format(Locale.getDefault(), "%s, %s", verbalValueType, verbalValue));
+ }
info.setContentDescription(contentDescription.toString());
slider.updateBoundsForVirtualViewId(virtualViewId, virtualViewBounds);
@@ -3847,7 +4252,7 @@ private String startOrEndDescription(int virtualViewId) {
@Override
protected boolean onPerformActionForVirtualView(
- int virtualViewId, int action, Bundle arguments) {
+ int virtualViewId, int action, @Nullable Bundle arguments) {
if (!slider.isEnabled()) {
return false;
}
@@ -3879,7 +4284,7 @@ protected boolean onPerformActionForVirtualView(
}
// Swap the increment if we're in RTL.
- if (slider.isRtl() || slider.isVertical()) {
+ if (slider.isRtl()) {
increment = -increment;
}
diff --git a/lib/java/com/google/android/material/slider/TickVisibilityMode.java b/lib/java/com/google/android/material/slider/TickVisibilityMode.java
new file mode 100644
index 00000000000..3c6524baa34
--- /dev/null
+++ b/lib/java/com/google/android/material/slider/TickVisibilityMode.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 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.slider;
+
+import androidx.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Mode to specify the visibility of tick marks. */
+@IntDef({
+ TickVisibilityMode.TICK_VISIBILITY_AUTO_LIMIT,
+ TickVisibilityMode.TICK_VISIBILITY_AUTO_HIDE,
+ TickVisibilityMode.TICK_VISIBILITY_HIDDEN
+})
+@Retention(RetentionPolicy.SOURCE)
+public @interface TickVisibilityMode {
+
+ /**
+ * All tick marks will be drawn if they are not spaced too densely. Otherwise, the maximum allowed
+ * number of tick marks will be drawn. Note that in this case the drawn ticks may not match the
+ * actual snap values.
+ */
+ int TICK_VISIBILITY_AUTO_LIMIT = 0;
+
+ /**
+ * All tick marks will be drawn if they are not spaced too densely. Otherwise, the tick marks will
+ * not be drawn.
+ */
+ int TICK_VISIBILITY_AUTO_HIDE = 1;
+
+ /** Tick marks will not be drawn. */
+ int TICK_VISIBILITY_HIDDEN = 2;
+}
diff --git a/lib/java/com/google/android/material/slider/res-public/values/public.xml b/lib/java/com/google/android/material/slider/res-public/values/public.xml
index 9401e49eca3..89d2189fe93 100644
--- a/lib/java/com/google/android/material/slider/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/slider/res-public/values/public.xml
@@ -17,6 +17,8 @@
+
+
@@ -35,6 +37,7 @@
+
diff --git a/lib/java/com/google/android/material/slider/res/values-af/strings.xml b/lib/java/com/google/android/material/slider/res/values-af/strings.xml
index efec523c5ef..1ecf2b92f05 100644
--- a/lib/java/com/google/android/material/slider/res/values-af/strings.xml
+++ b/lib/java/com/google/android/material/slider/res/values-af/strings.xml
@@ -1,6 +1,6 @@
+
+
+
@@ -70,8 +73,22 @@
-
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/slider/res/values/styles.xml b/lib/java/com/google/android/material/slider/res/values/styles.xml
index 9195016f58d..94ecabbb0c7 100644
--- a/lib/java/com/google/android/material/slider/res/values/styles.xml
+++ b/lib/java/com/google/android/material/slider/res/values/styles.xml
@@ -16,49 +16,43 @@
-->
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/slider/res/values/tokens.xml b/lib/java/com/google/android/material/slider/res/values/tokens.xml
index 390ffb246a6..06513bb1c82 100644
--- a/lib/java/com/google/android/material/slider/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/slider/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
@@ -52,4 +52,45 @@
?attr/colorOnSurfaceInverse12dp
+
+
+ 16dp
+ 8dp
+
+ 44dp
+
+
+
+ 24dp
+ 8dp
+
+ 44dp
+
+
+
+ 40dp
+ 12dp
+
+ 44dp
+
+ 24dp
+
+
+
+ 56dp
+ 16dp
+
+ 68dp
+
+ 24dp
+
+
+
+ 96dp
+ 28dp
+
+ 108dp
+
+ 32dp
+
diff --git a/lib/java/com/google/android/material/snackbar/BaseTransientBottomBar.java b/lib/java/com/google/android/material/snackbar/BaseTransientBottomBar.java
index 13d2c5b67ff..c6fd60d0224 100644
--- a/lib/java/com/google/android/material/snackbar/BaseTransientBottomBar.java
+++ b/lib/java/com/google/android/material/snackbar/BaseTransientBottomBar.java
@@ -500,7 +500,10 @@ private void updateMargins() {
}
private boolean shouldUpdateGestureInset() {
- return extraBottomMarginGestureInset > 0 && !gestureInsetBottomIgnored && isSwipeDismissable();
+ return extraBottomMarginGestureInset > 0
+ && !gestureInsetBottomIgnored
+ && isSwipeDismissable()
+ && getAnchorView() == null;
}
private boolean isSwipeDismissable() {
@@ -823,10 +826,6 @@ void onLayoutChange() {
}
private void showViewImpl() {
- if (ViewCompat.getAccessibilityPaneTitle(view) == null) {
- ViewCompat.setAccessibilityPaneTitle(
- view, getContext().getString(R.string.snackbar_accessibility_pane_title));
- }
if (shouldAnimate()) {
// If animations are enabled, animate it in
animateViewIn();
@@ -1160,8 +1159,7 @@ protected SnackbarBaseLayout(@NonNull Context context, AttributeSet attrs) {
context = getContext();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
if (a.hasValue(R.styleable.SnackbarLayout_elevation)) {
- ViewCompat.setElevation(
- this, a.getDimensionPixelSize(R.styleable.SnackbarLayout_elevation, 0));
+ setElevation(a.getDimensionPixelSize(R.styleable.SnackbarLayout_elevation, 0));
}
animationMode = a.getInt(R.styleable.SnackbarLayout_animationMode, ANIMATION_MODE_SLIDE);
if (a.hasValue(R.styleable.SnackbarLayout_shapeAppearance)
@@ -1202,8 +1200,8 @@ public void setBackground(@Nullable Drawable drawable) {
public void setBackgroundDrawable(@Nullable Drawable drawable) {
if (drawable != null && backgroundTint != null) {
drawable = DrawableCompat.wrap(drawable.mutate());
- DrawableCompat.setTintList(drawable, backgroundTint);
- DrawableCompat.setTintMode(drawable, backgroundTintMode);
+ drawable.setTintList(backgroundTint);
+ drawable.setTintMode(backgroundTintMode);
}
super.setBackgroundDrawable(drawable);
}
@@ -1213,8 +1211,8 @@ public void setBackgroundTintList(@Nullable ColorStateList backgroundTint) {
this.backgroundTint = backgroundTint;
if (getBackground() != null) {
Drawable wrappedBackground = DrawableCompat.wrap(getBackground().mutate());
- DrawableCompat.setTintList(wrappedBackground, backgroundTint);
- DrawableCompat.setTintMode(wrappedBackground, backgroundTintMode);
+ wrappedBackground.setTintList(backgroundTint);
+ wrappedBackground.setTintMode(backgroundTintMode);
if (wrappedBackground != getBackground()) {
super.setBackgroundDrawable(wrappedBackground);
}
@@ -1226,7 +1224,7 @@ public void setBackgroundTintMode(@Nullable PorterDuff.Mode backgroundTintMode)
this.backgroundTintMode = backgroundTintMode;
if (getBackground() != null) {
Drawable wrappedBackground = DrawableCompat.wrap(getBackground().mutate());
- DrawableCompat.setTintMode(wrappedBackground, backgroundTintMode);
+ wrappedBackground.setTintMode(backgroundTintMode);
if (wrappedBackground != getBackground()) {
super.setBackgroundDrawable(wrappedBackground);
}
@@ -1263,7 +1261,7 @@ protected void onAttachedToWindow() {
if (baseTransientBottomBar != null) {
baseTransientBottomBar.onAttachedToWindow();
}
- ViewCompat.requestApplyInsets(this);
+ requestApplyInsets();
}
@Override
@@ -1341,7 +1339,7 @@ private Drawable createThemedBackground() {
: createGradientDrawableBackground(backgroundColor, getResources());
if (backgroundTint != null) {
Drawable wrappedDrawable = DrawableCompat.wrap(background);
- DrawableCompat.setTintList(wrappedDrawable, backgroundTint);
+ wrappedDrawable.setTintList(backgroundTint);
return wrappedDrawable;
} else {
return DrawableCompat.wrap(background);
@@ -1381,6 +1379,12 @@ private void setBaseTransientBottomBar(
delegate.setBaseTransientBottomBar(baseTransientBottomBar);
}
+ /**
+ * Called when the user's input indicates that they want to swipe the given view.
+ *
+ * @param child View the user is attempting to swipe
+ * @return true if the view can be dismissed via swiping, false otherwise
+ */
@Override
public boolean canSwipeDismissView(View child) {
return delegate.canSwipeDismissView(child);
diff --git a/lib/java/com/google/android/material/snackbar/Snackbar.java b/lib/java/com/google/android/material/snackbar/Snackbar.java
index 946900306ea..5272af241cc 100644
--- a/lib/java/com/google/android/material/snackbar/Snackbar.java
+++ b/lib/java/com/google/android/material/snackbar/Snackbar.java
@@ -97,6 +97,12 @@ public static class Callback extends BaseCallback {
/** Indicates that the Snackbar was dismissed from a new Snackbar being shown. */
public static final int DISMISS_EVENT_CONSECUTIVE = BaseCallback.DISMISS_EVENT_CONSECUTIVE;
+ /**
+ * Called when the given {@link Snackbar} is visible.
+ *
+ * @param sb The snackbar which is now visible.
+ * @see Snackbar#show()
+ */
@Override
public void onShown(Snackbar sb) {
// Stub implementation to make API check happy.
@@ -164,6 +170,27 @@ public static Snackbar make(
return makeInternal(/* context= */ null, view, text, duration);
}
+ /**
+ * Make a Snackbar to display a message.
+ *
+ *
Snackbar will try and find a parent view to hold Snackbar's view from the value given to
+ * {@code view}. Snackbar will walk up the view tree trying to find a suitable parent, which is
+ * defined as a {@link CoordinatorLayout} or the window decor's content view, whichever comes
+ * first.
+ *
+ *
Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable certain
+ * features, such as swipe-to-dismiss and automatically moving of widgets.
+ *
+ * @param view The view to find a parent from.
+ * @param resId The resource id of the string resource to use. Can be formatted text.
+ * @param duration How long to display the message. Can be {@link #LENGTH_SHORT}, {@link
+ * #LENGTH_LONG}, {@link #LENGTH_INDEFINITE}, or a custom duration in milliseconds.
+ */
+ @NonNull
+ public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
+ return make(view, view.getResources().getText(resId), duration);
+ }
+
/**
* Make a Snackbar to display a message
*
@@ -248,27 +275,6 @@ private static boolean hasSnackbarContentStyleAttrs(@NonNull Context context) {
return snackbarButtonStyleResId != -1 && snackbarTextViewStyleResId != -1;
}
- /**
- * Make a Snackbar to display a message.
- *
- *
Snackbar will try and find a parent view to hold Snackbar's view from the value given to
- * {@code view}. Snackbar will walk up the view tree trying to find a suitable parent, which is
- * defined as a {@link CoordinatorLayout} or the window decor's content view, whichever comes
- * first.
- *
- *
Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable certain
- * features, such as swipe-to-dismiss and automatically moving of widgets.
- *
- * @param view The view to find a parent from.
- * @param resId The resource id of the string resource to use. Can be formatted text.
- * @param duration How long to display the message. Can be {@link #LENGTH_SHORT}, {@link
- * #LENGTH_LONG}, {@link #LENGTH_INDEFINITE}, or a custom duration in milliseconds.
- */
- @NonNull
- public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
- return make(view, view.getResources().getText(resId), duration);
- }
-
@Nullable
private static ViewGroup findSuitableParent(View view) {
ViewGroup fallback = null;
diff --git a/lib/java/com/google/android/material/snackbar/res/values/styles.xml b/lib/java/com/google/android/material/snackbar/res/values/styles.xml
index 737bc3853d4..8e7030f354f 100644
--- a/lib/java/com/google/android/material/snackbar/res/values/styles.xml
+++ b/lib/java/com/google/android/material/snackbar/res/values/styles.xml
@@ -57,7 +57,7 @@
@dimen/material_emphasis_high_typeend@integer/design_snackbar_text_max_lines
- viewStart
+ viewStart?attr/textAppearanceBody2?attr/colorSurface@dimen/design_snackbar_padding_vertical
diff --git a/lib/java/com/google/android/material/snackbar/res/values/tokens.xml b/lib/java/com/google/android/material/snackbar/res/values/tokens.xml
index 9a06809ce27..6d50ca51cd6 100644
--- a/lib/java/com/google/android/material/snackbar/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/snackbar/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/switchmaterial/SwitchMaterial.java b/lib/java/com/google/android/material/switchmaterial/SwitchMaterial.java
index 75a4c8dfdcb..6d573345aa2 100644
--- a/lib/java/com/google/android/material/switchmaterial/SwitchMaterial.java
+++ b/lib/java/com/google/android/material/switchmaterial/SwitchMaterial.java
@@ -63,7 +63,7 @@ public SwitchMaterial(@NonNull Context context) {
}
public SwitchMaterial(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, R.attr.switchStyle);
+ this(context, attrs, androidx.appcompat.R.attr.switchStyle);
}
public SwitchMaterial(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
@@ -120,7 +120,8 @@ public boolean isUseMaterialThemeColors() {
private ColorStateList getMaterialThemeColorsThumbTintList() {
if (materialThemeColorsThumbTintList == null) {
int colorSurface = MaterialColors.getColor(this, R.attr.colorSurface);
- int colorControlActivated = MaterialColors.getColor(this, R.attr.colorControlActivated);
+ int colorControlActivated =
+ MaterialColors.getColor(this, androidx.appcompat.R.attr.colorControlActivated);
float thumbElevation = getResources().getDimension(R.dimen.mtrl_switch_thumb_elevation);
if (elevationOverlayProvider.isThemeElevationOverlayEnabled()) {
thumbElevation += ViewUtils.getParentAbsoluteElevation(this);
@@ -145,7 +146,8 @@ private ColorStateList getMaterialThemeColorsTrackTintList() {
if (materialThemeColorsTrackTintList == null) {
int[] switchTrackColorsList = new int[ENABLED_CHECKED_STATES.length];
int colorSurface = MaterialColors.getColor(this, R.attr.colorSurface);
- int colorControlActivated = MaterialColors.getColor(this, R.attr.colorControlActivated);
+ int colorControlActivated =
+ MaterialColors.getColor(this, androidx.appcompat.R.attr.colorControlActivated);
int colorOnSurface = MaterialColors.getColor(this, R.attr.colorOnSurface);
switchTrackColorsList[0] =
MaterialColors.layer(colorSurface, colorControlActivated, MaterialColors.ALPHA_MEDIUM);
diff --git a/lib/java/com/google/android/material/tabs/TabLayout.java b/lib/java/com/google/android/material/tabs/TabLayout.java
index da5d81b0694..659d0c2b49a 100644
--- a/lib/java/com/google/android/material/tabs/TabLayout.java
+++ b/lib/java/com/google/android/material/tabs/TabLayout.java
@@ -259,6 +259,7 @@ public class TabLayout extends HorizontalScrollView {
/** @hide */
@IntDef(value = {TAB_LABEL_VISIBILITY_UNLABELED, TAB_LABEL_VISIBILITY_LABELED})
+ @Retention(RetentionPolicy.SOURCE)
public @interface LabelVisibility {}
/**
@@ -545,7 +546,7 @@ public TabLayout(@NonNull Context context, @Nullable AttributeSet attrs, int def
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
materialShapeDrawable.setFillColor(backgroundColorStateList);
materialShapeDrawable.initializeElevationOverlay(context);
- materialShapeDrawable.setElevation(ViewCompat.getElevation(this));
+ materialShapeDrawable.setElevation(getElevation());
setBackground(materialShapeDrawable);
}
@@ -2193,7 +2194,7 @@ public Tab setTag(@Nullable Object tag) {
*
*
Do not rely on this if using {@link TabLayout#setupWithViewPager(ViewPager)}
*
- * @param id, unique id for this tab
+ * @param id unique id for this tab
*/
@NonNull
@CanIgnoreReturnValue
@@ -2983,9 +2984,9 @@ private void updateTextAndIcon(
? DrawableCompat.wrap(tab.getIcon()).mutate()
: null;
if (icon != null) {
- DrawableCompat.setTintList(icon, tabIconTint);
+ icon.setTintList(tabIconTint);
if (tabIconTintMode != null) {
- DrawableCompat.setTintMode(icon, tabIconTintMode);
+ icon.setTintMode(tabIconTintMode);
}
}
diff --git a/lib/java/com/google/android/material/tabs/TabLayoutMediator.java b/lib/java/com/google/android/material/tabs/TabLayoutMediator.java
index 38a9a08e23c..9c8b8fdf431 100644
--- a/lib/java/com/google/android/material/tabs/TabLayoutMediator.java
+++ b/lib/java/com/google/android/material/tabs/TabLayoutMediator.java
@@ -147,6 +147,10 @@ public void attach() {
* called before {@link #attach()} when a ViewPager2's adapter is changed.
*/
public void detach() {
+ if (!attached) {
+ return;
+ }
+
if (autoRefresh && adapter != null) {
adapter.unregisterAdapterDataObserver(pagerAdapterObserver);
pagerAdapterObserver = null;
diff --git a/lib/java/com/google/android/material/tabs/res/values/styles.xml b/lib/java/com/google/android/material/tabs/res/values/styles.xml
index ad52e7e2730..32b2d74bf22 100644
--- a/lib/java/com/google/android/material/tabs/res/values/styles.xml
+++ b/lib/java/com/google/android/material/tabs/res/values/styles.xml
@@ -90,6 +90,7 @@
diff --git a/lib/java/com/google/android/material/tabs/res/values/tokens.xml b/lib/java/com/google/android/material/tabs/res/values/tokens.xml
index 30eb338b654..ea5340bbfb7 100644
--- a/lib/java/com/google/android/material/tabs/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/tabs/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java
index 0cdde89b5e7..510796c2556 100644
--- a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java
+++ b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java
@@ -27,6 +27,7 @@
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.os.SystemClock;
import android.text.Editable;
import android.view.MotionEvent;
import android.view.View;
@@ -280,7 +281,7 @@ private void setUpDropdownShowHideBehavior() {
}
private boolean isDropdownPopupActive() {
- long activeFor = System.currentTimeMillis() - dropdownPopupActivatedAt;
+ long activeFor = SystemClock.uptimeMillis() - dropdownPopupActivatedAt;
return activeFor < 0 || activeFor > 300;
}
@@ -297,7 +298,7 @@ private static AutoCompleteTextView castAutoCompleteTextViewOrThrow(EditText edi
private void updateDropdownPopupDirty() {
dropdownPopupDirty = true;
- dropdownPopupActivatedAt = System.currentTimeMillis();
+ dropdownPopupActivatedAt = SystemClock.uptimeMillis();
}
private void setEndIconChecked(boolean checked) {
diff --git a/lib/java/com/google/android/material/textfield/EndCompoundLayout.java b/lib/java/com/google/android/material/textfield/EndCompoundLayout.java
index 7e7fc8d26c2..548a5675b08 100644
--- a/lib/java/com/google/android/material/textfield/EndCompoundLayout.java
+++ b/lib/java/com/google/android/material/textfield/EndCompoundLayout.java
@@ -795,8 +795,7 @@ private void tintEndIconOnError(boolean tintEndIconOnError) {
// Setting the tint here instead of calling setEndIconTintList() in order to preserve and
// restore the icon's original tint.
Drawable endIconDrawable = DrawableCompat.wrap(getEndIconDrawable()).mutate();
- DrawableCompat.setTint(
- endIconDrawable, textInputLayout.getErrorCurrentTextColors());
+ endIconDrawable.setTint(textInputLayout.getErrorCurrentTextColors());
endIconView.setImageDrawable(endIconDrawable);
} else {
applyIconTint(textInputLayout, endIconView, endIconTintList, endIconTintMode);
diff --git a/lib/java/com/google/android/material/textfield/IconHelper.java b/lib/java/com/google/android/material/textfield/IconHelper.java
index f9c898d25b3..2e1e99ec5ae 100644
--- a/lib/java/com/google/android/material/textfield/IconHelper.java
+++ b/lib/java/com/google/android/material/textfield/IconHelper.java
@@ -85,12 +85,12 @@ static void applyIconTint(
int color =
iconTintList.getColorForState(
mergeIconState(textInputLayout, iconView), iconTintList.getDefaultColor());
- DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
+ icon.setTintList(ColorStateList.valueOf(color));
} else {
- DrawableCompat.setTintList(icon, iconTintList);
+ icon.setTintList(iconTintList);
}
if (iconTintMode != null) {
- DrawableCompat.setTintMode(icon, iconTintMode);
+ icon.setTintMode(iconTintMode);
}
}
@@ -116,7 +116,7 @@ static void refreshIconDrawableState(
mergeIconState(textInputLayout, iconView), colorStateList.getDefaultColor());
icon = DrawableCompat.wrap(icon).mutate();
- DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
+ icon.setTintList(ColorStateList.valueOf(color));
iconView.setImageDrawable(icon);
}
diff --git a/lib/java/com/google/android/material/textfield/MaterialAutoCompleteTextView.java b/lib/java/com/google/android/material/textfield/MaterialAutoCompleteTextView.java
index 627fbe147cc..7f0e70ad081 100644
--- a/lib/java/com/google/android/material/textfield/MaterialAutoCompleteTextView.java
+++ b/lib/java/com/google/android/material/textfield/MaterialAutoCompleteTextView.java
@@ -51,7 +51,6 @@
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.core.graphics.drawable.DrawableCompat;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.internal.ManufacturerUtils;
import com.google.android.material.internal.ThemeEnforcement;
@@ -90,7 +89,7 @@ public MaterialAutoCompleteTextView(@NonNull Context context) {
public MaterialAutoCompleteTextView(
@NonNull Context context, @Nullable AttributeSet attributeSet) {
- this(context, attributeSet, R.attr.autoCompleteTextViewStyle);
+ this(context, attributeSet, androidx.appcompat.R.attr.autoCompleteTextViewStyle);
}
public MaterialAutoCompleteTextView(
@@ -105,7 +104,7 @@ public MaterialAutoCompleteTextView(
attributeSet,
R.styleable.MaterialAutoCompleteTextView,
defStyleAttr,
- R.style.Widget_AppCompat_AutoCompleteTextView);
+ androidx.appcompat.R.style.Widget_AppCompat_AutoCompleteTextView);
// Due to a framework bug, setting android:inputType="none" on xml has no effect. Therefore,
// we check it here in case the autoCompleteTextView should be non-editable.
@@ -567,7 +566,7 @@ private Drawable getSelectedItemDrawable() {
// pressed states, but not to other states like focused and hovered. To solve that, we
// create the selectedItemRippleOverlaidColor that will work in those missing states, making
// the selected list item stateful as expected.
- DrawableCompat.setTintList(colorDrawable, selectedItemRippleOverlaidColor);
+ colorDrawable.setTintList(selectedItemRippleOverlaidColor);
return new RippleDrawable(pressedRippleColor, colorDrawable, null);
} else {
return colorDrawable;
diff --git a/lib/java/com/google/android/material/textfield/TextInputEditText.java b/lib/java/com/google/android/material/textfield/TextInputEditText.java
index 4f1b30aed93..88a36aa017a 100644
--- a/lib/java/com/google/android/material/textfield/TextInputEditText.java
+++ b/lib/java/com/google/android/material/textfield/TextInputEditText.java
@@ -66,7 +66,7 @@ public TextInputEditText(@NonNull Context context) {
}
public TextInputEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, R.attr.editTextStyle);
+ this(context, attrs, androidx.appcompat.R.attr.editTextStyle);
}
public TextInputEditText(
diff --git a/lib/java/com/google/android/material/textfield/TextInputLayout.java b/lib/java/com/google/android/material/textfield/TextInputLayout.java
index 08191eb660b..9709c6ce613 100644
--- a/lib/java/com/google/android/material/textfield/TextInputLayout.java
+++ b/lib/java/com/google/android/material/textfield/TextInputLayout.java
@@ -868,7 +868,9 @@ private Drawable getEditTextBoxBackground() {
return boxBackground;
}
- int rippleColor = MaterialColors.getColor(editText, R.attr.colorControlHighlight);
+ int rippleColor =
+ MaterialColors.getColor(
+ editText, androidx.appcompat.R.attr.colorControlHighlight);
if (boxBackgroundMode == TextInputLayout.BOX_BACKGROUND_OUTLINE) {
return getOutlinedBoxBackgroundWithRipple(
getContext(), boxBackground, rippleColor, EDIT_TEXT_BACKGROUND_RIPPLE_STATE);
@@ -2451,7 +2453,8 @@ public void setPlaceholderText(@Nullable final CharSequence placeholderText) {
if (placeholderTextView == null) {
placeholderTextView = new AppCompatTextView(getContext());
placeholderTextView.setId(R.id.textinput_placeholder);
- placeholderTextView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ placeholderTextView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ placeholderTextView.setAccessibilityLiveRegion(ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
placeholderFadeIn = createPlaceholderFadeTransition();
placeholderFadeIn.setStartDelay(PLACEHOLDER_START_DELAY);
@@ -2459,6 +2462,17 @@ public void setPlaceholderText(@Nullable final CharSequence placeholderText) {
setPlaceholderTextAppearance(placeholderTextAppearance);
setPlaceholderTextColor(placeholderTextColor);
+
+ ViewCompat.setAccessibilityDelegate(
+ placeholderTextView,
+ new AccessibilityDelegateCompat() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(
+ @NonNull View host, @NonNull AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setVisibleToUser(false);
+ }
+ });
}
// If placeholder text is null, disable placeholder.
@@ -2529,7 +2543,6 @@ private void showPlaceholderText() {
TransitionManager.beginDelayedTransition(inputFrame, placeholderFadeIn);
placeholderTextView.setVisibility(VISIBLE);
placeholderTextView.bringToFront();
- announceForAccessibility(placeholderText);
}
}
@@ -2877,7 +2890,8 @@ void setTextAppearanceCompatWithErrorFallback(
if (useDefaultColor) {
// Probably caused by our theme not extending from Theme.Design*. Instead
// we manually set something appropriate
- TextViewCompat.setTextAppearance(textView, R.style.TextAppearance_AppCompat_Caption);
+ TextViewCompat.setTextAppearance(
+ textView, androidx.appcompat.R.style.TextAppearance_AppCompat_Caption);
textView.setTextColor(ContextCompat.getColor(getContext(), R.color.design_error));
}
}
@@ -3588,7 +3602,7 @@ public CharSequence getStartIconContentDescription() {
*
*
Subsequent calls to {@link #setStartIconDrawable(Drawable)} will automatically mutate the
* drawable and apply the specified tint and tint mode using {@link
- * DrawableCompat#setTintList(Drawable, ColorStateList)}.
+ * Drawable#setTintList(ColorStateList)}.
*
* @param startIconTintList the tint to apply, may be null to clear tint
* @attr ref com.google.android.material.R.styleable#TextInputLayout_startIconTint
@@ -3808,9 +3822,9 @@ public int getEndIconMinSize() {
}
/**
- * Sets {@link ImageView.ScaleType} for the start icon's ImageButton.
+ * Sets {@link ScaleType} for the start icon's ImageButton.
*
- * @param scaleType {@link ImageView.ScaleType} for the start icon's ImageButton.
+ * @param scaleType {@link ScaleType} for the start icon's ImageButton.
* @attr ref android.support.design.button.R.styleable#TextInputLayout_startIconScaleType
* @see #getStartIconScaleType()
*/
@@ -3819,9 +3833,9 @@ public void setStartIconScaleType(@NonNull ScaleType scaleType) {
}
/**
- * Returns the {@link ImageView.ScaleType} for the start icon's ImageButton.
+ * Returns the {@link ScaleType} for the start icon's ImageButton.
*
- * @return Returns the {@link ImageView.ScaleType} for the start icon's ImageButton.
+ * @return Returns the {@link ScaleType} for the start icon's ImageButton.
* @attr ref android.support.design.button.R.styleable#TextInputLayout_startIconScaleType
* @see #setStartIconScaleType(ScaleType)
*/
@@ -3831,9 +3845,9 @@ public ScaleType getStartIconScaleType() {
}
/**
- * Sets {@link ImageView.ScaleType} for the end icon's ImageButton.
+ * Sets {@link ScaleType} for the end icon's ImageButton.
*
- * @param scaleType {@link ImageView.ScaleType} for the end icon's ImageButton.
+ * @param scaleType {@link ScaleType} for the end icon's ImageButton.
* @attr ref android.support.design.button.R.styleable#TextInputLayout_endIconScaleType
* @see #getEndIconScaleType()
*/
@@ -3842,9 +3856,9 @@ public void setEndIconScaleType(@NonNull ScaleType scaleType) {
}
/**
- * Returns the {@link ImageView.ScaleType} for the end icon's ImageButton.
+ * Returns the {@link ScaleType} for the end icon's ImageButton.
*
- * @return Returns the {@link ImageView.ScaleType} for the end icon's ImageButton.
+ * @return Returns the {@link ScaleType} for the end icon's ImageButton.
* @attr ref android.support.design.button.R.styleable#TextInputLayout_endIconScaleType
* @see #setEndIconScaleType(ScaleType)
*/
@@ -3897,7 +3911,7 @@ public CharSequence getEndIconContentDescription() {
*
*
Subsequent calls to {@link #setEndIconDrawable(Drawable)} will automatically mutate the
* drawable and apply the specified tint and tint mode using {@link
- * DrawableCompat#setTintList(Drawable, ColorStateList)}.
+ * Drawable#setTintList(ColorStateList)}.
*
* @param endIconTintList the tint to apply, may be null to clear tint
* @attr ref com.google.android.material.R.styleable#TextInputLayout_endIconTint
@@ -4537,9 +4551,11 @@ private void updateStrokeErrorColor(boolean hasFocus, boolean isHovered) {
@RequiresApi(VERSION_CODES.Q)
private void updateCursorColor() {
- ColorStateList color = cursorColor != null
- ? cursorColor
- : MaterialColors.getColorStateListOrNull(getContext(), R.attr.colorControlActivated);
+ ColorStateList color =
+ cursorColor != null
+ ? cursorColor
+ : MaterialColors.getColorStateListOrNull(
+ getContext(), androidx.appcompat.R.attr.colorControlActivated);
if (editText == null || editText.getTextCursorDrawable() == null) {
// If there's no cursor, return.
@@ -4550,7 +4566,7 @@ private void updateCursorColor() {
if (isOnError() && cursorErrorColor != null) {
color = cursorErrorColor;
}
- DrawableCompat.setTintList(cursorDrawable, color);
+ cursorDrawable.setTintList(color);
}
private void expandHint(boolean animate) {
diff --git a/lib/java/com/google/android/material/textfield/res/values-af/strings.xml b/lib/java/com/google/android/material/textfield/res/values-af/strings.xml
index e116fd1e233..7c5e1ad7c65 100644
--- a/lib/java/com/google/android/material/textfield/res/values-af/strings.xml
+++ b/lib/java/com/google/android/material/textfield/res/values-af/strings.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/lib/java/com/google/android/material/textfield/res/values/tokens_dropdown_menu.xml b/lib/java/com/google/android/material/textfield/res/values/tokens_dropdown_menu.xml
index d922d07f8f9..52f4893f08a 100644
--- a/lib/java/com/google/android/material/textfield/res/values/tokens_dropdown_menu.xml
+++ b/lib/java/com/google/android/material/textfield/res/values/tokens_dropdown_menu.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/textfield/res/values/tokens_textfield.xml b/lib/java/com/google/android/material/textfield/res/values/tokens_textfield.xml
index ba7e564c6ee..f956f4ff318 100644
--- a/lib/java/com/google/android/material/textfield/res/values/tokens_textfield.xml
+++ b/lib/java/com/google/android/material/textfield/res/values/tokens_textfield.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java b/lib/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java
index beacd68d522..08b189c6c43 100644
--- a/lib/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java
+++ b/lib/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java
@@ -45,7 +45,7 @@ public class MaterialThemeOverlay {
private MaterialThemeOverlay() {}
private static final int[] ANDROID_THEME_OVERLAY_ATTRS =
- new int[] {android.R.attr.theme, R.attr.theme};
+ new int[] {android.R.attr.theme, androidx.appcompat.R.attr.theme};
private static final int[] MATERIAL_THEME_OVERLAY_ATTR = new int[] {R.attr.materialThemeOverlay};
@@ -104,7 +104,7 @@ public static Context wrap(
obtainMaterialOverlayIds(context, set, optionalAttrs, defStyleAttr, defStyleRes);
for (int optionalOverlayId : optionalOverlayIds) {
if (optionalOverlayId != 0) {
- contextThemeWrapper = new ContextThemeWrapper(contextThemeWrapper, optionalOverlayId);
+ contextThemeWrapper.getTheme().applyStyle(optionalOverlayId, true);
}
}
diff --git a/lib/java/com/google/android/material/theme/res-public/values/public.xml b/lib/java/com/google/android/material/theme/res-public/values/public.xml
index 31ca7db01f2..e5f42a23d14 100644
--- a/lib/java/com/google/android/material/theme/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/theme/res-public/values/public.xml
@@ -15,6 +15,29 @@
~ limitations under the License.
-->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/theme/res/values-large/themes_base.xml b/lib/java/com/google/android/material/theme/res/values-large/themes_base.xml
index 29128c2e194..bef5060aee2 100644
--- a/lib/java/com/google/android/material/theme/res/values-large/themes_base.xml
+++ b/lib/java/com/google/android/material/theme/res/values-large/themes_base.xml
@@ -16,11 +16,17 @@
-->
+
+
+
+
+ parent="Base.Theme.Material3.Light.Dialog.FixedSize"/>
+ parent="Base.Theme.Material3.Dark.Dialog.FixedSize"/>
diff --git a/lib/java/com/google/android/material/theme/res/values-night/themes_daynight.xml b/lib/java/com/google/android/material/theme/res/values-night/themes_daynight.xml
index ba3ee2f2035..336dc309ace 100644
--- a/lib/java/com/google/android/material/theme/res/values-night/themes_daynight.xml
+++ b/lib/java/com/google/android/material/theme/res/values-night/themes_daynight.xml
@@ -15,6 +15,13 @@
~ limitations under the License.
-->
+
+
+
+
+
+
+
diff --git a/lib/java/com/google/android/material/theme/res/values-night/themes_overlay.xml b/lib/java/com/google/android/material/theme/res/values-night/themes_overlay.xml
index c92bd1431e1..fca5db54168 100644
--- a/lib/java/com/google/android/material/theme/res/values-night/themes_overlay.xml
+++ b/lib/java/com/google/android/material/theme/res/values-night/themes_overlay.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
+
-
diff --git a/lib/java/com/google/android/material/theme/res/values-v31/themes.xml b/lib/java/com/google/android/material/theme/res/values-v31/themes.xml
index 4e1783db51a..2eb553cab42 100644
--- a/lib/java/com/google/android/material/theme/res/values-v31/themes.xml
+++ b/lib/java/com/google/android/material/theme/res/values-v31/themes.xml
@@ -15,6 +15,10 @@
~ limitations under the License.
-->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -25,6 +54,31 @@
-->
diff --git a/lib/java/com/google/android/material/typography/res/values/tokens.xml b/lib/java/com/google/android/material/typography/res/values/tokens.xml
index e749e6823fa..64e51c351a8 100644
--- a/lib/java/com/google/android/material/typography/res/values/tokens.xml
+++ b/lib/java/com/google/android/material/typography/res/values/tokens.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/lib/javatests/com/google/android/material/appbar/MaterialToolbarTest.java b/lib/javatests/com/google/android/material/appbar/MaterialToolbarTest.java
index ee4032ac19d..1086c9f9ae4 100644
--- a/lib/javatests/com/google/android/material/appbar/MaterialToolbarTest.java
+++ b/lib/javatests/com/google/android/material/appbar/MaterialToolbarTest.java
@@ -20,7 +20,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.graphics.Color;
-import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.annotation.LayoutRes;
@@ -37,7 +36,7 @@
/** Tests for {@link com.google.android.material.appbar.MaterialToolbar}. */
@RunWith(RobolectricTestRunner.class)
-@Config(sdk = VERSION_CODES.LOLLIPOP)
+@Config(sdk = Config.OLDEST_SDK)
@DoNotInstrument
public class MaterialToolbarTest {
diff --git a/lib/javatests/com/google/android/material/bottomsheet/BottomSheetDragHandleTest.java b/lib/javatests/com/google/android/material/bottomsheet/BottomSheetDragHandleTest.java
index 0df20bf7f40..d8e74b673ea 100644
--- a/lib/javatests/com/google/android/material/bottomsheet/BottomSheetDragHandleTest.java
+++ b/lib/javatests/com/google/android/material/bottomsheet/BottomSheetDragHandleTest.java
@@ -19,15 +19,15 @@
import com.google.android.material.test.R;
import static android.content.Context.ACCESSIBILITY_SERVICE;
-import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_COLLAPSE;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_DISMISS;
-import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_EXPAND;
import static com.google.android.material.bottomsheet.BottomSheetBehavior.VIEW_INDEX_ACCESSIBILITY_DELEGATE_VIEW;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityManager;
@@ -62,31 +62,77 @@ public void setUp() throws Exception {
dragHandleView = new BottomSheetDragHandleView(activity);
}
+ @Test
+ public void test_importantForAccessibilityEnabledByDefault() {
+ activity.addViewToBottomSheet(dragHandleView);
+ assertImportantForAccessibility(true);
+ }
+
+ @Test
+ public void test_importantForAccessibilityNotAltered() {
+ dragHandleView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ activity.addViewToBottomSheet(dragHandleView);
+ shadowOf(accessibilityManager).setEnabled(true);
+ assertThat(dragHandleView.getImportantForAccessibility())
+ .isEqualTo(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+
@Test
public void test_notInteractableWhenDetachedAndAccessibilityDisabled() {
- assertImportantForAccessibility(false);
assertThat(dragHandleView.isClickable()).isFalse();
}
@Test
public void test_notInteractableWhenDetachedAndAccessibilityEnabled() {
shadowOf(accessibilityManager).setEnabled(true);
- assertImportantForAccessibility(false);
assertThat(dragHandleView.isClickable()).isFalse();
}
@Test
- public void test_notInteractableWhenAttachedAndAccessibilityDisabled() {
+ public void test_interactableWhenAttachedAndAccessibilityDisabled() {
activity.addViewToBottomSheet(dragHandleView);
- assertImportantForAccessibility(true);
- assertThat(dragHandleView.isClickable()).isFalse();
+ assertThat(dragHandleView.isClickable()).isTrue();
+ }
+
+ @Test
+ public void test_customClickListenerOverridesInternalClickBehavior() {
+ activity.addViewToBottomSheet(dragHandleView);
+ activity.bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ shadowOf(accessibilityManager).setEnabled(true);
+ dragHandleView.setOnClickListener(v -> {
+ // do nothing
+ });
+
+ dragHandleView.callOnClick();
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(activity.bottomSheetBehavior.getState())
+ .isEqualTo(BottomSheetBehavior.STATE_EXPANDED);
+ }
+
+ @Test
+ public void test_customClickListenerOverridesInternalClickBehaviorWithKeyboardEnter() {
+ activity.addViewToBottomSheet(dragHandleView);
+ activity.bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ shadowOf(accessibilityManager).setEnabled(true);
+ dragHandleView.setOnClickListener(
+ v -> {
+ // do nothing
+ });
+
+ boolean unused =
+ dragHandleView.onKeyDown(
+ KeyEvent.KEYCODE_ENTER, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(activity.bottomSheetBehavior.getState())
+ .isEqualTo(BottomSheetBehavior.STATE_EXPANDED);
}
@Test
public void test_notInteractableWhenNotAttachedToBottomSheetAndAccessibilityEnabled() {
activity.addViewToContainer(dragHandleView);
shadowOf(accessibilityManager).setEnabled(true);
- assertImportantForAccessibility(false);
assertThat(dragHandleView.isClickable()).isFalse();
}
@@ -94,7 +140,6 @@ public void test_notInteractableWhenNotAttachedToBottomSheetAndAccessibilityEnab
public void test_interactableWhenAttachedAndAccessibilityEnabled() {
activity.addViewToBottomSheet(dragHandleView);
shadowOf(accessibilityManager).setEnabled(true);
- assertImportantForAccessibility(true);
assertThat(dragHandleView.isClickable()).isTrue();
}
@@ -127,6 +172,39 @@ public void test_collapseExpandedBottomSheetWhenClicked() {
.isEqualTo(BottomSheetBehavior.STATE_COLLAPSED);
}
+ @Test
+ public void test_expandCollapsedBottomSheetWithKeyboardEnter() {
+ activity.bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ activity.addViewToBottomSheet(dragHandleView);
+ shadowOf(accessibilityManager).setEnabled(true);
+ boolean unused =
+ dragHandleView.onKeyDown(
+ KeyEvent.KEYCODE_ENTER, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(activity.bottomSheetBehavior.getState())
+ .isEqualTo(BottomSheetBehavior.STATE_EXPANDED);
+ }
+
+ @Test
+ public void test_collapseExpandedBottomSheetWithKeyboardEnter() {
+ activity.bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ activity.addViewToBottomSheet(dragHandleView);
+ shadowOf(accessibilityManager).setEnabled(true);
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ boolean unused =
+ dragHandleView.onKeyDown(
+ KeyEvent.KEYCODE_ENTER, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(activity.bottomSheetBehavior.getState())
+ .isEqualTo(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+
@Test
public void test_collapsedBottomSheetMoveToHalfExpanded_whenClickedAndFitToContentsFalse() {
activity.bottomSheetBehavior.setFitToContents(false);
@@ -208,7 +286,9 @@ public void test_customActionExpand() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- dragHandleView.performAccessibilityAction(ACTION_EXPAND.getId(), /* args= */ null);
+ dragHandleView.performAccessibilityAction(
+ getCustomAccessibilityActionId(activity.bottomSheetBehavior.expandActionIds),
+ /* arguments= */ null);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -225,7 +305,9 @@ public void test_customActionHalfExpand() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- dragHandleView.performAccessibilityAction(getHalfExpandActionId(), /* args= */ null);
+ dragHandleView.performAccessibilityAction(
+ getCustomAccessibilityActionId(activity.bottomSheetBehavior.expandHalfwayActionIds),
+ /* arguments= */ null);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -241,7 +323,9 @@ public void test_customActionCollapse() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- dragHandleView.performAccessibilityAction(ACTION_COLLAPSE.getId(), /* args= */ null);
+ dragHandleView.performAccessibilityAction(
+ getCustomAccessibilityActionId(activity.bottomSheetBehavior.collapseActionIds),
+ /* arguments= */ null);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -258,7 +342,7 @@ public void test_customActionDismiss() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- dragHandleView.performAccessibilityAction(ACTION_DISMISS.getId(), /* args= */ null);
+ dragHandleView.performAccessibilityAction(ACTION_DISMISS.getId(), /* arguments= */ null);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -275,10 +359,11 @@ public void test_customActionSetInCollapsedStateWhenHalfExpandableAndHideable()
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- assertThat(hasAccessibilityAction(dragHandleView, getHalfExpandActionId())).isTrue();
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_EXPAND.getId())).isTrue();
+ BottomSheetBehavior behavior = activity.bottomSheetBehavior;
+ assertThat(hasCustomAccessibilityAction(behavior.expandHalfwayActionIds)).isTrue();
+ assertThat(hasCustomAccessibilityAction(behavior.expandActionIds)).isTrue();
+ assertThat(hasCustomAccessibilityAction(behavior.collapseActionIds)).isFalse();
assertThat(hasAccessibilityAction(dragHandleView, ACTION_DISMISS.getId())).isTrue();
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_COLLAPSE.getId())).isFalse();
}
@Test
@@ -291,10 +376,11 @@ public void test_customActionSetInExpandedStateWhenHalfExpandableAndNotHideable(
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- assertThat(hasAccessibilityAction(dragHandleView, getHalfExpandActionId())).isTrue();
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_EXPAND.getId())).isFalse();
+ BottomSheetBehavior behavior = activity.bottomSheetBehavior;
+ assertThat(hasCustomAccessibilityAction(behavior.expandHalfwayActionIds)).isTrue();
+ assertThat(hasCustomAccessibilityAction(behavior.expandActionIds)).isFalse();
+ assertThat(hasCustomAccessibilityAction(behavior.collapseActionIds)).isTrue();
assertThat(hasAccessibilityAction(dragHandleView, ACTION_DISMISS.getId())).isFalse();
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_COLLAPSE.getId())).isTrue();
}
@Test
@@ -306,10 +392,11 @@ public void test_customActionSetInCollapsedStateWhenNotHideable() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- assertThat(getHalfExpandActionId()).isEqualTo(View.NO_ID);
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_EXPAND.getId())).isTrue();
+ BottomSheetBehavior behavior = activity.bottomSheetBehavior;
+ assertThat(hasCustomAccessibilityAction(behavior.expandHalfwayActionIds)).isFalse();
+ assertThat(hasCustomAccessibilityAction(behavior.expandActionIds)).isTrue();
+ assertThat(hasCustomAccessibilityAction(behavior.collapseActionIds)).isFalse();
assertThat(hasAccessibilityAction(dragHandleView, ACTION_DISMISS.getId())).isFalse();
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_COLLAPSE.getId())).isFalse();
}
@Test
@@ -321,15 +408,11 @@ public void test_customActionSetInExpandedStateWhenHideable() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- assertThat(getHalfExpandActionId()).isEqualTo(View.NO_ID);
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_EXPAND.getId())).isFalse();
+ BottomSheetBehavior behavior = activity.bottomSheetBehavior;
+ assertThat(hasCustomAccessibilityAction(behavior.expandHalfwayActionIds)).isFalse();
+ assertThat(hasCustomAccessibilityAction(behavior.expandActionIds)).isFalse();
+ assertThat(hasCustomAccessibilityAction(behavior.collapseActionIds)).isTrue();
assertThat(hasAccessibilityAction(dragHandleView, ACTION_DISMISS.getId())).isTrue();
- assertThat(hasAccessibilityAction(dragHandleView, ACTION_COLLAPSE.getId())).isTrue();
- }
-
- private int getHalfExpandActionId() {
- return activity.bottomSheetBehavior.expandHalfwayActionIds.get(
- VIEW_INDEX_ACCESSIBILITY_DELEGATE_VIEW, View.NO_ID);
}
private void assertImportantForAccessibility(boolean important) {
@@ -347,6 +430,14 @@ private static boolean hasAccessibilityAction(View view, int actionId) {
return getAccessibilityActionList(view).stream().anyMatch(action -> action.getId() == actionId);
}
+ private static boolean hasCustomAccessibilityAction(SparseIntArray actionIds) {
+ return getCustomAccessibilityActionId(actionIds) != View.NO_ID;
+ }
+
+ private static int getCustomAccessibilityActionId(SparseIntArray actionIds) {
+ return actionIds.get(VIEW_INDEX_ACCESSIBILITY_DELEGATE_VIEW, View.NO_ID);
+ }
+
private static ArrayList getAccessibilityActionList(View view) {
@SuppressWarnings({"unchecked"})
ArrayList actions =
diff --git a/lib/javatests/com/google/android/material/button/MaterialButtonTest.java b/lib/javatests/com/google/android/material/button/MaterialButtonTest.java
index b038eddc1dc..f74be47047b 100644
--- a/lib/javatests/com/google/android/material/button/MaterialButtonTest.java
+++ b/lib/javatests/com/google/android/material/button/MaterialButtonTest.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.view.View.MeasureSpec;
import android.widget.Button;
import android.widget.CompoundButton;
@@ -71,7 +70,7 @@ public void testSetShapeAppearanceModel_setCornerRadius() {
}
@Test
- @Config(sdk = Build.VERSION_CODES.LOLLIPOP)
+ @Config(sdk = Config.OLDEST_SDK)
public void testShapeRippleDrawableInLollipop() {
MaterialButton materialButton = new MaterialButton(context);
ShapeAppearanceModel shapeAppearanceModel = materialButton.getShapeAppearanceModel();
diff --git a/lib/javatests/com/google/android/material/button/MaterialButtonToggleGroupTest.java b/lib/javatests/com/google/android/material/button/MaterialButtonToggleGroupTest.java
index ec4897166bf..21b3627afe1 100644
--- a/lib/javatests/com/google/android/material/button/MaterialButtonToggleGroupTest.java
+++ b/lib/javatests/com/google/android/material/button/MaterialButtonToggleGroupTest.java
@@ -47,7 +47,7 @@
@LooperMode(LooperMode.Mode.LEGACY)
/** Tests for {@link com.google.android.material.button.MaterialButtonToggleGroup}. */
@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 21)
+@Config(sdk = Config.OLDEST_SDK)
public class MaterialButtonToggleGroupTest {
private static final float CORNER_SIZE = 10f;
diff --git a/lib/javatests/com/google/android/material/carousel/CarouselHelper.java b/lib/javatests/com/google/android/material/carousel/CarouselHelper.java
index 8641ca38291..a55b4fa4f75 100644
--- a/lib/javatests/com/google/android/material/carousel/CarouselHelper.java
+++ b/lib/javatests/com/google/android/material/carousel/CarouselHelper.java
@@ -457,7 +457,7 @@ static KeylineState getTestCenteredKeylineState() {
float smallMask = getKeylineMaskPercentage(smallSize, largeSize);
float mediumMask = getKeylineMaskPercentage(mediumSize, largeSize);
- return new KeylineState.Builder(450F, 1320F)
+ return new KeylineState.Builder(450F, 1320)
.addKeyline(5F, extraSmallMask, extraSmallSize)
.addKeylineRange(38F, smallMask, smallSize, 2)
.addKeyline(166F, mediumMask, mediumSize)
@@ -478,7 +478,7 @@ static KeylineState getTestCenteredVerticalKeylineState() {
float smallMask = getKeylineMaskPercentage(smallSize, largeSize);
float mediumMask = getKeylineMaskPercentage(mediumSize, largeSize);
- return new KeylineState.Builder(100F, 200F)
+ return new KeylineState.Builder(100F, 200)
.addKeyline(9F, smallMask, smallSize)
.addKeyline(25F, mediumMask, mediumSize)
.addKeyline(66F, 0F, largeSize, true)
diff --git a/lib/javatests/com/google/android/material/carousel/KeylineStateListTest.java b/lib/javatests/com/google/android/material/carousel/KeylineStateListTest.java
index d712fe24e87..769a10be6e7 100644
--- a/lib/javatests/com/google/android/material/carousel/KeylineStateListTest.java
+++ b/lib/javatests/com/google/android/material/carousel/KeylineStateListTest.java
@@ -238,7 +238,7 @@ public void testStartArrangementWithOutOfBoundsKeyline_shouldShiftEnd() {
};
KeylineState state =
- new KeylineState.Builder(40F, 100F)
+ new KeylineState.Builder(40F, 100)
.addAnchorKeyline(-10F, getKeylineMaskPercentage(20F, 40F), 20F)
.addKeyline(10F, getKeylineMaskPercentage(20F, 40F), 20F, false)
.addKeyline(40F, 0F, 40F, true)
diff --git a/lib/javatests/com/google/android/material/carousel/MaskableFrameLayoutTest.java b/lib/javatests/com/google/android/material/carousel/MaskableFrameLayoutTest.java
index 0ab8f17ce30..8b021a14fe8 100644
--- a/lib/javatests/com/google/android/material/carousel/MaskableFrameLayoutTest.java
+++ b/lib/javatests/com/google/android/material/carousel/MaskableFrameLayoutTest.java
@@ -76,7 +76,7 @@ public void testShapeAppearanceWithAbsoluteCornerSizes_shouldBeClamped() {
}
@RequiresApi(api = VERSION_CODES.LOLLIPOP_MR1)
- @Config(sdk = VERSION_CODES.LOLLIPOP_MR1)
+ @Config(sdk = VERSION_CODES.M)
@Test
public void testForceCompatClipping_shouldNotUseViewOutlineProvider() {
MaskableFrameLayout maskableFrameLayout = createMaskableFrameLayoutWithSize(50, 50);
@@ -90,7 +90,7 @@ public void testForceCompatClipping_shouldNotUseViewOutlineProvider() {
}
@RequiresApi(api = VERSION_CODES.LOLLIPOP_MR1)
- @Config(sdk = VERSION_CODES.LOLLIPOP_MR1)
+ @Config(sdk = VERSION_CODES.M)
@Test
public void testRoundedCornersApi22_usesViewOutlineProvider() {
MaskableFrameLayout maskableFrameLayout = createMaskableFrameLayoutWithSize(50, 50);
@@ -102,7 +102,7 @@ public void testRoundedCornersApi22_usesViewOutlineProvider() {
}
@RequiresApi(api = VERSION_CODES.LOLLIPOP_MR1)
- @Config(sdk = VERSION_CODES.LOLLIPOP_MR1)
+ @Config(sdk = VERSION_CODES.M)
@Test
public void testCutCornersApi22_doesNotUseViewOutlineProvider() {
MaskableFrameLayout maskableFrameLayout = createMaskableFrameLayoutWithSize(50, 50);
diff --git a/lib/javatests/com/google/android/material/carousel/MultiBrowseCarouselStrategyTest.java b/lib/javatests/com/google/android/material/carousel/MultiBrowseCarouselStrategyTest.java
index 298b01bb4d5..65dc22e2e9f 100644
--- a/lib/javatests/com/google/android/material/carousel/MultiBrowseCarouselStrategyTest.java
+++ b/lib/javatests/com/google/android/material/carousel/MultiBrowseCarouselStrategyTest.java
@@ -95,6 +95,22 @@ public void testSmallContainer_shouldShowOneLargeItem() {
assertThat(keylineState.getKeylines().get(1).maskedItemSize).isEqualTo((float) carouselWidth);
}
+ @Test
+ public void testContainer_shouldShowOneLargeOneSmallItem() {
+ View view = createViewWithSize(ApplicationProvider.getApplicationContext(), 100, 400);
+ float minSmallItemSize =
+ view.getResources().getDimension(R.dimen.m3_carousel_small_item_size_min);
+ // Create a carousel that will fit at least one small item and one larger item
+ int carouselWidth = ((int) (minSmallItemSize * 2f)) + 1;
+ Carousel carousel = createCarouselWithWidth(carouselWidth);
+
+ MultiBrowseCarouselStrategy config = setupStrategy();
+ KeylineState keylineState = config.onFirstChildMeasuredWithMargins(carousel, view);
+
+ assertThat(keylineState.getKeylines()).hasSize(4);
+ assertThat(keylineState.getKeylines().get(2).maskedItemSize).isEqualTo(minSmallItemSize);
+ }
+
@Test
public void testKnownArrangementWithMediumItem_correctlyCalculatesKeylineLocations() {
float[] locOffsets = new float[] {-.5F, 100F, 300F, 464F, 556F, 584.5F};
diff --git a/lib/javatests/com/google/android/material/checkbox/MaterialCheckBoxTest.java b/lib/javatests/com/google/android/material/checkbox/MaterialCheckBoxTest.java
index 4ef369cbe6d..db394619951 100644
--- a/lib/javatests/com/google/android/material/checkbox/MaterialCheckBoxTest.java
+++ b/lib/javatests/com/google/android/material/checkbox/MaterialCheckBoxTest.java
@@ -227,7 +227,7 @@ public void testSetErrorA11yLabel_succeeds() {
}
@Test
- @Config(sdk = VERSION_CODES.LOLLIPOP)
+ @Config(sdk = Config.OLDEST_SDK)
public void testThemeableAppButtonTint() {
testThemeableButtonTint((CheckBox) checkboxes.findViewById(R.id.test_checkbox_app_button_tint));
}
diff --git a/lib/javatests/com/google/android/material/circularreveal/CircularRevealHelperTest.java b/lib/javatests/com/google/android/material/circularreveal/CircularRevealHelperTest.java
index 7004c0e3e6f..2bcf9a7ac5e 100644
--- a/lib/javatests/com/google/android/material/circularreveal/CircularRevealHelperTest.java
+++ b/lib/javatests/com/google/android/material/circularreveal/CircularRevealHelperTest.java
@@ -29,7 +29,6 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION_CODES;
import android.view.View;
import android.view.View.MeasureSpec;
import androidx.annotation.ColorInt;
@@ -87,7 +86,7 @@ public void hugeRevealInfoRadiusIsModified() {
}
@Test
- @Config(sdk = VERSION_CODES.LOLLIPOP)
+ @Config(sdk = Config.OLDEST_SDK)
public void lUsesRevealAnimatorStrategy() {
helper = new CircularRevealHelper(delegate);
helper.setRevealInfo(smallRevealInfo);
@@ -100,7 +99,7 @@ public void lUsesRevealAnimatorStrategy() {
}
@Test
- @Config(sdk = VERSION_CODES.LOLLIPOP)
+ @Config(sdk = Config.OLDEST_SDK)
public void lDrawsScrim() {
helper = new CircularRevealHelper(delegate);
helper.setCircularRevealScrimColor(Color.RED);
diff --git a/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java b/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java
index 289fc1ec825..39b27b50105 100644
--- a/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java
+++ b/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java
@@ -599,6 +599,21 @@ public void focusAndShowKeyboardAtStartup() {
assertThat(shadowIMM.isSoftInputVisible()).isTrue();
}
+ @Test
+ public void textField_shouldSetCursorToEndOfText() {
+ Calendar calendar = UtcDates.getUtcCalendar();
+ calendar.set(2025, Calendar.FEBRUARY, 1);
+ rangeDateSelector.setSelection(new Pair<>(calendar.getTimeInMillis(), null));
+ View root = getRootView();
+ ((ViewGroup) activity.findViewById(android.R.id.content)).addView(root);
+ TextInputLayout startTextInput = root.findViewById(R.id.mtrl_picker_text_input_range_start);
+ EditText editText = startTextInput.getEditText();
+
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(editText.getSelectionStart()).isEqualTo(editText.getText().length());
+ }
+
private View getRootView() {
return rangeDateSelector.onCreateTextInputView(
LayoutInflater.from(context),
diff --git a/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java b/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java
index 7490c89dd3b..330454f4034 100644
--- a/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java
+++ b/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java
@@ -345,6 +345,21 @@ public void focusAndShowKeyboardAtStartup() {
assertThat(shadowIMM.isSoftInputVisible()).isTrue();
}
+ @Test
+ public void textField_shouldSetCursorToEndOfText() {
+ Calendar calendar = UtcDates.getUtcCalendar();
+ calendar.set(2025, Calendar.FEBRUARY, 1);
+ singleDateSelector.setSelection(calendar.getTimeInMillis());
+ View root = getRootView();
+ ((ViewGroup) activity.findViewById(android.R.id.content)).addView(root);
+ TextInputLayout textInputLayout = root.findViewById(R.id.mtrl_picker_text_input_date);
+ EditText editText = textInputLayout.getEditText();
+
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(editText.getSelectionStart()).isEqualTo(editText.getText().length());
+ }
+
private View getRootView() {
return singleDateSelector.onCreateTextInputView(
LayoutInflater.from(context),
diff --git a/lib/javatests/com/google/android/material/datepicker/UtcDatesTest.java b/lib/javatests/com/google/android/material/datepicker/UtcDatesTest.java
index a24cb6d2ca4..fb497a199d8 100644
--- a/lib/javatests/com/google/android/material/datepicker/UtcDatesTest.java
+++ b/lib/javatests/com/google/android/material/datepicker/UtcDatesTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import androidx.appcompat.app.AppCompatActivity;
+import android.text.SpannableString;
import androidx.test.core.app.ApplicationProvider;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -87,6 +88,15 @@ public void textInputHintForKorean() {
assertEquals("년.월.일.", hint);
}
+ @Test
+ public void verbatimTextInputHintForUK() {
+ SimpleDateFormat sdf = new SimpleDateFormat("dd/mm/yyyy", Locale.UK);
+ String hint = UtcDates.getDefaultTextInputHint(context.getResources(), sdf);
+ SpannableString hintSpannable = UtcDates.getVerbatimTextInputHint(hint);
+
+ assertEquals("dd/mm/yyyy", hintSpannable.toString());
+ }
+
@Test
public void normalizeTextInputFormat() {
SimpleDateFormat sdf = new SimpleDateFormat("M/d/y");
diff --git a/lib/javatests/com/google/android/material/floatingactionbutton/ExtendedFloatingActionButtonTest.java b/lib/javatests/com/google/android/material/floatingactionbutton/ExtendedFloatingActionButtonTest.java
index 8739991e440..1ec5df0c4d7 100644
--- a/lib/javatests/com/google/android/material/floatingactionbutton/ExtendedFloatingActionButtonTest.java
+++ b/lib/javatests/com/google/android/material/floatingactionbutton/ExtendedFloatingActionButtonTest.java
@@ -18,7 +18,6 @@
import com.google.android.material.test.R;
-import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -41,7 +40,7 @@
import org.robolectric.annotation.LooperMode;
@RunWith(RobolectricTestRunner.class)
-@Config(sdk = LOLLIPOP)
+@Config(sdk = Config.OLDEST_SDK)
@LooperMode(LooperMode.Mode.PAUSED)
public class ExtendedFloatingActionButtonTest {
diff --git a/lib/javatests/com/google/android/material/floatingactionbutton/FabTest.java b/lib/javatests/com/google/android/material/floatingactionbutton/FabTest.java
index 359983807c0..9e07407c3cb 100644
--- a/lib/javatests/com/google/android/material/floatingactionbutton/FabTest.java
+++ b/lib/javatests/com/google/android/material/floatingactionbutton/FabTest.java
@@ -18,7 +18,6 @@
import com.google.android.material.test.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;
@@ -37,7 +36,7 @@
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(sdk = LOLLIPOP)
+@Config(sdk = Config.OLDEST_SDK)
public class FabTest {
private static final double DELTA = 0.01;
diff --git a/lib/javatests/com/google/android/material/internal/StaticLayoutBuilderCompatTest.java b/lib/javatests/com/google/android/material/internal/StaticLayoutBuilderCompatTest.java
index b11c6e73372..49fdfdb2262 100644
--- a/lib/javatests/com/google/android/material/internal/StaticLayoutBuilderCompatTest.java
+++ b/lib/javatests/com/google/android/material/internal/StaticLayoutBuilderCompatTest.java
@@ -16,7 +16,6 @@
package com.google.android.material.internal;
-import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static com.google.common.truth.Truth.assertThat;
import android.os.Build.VERSION_CODES;
@@ -40,7 +39,7 @@ public class StaticLayoutBuilderCompatTest {
+ "Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, "
+ "vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a";
- @Config(minSdk = LOLLIPOP, maxSdk = VERSION_CODES.P)
+ @Config(minSdk = Config.OLDEST_SDK, maxSdk = VERSION_CODES.P)
@Test
public void createStaticLayout_withMaxLines_LongString() throws Exception {
int maxLines = 3;
diff --git a/lib/javatests/com/google/android/material/motion/MotionUtilsTest.java b/lib/javatests/com/google/android/material/motion/MotionUtilsTest.java
index 1cb0e7e6775..b757a09eee2 100644
--- a/lib/javatests/com/google/android/material/motion/MotionUtilsTest.java
+++ b/lib/javatests/com/google/android/material/motion/MotionUtilsTest.java
@@ -19,16 +19,18 @@
import com.google.android.material.test.R;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import android.animation.TimeInterpolator;
-import android.os.Build.VERSION_CODES;
+import android.content.Context;
import androidx.appcompat.app.AppCompatActivity;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
-import androidx.annotation.RequiresApi;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.dynamicanimation.animation.SpringForce;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Rule;
import org.junit.Test;
@@ -42,15 +44,54 @@
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
-@Config(sdk = VERSION_CODES.LOLLIPOP)
+@Config(sdk = Config.OLDEST_SDK)
@DoNotInstrument
-@RequiresApi(api = VERSION_CODES.LOLLIPOP)
public class MotionUtilsTest {
private ActivityController activityController;
@Rule public final ExpectedException thrown = ExpectedException.none();
+ @Test
+ public void testResolvesThemeSpring() {
+ createActivityAndSetTheme(R.style.Theme_Material3_DayNight);
+ Context context = activityController.get().getApplicationContext();
+ float expectedStiffness = ResourcesCompat.getFloat(
+ context.getResources(), R.dimen.m3_sys_motion_standard_spring_fast_spatial_stiffness);
+ float expectedDampingRatio = ResourcesCompat.getFloat(
+ context.getResources(), R.dimen.m3_sys_motion_standard_spring_fast_spatial_damping);
+ SpringForce spring = MotionUtils.resolveThemeSpringForce(context,
+ R.attr.motionSpringFastSpatial, R.style.Motion_Material3_Spring_Standard_Fast_Spatial);
+
+ assertThat(spring.getStiffness()).isEqualTo(expectedStiffness);
+ assertThat(spring.getDampingRatio()).isEqualTo(expectedDampingRatio);
+ }
+
+ @Test
+ public void testAbsentThemeSpring_shouldResolveDefault() {
+ createActivityAndSetTheme(R.style.Theme_AppCompat_DayNight);
+ Context context = activityController.get().getApplicationContext();
+
+ SpringForce spring = MotionUtils.resolveThemeSpringForce(context,
+ R.attr.motionSpringFastSpatial, R.style.Motion_MyApp_Spring_Custom_Default);
+
+ assertThat(spring.getStiffness()).isEqualTo(1450f);
+ assertThat(spring.getDampingRatio()).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testPartialSpring_shouldThrow() {
+ createActivityAndSetTheme(R.style.Theme_Material3_DayNight_PartialSpring);
+ Context context = activityController.get().getApplicationContext();
+
+ IllegalArgumentException thrown = assertThrows(
+ IllegalArgumentException.class,
+ () -> MotionUtils.resolveThemeSpringForce(context, R.attr.motionSpringFastSpatial,
+ R.style.Motion_Material3_Spring_Standard_Fast_Spatial)
+ );
+ assertThat(thrown).hasMessageThat().contains("must have a damping");
+ }
+
@Test
public void testResolvesThemeInterpolator() {
assertThemeInterpolatorIsInstanceOf(
diff --git a/lib/javatests/com/google/android/material/motion/res/values/themes.xml b/lib/javatests/com/google/android/material/motion/res/values/themes.xml
index c96035710bc..f117b195b84 100644
--- a/lib/javatests/com/google/android/material/motion/res/values/themes.xml
+++ b/lib/javatests/com/google/android/material/motion/res/values/themes.xml
@@ -15,6 +15,17 @@
limitations under the License.
-->
+
+
+
+
-
diff --git a/testing/java/com/google/android/material/testapp/animation/build.gradle b/testing/java/com/google/android/material/testapp/animation/build.gradle
index fb6a88c2521..1880df59cdd 100644
--- a/testing/java/com/google/android/material/testapp/animation/build.gradle
+++ b/testing/java/com/google/android/material/testapp/animation/build.gradle
@@ -17,24 +17,19 @@
apply plugin: 'com.android.application'
dependencies {
- api compatibility("appcompat")
- api compatibility("core")
+ api libs.androidx.appcompat
+ api libs.androidx.core
api project(fromPath("lib"))
api project(fromPath("testing/java/com/google/android/material/testapp/base"))
- api 'androidx.multidex:multidex:2.0.0'
+ api libs.androidx.multidex
}
android {
+ namespace "com.google.android.material.testapp.animation"
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = [ '.' ]
- main.java.excludes = [
- '**/build/**',
- ]
- // Only include things in this directory, not subdirectories
- main.java.includes = [ '*.java' ]
main.res.srcDirs = [ 'res' ]
}
defaultConfig {
diff --git a/testing/java/com/google/android/material/testapp/base/build.gradle b/testing/java/com/google/android/material/testapp/base/build.gradle
index b6131f8668e..1e91139a238 100644
--- a/testing/java/com/google/android/material/testapp/base/build.gradle
+++ b/testing/java/com/google/android/material/testapp/base/build.gradle
@@ -17,20 +17,22 @@
apply plugin: 'com.android.library'
dependencies {
- api compatibility("appcompat")
- api compatibility("core")
- api compatibility("fragment")
+ api libs.androidx.appcompat
+ api libs.androidx.core
+ api libs.androidx.fragment
}
android {
+ namespace "com.google.android.material.testapp.base"
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = [ '.' ]
- main.java.excludes = [
- '**/build/**',
+ // Manually add src files. Gradle 8 doesn't work well with
+ // a flat source directory (src = [ '.' ]) where java source, a gradle
+ // file, and a build output directory are all in the same location.
+ main.java.srcDirs = [
+ 'BaseTestActivity.java',
+ 'RecreatableAppCompatActivity.java'
]
- // Only include things in this directory, not subdirectories
- main.java.includes = [ '*.java' ]
}
defaultConfig {
minSdkVersion 21
diff --git a/testing/java/com/google/android/material/testapp/build.gradle b/testing/java/com/google/android/material/testapp/build.gradle
index 7e61aa890d7..be7e5e3af27 100644
--- a/testing/java/com/google/android/material/testapp/build.gradle
+++ b/testing/java/com/google/android/material/testapp/build.gradle
@@ -2,17 +2,18 @@
apply plugin: 'com.android.application'
dependencies {
- api compatibility("annotation")
- api compatibility("appcompat")
+ api libs.androidx.annotation
+ api libs.androidx.appcompat
api project(fromPath("lib"))
api project(fromPath("testing/java/com/google/android/material/testapp/base"))
api project(fromPath("testing/java/com/google/android/material/testapp/custom"))
- api 'androidx.multidex:multidex:2.0.0'
+ api libs.androidx.multidex
}
android {
+ namespace "com.google.android.material.testapp"
defaultConfig {
multiDexEnabled true
minSdkVersion 21
@@ -21,12 +22,31 @@ android {
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = [ '.' ]
- main.java.excludes = [
- '**/build/**',
+ // Manually add src files. Gradle 8 doesn't work well with
+ // a flat source directory (src = [ '.' ]) where java source, a gradle
+ // file, and a build output directory are all in the same location.
+ main.java.srcDirs = [
+ 'AppBarHorizontalScrollingActivity.java',
+ 'AppBarLayoutCollapsePinActivity.java',
+ 'AppBarWithScrollbarsActivity.java',
+ 'BottomNavigationViewActivity.java',
+ 'BottomSheetBehaviorActivity.java',
+ 'BottomSheetBehaviorWithInsetsActivity.java',
+ 'BottomSheetDialogActivity.java',
+ 'CoordinatorLayoutActivity.java',
+ 'DynamicCoordinatorLayoutActivity.java',
+ 'ExpandableTransformationActivity.java',
+ 'ExposedDropdownMenuActivity.java',
+ 'FloatingActionButtonActivity.java',
+ 'NavigationViewActivity.java',
+ 'SnackbarActivity.java',
+ 'SnackbarWithFabActivity.java',
+ 'SnackbarWithTranslucentNavBarActivity.java',
+ 'TabLayoutPoolingActivity.java',
+ 'TabLayoutWithViewPagerActivity.java',
+ 'TextInputLayoutActivity.java',
+ 'TextInputLayoutWithIconsActivity.java'
]
- // Only include things in this directory, not subdirectories
- main.java.includes = [ '*.java' ]
main.res.srcDirs = [ 'res' ]
}
diff --git a/testing/java/com/google/android/material/testapp/custom/build.gradle b/testing/java/com/google/android/material/testapp/custom/build.gradle
index fe48cfaf468..96854d7fe94 100644
--- a/testing/java/com/google/android/material/testapp/custom/build.gradle
+++ b/testing/java/com/google/android/material/testapp/custom/build.gradle
@@ -1,23 +1,28 @@
apply plugin: 'com.android.library'
dependencies {
- api compatibility("appcompat")
- api compatibility("core")
+ api libs.androidx.appcompat
+ api libs.androidx.core
api project(fromPath("lib"))
- implementation "com.google.errorprone:error_prone_annotations:${errorproneVersion}"
+ implementation libs.errorprone.annotations
}
android {
+ namespace "com.google.android.material.testapp.custom"
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = [ '.' ]
- main.java.excludes = [
- '**/build/**',
+ // Manually add src files. Gradle 8 doesn't work well with
+ // a flat source directory (src = [ '.' ]) where java source, a gradle
+ // file, and a build output directory are all in the same location.
+ main.java.srcDirs = [
+ 'CustomSnackbar.java',
+ 'CustomSnackbarMainContent.java',
+ 'CustomTextView.java',
+ 'NavigationTestView.java',
+ 'TestFloatingBehavior.java'
]
- // Only include things in this directory, not subdirectories
- main.java.includes = [ '*.java' ]
main.res.srcDirs = [ 'res' ]
}
defaultConfig {
diff --git a/testing/java/com/google/android/material/testapp/theme/build.gradle b/testing/java/com/google/android/material/testapp/theme/build.gradle
index cc85fb968ef..c8cad724b58 100644
--- a/testing/java/com/google/android/material/testapp/theme/build.gradle
+++ b/testing/java/com/google/android/material/testapp/theme/build.gradle
@@ -17,29 +17,25 @@
apply plugin: 'com.android.application'
dependencies {
- api compatibility("appcompat")
- api compatibility("core")
+ api libs.androidx.appcompat
+ api libs.androidx.core
- /* api project(fromPath("lib/java/com/google/android/material/theme"))
- api project(fromPath("lib/java/com/google/android/material/button"))
- api project(fromPath("lib/java/com/google/android/material/radiobutton"))
- api project(fromPath("lib/java/com/google/android/material/checkbox"))
-*/
api project(fromPath("lib"))
api project(fromPath("testing/java/com/google/android/material/testapp/base"))
- api 'androidx.multidex:multidex:2.0.1'
+ api libs.androidx.multidex
}
android {
+ namespace "com.google.android.material.testapp.theme"
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = [ '.' ]
- main.java.excludes = [
- '**/build/**',
+ // Manually add src files. Gradle 8 doesn't work well with
+ // a flat source directory (src = [ '.' ]) where java source, a gradle
+ // file, and a build output directory are all in the same location.
+ main.java.srcDirs = [
+ 'MaterialComponentsViewInflaterActivity.java'
]
- // Only include things in this directory, not subdirectories
- main.java.includes = [ '*.java' ]
main.res.srcDirs = [ 'res' ]
}
defaultConfig {
diff --git a/tests/build.gradle b/tests/build.gradle
index ce3ef91891b..256e3769d2c 100644
--- a/tests/build.gradle
+++ b/tests/build.gradle
@@ -1,15 +1,15 @@
apply plugin: 'com.android.test'
dependencies {
- implementation "androidx.test:core:${project.rootProject.ext.testRunnerVersion}"
- implementation "androidx.test:runner:${project.rootProject.ext.testRunnerVersion}"
- implementation "androidx.test:rules:${project.rootProject.ext.testRunnerVersion}"
- implementation "androidx.test.espresso:espresso-accessibility:${project.rootProject.ext.espressoVersion}"
- implementation "androidx.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}"
- implementation "androidx.test.espresso:espresso-contrib:${project.rootProject.ext.espressoVersion}"
- implementation "org.mockito:mockito-core:${project.rootProject.ext.mockitoCoreVersion}"
- implementation 'com.google.dexmaker:dexmaker:1.2'
- implementation 'com.google.dexmaker:dexmaker-mockito:1.2'
+ implementation libs.androidx.test.core
+ implementation libs.androidx.test.runner
+ implementation libs.androidx.test.rules
+ implementation libs.androidx.espresso.accessibility
+ implementation libs.androidx.espresso.core
+ implementation libs.androidx.espresso.contrib
+ implementation libs.mockito.core
+ implementation libs.dexmaker
+ implementation libs.dexmaker.mokito
}
android {
diff --git a/tests/javatests/com/google/android/material/appbar/AppBarWithDodgingTest.java b/tests/javatests/com/google/android/material/appbar/AppBarWithDodgingTest.java
index 1215b7ade07..90ed68361b4 100644
--- a/tests/javatests/com/google/android/material/appbar/AppBarWithDodgingTest.java
+++ b/tests/javatests/com/google/android/material/appbar/AppBarWithDodgingTest.java
@@ -43,8 +43,8 @@ public void testLeftDodge() throws Throwable {
final Rect fabRect = new Rect();
final Rect fab2Rect = new Rect();
- fab.getContentRect(fabRect);
- fab2.getContentRect(fab2Rect);
+ fab.getMeasuredContentRect(fabRect);
+ fab2.getMeasuredContentRect(fab2Rect);
// Our second FAB is configured to "dodge" the first one - to be displayed to the
// right of it
@@ -72,8 +72,8 @@ public void testRightDodge() throws Throwable {
final Rect fabRect = new Rect();
final Rect fab2Rect = new Rect();
- fab.getContentRect(fabRect);
- fab2.getContentRect(fab2Rect);
+ fab.getMeasuredContentRect(fabRect);
+ fab2.getMeasuredContentRect(fab2Rect);
// Our second FAB is configured to "dodge" the first one - to be displayed to the
// left of it
diff --git a/tests/javatests/com/google/android/material/bottomsheet/BottomSheetBehaviorTest.java b/tests/javatests/com/google/android/material/bottomsheet/BottomSheetBehaviorTest.java
index 4a23ecebc4b..3a8c707da4f 100644
--- a/tests/javatests/com/google/android/material/bottomsheet/BottomSheetBehaviorTest.java
+++ b/tests/javatests/com/google/android/material/bottomsheet/BottomSheetBehaviorTest.java
@@ -16,6 +16,7 @@
package com.google.android.material.bottomsheet;
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.VIEW_INDEX_BOTTOM_SHEET;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
@@ -26,8 +27,8 @@
import static org.junit.Assert.fail;
import android.content.Context;
-import android.os.Build.VERSION;
import android.os.SystemClock;
+import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -1041,25 +1042,32 @@ private void withFabVisibilityChange(boolean shown, Runnable action) {
private static void assertAccessibilityActions(
BottomSheetBehavior> behavior, ViewGroup bottomSheet) {
- if (VERSION.SDK_INT >= 21) {
- int state = behavior.getState();
- boolean hasExpandAction =
- state == BottomSheetBehavior.STATE_COLLAPSED
- || state == BottomSheetBehavior.STATE_HALF_EXPANDED;
- boolean hasCollapseAction =
- state == BottomSheetBehavior.STATE_EXPANDED
- || state == BottomSheetBehavior.STATE_HALF_EXPANDED;
- boolean hasDismissAction = state != BottomSheetBehavior.STATE_HIDDEN && behavior.isHideable();
- assertThat(
- AccessibilityUtils.hasAction(bottomSheet, AccessibilityNodeInfoCompat.ACTION_COLLAPSE),
- equalTo(hasCollapseAction));
- assertThat(
- AccessibilityUtils.hasAction(bottomSheet, AccessibilityNodeInfoCompat.ACTION_EXPAND),
- equalTo(hasExpandAction));
- assertThat(
- AccessibilityUtils.hasAction(bottomSheet, AccessibilityNodeInfoCompat.ACTION_DISMISS),
- equalTo(hasDismissAction));
- }
+ int state = behavior.getState();
+ boolean hasExpandAction =
+ state == BottomSheetBehavior.STATE_COLLAPSED
+ || state == BottomSheetBehavior.STATE_HALF_EXPANDED;
+ boolean hasHalfExpandAction =
+ state != BottomSheetBehavior.STATE_HALF_EXPANDED && !behavior.isFitToContents();
+ boolean hasCollapseAction =
+ state == BottomSheetBehavior.STATE_EXPANDED
+ || state == BottomSheetBehavior.STATE_HALF_EXPANDED;
+ boolean hasDismissAction = state != BottomSheetBehavior.STATE_HIDDEN && behavior.isHideable();
+ assertThat(
+ hasCustomAccessibilityAction(behavior.expandActionIds),
+ equalTo(hasExpandAction));
+ assertThat(
+ hasCustomAccessibilityAction(behavior.expandHalfwayActionIds),
+ equalTo(hasHalfExpandAction));
+ assertThat(
+ hasCustomAccessibilityAction(behavior.collapseActionIds),
+ equalTo(hasCollapseAction));
+ assertThat(
+ AccessibilityUtils.hasAction(bottomSheet, AccessibilityNodeInfoCompat.ACTION_DISMISS),
+ equalTo(hasDismissAction));
+ }
+
+ private static boolean hasCustomAccessibilityAction(SparseIntArray actionIds) {
+ return actionIds.get(VIEW_INDEX_BOTTOM_SHEET, View.NO_ID) != View.NO_ID;
}
private ViewGroup getBottomSheet() {
diff --git a/tests/javatests/com/google/android/material/datepicker/MaterialDatePickerPagesTest.java b/tests/javatests/com/google/android/material/datepicker/MaterialDatePickerPagesTest.java
index 0db0a612eef..5ac0ba7e6d8 100644
--- a/tests/javatests/com/google/android/material/datepicker/MaterialDatePickerPagesTest.java
+++ b/tests/javatests/com/google/android/material/datepicker/MaterialDatePickerPagesTest.java
@@ -15,8 +15,14 @@
*/
package com.google.android.material.datepicker;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
import static com.google.android.material.datepicker.MaterialDatePickerTestUtils.findFirstVisibleItem;
import static org.hamcrest.Matchers.lessThan;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
@@ -141,4 +147,44 @@ public void accessibility_daySelection_notScrollable() {
assertFalse(nodeInfo.isScrollable());
}
+
+ @Test
+ public void previousButtonDisabledAtStartBoundary() {
+ MaterialDatePickerTestUtils.clickPrev();
+ onView(withTagValue(equalTo(MaterialCalendar.NAVIGATION_PREV_TAG)))
+ .check(matches(not(isEnabled())));
+ }
+
+ @Test
+ public void nextButtonDisabledAtEndBoundary() {
+ MaterialDatePickerTestUtils.clickNext();
+ MaterialDatePickerTestUtils.clickNext();
+ MaterialDatePickerTestUtils.clickNext();
+ onView(withTagValue(equalTo(MaterialCalendar.NAVIGATION_NEXT_TAG)))
+ .check(matches(not(isEnabled())));
+ }
+
+ @Test
+ public void nextButtonEnabledAtStartBoundary() {
+ MaterialDatePickerTestUtils.clickPrev();
+ onView(withTagValue(equalTo(MaterialCalendar.NAVIGATION_NEXT_TAG)))
+ .check(matches(isEnabled()));
+ }
+
+ @Test
+ public void previousButtonEnabledAtEndBoundary() {
+ MaterialDatePickerTestUtils.clickNext();
+ MaterialDatePickerTestUtils.clickNext();
+ MaterialDatePickerTestUtils.clickNext();
+ onView(withTagValue(equalTo(MaterialCalendar.NAVIGATION_PREV_TAG)))
+ .check(matches(isEnabled()));
+ }
+
+ @Test
+ public void navigationButtonsEnabledInMiddleOfBoundary() {
+ onView(withTagValue(equalTo(MaterialCalendar.NAVIGATION_NEXT_TAG)))
+ .check(matches(isEnabled()));
+ onView(withTagValue(equalTo(MaterialCalendar.NAVIGATION_NEXT_TAG)))
+ .check(matches(isEnabled()));
+ }
}
diff --git a/tests/javatests/com/google/android/material/datepicker/TestBackgroundHighlightDecorator.java b/tests/javatests/com/google/android/material/datepicker/TestBackgroundHighlightDecorator.java
index 1f6aa421631..0c60c07f775 100644
--- a/tests/javatests/com/google/android/material/datepicker/TestBackgroundHighlightDecorator.java
+++ b/tests/javatests/com/google/android/material/datepicker/TestBackgroundHighlightDecorator.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Color;
import android.os.Parcel;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
@@ -73,7 +74,7 @@ private int getBackgroundHighlightColor(Context context) {
@ColorInt
private int getTextHighlightColor(Context context) {
- return MaterialColors.getColor(context, R.attr.colorOnTertiary, R.attr.colorControlNormal);
+ return MaterialColors.getColor(context, R.attr.colorOnTertiary, Color.BLACK);
}
@ColorInt
diff --git a/tests/javatests/com/google/android/material/floatingactionbutton/FloatingActionButtonTest.java b/tests/javatests/com/google/android/material/floatingactionbutton/FloatingActionButtonTest.java
index 2eb31924265..7069c76b554 100644
--- a/tests/javatests/com/google/android/material/floatingactionbutton/FloatingActionButtonTest.java
+++ b/tests/javatests/com/google/android/material/floatingactionbutton/FloatingActionButtonTest.java
@@ -236,7 +236,7 @@ public void testClickableTouchAndDragOffView() {
final int[] xy = new int[2];
fab.getLocationOnScreen(xy);
final Rect rect = new Rect();
- fab.getContentRect(rect);
+ fab.getMeasuredContentRect(rect);
return new float[] {xy[0] + rect.centerX(), xy[1] + rect.centerY()};
},
diff --git a/tests/javatests/com/google/android/material/snackbar/SnackbarTest.java b/tests/javatests/com/google/android/material/snackbar/SnackbarTest.java
index c99cc981d36..dfc9773900d 100644
--- a/tests/javatests/com/google/android/material/snackbar/SnackbarTest.java
+++ b/tests/javatests/com/google/android/material/snackbar/SnackbarTest.java
@@ -29,7 +29,6 @@
import static com.google.android.material.testutils.TestUtilsActions.setLayoutDirection;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.AllOf.allOf;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -400,17 +399,6 @@ public void testMultipleCallbacksWithRemoval() throws Throwable {
.onDismissed(snackbar, BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
}
- @Test
- public void testAccessibilityPaneTitle() throws Throwable {
- final Snackbar snackbar =
- Snackbar.make(coordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
- .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
- SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
- assertEquals(
- snackbar.getContext().getString(R.string.snackbar_accessibility_pane_title),
- ViewCompat.getAccessibilityPaneTitle(snackbar.getView()).toString());
- }
-
@Test
public void testDefaultContext_usesAppCompat() throws Throwable {
final Snackbar snackbar =
diff --git a/tests/javatests/com/google/android/material/testutils/TestUtilsMatchers.java b/tests/javatests/com/google/android/material/testutils/TestUtilsMatchers.java
index f7791d1b7b4..7d30dc5fefb 100644
--- a/tests/javatests/com/google/android/material/testutils/TestUtilsMatchers.java
+++ b/tests/javatests/com/google/android/material/testutils/TestUtilsMatchers.java
@@ -330,7 +330,7 @@ public boolean matchesSafely(final View view) {
// Since the FAB background is round, and may contain the shadow, we'll look at
// just the center half rect of the content area
final Rect area = new Rect();
- fab.getContentRect(area);
+ fab.getMeasuredContentRect(area);
final int rectHeightQuarter = area.height() / 4;
final int rectWidthQuarter = area.width() / 4;
@@ -404,7 +404,7 @@ public boolean matchesSafely(final View view) {
final FloatingActionButton fab = (FloatingActionButton) view;
final Rect area = new Rect();
- fab.getContentRect(area);
+ fab.getMeasuredContentRect(area);
if (area.height() != size) {
failedCheckDescription =
@@ -439,7 +439,7 @@ public boolean matchesSafely(final View view) {
final ViewGroup parent = (ViewGroup) view.getParent();
final Rect area = new Rect();
- fab.getContentRect(area);
+ fab.getMeasuredContentRect(area);
final int absGravity =
GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(view));
diff --git a/tests/javatests/com/google/android/material/theme/build.gradle b/tests/javatests/com/google/android/material/theme/build.gradle
index 82013d3b53b..b36c0412949 100644
--- a/tests/javatests/com/google/android/material/theme/build.gradle
+++ b/tests/javatests/com/google/android/material/theme/build.gradle
@@ -17,20 +17,20 @@
apply plugin: 'com.android.test'
dependencies {
- implementation "androidx.test.ext:junit:1.1.1"
- implementation "androidx.test:runner:${project.rootProject.ext.testRunnerVersion}"
- implementation "androidx.test:rules:${project.rootProject.ext.testRunnerVersion}"
- implementation "androidx.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}"
- implementation "androidx.test.espresso:espresso-contrib:${project.rootProject.ext.espressoVersion}"
- implementation "org.mockito:mockito-core:${project.rootProject.ext.mockitoCoreVersion}"
- implementation 'com.google.dexmaker:dexmaker:1.2'
- implementation 'com.google.dexmaker:dexmaker-mockito:1.2'
- implementation "com.google.truth:truth:${project.rootProject.ext.truthVersion}"
- implementation compatibility("annotation")
- implementation compatibility("appcompat")
- implementation compatibility("core")
+ implementation libs.junit
+ implementation libs.androidx.test.runner
+ implementation libs.androidx.test.rules
+ implementation libs.androidx.espresso.core
+ implementation libs.androidx.espresso.contrib
+ implementation libs.mockito.core
+ implementation libs.dexmaker
+ implementation libs.dexmaker.mokito
+ implementation libs.truth
+ implementation libs.androidx.annotation
+ implementation libs.androidx.appcompat
+ implementation libs.androidx.core
- api 'androidx.multidex:multidex:2.0.1'
+ api libs.androidx.multidex
}
android {