Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit 32ee2df

Browse files
authored
Merge pull request #2 from android/fab-transformation
FAB transformation
2 parents 2510959 + 1daa258 commit 32ee2df

File tree

12 files changed

+375
-0
lines changed

12 files changed

+375
-0
lines changed

Motion/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package.
2121

2222
- [Layout > Dissolve](app/src/main/java/com/example/android/motion/demo/dissolve)
2323
- [Layout > Fade through](app/src/main/java/com/example/android/motion/demo/fadethrough)
24+
- [Layout > FAB transformation](app/src/main/java/com/example/android/motion/demo/fabtransformation)
2425
- [List > Reorder](app/src/main/java/com/example/android/motion/demo/reorder)
2526
- [List > Stagger](app/src/main/java/com/example/android/motion/demo/stagger)
2627
- [Navigation > Shared element](app/src/main/java/com/example/android/motion/demo/sharedelement)

Motion/app/src/main/AndroidManifest.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@
6969
android:resource="@array/fade_through_apis" />
7070
</activity>
7171

72+
<activity
73+
android:name=".demo.fabtransformation.FabTransformationActivity"
74+
android:label="@string/fab_transformation_label">
75+
<intent-filter>
76+
<action android:name="android.intent.action.MAIN" />
77+
<category android:name="com.example.android.motion.intent.category.DEMO" />
78+
</intent-filter>
79+
80+
<meta-data
81+
android:name="com.example.android.motion.demo.DESCRIPTION"
82+
android:value="@string/fab_transformation_description" />
83+
<meta-data
84+
android:name="com.example.android.motion.demo.APIS"
85+
android:resource="@array/fab_transformation_apis" />
86+
</activity>
87+
7288
<activity
7389
android:name=".demo.reorder.ReorderActivity"
7490
android:label="@string/reorder_label">
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright 2019 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.android.motion.demo.fabtransformation
18+
19+
import android.os.Bundle
20+
import android.view.View
21+
import android.widget.ImageView
22+
import android.widget.LinearLayout
23+
import android.widget.TextView
24+
import androidx.activity.viewModels
25+
import androidx.appcompat.app.AppCompatActivity
26+
import androidx.appcompat.widget.Toolbar
27+
import androidx.coordinatorlayout.widget.CoordinatorLayout
28+
import androidx.core.view.ViewCompat
29+
import androidx.core.view.isVisible
30+
import androidx.core.view.updateLayoutParams
31+
import androidx.lifecycle.observe
32+
import com.bumptech.glide.Glide
33+
import com.bumptech.glide.load.resource.bitmap.CircleCrop
34+
import com.example.android.motion.R
35+
import com.example.android.motion.ui.EdgeToEdge
36+
import com.google.android.material.circularreveal.cardview.CircularRevealCardView
37+
import com.google.android.material.floatingactionbutton.FloatingActionButton
38+
39+
/**
40+
* FAB transformation is provided by Material Components. This activity demonstrates how to use
41+
* [FloatingActionButton.setExpanded] to transform the FAB into a sheet.
42+
*/
43+
class FabTransformationActivity : AppCompatActivity() {
44+
45+
private val viewModel: FabTransformationViewModel by viewModels()
46+
47+
private lateinit var fab: FloatingActionButton
48+
private lateinit var message: TextView
49+
50+
private class CheeseItemHolder(val parent: LinearLayout, listener: View.OnClickListener) {
51+
52+
val image: ImageView = parent.findViewById(R.id.image)
53+
val name: TextView = parent.findViewById(R.id.name)
54+
55+
init {
56+
parent.setOnClickListener(listener)
57+
}
58+
}
59+
60+
private val cheeseOnClick = View.OnClickListener { v ->
61+
val name = v.getTag(R.id.tag_name) as String
62+
message.text = getString(R.string.you_selected, name)
63+
fab.isExpanded = false
64+
}
65+
66+
override fun onCreate(savedInstanceState: Bundle?) {
67+
super.onCreate(savedInstanceState)
68+
// See the layout file for how to set up the CoordinatorLayout behaviors.
69+
setContentView(R.layout.fab_transformation_activity)
70+
val root: CoordinatorLayout = findViewById(R.id.root)
71+
val toolbar: Toolbar = findViewById(R.id.toolbar)
72+
val sheet: CircularRevealCardView = findViewById(R.id.sheet)
73+
val scrim: View = findViewById(R.id.scrim)
74+
val cheeseHolders: List<CheeseItemHolder> = listOf(
75+
CheeseItemHolder(findViewById(R.id.cheese_1), cheeseOnClick),
76+
CheeseItemHolder(findViewById(R.id.cheese_2), cheeseOnClick),
77+
CheeseItemHolder(findViewById(R.id.cheese_3), cheeseOnClick),
78+
CheeseItemHolder(findViewById(R.id.cheese_4), cheeseOnClick)
79+
)
80+
message = findViewById(R.id.message)
81+
fab = findViewById(R.id.fab)
82+
setSupportActionBar(toolbar)
83+
84+
// Set up edge-to-edge display.
85+
EdgeToEdge.setUpRoot(root)
86+
EdgeToEdge.setUpAppBar(findViewById(R.id.app_bar), toolbar)
87+
val fabMargin = resources.getDimensionPixelSize(R.dimen.spacing_medium)
88+
ViewCompat.setOnApplyWindowInsetsListener(root) { _, insets ->
89+
fab.updateLayoutParams<CoordinatorLayout.LayoutParams> {
90+
leftMargin = fabMargin + insets.systemWindowInsetLeft
91+
rightMargin = fabMargin + insets.systemWindowInsetRight
92+
bottomMargin = fabMargin + insets.systemWindowInsetBottom
93+
}
94+
sheet.updateLayoutParams<CoordinatorLayout.LayoutParams> {
95+
leftMargin = fabMargin + insets.systemWindowInsetLeft
96+
rightMargin = fabMargin + insets.systemWindowInsetRight
97+
bottomMargin = fabMargin + insets.systemWindowInsetBottom
98+
}
99+
insets
100+
}
101+
102+
// Populate the sheet content.
103+
viewModel.cheeses.observe(this) { cheeses ->
104+
cheeseHolders.forEachIndexed { i, holder ->
105+
if (cheeses.size > i) {
106+
val cheese = cheeses[i]
107+
holder.parent.isVisible = true
108+
holder.parent.setTag(R.id.tag_name, cheese.name)
109+
holder.name.text = cheese.name
110+
Glide.with(holder.image)
111+
.load(cheese.image)
112+
.transform(CircleCrop())
113+
.into(holder.image)
114+
} else {
115+
holder.parent.isVisible = false
116+
}
117+
}
118+
}
119+
120+
// Bind events.
121+
fab.setOnClickListener {
122+
// Expand the FAB. The CoordinatorLayout transforms the FAB into the view marked with
123+
// FabTransformationSheetBehavior. Also the view marked with
124+
// FabTransformationScrimBehavior is faded in as a content scrim.
125+
fab.isExpanded = true
126+
}
127+
scrim.setOnClickListener {
128+
// Shrink the menu sheet back into the FAB.
129+
fab.isExpanded = false
130+
}
131+
}
132+
133+
override fun onBackPressed() {
134+
if (fab.isExpanded) {
135+
fab.isExpanded = false
136+
} else {
137+
super.onBackPressed()
138+
}
139+
}
140+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2019 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.android.motion.demo.fabtransformation
18+
19+
import androidx.lifecycle.LiveData
20+
import androidx.lifecycle.MutableLiveData
21+
import androidx.lifecycle.ViewModel
22+
import com.example.android.motion.model.Cheese
23+
24+
class FabTransformationViewModel : ViewModel() {
25+
26+
val cheeses: LiveData<List<Cheese>> = MutableLiveData(Cheese.ALL.shuffled().take(4))
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright 2019 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<vector
18+
xmlns:android="http://schemas.android.com/apk/res/android"
19+
android:width="24dp"
20+
android:height="24dp"
21+
android:tint="?attr/colorControlNormal"
22+
android:viewportWidth="24.0"
23+
android:viewportHeight="24.0">
24+
<path
25+
android:fillColor="#FF000000"
26+
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
27+
</vector>

Motion/app/src/main/res/layout/cheese_list_item.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
android:id="@+id/cheese"
2121
android:layout_width="match_parent"
2222
android:layout_height="wrap_content"
23+
android:background="?attr/selectableItemBackground"
2324
android:orientation="horizontal"
2425
android:padding="@dimen/spacing_small">
2526

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright 2019 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<androidx.coordinatorlayout.widget.CoordinatorLayout
18+
xmlns:android="http://schemas.android.com/apk/res/android"
19+
xmlns:app="http://schemas.android.com/apk/res-auto"
20+
xmlns:tools="http://schemas.android.com/tools"
21+
android:id="@+id/root"
22+
android:layout_width="match_parent"
23+
android:layout_height="match_parent"
24+
android:orientation="vertical">
25+
26+
<com.google.android.material.appbar.AppBarLayout
27+
android:id="@+id/app_bar"
28+
android:layout_width="match_parent"
29+
android:layout_height="wrap_content"
30+
android:background="?attr/colorPrimaryVariant"
31+
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar">
32+
33+
<com.google.android.material.appbar.MaterialToolbar
34+
android:id="@+id/toolbar"
35+
android:layout_width="match_parent"
36+
android:layout_height="?attr/actionBarSize"
37+
android:background="?attr/colorPrimary" />
38+
39+
</com.google.android.material.appbar.AppBarLayout>
40+
41+
<androidx.core.widget.NestedScrollView
42+
android:id="@+id/scroll"
43+
android:layout_width="match_parent"
44+
android:layout_height="match_parent"
45+
app:layout_behavior="@string/appbar_scrolling_view_behavior">
46+
47+
<FrameLayout
48+
android:id="@+id/content"
49+
android:layout_width="match_parent"
50+
android:layout_height="wrap_content">
51+
52+
<TextView
53+
android:id="@+id/message"
54+
android:layout_width="match_parent"
55+
android:layout_height="wrap_content"
56+
android:layout_margin="@dimen/spacing_medium"
57+
android:text="@string/tap_fab"
58+
android:textAppearance="?attr/textAppearanceHeadline6" />
59+
60+
</FrameLayout>
61+
62+
</androidx.core.widget.NestedScrollView>
63+
64+
<!-- A scrim that appears when the FAB is expanded. -->
65+
<View
66+
android:id="@+id/scrim"
67+
android:layout_width="match_parent"
68+
android:layout_height="match_parent"
69+
android:background="@color/dark_scrim"
70+
android:visibility="invisible"
71+
app:layout_behavior="@string/fab_transformation_scrim_behavior" />
72+
73+
<!-- A FAB that expands into a sheet. -->
74+
<com.google.android.material.floatingactionbutton.FloatingActionButton
75+
android:id="@+id/fab"
76+
android:layout_width="wrap_content"
77+
android:layout_height="wrap_content"
78+
android:layout_gravity="end|bottom"
79+
android:layout_margin="@dimen/spacing_medium"
80+
app:elevation="@dimen/fab_elevation"
81+
android:contentDescription="@string/add"
82+
app:srcCompat="@drawable/ic_add" />
83+
84+
<!--
85+
A sheet that the FAB expands into.
86+
Use CircularRevealCardView to apply circular reveal effect.
87+
-->
88+
<com.google.android.material.circularreveal.cardview.CircularRevealCardView
89+
android:id="@+id/sheet"
90+
android:layout_width="256dp"
91+
android:layout_height="wrap_content"
92+
android:layout_gravity="end|bottom"
93+
android:layout_margin="@dimen/spacing_medium"
94+
android:background="?attr/colorSurface"
95+
android:visibility="invisible"
96+
app:elevation="@dimen/fab_elevation"
97+
app:layout_behavior="@string/fab_transformation_sheet_behavior"
98+
tools:visibility="visible">
99+
100+
<LinearLayout
101+
android:id="@+id/sheet_content"
102+
android:layout_width="match_parent"
103+
android:layout_height="wrap_content"
104+
android:orientation="vertical"
105+
android:paddingTop="@dimen/spacing_small"
106+
android:paddingBottom="@dimen/spacing_small">
107+
108+
<include
109+
android:id="@+id/cheese_1"
110+
layout="@layout/cheese_list_item" />
111+
112+
<include
113+
android:id="@+id/cheese_2"
114+
layout="@layout/cheese_list_item" />
115+
116+
<include
117+
android:id="@+id/cheese_3"
118+
layout="@layout/cheese_list_item" />
119+
120+
<include
121+
android:id="@+id/cheese_4"
122+
layout="@layout/cheese_list_item" />
123+
124+
</LinearLayout>
125+
126+
</com.google.android.material.circularreveal.cardview.CircularRevealCardView>
127+
128+
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Motion/app/src/main/res/values/demos.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@
4040
<item>Transition</item>
4141
</string-array>
4242

43+
<!-- FAB transformation -->
44+
<string name="fab_transformation_label">Layout > FAB transformation</string>
45+
<string name="fab_transformation_description">
46+
A floating action button (FAB) can transform into a card by tweening FAB size and corner
47+
radius.
48+
</string>
49+
<string-array name="fab_transformation_apis">
50+
<item>FabTransformationSheetBehavior</item>
51+
<item>CircularRevealCardView</item>
52+
</string-array>
53+
4354
<!-- Reorder -->
4455
<string name="reorder_label">List > Reorder</string>
4556
<string name="reorder_description">

Motion/app/src/main/res/values/dimens.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
<dimen name="spacing_small">8dp</dimen>
2020
<dimen name="spacing_tiny">4dp</dimen>
2121
<dimen name="pick_up_elevation">8dp</dimen>
22+
<dimen name="fab_elevation">6dp</dimen>
2223
<dimen name="icon_size">24dp</dimen>
2324
</resources>

0 commit comments

Comments
 (0)