├── .gitignore
├── LICENSE.txt
├── README.md
├── art
├── CoordinatorLayout.gif
├── bottomsheet.gif
└── toolbar.gif
├── build.gradle
├── demo
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── bowyer
│ │ └── fabtransitionlayout
│ │ └── demo
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── bowyer
│ │ └── fabtransitionlayout
│ │ └── demo
│ │ ├── BottomSheetDemoActivity.java
│ │ ├── CoordinatorLayoutActivity.java
│ │ ├── FabToolBarDemoActivity.java
│ │ ├── MainActivity.java
│ │ ├── adapter
│ │ ├── BottomSheetAdapter.java
│ │ └── RecyclerViewAdapter.java
│ │ └── model
│ │ └── BottomSheet.java
│ └── res
│ ├── drawable-xhdpi
│ ├── ic_account_circle_white_24dp.png
│ ├── ic_add_white_24dp.png
│ ├── ic_build_white_24dp.png
│ ├── ic_call_white_36dp.png
│ ├── ic_drafts_white_24dp.png
│ ├── ic_email_white_36dp.png
│ └── ic_forum_white_36dp.png
│ ├── drawable-xxhdpi
│ ├── ic_account_circle_white_24dp.png
│ ├── ic_add_white_24dp.png
│ ├── ic_build_white_24dp.png
│ ├── ic_call_white_36dp.png
│ ├── ic_drafts_white_24dp.png
│ ├── ic_email_white_36dp.png
│ └── ic_forum_white_36dp.png
│ ├── drawable-xxxhdpi
│ ├── ic_account_circle_white_24dp.png
│ ├── ic_add_white_24dp.png
│ ├── ic_build_white_24dp.png
│ ├── ic_call_white_36dp.png
│ ├── ic_drafts_white_24dp.png
│ ├── ic_email_white_36dp.png
│ └── ic_forum_white_36dp.png
│ ├── layout
│ ├── activity_bottom_sheet.xml
│ ├── activity_coordinator_layout.xml
│ ├── activity_fab_toolbar.xml
│ ├── activity_main.xml
│ └── row_item_bottom_sheet.xml
│ ├── menu
│ ├── menu_coordinator.xml
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-v21
│ └── styles.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── fabtransitionlayout
├── .gitignore
├── bintray-publish.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── bowyer
│ │ └── fabtransitionlayout
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── bowyer
│ │ └── app
│ │ └── fabtransitionlayout
│ │ ├── BottomSheetLayout.java
│ │ ├── FooterBehavior.java
│ │ ├── FooterLayout.java
│ │ └── ViewUtils.java
│ └── res
│ ├── layout
│ ├── bottom_sheet_layout.xml
│ ├── footer_layout.xml
│ └── sheet_layout.xml
│ └── values
│ ├── attr.xml
│ ├── dimens.xml
│ └── strings.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | ### Android template
3 | # Built application files
4 | *.apk
5 | *.ap_
6 |
7 | # Files for the Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 |
17 | # Gradle files
18 | .gradle/
19 | build/
20 | /*/build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 |
32 | ### JetBrains template
33 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
34 |
35 | *.iml
36 |
37 | ## Directory-based project format:
38 | .idea/
39 | # if you remove the above rule, at least ignore the following:
40 |
41 | # User-specific stuff:
42 | # .idea/workspace.xml
43 | # .idea/tasks.xml
44 | # .idea/dictionaries
45 |
46 | # Sensitive or high-churn files:
47 | # .idea/dataSources.ids
48 | # .idea/dataSources.xml
49 | # .idea/sqlDataSources.xml
50 | # .idea/dynamic.xml
51 | # .idea/uiDesigner.xml
52 |
53 | # Gradle:
54 | # .idea/gradle.xml
55 | # .idea/libraries
56 |
57 | # Mongo Explorer plugin:
58 | # .idea/mongoSettings.xml
59 |
60 | ## File-based project format:
61 | *.ipr
62 | *.iws
63 |
64 | ## Plugin-specific files:
65 |
66 | # IntelliJ
67 | /out/
68 |
69 | # mpeltonen/sbt-idea plugin
70 | .idea_modules/
71 |
72 | # JIRA plugin
73 | atlassian-ide-plugin.xml
74 |
75 | # Crashlytics plugin (for Android Studio and IntelliJ)
76 | com_crashlytics_export_strings.xml
77 | crashlytics.properties
78 | crashlytics-build.properties
79 |
80 |
81 | ### OSX template
82 | .DS_Store
83 | .AppleDouble
84 | .LSOverride
85 |
86 | # Icon must end with two \r
87 | Icon
88 |
89 | # Thumbnails
90 | ._*
91 |
92 | # Files that might appear in the root of a volume
93 | .DocumentRevisions-V100
94 | .fseventsd
95 | .Spotlight-V100
96 | .TemporaryItems
97 | .Trashes
98 | .VolumeIcon.icns
99 |
100 | # Directories potentially created on remote AFP share
101 | .AppleDB
102 | .AppleDesktop
103 | Network Trash Folder
104 | Temporary Items
105 | .apdisk
106 |
107 |
108 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Bowyer
2 | Released under the MIT license
3 | http://opensource.org/licenses/mit-license.php
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FabTransitionLayout
2 | [](https://android-arsenal.com/details/1/2541)
3 |
4 | Provides the Floating Action Button Transition [as specified in the Material Design Guide](http://www.google.com/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-transitions) in a simple library.
5 |
6 | It is strongly inspired by [fab-toolbar](https://github.com/bowyer-app/fab-toolbar)
7 |
8 | | bottomsheet | toolbar | CoordinatorLayout|
9 | | :---------------: |:---------------:| :---------------:|
10 | ||||
11 |
12 | Usage
13 | ====
14 | ### build.gradle
15 |
16 | ```
17 | repositories {
18 | jcenter()
19 | }
20 |
21 | dependencies {
22 | compile 'com.bowyer.app:fabtransitionlayout:0.4.0@aar'
23 | }
24 | ```
25 |
26 | ### Layout XML
27 | #### BottomSheetLayout
28 | ```xml
29 |
35 |
36 |
41 |
42 |
54 |
55 |
62 |
63 |
69 |
70 |
71 |
72 |
73 | ```
74 | #### FooterLayout
75 | ```xml
76 |
82 |
83 |
88 |
89 |
101 |
102 |
109 |
110 |
111 |
112 | ```
113 |
114 | ### Set up
115 |
116 | ```java
117 | @Override
118 | protected void onCreate(Bundle savedInstanceState) {
119 | super.onCreate(savedInstanceState);
120 | setContentView(R.layout.activity_main);
121 | ButterKnife.bind(this);
122 | initListView();
123 | //set floating button to FabToolbar
124 | mBottomSheetLayout.setFab(mFab);
125 | }
126 | ```
127 |
128 | ### Show Hide
129 |
130 | ```java
131 | //expand FabToolbar
132 | mBottomSheetLayout.expandFab();
133 |
134 | //if mBottomSheetLayout is expand,mBottomSheetLayout contract. else fab slide out.
135 | mBottomSheetLayout.slideOutFab();
136 |
137 | //fab slide in
138 | mBottomSheetLayout.slideInFab();
139 |
140 |
141 | ```
142 |
143 | # Credits
144 | This library use following libraries.
145 | * [CircularReveal](https://github.com/ozodrukh/CircularReveal)
146 |
147 | # Code Style
148 |
149 | Follow [SquareAndroid](https://github.com/square/java-code-styles/blob/master/configs/codestyles/SquareAndroid.xml).
150 |
151 | Feature
152 | ====
153 | - [ ] A floating action button transforming into a single sheet of material
154 |
155 | License
156 | --------
157 | ```
158 | Copyright (c) 2015 Bowyer
159 | Released under the MIT license
160 | http://opensource.org/licenses/mit-license.php
161 | ```
162 |
--------------------------------------------------------------------------------
/art/CoordinatorLayout.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/art/CoordinatorLayout.gif
--------------------------------------------------------------------------------
/art/bottomsheet.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/art/bottomsheet.gif
--------------------------------------------------------------------------------
/art/toolbar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/art/toolbar.gif
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.1.2'
9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
10 | classpath 'com.github.dcendents:android-maven-plugin:1.2'
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | }
20 | }
21 |
22 | ext {
23 | androidSupportAppCompatV7Lib = 'com.android.support:appcompat-v7:23.4.0'
24 | }
25 |
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | repositories {
4 | mavenCentral()
5 |
6 | maven {
7 | url "https://jitpack.io"
8 | }
9 | }
10 |
11 | android {
12 | compileSdkVersion COMPILE_SDK_VERSION as int
13 | buildToolsVersion BUILD_TOOLS_VERSION
14 |
15 | defaultConfig {
16 | applicationId "com.bowyer.fabtransitionlayout.demo"
17 | minSdkVersion MIN_SDK_VERSION
18 | targetSdkVersion TARGET_SDK_VERSION as int
19 | versionCode VERSION_CODE as int
20 | versionName VERSION_NAME
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | }
29 |
30 | dependencies {
31 | compile fileTree(dir: 'libs', include: ['*.jar'])
32 | compile rootProject.ext.androidSupportAppCompatV7Lib
33 | compile 'com.android.support:design:23.4.0'
34 | compile 'com.squareup.picasso:picasso:2.5.2'
35 | compile 'com.jakewharton:butterknife:7.0.1'
36 | compile 'com.github.ksoichiro:android-observablescrollview:1.5.1'
37 | compile project(':fabtransitionlayout')
38 | }
39 |
--------------------------------------------------------------------------------
/demo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/a13089/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/demo/src/androidTest/java/com/bowyer/fabtransitionlayout/demo/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 |
11 | public ApplicationTest() {
12 | super(Application.class);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/bowyer/fabtransitionlayout/demo/BottomSheetDemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.FloatingActionButton;
5 | import android.support.v7.app.ActionBarActivity;
6 | import android.widget.ArrayAdapter;
7 | import android.widget.ListView;
8 | import butterknife.Bind;
9 | import butterknife.ButterKnife;
10 | import butterknife.OnClick;
11 | import com.bowyer.app.fabtransitionlayout.BottomSheetLayout;
12 | import com.bowyer.fabtransitionlayout.demo.adapter.BottomSheetAdapter;
13 | import com.bowyer.fabtransitionlayout.demo.model.BottomSheet;
14 | import com.github.ksoichiro.android.observablescrollview.ObservableListView;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * Created by Bowyer on 15/08/07.
20 | */
21 | public class BottomSheetDemoActivity extends ActionBarActivity {
22 |
23 | @Bind(R.id.list_view) ObservableListView mObservableListView;
24 |
25 | @Bind(R.id.bottom_sheet) BottomSheetLayout mBottomSheetLayout;
26 |
27 | @Bind(R.id.list_menu) ListView mMenuList;
28 |
29 | @Bind(R.id.fab) FloatingActionButton mFab;
30 |
31 | @Override protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_bottom_sheet);
34 | ButterKnife.bind(this);
35 | initListView();
36 | initListMenu();
37 | mBottomSheetLayout.setFab(mFab);
38 | }
39 |
40 | private void initListView() {
41 | List list = new ArrayList(100);
42 | for (int i = 0; i < 100; i++) {
43 | list.add("Item " + i);
44 | }
45 |
46 | ArrayAdapter adapter =
47 | new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
48 | mObservableListView.setAdapter(adapter);
49 | }
50 |
51 | private void initListMenu() {
52 | ArrayList bottomSheets = new ArrayList<>();
53 | bottomSheets.add(
54 | BottomSheet.to().setBottomSheetMenuType(BottomSheet.BottomSheetMenuType.EMAIL));
55 | bottomSheets.add(
56 | BottomSheet.to().setBottomSheetMenuType(BottomSheet.BottomSheetMenuType.ACCOUNT));
57 | bottomSheets.add(
58 | BottomSheet.to().setBottomSheetMenuType(BottomSheet.BottomSheetMenuType.SETTING));
59 | BottomSheetAdapter adapter = new BottomSheetAdapter(this, bottomSheets);
60 | mMenuList.setAdapter(adapter);
61 | }
62 |
63 | @OnClick(R.id.fab) void onFabClick() {
64 | mBottomSheetLayout.expandFab();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/bowyer/fabtransitionlayout/demo/CoordinatorLayoutActivity.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.animation.PropertyValuesHolder;
6 | import android.os.Bundle;
7 | import android.support.design.widget.FloatingActionButton;
8 | import android.support.design.widget.Snackbar;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.support.v7.widget.LinearLayoutManager;
11 | import android.support.v7.widget.RecyclerView;
12 | import android.support.v7.widget.Toolbar;
13 | import android.view.Menu;
14 | import android.view.MenuItem;
15 | import android.view.View;
16 | import android.widget.ImageView;
17 | import butterknife.Bind;
18 | import butterknife.ButterKnife;
19 | import butterknife.OnClick;
20 | import com.bowyer.app.fabtransitionlayout.FooterLayout;
21 | import com.bowyer.fabtransitionlayout.demo.adapter.RecyclerViewAdapter;
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | public class CoordinatorLayoutActivity extends AppCompatActivity {
26 |
27 | @Bind(R.id.toolbar) Toolbar mToolbar;
28 | @Bind(R.id.fabtoolbar) FooterLayout mFabToolbar;
29 | @Bind(R.id.fab) FloatingActionButton mFab;
30 | @Bind(R.id.list_view) RecyclerView mListView;
31 | @Bind(R.id.ic_call) ImageView mIcCall;
32 | @Bind(R.id.ic_email) ImageView mIcEmail;
33 | @Bind(R.id.ic_forum) ImageView mIcForum;
34 |
35 | @Override protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_coordinator_layout);
38 |
39 | ButterKnife.bind(this);
40 | setSupportActionBar(mToolbar);
41 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
42 |
43 | initListView();
44 | mFabToolbar.setFab(mFab);
45 | }
46 |
47 | private void initListView() {
48 | List list = new ArrayList(100);
49 | for (int i = 0; i < 100; i++) {
50 | list.add("Item " + i);
51 | }
52 |
53 | RecyclerViewAdapter adapter = new RecyclerViewAdapter(list, this.getBaseContext());
54 | mListView.setLayoutManager(new LinearLayoutManager(this.getBaseContext()));
55 | mListView.setAdapter(adapter);
56 | }
57 |
58 | @OnClick(R.id.fab) void onFabClick() {
59 | mFabToolbar.expandFab();
60 | }
61 |
62 | @OnClick(R.id.call) void onClickCall() {
63 | iconAnim(mIcCall);
64 | }
65 |
66 | @OnClick(R.id.ic_email) void onClickEmail() {
67 | iconAnim(mIcEmail);
68 | }
69 |
70 | @OnClick(R.id.ic_forum) void onClickForum() {
71 | iconAnim(mIcForum);
72 | }
73 |
74 | private void iconAnim(View icon) {
75 | Animator iconAnim = ObjectAnimator.ofPropertyValuesHolder(icon,
76 | PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 1.5f, 1f),
77 | PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 1.5f, 1f));
78 | iconAnim.start();
79 | }
80 |
81 | @Override public boolean onCreateOptionsMenu(Menu menu) {
82 | getMenuInflater().inflate(R.menu.menu_coordinator, menu);
83 | return true;
84 | }
85 |
86 | @Override public boolean onOptionsItemSelected(MenuItem item) {
87 |
88 | int id = item.getItemId();
89 |
90 | if (id == R.id.action_snackbar) {
91 | Snackbar.make(mListView, "This is a snackbar", Snackbar.LENGTH_SHORT).show();
92 | return true;
93 | }
94 |
95 | return super.onOptionsItemSelected(item);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/bowyer/fabtransitionlayout/demo/FabToolBarDemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.animation.PropertyValuesHolder;
6 | import android.os.Bundle;
7 | import android.support.design.widget.FloatingActionButton;
8 | import android.support.v7.app.ActionBarActivity;
9 | import android.view.View;
10 | import android.widget.ArrayAdapter;
11 | import android.widget.ImageView;
12 | import butterknife.Bind;
13 | import butterknife.ButterKnife;
14 | import butterknife.OnClick;
15 | import com.bowyer.app.fabtransitionlayout.FooterLayout;
16 | import com.github.ksoichiro.android.observablescrollview.ObservableListView;
17 | import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;
18 | import com.github.ksoichiro.android.observablescrollview.ScrollState;
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | /**
23 | * Created by Bowyer on 15/08/07.
24 | */
25 | public class FabToolBarDemoActivity extends ActionBarActivity
26 | implements ObservableScrollViewCallbacks {
27 |
28 | @Bind(R.id.list_view) ObservableListView mObservableListView;
29 |
30 | @Bind(R.id.fabtoolbar) FooterLayout mFabToolbar;
31 |
32 | @Bind(R.id.fab) FloatingActionButton mFab;
33 |
34 | @Bind(R.id.ic_call) ImageView mIcCall;
35 |
36 | @Bind(R.id.ic_email) ImageView mIcEmail;
37 |
38 | @Bind(R.id.ic_forum) ImageView mIcForum;
39 |
40 | @Override protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_fab_toolbar);
43 | ButterKnife.bind(this);
44 | initListView();
45 | mFabToolbar.setFab(mFab);
46 | }
47 |
48 | private void initListView() {
49 | List list = new ArrayList(100);
50 | for (int i = 0; i < 100; i++) {
51 | list.add("Item " + i);
52 | }
53 |
54 | ArrayAdapter adapter =
55 | new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
56 | mObservableListView.setAdapter(adapter);
57 | mObservableListView.setScrollViewCallbacks(this);
58 | }
59 |
60 | @Override public void onScrollChanged(int i, boolean b, boolean b1) {
61 |
62 | }
63 |
64 | @Override public void onDownMotionEvent() {
65 |
66 | }
67 |
68 | @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) {
69 | if (scrollState == ScrollState.UP) {
70 | mFabToolbar.slideOutFab();
71 | } else if (scrollState == ScrollState.DOWN) {
72 | mFabToolbar.slideInFab();
73 | }
74 | }
75 |
76 | @OnClick(R.id.fab) void onFabClick() {
77 | mFabToolbar.expandFab();
78 | }
79 |
80 | @OnClick(R.id.call) void onClickCall() {
81 | iconAnim(mIcCall);
82 | }
83 |
84 | @OnClick(R.id.ic_email) void onClickEmail() {
85 | iconAnim(mIcEmail);
86 | }
87 |
88 | @OnClick(R.id.ic_forum) void onClickForum() {
89 | iconAnim(mIcForum);
90 | }
91 |
92 | private void iconAnim(View icon) {
93 | Animator iconAnim = ObjectAnimator.ofPropertyValuesHolder(icon,
94 | PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 1.5f, 1f),
95 | PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 1.5f, 1f));
96 | iconAnim.start();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/bowyer/fabtransitionlayout/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.ActionBarActivity;
6 | import butterknife.ButterKnife;
7 | import butterknife.OnClick;
8 |
9 | public class MainActivity extends ActionBarActivity {
10 |
11 | @Override protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 | ButterKnife.bind(this);
15 | }
16 |
17 | @OnClick(R.id.start_bottom_sheet_demo) void startBottomSheetDemo() {
18 | Intent intet = new Intent(this, BottomSheetDemoActivity.class);
19 | startActivity(intet);
20 | }
21 |
22 | @OnClick(R.id.start_fab_toolbar_demo) void startFabToolbarDemo() {
23 | Intent intet = new Intent(this, FabToolBarDemoActivity.class);
24 | startActivity(intet);
25 | }
26 |
27 | @OnClick(R.id.start_coordinator_demo) void startCoordinatorDemo() {
28 | Intent intet = new Intent(this, CoordinatorLayoutActivity.class);
29 | startActivity(intet);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/bowyer/fabtransitionlayout/demo/adapter/BottomSheetAdapter.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 | import butterknife.Bind;
11 | import butterknife.ButterKnife;
12 | import com.bowyer.fabtransitionlayout.demo.R;
13 | import com.bowyer.fabtransitionlayout.demo.model.BottomSheet;
14 | import com.squareup.picasso.Picasso;
15 | import java.util.ArrayList;
16 |
17 | /**
18 | * Created by Bowyer on 15/08/06.
19 | */
20 | public class BottomSheetAdapter extends BaseAdapter {
21 |
22 | Context mContext;
23 |
24 | LayoutInflater mLayoutInflater = null;
25 |
26 | ArrayList mBottomSheets;
27 |
28 | public BottomSheetAdapter(Context context, ArrayList bottomSheets) {
29 | mContext = context;
30 | mBottomSheets = bottomSheets;
31 | mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
32 | }
33 |
34 | @Override public int getCount() {
35 | return mBottomSheets.size();
36 | }
37 |
38 | @Override public Object getItem(int position) {
39 | return mBottomSheets.get(position);
40 | }
41 |
42 | @Override public long getItemId(int position) {
43 | return 0;
44 | }
45 |
46 | @Override public View getView(int position, View convertView, ViewGroup parent) {
47 | ViewHolder viewHolder;
48 | if (convertView == null) {
49 | convertView = mLayoutInflater.inflate(R.layout.row_item_bottom_sheet, parent, false);
50 | viewHolder = new ViewHolder(convertView);
51 | convertView.setTag(viewHolder);
52 | } else {
53 | viewHolder = (ViewHolder) convertView.getTag();
54 | }
55 |
56 | BottomSheet sheet = (BottomSheet) getItem(position);
57 | Picasso.with(mContext)
58 | .load(sheet.getBottomSheetMenuType().getResId())
59 | .into(viewHolder.mMenuIcon);
60 | viewHolder.mMenuTitle.setText(sheet.getBottomSheetMenuType().getName());
61 | return convertView;
62 | }
63 |
64 | static class ViewHolder {
65 |
66 | @Bind(R.id.menu_icon) ImageView mMenuIcon;
67 |
68 | @Bind(R.id.menu_title) TextView mMenuTitle;
69 |
70 | public ViewHolder(View view) {
71 | ButterKnife.bind(this, view);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/bowyer/fabtransitionlayout/demo/adapter/RecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 | import java.util.List;
10 |
11 | public class RecyclerViewAdapter extends RecyclerView.Adapter {
12 |
13 | List mList;
14 |
15 | public RecyclerViewAdapter(List list, Context context) {
16 | this.mList = list;
17 | }
18 |
19 | @Override
20 | public RecyclerViewAdapter.RecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {
21 | View v = LayoutInflater.from(parent.getContext())
22 | .inflate(android.R.layout.simple_list_item_1, parent, false);
23 | RecyclerHolder viewHolder = new RecyclerHolder(v);
24 | return viewHolder;
25 | }
26 |
27 | @Override public void onBindViewHolder(RecyclerViewAdapter.RecyclerHolder holder, int position) {
28 | TextView tv = (TextView) holder.itemView;
29 | tv.setText(mList.get(position));
30 | }
31 |
32 | @Override public int getItemCount() {
33 | return mList.size();
34 | }
35 |
36 | public static class RecyclerHolder extends RecyclerView.ViewHolder {
37 |
38 | public RecyclerHolder(View itemView) {
39 | super(itemView);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/bowyer/fabtransitionlayout/demo/model/BottomSheet.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout.demo.model;
2 |
3 | import com.bowyer.fabtransitionlayout.demo.R;
4 |
5 | /**
6 | * Created by Bowyer on 15/08/06.
7 | */
8 | public class BottomSheet {
9 |
10 | public enum BottomSheetMenuType {
11 | EMAIL(R.drawable.ic_drafts_white_24dp, "Mail"), ACCOUNT(R.drawable.ic_account_circle_white_24dp,
12 | "Acount"), SETTING(R.drawable.ic_build_white_24dp, "Setitng");
13 |
14 | int resId;
15 |
16 | String name;
17 |
18 | BottomSheetMenuType(int resId, String name) {
19 | this.resId = resId;
20 | this.name = name;
21 | }
22 |
23 | public int getResId() {
24 | return resId;
25 | }
26 |
27 | public String getName() {
28 | return name;
29 | }
30 | }
31 |
32 | BottomSheetMenuType bottomSheetMenuType;
33 |
34 | public static BottomSheet to() {
35 | return new BottomSheet();
36 | }
37 |
38 | public BottomSheetMenuType getBottomSheetMenuType() {
39 | return bottomSheetMenuType;
40 | }
41 |
42 | public BottomSheet setBottomSheetMenuType(BottomSheetMenuType bottomSheetMenuType) {
43 | this.bottomSheetMenuType = bottomSheetMenuType;
44 | return this;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xhdpi/ic_account_circle_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xhdpi/ic_account_circle_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xhdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xhdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xhdpi/ic_build_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xhdpi/ic_build_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xhdpi/ic_call_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xhdpi/ic_call_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xhdpi/ic_drafts_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xhdpi/ic_drafts_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xhdpi/ic_email_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xhdpi/ic_email_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xhdpi/ic_forum_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xhdpi/ic_forum_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/ic_account_circle_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxhdpi/ic_account_circle_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/ic_build_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxhdpi/ic_build_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/ic_call_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxhdpi/ic_call_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/ic_drafts_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxhdpi/ic_drafts_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/ic_email_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxhdpi/ic_email_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxhdpi/ic_forum_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxhdpi/ic_forum_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxxhdpi/ic_account_circle_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxxhdpi/ic_account_circle_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxxhdpi/ic_build_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxxhdpi/ic_build_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxxhdpi/ic_call_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxxhdpi/ic_call_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxxhdpi/ic_drafts_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxxhdpi/ic_drafts_white_24dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxxhdpi/ic_email_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxxhdpi/ic_email_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable-xxxhdpi/ic_forum_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/drawable-xxxhdpi/ic_forum_white_36dp.png
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_bottom_sheet.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
25 |
26 |
34 |
35 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_coordinator_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
24 |
25 |
26 |
27 |
33 |
34 |
43 |
44 |
53 |
54 |
59 |
60 |
67 |
68 |
76 |
77 |
78 |
85 |
86 |
94 |
95 |
96 |
103 |
104 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_fab_toolbar.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
14 |
15 |
27 |
28 |
36 |
37 |
41 |
42 |
48 |
49 |
55 |
56 |
64 |
65 |
66 |
72 |
73 |
81 |
82 |
83 |
89 |
90 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
13 |
19 |
25 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/row_item_bottom_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
16 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/src/main/res/menu/menu_coordinator.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/demo/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ff2d8c3c
4 | #ff2d8c3c
5 | #F4FF81
6 | #FFF5F5F5
7 |
8 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 56dp
6 | 48dp
7 | 16sp
8 | 16dp
9 |
10 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | FabTransitionLayout
3 |
4 | Hello world!
5 | Settings
6 | FabToolBarDemo
7 | BottomSheetDemo
8 | Coordinator Layout
9 | Coordinator Layout Demo
10 | Snackbar
11 |
12 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/fabtransitionlayout/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/fabtransitionlayout/bintray-publish.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 | apply plugin: 'com.jfrog.bintray'
3 |
4 | task sourcesJar(type: Jar) {
5 | from android.sourceSets.main.java.srcDirs
6 | classifier = 'sources'
7 | }
8 |
9 | task javadoc(type: Javadoc) {
10 | source = android.sourceSets.main.java.srcDirs
11 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
12 | }
13 |
14 | task javadocJar(type: Jar, dependsOn: javadoc) {
15 | classifier = 'javadoc'
16 | from javadoc.destinationDir
17 | }
18 |
19 | artifacts {
20 | archives sourcesJar, javadocJar
21 | }
22 |
23 | def getBintrayUser() {
24 | return hasProperty('BINTRAY_USER') ? BINTRAY_USER : ""
25 | }
26 |
27 | def getBintrayApiKey() {
28 | return hasProperty('BINTRAY_APIKEY') ? BINTRAY_APIKEY : ""
29 | }
30 |
31 | def getGpgPassphrase() {
32 | return hasProperty('BINTRAY_GPG_PASSPHRASE') ? BINTRAY_GPG_PASSPHRASE : ""
33 | }
34 |
35 | publishing {
36 | publications {
37 | mavenJava(MavenPublication) {
38 | groupId GROUP
39 | version VERSION_NAME
40 | artifactId ARTIFACT_ID
41 | artifact "build/outputs/aar/fabtransitionlayout-release.aar"
42 | artifact javadocJar
43 | artifact sourcesJar
44 | pom.withXml {
45 | Node root = asNode()
46 | root.appendNode('name', ARTIFACT_ID)
47 | root.appendNode('description', POM_DESCRIPTION)
48 | root.appendNode('url', POM_URL)
49 |
50 | def issues = root.appendNode('issueManagement')
51 | issues.appendNode('system', 'github')
52 | issues.appendNode('url', ISSUE_URL)
53 |
54 | def scm = root.appendNode('scm')
55 | scm.appendNode('url', POM_SCM_URL)
56 | scm.appendNode('connection', POM_SCM_CONNECTION)
57 | scm.appendNode('developerConnection', POM_SCM_DEV_CONNECTION)
58 |
59 | def license = root.appendNode('licenses').appendNode('license')
60 | license.appendNode('name', POM_LICENCE_NAME)
61 | license.appendNode('url', POM_LICENCE_URL)
62 | license.appendNode('distribution', POM_LICENCE_DIST)
63 |
64 | def dependenciesNode = asNode().appendNode('dependencies')
65 | configurations.compile.allDependencies.each {
66 | def dependencyNode = dependenciesNode.appendNode('dependency')
67 | dependencyNode.appendNode('groupId', it.group)
68 | dependencyNode.appendNode('artifactId', it.name)
69 | dependencyNode.appendNode('version', it.version)
70 | dependencyNode.appendNode('scope', 'compile')
71 | }
72 | }
73 | }
74 | }
75 | }
76 |
77 | bintray {
78 | user = bintrayUser
79 | key = bintrayApiKey
80 |
81 | publications = ['mavenJava']
82 |
83 | dryRun = false
84 | publish = true
85 |
86 | pkg {
87 | repo = "maven"
88 | name = ARTIFACT_NAME
89 | desc = POM_DESCRIPTION
90 | websiteUrl = POM_URL
91 | issueTrackerUrl = ISSUE_URL
92 | vcsUrl = POM_SCM_URL
93 | licenses = ["MIT"]
94 | labels = ['android']
95 | publicDownloadNumbers = true
96 |
97 | version {
98 | name = VERSION_NAME
99 | vcsTag = VERSION_NAME
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/fabtransitionlayout/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | repositories {
4 | mavenCentral()
5 |
6 | maven {
7 | url "https://jitpack.io"
8 | }
9 | }
10 |
11 | android {
12 | compileSdkVersion COMPILE_SDK_VERSION as int
13 | buildToolsVersion BUILD_TOOLS_VERSION
14 |
15 | defaultConfig {
16 | minSdkVersion MIN_SDK_VERSION
17 | targetSdkVersion TARGET_SDK_VERSION as int
18 | versionCode VERSION_CODE as int
19 | versionName VERSION_NAME
20 | }
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | }
28 |
29 |
30 | dependencies {
31 | compile rootProject.ext.androidSupportAppCompatV7Lib
32 | compile 'com.github.ozodrukh:CircularReveal:1.3.1'
33 | compile 'com.android.support:design:23.4.0'
34 | }
35 | apply from: 'bintray-publish.gradle'
36 |
--------------------------------------------------------------------------------
/fabtransitionlayout/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/a13089/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/androidTest/java/com/bowyer/fabtransitionlayout/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.fabtransitionlayout;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 |
11 | public ApplicationTest() {
12 | super(Application.class);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/java/com/bowyer/app/fabtransitionlayout/BottomSheetLayout.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.app.fabtransitionlayout;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ObjectAnimator;
7 | import android.animation.PropertyValuesHolder;
8 | import android.annotation.TargetApi;
9 | import android.content.Context;
10 | import android.content.res.Resources;
11 | import android.content.res.TypedArray;
12 | import android.os.Build;
13 | import android.support.annotation.IntDef;
14 | import android.support.annotation.NonNull;
15 | import android.support.v4.view.animation.FastOutLinearInInterpolator;
16 | import android.util.AttributeSet;
17 | import android.util.DisplayMetrics;
18 | import android.util.TypedValue;
19 | import android.view.Gravity;
20 | import android.view.MotionEvent;
21 | import android.view.View;
22 | import android.view.ViewAnimationUtils;
23 | import android.view.ViewGroup;
24 | import android.view.animation.AccelerateInterpolator;
25 | import android.view.animation.DecelerateInterpolator;
26 | import android.widget.FrameLayout;
27 | import android.widget.ImageView;
28 | import android.widget.LinearLayout;
29 | import io.codetail.animation.SupportAnimator;
30 |
31 | /**
32 | * Created by Bowyer on 2015/08/05.
33 | */
34 | public class BottomSheetLayout extends FrameLayout implements View.OnTouchListener {
35 |
36 | private static final int DEFAULT_ANIMATION_DURATION = 400;
37 |
38 | private static final int DEFAULT_FAB_SIZE = 56;
39 |
40 | @IntDef({ FAB_CIRCLE, FAB_EXPAND }) private @interface Fab {
41 |
42 | }
43 |
44 | private static final int FAB_CIRCLE = 0;
45 |
46 | private static final int FAB_EXPAND = 1;
47 |
48 | int mFabType = FAB_CIRCLE;
49 |
50 | boolean mAnimatingFab = false;
51 |
52 | private LinearLayout mFabExpandLayout;
53 |
54 | private ImageView mFab;
55 |
56 | private View mFakeBg;
57 |
58 | private int animationDuration;
59 |
60 | private int mFabSize;
61 |
62 | @Override public boolean onTouch(View v, MotionEvent event) {
63 | contractFab();
64 | return true;
65 | }
66 |
67 | public BottomSheetLayout(Context context) {
68 | super(context);
69 | init();
70 | }
71 |
72 | public BottomSheetLayout(Context context, AttributeSet attrs) {
73 | super(context, attrs);
74 | init();
75 | loadAttributes(context, attrs);
76 | }
77 |
78 | public BottomSheetLayout(Context context, AttributeSet attrs, int defStyle) {
79 | super(context, attrs, defStyle);
80 | init();
81 | loadAttributes(context, attrs);
82 | }
83 |
84 | private void init() {
85 | inflate(getContext(), R.layout.bottom_sheet_layout, this);
86 | mFabExpandLayout = ((LinearLayout) findViewById(R.id.container));
87 | mFakeBg = findViewById(R.id.fake_bg);
88 | }
89 |
90 | private void loadAttributes(Context context, AttributeSet attrs) {
91 | TypedValue outValue = new TypedValue();
92 | Resources.Theme theme = context.getTheme();
93 |
94 | // use ?attr/colorPrimary as background color
95 | theme.resolveAttribute(R.attr.colorPrimary, outValue, true);
96 |
97 | TypedArray a =
98 | getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.FooterLayout, 0, 0);
99 |
100 | int containerGravity;
101 | try {
102 | setColor(a.getColor(R.styleable.FooterLayout_ft_color, outValue.data));
103 | animationDuration =
104 | a.getInteger(R.styleable.FooterLayout_ft_anim_duration, DEFAULT_ANIMATION_DURATION);
105 | containerGravity = a.getInteger(R.styleable.FooterLayout_ft_container_gravity, 1);
106 | mFabSize = a.getInteger(R.styleable.FooterLayout_ft_fab_type, DEFAULT_FAB_SIZE);
107 | } finally {
108 | a.recycle();
109 | }
110 |
111 | mFabExpandLayout.setGravity(getGravity(containerGravity));
112 | }
113 |
114 | public void setFab(ImageView imageView) {
115 | mFab = imageView;
116 | }
117 |
118 | private int getGravity(int gravityEnum) {
119 | return (gravityEnum == 0 ? Gravity.START
120 | : gravityEnum == 1 ? Gravity.CENTER_HORIZONTAL : Gravity.END) | Gravity.CENTER_VERTICAL;
121 | }
122 |
123 | public void setColor(int color) {
124 | mFabExpandLayout.setBackgroundColor(color);
125 | }
126 |
127 | @Override public void addView(@NonNull View child) {
128 | if (canAddViewToContainer()) {
129 | mFabExpandLayout.addView(child);
130 | } else {
131 | super.addView(child);
132 | }
133 | }
134 |
135 | @Override public void addView(@NonNull View child, int width, int height) {
136 | if (canAddViewToContainer()) {
137 | mFabExpandLayout.addView(child, width, height);
138 | } else {
139 | super.addView(child, width, height);
140 | }
141 | }
142 |
143 | @Override public void addView(@NonNull View child, ViewGroup.LayoutParams params) {
144 | if (canAddViewToContainer()) {
145 | mFabExpandLayout.addView(child, params);
146 | } else {
147 | super.addView(child, params);
148 | }
149 | }
150 |
151 | @Override public void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) {
152 | if (canAddViewToContainer()) {
153 | mFabExpandLayout.addView(child, index, params);
154 | } else {
155 | super.addView(child, index, params);
156 | }
157 | }
158 |
159 | /**
160 | * hide() and show() methods are useful for remembering the toolbar state on screen rotation.
161 | */
162 | public void hide() {
163 | mFabExpandLayout.setVisibility(View.INVISIBLE);
164 | mFabType = FAB_CIRCLE;
165 | }
166 |
167 | public void show() {
168 | mFabExpandLayout.setVisibility(View.VISIBLE);
169 | mFabType = FAB_EXPAND;
170 | }
171 |
172 | private boolean canAddViewToContainer() {
173 | return mFabExpandLayout != null;
174 | }
175 |
176 | public void slideInFab() {
177 | if (mAnimatingFab) {
178 | return;
179 | }
180 |
181 | if (isFabExpanded()) {
182 | contractFab();
183 | return;
184 | }
185 |
186 | ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mFab.getLayoutParams();
187 | float dy = mFab.getHeight() + lp.bottomMargin;
188 | if (mFab.getTranslationY() != dy) {
189 | return;
190 | }
191 |
192 | mAnimatingFab = true;
193 | mFab.setVisibility(View.VISIBLE);
194 | mFab.animate()
195 | .setStartDelay(0)
196 | .setDuration(200)
197 | .setInterpolator(new FastOutLinearInInterpolator())
198 | .translationY(0f)
199 | .setListener(new AnimatorListenerAdapter() {
200 | @Override public void onAnimationEnd(final Animator animation) {
201 | super.onAnimationEnd(animation);
202 | mAnimatingFab = false;
203 | }
204 | })
205 | .start();
206 | }
207 |
208 | public void slideOutFab() {
209 | if (mAnimatingFab) {
210 | return;
211 | }
212 |
213 | if (isFabExpanded()) {
214 | contractFab();
215 | return;
216 | }
217 |
218 | ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mFab.getLayoutParams();
219 | if (mFab.getTranslationY() != 0f) {
220 | return;
221 | }
222 |
223 | mAnimatingFab = true;
224 | mFab.animate()
225 | .setStartDelay(0)
226 | .setDuration(200)
227 | .setInterpolator(new FastOutLinearInInterpolator())
228 | .translationY(mFab.getHeight() + lp.bottomMargin)
229 | .setListener(new AnimatorListenerAdapter() {
230 | @Override public void onAnimationEnd(final Animator animation) {
231 | super.onAnimationEnd(animation);
232 | mAnimatingFab = false;
233 | mFab.setVisibility(View.INVISIBLE);
234 | }
235 | })
236 | .start();
237 | }
238 |
239 | public void expandFab() {
240 | if (mAnimatingFab) return;
241 |
242 | mFabType = FAB_EXPAND;
243 |
244 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
245 | expandPreLollipop();
246 | } else {
247 | expandLollipop();
248 | }
249 | }
250 |
251 | public void contractFab() {
252 | if (!isFabExpanded()) {
253 | return;
254 | }
255 |
256 | mFabType = FAB_CIRCLE;
257 | mFakeBg.setVisibility(INVISIBLE);
258 | mFakeBg.setOnTouchListener(null);
259 |
260 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
261 | contractPreLollipop();
262 | } else {
263 | contractLollipop();
264 | }
265 | }
266 |
267 | public boolean isFabExpanded() {
268 | return mFabType == FAB_EXPAND;
269 | }
270 |
271 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void expandLollipop() {
272 | mAnimatingFab = true;
273 |
274 | // Translation vector for the FAB. Basically move it just below the toolbar.
275 | float dx = ViewUtils.centerX(mFabExpandLayout) + mFab.getWidth() - ViewUtils.centerX(mFab);
276 | float dy = ViewUtils.centerY(mFabExpandLayout) / 20;
277 |
278 | // Center point on the screen of the FAB after translation. Used as the start point
279 | // for the expansion animation of the toolbar.
280 | int x = (int) (ViewUtils.centerX(mFab) + dx);
281 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
282 |
283 | // Start and end radii of the toolbar expand animation.
284 | float startRadius = getFabSizePx() / 2;
285 | float endRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
286 | Math.max(y, mFabExpandLayout.getHeight() - y));
287 |
288 | mFabExpandLayout.setAlpha(0f);
289 | mFabExpandLayout.setVisibility(View.VISIBLE);
290 |
291 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
292 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f, dx));
293 | fabSlideXAnim.setDuration(animationDuration / 2);
294 |
295 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
296 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f, dy));
297 | fabSlideYAnim.setDuration(animationDuration / 2);
298 |
299 | Animator toolbarExpandAnim =
300 | ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y, startRadius, endRadius);
301 | toolbarExpandAnim.setStartDelay(animationDuration / 2);
302 | toolbarExpandAnim.setDuration(animationDuration / 2);
303 |
304 | // Play All animations together. Interpolators must be added after playTogether()
305 | // or the won't be used.
306 | AnimatorSet animSet = new AnimatorSet();
307 | animSet.playTogether(fabSlideXAnim, fabSlideYAnim, toolbarExpandAnim);
308 | fabSlideXAnim.setInterpolator(new AccelerateInterpolator(1.0f));
309 | fabSlideYAnim.setInterpolator(new DecelerateInterpolator(0.8f));
310 |
311 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
312 | @Override public void onAnimationEnd(Animator animation) {
313 | super.onAnimationEnd(animation);
314 | mFab.setVisibility(View.INVISIBLE);
315 | mFab.setTranslationX(0f);
316 | mFab.setTranslationY(0f);
317 | mFab.setAlpha(1f);
318 | }
319 | });
320 |
321 | toolbarExpandAnim.addListener(new AnimatorListenerAdapter() {
322 | @Override public void onAnimationStart(Animator animation) {
323 | super.onAnimationStart(animation);
324 | mFabExpandLayout.setAlpha(1f);
325 | }
326 |
327 | @Override public void onAnimationEnd(Animator animation) {
328 | super.onAnimationEnd(animation);
329 | mAnimatingFab = false;
330 | mFakeBg.setVisibility(VISIBLE);
331 | mFakeBg.setOnTouchListener(BottomSheetLayout.this);
332 | }
333 | });
334 |
335 | animSet.start();
336 | }
337 |
338 | private void expandPreLollipop() {
339 | mAnimatingFab = true;
340 |
341 | // Translation vector for the FAB. Basically move it just below the toolbar.
342 | float dx = ViewUtils.centerX(mFabExpandLayout) + mFab.getWidth() - ViewUtils.centerX(mFab);
343 | float dy = ViewUtils.centerY(mFabExpandLayout) / 20;
344 |
345 | // Center point on the screen of the FAB after translation. Used as the start point
346 | // for the expansion animation of the toolbar.
347 | int x = (int) (ViewUtils.centerX(mFab) + dx);
348 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
349 |
350 | // Start and end radii of the toolbar expand animation.
351 | float startRadius = getFabSizePx() / 2;
352 | float endRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
353 | Math.max(y, mFabExpandLayout.getHeight() - y));
354 |
355 | mFabExpandLayout.setAlpha(0f);
356 | mFabExpandLayout.setVisibility(View.VISIBLE);
357 |
358 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
359 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f, dx));
360 | fabSlideXAnim.setDuration(animationDuration / 2);
361 |
362 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
363 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f, dy));
364 | fabSlideYAnim.setDuration(animationDuration / 2);
365 |
366 | final SupportAnimator toolbarExpandAnim =
367 | io.codetail.animation.ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y,
368 | startRadius, endRadius);
369 | toolbarExpandAnim.setDuration(animationDuration / 2);
370 |
371 | // Play slide animations together. Interpolators must be added after playTogether()
372 | // or the won't be used.
373 | AnimatorSet animSet = new AnimatorSet();
374 | animSet.playTogether(fabSlideXAnim, fabSlideYAnim);
375 | fabSlideXAnim.setInterpolator(new AccelerateInterpolator(1.0f));
376 | fabSlideYAnim.setInterpolator(new DecelerateInterpolator(0.8f));
377 |
378 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
379 | @Override public void onAnimationEnd(Animator animation) {
380 | super.onAnimationEnd(animation);
381 | mFab.setAlpha(0f);
382 | mFab.setVisibility(View.INVISIBLE);
383 | mFab.setTranslationX(0f);
384 | mFab.setTranslationY(0f);
385 |
386 | // Play toolbar expand animation after slide animations finish.
387 | toolbarExpandAnim.start();
388 | }
389 | });
390 |
391 | toolbarExpandAnim.addListener(new SupportAnimator.AnimatorListener() {
392 | @Override public void onAnimationStart() {
393 | mFabExpandLayout.setAlpha(1f);
394 | }
395 |
396 | @Override public void onAnimationEnd() {
397 | mFab.setAlpha(1f);
398 | mAnimatingFab = false;
399 | mFakeBg.setVisibility(VISIBLE);
400 | mFakeBg.setOnTouchListener(BottomSheetLayout.this);
401 | }
402 |
403 | @Override public void onAnimationCancel() {
404 |
405 | }
406 |
407 | @Override public void onAnimationRepeat() {
408 |
409 | }
410 | });
411 |
412 | animSet.start();
413 | }
414 |
415 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void contractLollipop() {
416 | mAnimatingFab = true;
417 |
418 | // Translation vector for the FAB. Basically move it just below the toolbar.
419 | float dx = ViewUtils.centerX(mFabExpandLayout) + mFab.getWidth() - ViewUtils.centerX(mFab);
420 | float dy = ViewUtils.centerY(mFabExpandLayout) / 20;
421 |
422 | mFab.setAlpha(0f);
423 | mFab.setTranslationX(dx);
424 | mFab.setTranslationY(dy);
425 | mFab.setVisibility(View.VISIBLE);
426 |
427 | // Center point on the screen of the FAB before translation. Used as the start point
428 | // for the expansion animation of the toolbar.
429 | int x = (int) (ViewUtils.centerX(mFab));
430 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
431 |
432 | // Start and end radii of the toolbar contract animation.
433 | float endRadius = getFabSizePx() / 2;
434 | float startRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
435 | Math.max(y, mFabExpandLayout.getHeight() - y));
436 |
437 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
438 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, dx, 0f));
439 | fabSlideXAnim.setStartDelay(animationDuration / 2);
440 | fabSlideXAnim.setDuration(animationDuration / 2);
441 |
442 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
443 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, dy, 0f));
444 | fabSlideYAnim.setStartDelay(animationDuration / 2);
445 | fabSlideYAnim.setDuration(animationDuration / 2);
446 |
447 | Animator toolbarContractAnim =
448 | ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y, startRadius, endRadius);
449 | toolbarContractAnim.setDuration(animationDuration / 2);
450 |
451 | // Play All animations together. Interpolators must be added after playTogether()
452 | // or the won't be used.
453 | AnimatorSet animSet = new AnimatorSet();
454 | animSet.playTogether(toolbarContractAnim, fabSlideXAnim, fabSlideYAnim);
455 | fabSlideXAnim.setInterpolator(new DecelerateInterpolator(0.8f));
456 | fabSlideYAnim.setInterpolator(new AccelerateInterpolator(1.0f));
457 |
458 | toolbarContractAnim.addListener(new AnimatorListenerAdapter() {
459 | @Override public void onAnimationEnd(Animator animation) {
460 | super.onAnimationEnd(animation);
461 | mFab.setAlpha(1f);
462 | mFabExpandLayout.setAlpha(0f);
463 | }
464 | });
465 |
466 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
467 | @Override public void onAnimationEnd(Animator animation) {
468 | super.onAnimationEnd(animation);
469 | mFabExpandLayout.setVisibility(View.INVISIBLE);
470 | mFabExpandLayout.setAlpha(1f);
471 | mAnimatingFab = false;
472 | }
473 | });
474 |
475 | animSet.start();
476 | }
477 |
478 | private void contractPreLollipop() {
479 | mAnimatingFab = true;
480 |
481 | // Translation vector for the FAB. Basically move it just below the toolbar.
482 | float dx = ViewUtils.centerX(mFabExpandLayout) + mFab.getWidth() - ViewUtils.centerX(mFab);
483 | float dy = ViewUtils.centerY(mFabExpandLayout) / 20;
484 |
485 | mFab.setAlpha(0f);
486 | mFab.setTranslationX(dx);
487 | mFab.setTranslationY(dy);
488 | mFab.setVisibility(View.VISIBLE);
489 |
490 | // Center point on the screen of the FAB before translation. Used as the start point
491 | // for the expansion animation of the toolbar.
492 | int x = (int) (ViewUtils.centerX(mFab));
493 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
494 |
495 | // Start and end radii of the toolbar contract animation.
496 | float endRadius = getFabSizePx() / 2;
497 | float startRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
498 | Math.max(y, mFabExpandLayout.getHeight() - y));
499 |
500 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
501 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, dx, 0f));
502 | fabSlideXAnim.setDuration(animationDuration / 2);
503 |
504 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
505 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, dy, 0f));
506 | fabSlideYAnim.setDuration(animationDuration / 2);
507 |
508 | final SupportAnimator toolbarContractAnim =
509 | io.codetail.animation.ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y,
510 | startRadius, endRadius);
511 | toolbarContractAnim.setDuration(animationDuration / 2);
512 |
513 | // Play slide animations together. Interpolators must be added after playTogether()
514 | // or the won't be used.
515 | final AnimatorSet animSet = new AnimatorSet();
516 | animSet.playTogether(fabSlideXAnim, fabSlideYAnim);
517 | fabSlideXAnim.setInterpolator(new DecelerateInterpolator(0.8f));
518 | fabSlideYAnim.setInterpolator(new AccelerateInterpolator(1.0f));
519 |
520 | toolbarContractAnim.addListener(new SupportAnimator.AnimatorListener() {
521 | @Override public void onAnimationStart() {
522 |
523 | }
524 |
525 | @Override public void onAnimationEnd() {
526 | mFab.setAlpha(1f);
527 | mFabExpandLayout.setAlpha(0f);
528 |
529 | // Play fab animation after contract animation finishes.
530 | animSet.start();
531 | }
532 |
533 | @Override public void onAnimationCancel() {
534 |
535 | }
536 |
537 | @Override public void onAnimationRepeat() {
538 |
539 | }
540 | });
541 |
542 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
543 | @Override public void onAnimationEnd(Animator animation) {
544 | super.onAnimationEnd(animation);
545 | mFabExpandLayout.setVisibility(View.INVISIBLE);
546 | mFabExpandLayout.setAlpha(1f);
547 | mAnimatingFab = false;
548 | }
549 | });
550 |
551 | toolbarContractAnim.start();
552 | }
553 |
554 | private int getFabSizePx() {
555 | DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
556 | return Math.round(mFabSize * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
557 | }
558 | }
559 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/java/com/bowyer/app/fabtransitionlayout/FooterBehavior.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.app.fabtransitionlayout;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.support.design.widget.CoordinatorLayout;
7 | import android.support.design.widget.Snackbar;
8 | import android.support.v4.view.ViewCompat;
9 | import android.util.AttributeSet;
10 | import android.view.View;
11 | import java.util.List;
12 |
13 | public class FooterBehavior extends CoordinatorLayout.Behavior {
14 |
15 | private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
16 |
17 | private ValueAnimator mTranslationYAnimator;
18 | private float mFooterTranslationY;
19 |
20 | public FooterBehavior() {
21 | super();
22 | }
23 |
24 | public FooterBehavior(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 | }
27 |
28 | @Override
29 | public void onNestedScroll(CoordinatorLayout coordinatorLayout, FooterLayout child, View target,
30 | int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
31 |
32 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed,
33 | dyUnconsumed);
34 |
35 | if (dyConsumed > 0) {
36 | child.contractFab();
37 | } else if (dyConsumed < 0) {
38 | child.contractFab();
39 | }
40 | }
41 |
42 | @Override
43 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FooterLayout child,
44 | View directTargetChild, View target, int nestedScrollAxes) {
45 |
46 | return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(
47 | coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
48 | }
49 |
50 | @Override
51 | public boolean layoutDependsOn(CoordinatorLayout parent, FooterLayout child, View dependency) {
52 |
53 | return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
54 | }
55 |
56 | @Override public boolean onDependentViewChanged(CoordinatorLayout parent, FooterLayout child,
57 | View dependency) {
58 | if (dependency instanceof Snackbar.SnackbarLayout) {
59 | updateTranslationForSnackbar(parent, child);
60 | }
61 | return false;
62 | }
63 |
64 | private void updateTranslationForSnackbar(CoordinatorLayout parent,
65 | final FooterLayout footerLayout) {
66 | final float targetTransY = getTranslationYForSnackbar(parent, footerLayout);
67 | if (mFooterTranslationY == targetTransY) {
68 | return;
69 | }
70 |
71 | final float currentTransY = ViewCompat.getTranslationY(footerLayout);
72 |
73 | if (mTranslationYAnimator != null && mTranslationYAnimator.isRunning()) {
74 | mTranslationYAnimator.cancel();
75 | }
76 |
77 | if (footerLayout.isShown() && Math.abs(currentTransY - targetTransY) > (footerLayout.getHeight()
78 | * 0.667f)) {
79 | mTranslationYAnimator.setFloatValues(currentTransY, targetTransY);
80 | mTranslationYAnimator.start();
81 | } else {
82 |
83 | ViewCompat.setTranslationY(footerLayout, targetTransY);
84 | }
85 |
86 | mFooterTranslationY = targetTransY;
87 | }
88 |
89 | private float getTranslationYForSnackbar(CoordinatorLayout parent, FooterLayout footerLayout) {
90 |
91 | float minOffset = 0;
92 | final List dependencies = parent.getDependencies(footerLayout);
93 | for (int i = 0, z = dependencies.size(); i < z; i++) {
94 | final View view = dependencies.get(i);
95 | if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(footerLayout, view)) {
96 | minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - view.getHeight());
97 | }
98 | }
99 |
100 | return minOffset;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/java/com/bowyer/app/fabtransitionlayout/FooterLayout.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.app.fabtransitionlayout;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ObjectAnimator;
7 | import android.animation.PropertyValuesHolder;
8 | import android.annotation.TargetApi;
9 | import android.content.Context;
10 | import android.content.res.Resources;
11 | import android.content.res.TypedArray;
12 | import android.os.Build;
13 | import android.support.annotation.IntDef;
14 | import android.support.annotation.NonNull;
15 | import android.support.design.widget.CoordinatorLayout;
16 | import android.support.v4.view.animation.FastOutLinearInInterpolator;
17 | import android.util.AttributeSet;
18 | import android.util.DisplayMetrics;
19 | import android.util.TypedValue;
20 | import android.view.Gravity;
21 | import android.view.View;
22 | import android.view.ViewAnimationUtils;
23 | import android.view.ViewGroup;
24 | import android.view.animation.AccelerateInterpolator;
25 | import android.view.animation.DecelerateInterpolator;
26 | import android.widget.FrameLayout;
27 | import android.widget.ImageView;
28 | import android.widget.LinearLayout;
29 | import io.codetail.animation.SupportAnimator;
30 |
31 | /**
32 | * Created by Bowyer on 2015/08/04.
33 | */
34 |
35 | @CoordinatorLayout.DefaultBehavior(FooterBehavior.class) public class FooterLayout
36 | extends FrameLayout {
37 |
38 | private static final int DEFAULT_ANIMATION_DURATION = 400;
39 | private static final int DEFAULT_FAB_SIZE = 56;
40 |
41 | @IntDef({ FAB_CIRCLE, FAB_EXPAND }) private @interface Fab {
42 |
43 | }
44 |
45 | private static final int FAB_CIRCLE = 0;
46 | private static final int FAB_EXPAND = 1;
47 |
48 | int mFabType = FAB_CIRCLE;
49 | boolean mAnimatingFab = false;
50 |
51 | private LinearLayout mFabExpandLayout;
52 | private ImageView mFab;
53 |
54 | private int animationDuration;
55 | private int mFabSize;
56 |
57 | public FooterLayout(Context context) {
58 | super(context);
59 | init();
60 | }
61 |
62 | public FooterLayout(Context context, AttributeSet attrs) {
63 | super(context, attrs);
64 | init();
65 | loadAttributes(context, attrs);
66 | }
67 |
68 | public FooterLayout(Context context, AttributeSet attrs, int defStyle) {
69 | super(context, attrs, defStyle);
70 | init();
71 | loadAttributes(context, attrs);
72 | }
73 |
74 | private void init() {
75 | inflate(getContext(), R.layout.footer_layout, this);
76 | mFabExpandLayout = ((LinearLayout) findViewById(R.id.container));
77 | }
78 |
79 | private void loadAttributes(Context context, AttributeSet attrs) {
80 | TypedValue outValue = new TypedValue();
81 | Resources.Theme theme = context.getTheme();
82 |
83 | // use ?attr/colorPrimary as background color
84 | theme.resolveAttribute(R.attr.colorPrimary, outValue, true);
85 |
86 | TypedArray a =
87 | getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.FooterLayout, 0, 0);
88 |
89 | int containerGravity;
90 | try {
91 | setColor(a.getColor(R.styleable.FooterLayout_ft_color, outValue.data));
92 | animationDuration =
93 | a.getInteger(R.styleable.FooterLayout_ft_anim_duration, DEFAULT_ANIMATION_DURATION);
94 | containerGravity = a.getInteger(R.styleable.FooterLayout_ft_container_gravity, 1);
95 | mFabSize = a.getInteger(R.styleable.FooterLayout_ft_fab_type, DEFAULT_FAB_SIZE);
96 | } finally {
97 | a.recycle();
98 | }
99 |
100 | mFabExpandLayout.setGravity(getGravity(containerGravity));
101 | }
102 |
103 | public void setFab(ImageView imageView) {
104 | mFab = imageView;
105 | }
106 |
107 | private int getGravity(int gravityEnum) {
108 | return (gravityEnum == 0 ? Gravity.START
109 | : gravityEnum == 1 ? Gravity.CENTER_HORIZONTAL : Gravity.END) | Gravity.CENTER_VERTICAL;
110 | }
111 |
112 | public void setColor(int color) {
113 | mFabExpandLayout.setBackgroundColor(color);
114 | }
115 |
116 | @Override public void addView(@NonNull View child) {
117 | if (canAddViewToContainer()) {
118 | mFabExpandLayout.addView(child);
119 | } else {
120 | super.addView(child);
121 | }
122 | }
123 |
124 | @Override public void addView(@NonNull View child, int width, int height) {
125 | if (canAddViewToContainer()) {
126 | mFabExpandLayout.addView(child, width, height);
127 | } else {
128 | super.addView(child, width, height);
129 | }
130 | }
131 |
132 | @Override public void addView(@NonNull View child, ViewGroup.LayoutParams params) {
133 | if (canAddViewToContainer()) {
134 | mFabExpandLayout.addView(child, params);
135 | } else {
136 | super.addView(child, params);
137 | }
138 | }
139 |
140 | @Override public void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) {
141 | if (canAddViewToContainer()) {
142 | mFabExpandLayout.addView(child, index, params);
143 | } else {
144 | super.addView(child, index, params);
145 | }
146 | }
147 |
148 | /**
149 | * hide() and show() methods are useful for remembering the toolbar state on screen rotation.
150 | */
151 | public void hide() {
152 | mFabExpandLayout.setVisibility(View.INVISIBLE);
153 | mFabType = FAB_CIRCLE;
154 | }
155 |
156 | public void show() {
157 | mFabExpandLayout.setVisibility(View.VISIBLE);
158 | mFabType = FAB_EXPAND;
159 | }
160 |
161 | private boolean canAddViewToContainer() {
162 | return mFabExpandLayout != null;
163 | }
164 |
165 | public void slideInFab() {
166 | if (mAnimatingFab) {
167 | return;
168 | }
169 |
170 | if (isFabExpanded()) {
171 | contractFab();
172 | return;
173 | }
174 |
175 | ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mFab.getLayoutParams();
176 | float dy = mFab.getHeight() + lp.bottomMargin;
177 | if (mFab.getTranslationY() != dy) {
178 | return;
179 | }
180 |
181 | mAnimatingFab = true;
182 | mFab.setVisibility(View.VISIBLE);
183 | mFab.animate()
184 | .setStartDelay(0)
185 | .setDuration(200)
186 | .setInterpolator(new FastOutLinearInInterpolator())
187 | .translationY(0f)
188 | .setListener(new AnimatorListenerAdapter() {
189 | @Override public void onAnimationEnd(final Animator animation) {
190 | super.onAnimationEnd(animation);
191 | mAnimatingFab = false;
192 | }
193 | })
194 | .start();
195 | }
196 |
197 | public void slideOutFab() {
198 | if (mAnimatingFab) {
199 | return;
200 | }
201 |
202 | if (isFabExpanded()) {
203 | contractFab();
204 | return;
205 | }
206 |
207 | ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mFab.getLayoutParams();
208 | if (mFab.getTranslationY() != 0f) {
209 | return;
210 | }
211 |
212 | mAnimatingFab = true;
213 | mFab.animate()
214 | .setStartDelay(0)
215 | .setDuration(200)
216 | .setInterpolator(new FastOutLinearInInterpolator())
217 | .translationY(mFab.getHeight() + lp.bottomMargin)
218 | .setListener(new AnimatorListenerAdapter() {
219 | @Override public void onAnimationEnd(final Animator animation) {
220 | super.onAnimationEnd(animation);
221 | mAnimatingFab = false;
222 | mFab.setVisibility(View.INVISIBLE);
223 | }
224 | })
225 | .start();
226 | }
227 |
228 | public void expandFab() {
229 | mFabType = FAB_EXPAND;
230 |
231 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
232 | expandPreLollipop();
233 | } else {
234 | expandLollipop();
235 | }
236 | }
237 |
238 | public void contractFab() {
239 | if (!isFabExpanded()) {
240 | return;
241 | }
242 |
243 | mFabType = FAB_CIRCLE;
244 |
245 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
246 | contractPreLollipop();
247 | } else {
248 | contractLollipop();
249 | }
250 | }
251 |
252 | public boolean isFabExpanded() {
253 | return mFabType == FAB_EXPAND;
254 | }
255 |
256 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void expandLollipop() {
257 | mAnimatingFab = true;
258 |
259 | // Translation vector for the FAB. Basically move it just below the toolbar.
260 | float dx = ViewUtils.centerX(mFabExpandLayout) + getFabSizePx() - ViewUtils.centerX(mFab);
261 | float dy = ViewUtils.getRelativeTop(mFabExpandLayout)
262 | - ViewUtils.getRelativeTop(mFab)
263 | - (mFab.getHeight() - getFabSizePx()) / 2;
264 |
265 | // Center point on the screen of the FAB after translation. Used as the start point
266 | // for the expansion animation of the toolbar.
267 | int x = (int) (ViewUtils.centerX(mFab) + dx);
268 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
269 |
270 | // Start and end radii of the toolbar expand animation.
271 | float startRadius = getFabSizePx() / 2;
272 | float endRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
273 | Math.max(y, mFabExpandLayout.getHeight() - y));
274 |
275 | mFabExpandLayout.setAlpha(0f);
276 | mFabExpandLayout.setVisibility(View.VISIBLE);
277 |
278 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
279 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f, dx));
280 | fabSlideXAnim.setDuration(animationDuration / 2);
281 |
282 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
283 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f, dy));
284 | fabSlideYAnim.setDuration(animationDuration / 2);
285 |
286 | Animator toolbarExpandAnim =
287 | ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y, startRadius, endRadius);
288 | toolbarExpandAnim.setStartDelay(animationDuration / 2);
289 | toolbarExpandAnim.setDuration(animationDuration / 2);
290 |
291 | // Play All animations together. Interpolators must be added after playTogether()
292 | // or the won't be used.
293 | AnimatorSet animSet = new AnimatorSet();
294 | animSet.playTogether(fabSlideXAnim, fabSlideYAnim, toolbarExpandAnim);
295 | fabSlideXAnim.setInterpolator(new AccelerateInterpolator(1.0f));
296 | fabSlideYAnim.setInterpolator(new DecelerateInterpolator(0.8f));
297 |
298 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
299 | @Override public void onAnimationEnd(Animator animation) {
300 | super.onAnimationEnd(animation);
301 | mFab.setVisibility(View.INVISIBLE);
302 | mFab.setTranslationX(0f);
303 | mFab.setTranslationY(0f);
304 | mFab.setAlpha(1f);
305 | }
306 | });
307 |
308 | toolbarExpandAnim.addListener(new AnimatorListenerAdapter() {
309 | @Override public void onAnimationStart(Animator animation) {
310 | super.onAnimationStart(animation);
311 | mFabExpandLayout.setAlpha(1f);
312 | }
313 |
314 | @Override public void onAnimationEnd(Animator animation) {
315 | super.onAnimationEnd(animation);
316 | mAnimatingFab = false;
317 | }
318 | });
319 |
320 | animSet.start();
321 | }
322 |
323 | private void expandPreLollipop() {
324 | mAnimatingFab = true;
325 |
326 | // Translation vector for the FAB. Basically move it just below the toolbar.
327 | float dx = ViewUtils.centerX(mFabExpandLayout) + getFabSizePx() - ViewUtils.centerX(mFab);
328 | float dy = ViewUtils.getRelativeTop(mFabExpandLayout)
329 | - ViewUtils.getRelativeTop(mFab)
330 | - (mFab.getHeight() - getFabSizePx()) / 2;
331 |
332 | // Center point on the screen of the FAB after translation. Used as the start point
333 | // for the expansion animation of the toolbar.
334 | int x = (int) (ViewUtils.centerX(mFab) + dx);
335 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
336 |
337 | // Start and end radii of the toolbar expand animation.
338 | float startRadius = getFabSizePx() / 2;
339 | float endRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
340 | Math.max(y, mFabExpandLayout.getHeight() - y));
341 |
342 | mFabExpandLayout.setAlpha(0f);
343 | mFabExpandLayout.setVisibility(View.VISIBLE);
344 |
345 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
346 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f, dx));
347 | fabSlideXAnim.setDuration(animationDuration / 2);
348 |
349 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
350 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f, dy));
351 | fabSlideYAnim.setDuration(animationDuration / 2);
352 |
353 | final SupportAnimator toolbarExpandAnim =
354 | io.codetail.animation.ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y,
355 | startRadius, endRadius);
356 | toolbarExpandAnim.setDuration(animationDuration / 2);
357 |
358 | // Play slide animations together. Interpolators must be added after playTogether()
359 | // or the won't be used.
360 | AnimatorSet animSet = new AnimatorSet();
361 | animSet.playTogether(fabSlideXAnim, fabSlideYAnim);
362 | fabSlideXAnim.setInterpolator(new AccelerateInterpolator(1.0f));
363 | fabSlideYAnim.setInterpolator(new DecelerateInterpolator(0.8f));
364 |
365 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
366 | @Override public void onAnimationEnd(Animator animation) {
367 | super.onAnimationEnd(animation);
368 | mFab.setAlpha(0f);
369 | mFab.setVisibility(View.INVISIBLE);
370 | mFab.setTranslationX(0f);
371 | mFab.setTranslationY(0f);
372 |
373 | // Play toolbar expand animation after slide animations finish.
374 | toolbarExpandAnim.start();
375 | }
376 | });
377 |
378 | toolbarExpandAnim.addListener(new SupportAnimator.AnimatorListener() {
379 | @Override public void onAnimationStart() {
380 | mFabExpandLayout.setAlpha(1f);
381 | }
382 |
383 | @Override public void onAnimationEnd() {
384 | mFab.setAlpha(1f);
385 | mAnimatingFab = false;
386 | }
387 |
388 | @Override public void onAnimationCancel() {
389 |
390 | }
391 |
392 | @Override public void onAnimationRepeat() {
393 |
394 | }
395 | });
396 |
397 | animSet.start();
398 | }
399 |
400 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void contractLollipop() {
401 | mAnimatingFab = true;
402 |
403 | // Translation vector for the FAB. Basically move it just below the toolbar.
404 | float dx = ViewUtils.centerX(mFabExpandLayout) + getFabSizePx() - ViewUtils.centerX(mFab);
405 | float dy = ViewUtils.getRelativeTop(mFabExpandLayout)
406 | - ViewUtils.getRelativeTop(mFab)
407 | - (mFab.getHeight() - getFabSizePx()) / 2;
408 |
409 | mFab.setAlpha(0f);
410 | mFab.setTranslationX(dx);
411 | mFab.setTranslationY(dy);
412 | mFab.setVisibility(View.VISIBLE);
413 |
414 | // Center point on the screen of the FAB before translation. Used as the start point
415 | // for the expansion animation of the toolbar.
416 | int x = (int) (ViewUtils.centerX(mFab));
417 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
418 |
419 | // Start and end radii of the toolbar contract animation.
420 | float endRadius = getFabSizePx() / 2;
421 | float startRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
422 | Math.max(y, mFabExpandLayout.getHeight() - y));
423 |
424 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
425 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, dx, 0f));
426 | fabSlideXAnim.setStartDelay(animationDuration / 2);
427 | fabSlideXAnim.setDuration(animationDuration / 2);
428 |
429 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
430 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, dy, 0f));
431 | fabSlideYAnim.setStartDelay(animationDuration / 2);
432 | fabSlideYAnim.setDuration(animationDuration / 2);
433 |
434 | Animator toolbarContractAnim =
435 | ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y, startRadius, endRadius);
436 | toolbarContractAnim.setDuration(animationDuration / 2);
437 |
438 | // Play All animations together. Interpolators must be added after playTogether()
439 | // or the won't be used.
440 | AnimatorSet animSet = new AnimatorSet();
441 | animSet.playTogether(toolbarContractAnim, fabSlideXAnim, fabSlideYAnim);
442 | fabSlideXAnim.setInterpolator(new DecelerateInterpolator(0.8f));
443 | fabSlideYAnim.setInterpolator(new AccelerateInterpolator(1.0f));
444 |
445 | toolbarContractAnim.addListener(new AnimatorListenerAdapter() {
446 | @Override public void onAnimationEnd(Animator animation) {
447 | super.onAnimationEnd(animation);
448 | mFab.setAlpha(1f);
449 | mFabExpandLayout.setAlpha(0f);
450 | }
451 | });
452 |
453 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
454 | @Override public void onAnimationEnd(Animator animation) {
455 | super.onAnimationEnd(animation);
456 | mFabExpandLayout.setVisibility(View.INVISIBLE);
457 | mFabExpandLayout.setAlpha(1f);
458 | mAnimatingFab = false;
459 | }
460 | });
461 |
462 | animSet.start();
463 | }
464 |
465 | private void contractPreLollipop() {
466 | mAnimatingFab = true;
467 |
468 | // Translation vector for the FAB. Basically move it just below the toolbar.
469 | float dx = ViewUtils.centerX(mFabExpandLayout) + getFabSizePx() - ViewUtils.centerX(mFab);
470 | float dy = ViewUtils.getRelativeTop(mFabExpandLayout)
471 | - ViewUtils.getRelativeTop(mFab)
472 | - (mFab.getHeight() - getFabSizePx()) / 2;
473 |
474 | mFab.setAlpha(0f);
475 | mFab.setTranslationX(dx);
476 | mFab.setTranslationY(dy);
477 | mFab.setVisibility(View.VISIBLE);
478 |
479 | // Center point on the screen of the FAB before translation. Used as the start point
480 | // for the expansion animation of the toolbar.
481 | int x = (int) (ViewUtils.centerX(mFab));
482 | int y = (mFabExpandLayout.getBottom() - mFabExpandLayout.getTop()) / 2;
483 |
484 | // Start and end radii of the toolbar contract animation.
485 | float endRadius = getFabSizePx() / 2;
486 | float startRadius = (float) Math.hypot(Math.max(x, mFabExpandLayout.getWidth() - x),
487 | Math.max(y, mFabExpandLayout.getHeight() - y));
488 |
489 | Animator fabSlideXAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
490 | PropertyValuesHolder.ofFloat(View.TRANSLATION_X, dx, 0f));
491 | fabSlideXAnim.setDuration(animationDuration / 2);
492 |
493 | Animator fabSlideYAnim = ObjectAnimator.ofPropertyValuesHolder(mFab,
494 | PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, dy, 0f));
495 | fabSlideYAnim.setDuration(animationDuration / 2);
496 |
497 | final SupportAnimator toolbarContractAnim =
498 | io.codetail.animation.ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y,
499 | startRadius, endRadius);
500 | toolbarContractAnim.setDuration(animationDuration / 2);
501 |
502 | // Play slide animations together. Interpolators must be added after playTogether()
503 | // or the won't be used.
504 | final AnimatorSet animSet = new AnimatorSet();
505 | animSet.playTogether(fabSlideXAnim, fabSlideYAnim);
506 | fabSlideXAnim.setInterpolator(new DecelerateInterpolator(0.8f));
507 | fabSlideYAnim.setInterpolator(new AccelerateInterpolator(1.0f));
508 |
509 | toolbarContractAnim.addListener(new SupportAnimator.AnimatorListener() {
510 | @Override public void onAnimationStart() {
511 |
512 | }
513 |
514 | @Override public void onAnimationEnd() {
515 | mFab.setAlpha(1f);
516 | mFabExpandLayout.setAlpha(0f);
517 |
518 | // Play fab animation after contract animation finishes.
519 | animSet.start();
520 | }
521 |
522 | @Override public void onAnimationCancel() {
523 |
524 | }
525 |
526 | @Override public void onAnimationRepeat() {
527 |
528 | }
529 | });
530 |
531 | fabSlideYAnim.addListener(new AnimatorListenerAdapter() {
532 | @Override public void onAnimationEnd(Animator animation) {
533 | super.onAnimationEnd(animation);
534 | mFabExpandLayout.setVisibility(View.INVISIBLE);
535 | mFabExpandLayout.setAlpha(1f);
536 | mAnimatingFab = false;
537 | }
538 | });
539 |
540 | toolbarContractAnim.start();
541 | }
542 |
543 | private int getFabSizePx() {
544 | DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
545 | return Math.round(mFabSize * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
546 | }
547 | }
548 |
549 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/java/com/bowyer/app/fabtransitionlayout/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.bowyer.app.fabtransitionlayout;
2 |
3 | import android.support.v4.view.ViewCompat;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by Bowyer on 2015/07/15.
8 | */
9 | public class ViewUtils {
10 |
11 | public static boolean setVisibility(View v, boolean visible) {
12 | int visibility;
13 | if (visible) {
14 | visibility = View.VISIBLE;
15 | } else {
16 | visibility = View.GONE;
17 | }
18 | v.setVisibility(visibility);
19 | return visible;
20 | }
21 |
22 | public static boolean setInVisibility(View v, boolean visible) {
23 | int visibility;
24 | if (visible) {
25 | visibility = View.VISIBLE;
26 | } else {
27 | visibility = View.INVISIBLE;
28 | }
29 | v.setVisibility(visibility);
30 | return visible;
31 | }
32 |
33 | public static float centerX(View view) {
34 | return ViewCompat.getX(view) + view.getWidth() / 2f;
35 | }
36 |
37 | public static float centerY(View view) {
38 | return ViewCompat.getY(view) + view.getHeight() / 2f;
39 | }
40 |
41 | public static int getRelativeTop(View myView) {
42 | if (myView.getParent() == myView.getRootView()) {
43 | return myView.getTop();
44 | } else {
45 | return myView.getTop() + getRelativeTop((View) myView.getParent());
46 | }
47 | }
48 |
49 | private ViewUtils() {
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/res/layout/bottom_sheet_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/res/layout/footer_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/res/layout/sheet_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
21 |
22 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 56dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/fabtransitionlayout/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Fabtransitionlayout
3 |
4 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 | VERSION_NAME=0.4.0
20 | VERSION_CODE=4
21 | COMPILE_SDK_VERSION=23
22 | BUILD_TOOLS_VERSION=23.0.1
23 | TARGET_SDK_VERSION=23
24 | MIN_SDK_VERSION=15
25 |
26 | GROUP=com.bowyer.app
27 | ARTIFACT_ID=fabtransitionlayout
28 | ARTIFACT_NAME=FabTransitionLayout
29 |
30 | POM_DESCRIPTION=Fab Transition Layout
31 | POM_URL=https://github.com/bowyer-app/FabTransitionLayout
32 | POM_SCM_URL=https://github.com/bowyer-app/FabTransitionLayout.git
33 | POM_SCM_CONNECTION=https://github.com/bowyer-app/FabTransitionLayout.git
34 | POM_SCM_DEV_CONNECTION=https://github.com/bowyer-app/FabTransitionLayout.git
35 | POM_LICENCE_NAME=Released under the MIT license
36 | POM_LICENCE_URL=http://opensource.org/licenses/mit-license.php
37 | POM_LICENCE_DIST=repo
38 | POM_DEVELOPER_ID=bowyer-app
39 | POM_DEVELOPER_NAME=bowyer-app
40 | ISSUE_URL=https://github.com/bowyer-app/FabTransitionLayout/issues
41 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bowyer-app/FabTransitionLayout/bcd3a5e1f1f061ea3e69a295af92d86f0c22a5f3/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Aug 03 14:11:36 AMT 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':demo', ':fabtransitionlayout'
2 |
--------------------------------------------------------------------------------