├── .gitignore ├── LICENCE ├── README.md ├── assets ├── circularfab.gif ├── customViews.png └── customizableButtons.png ├── build.gradle ├── gradle-mvn-push.gradle ├── gradle.properties.sample ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── oguzdev │ │ └── circularfloatingactionmenu │ │ └── library │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── oguzdev │ │ └── circularfloatingactionmenu │ │ └── library │ │ ├── FloatingActionButton.java │ │ ├── FloatingActionMenu.java │ │ ├── SubActionButton.java │ │ └── animation │ │ ├── DefaultAnimationHandler.java │ │ └── MenuAnimationHandler.java │ └── res │ ├── drawable-hdpi │ ├── button_action.png │ ├── button_action_dark.png │ ├── button_action_dark_touch.png │ ├── button_action_touch.png │ ├── button_sub_action.png │ ├── button_sub_action_dark.png │ ├── button_sub_action_dark_touch.png │ └── button_sub_action_touch.png │ ├── drawable-mdpi │ ├── button_action.png │ ├── button_action_dark.png │ ├── button_action_dark_touch.png │ ├── button_action_touch.png │ ├── button_sub_action.png │ ├── button_sub_action_dark.png │ ├── button_sub_action_dark_touch.png │ └── button_sub_action_touch.png │ ├── drawable-xhdpi │ ├── button_action.png │ ├── button_action_dark.png │ ├── button_action_dark_touch.png │ ├── button_action_touch.png │ ├── button_sub_action.png │ ├── button_sub_action_dark.png │ ├── button_sub_action_dark_touch.png │ └── button_sub_action_touch.png │ ├── drawable-xxhdpi │ ├── button_action.png │ ├── button_action_dark.png │ ├── button_action_dark_touch.png │ ├── button_action_touch.png │ ├── button_sub_action.png │ ├── button_sub_action_dark.png │ ├── button_sub_action_dark_touch.png │ └── button_sub_action_touch.png │ ├── drawable-xxxhdpi │ ├── button_action.png │ ├── button_action_dark.png │ ├── button_action_dark_touch.png │ ├── button_action_touch.png │ ├── button_sub_action.png │ ├── button_sub_action_dark.png │ ├── button_sub_action_dark_touch.png │ └── button_sub_action_touch.png │ ├── drawable │ ├── button_action_dark_selector.xml │ ├── button_action_selector.xml │ ├── button_sub_action_dark_selector.xml │ └── button_sub_action_selector.xml │ └── values │ └── dimens.xml ├── samples ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── oguzdev │ │ └── circularfloatingactionmenu │ │ └── samples │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── oguzdev │ │ └── circularfloatingactionmenu │ │ └── samples │ │ ├── DemoActivity.java │ │ ├── MenuInScrollViewActivity.java │ │ ├── MenuWithCustomActionButtonActivity.java │ │ ├── MenuWithCustomAnimationActivity.java │ │ ├── MenuWithFABActivity.java │ │ ├── SlideInAnimationHandler.java │ │ ├── SystemOverlayMenuActivity.java │ │ └── SystemOverlayMenuService.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── button_action_blue.png │ ├── button_action_blue_touch.png │ ├── button_action_red.png │ ├── button_action_red_touch.png │ ├── ic_action_camera.png │ ├── ic_action_camera_light.png │ ├── ic_action_cancel.png │ ├── ic_action_chat.png │ ├── ic_action_chat_light.png │ ├── ic_action_headphones.png │ ├── ic_action_important.png │ ├── ic_action_location_found.png │ ├── ic_action_new.png │ ├── ic_action_new_light.png │ ├── ic_action_picture.png │ ├── ic_action_picture_light.png │ ├── ic_action_place.png │ ├── ic_action_place_light.png │ ├── ic_action_send_now.png │ ├── ic_action_send_now_light.png │ ├── ic_action_settings.png │ ├── ic_action_video.png │ ├── ic_action_video_light.png │ └── ic_launcher.png │ ├── drawable │ ├── bottom_bar_background.xml │ ├── button_action_blue_selector.xml │ └── button_action_red_selector.xml │ ├── layout │ ├── activity_demo.xml │ ├── activity_menu_in_scroll_view.xml │ ├── activity_menu_with_custom_action_button.xml │ ├── activity_menu_with_custom_animation.xml │ ├── activity_menu_with_fab.xml │ ├── activity_menu_with_overlay.xml │ ├── fragment_demo.xml │ ├── fragment_menu_with_custom_action_button.xml │ ├── fragment_menu_with_custom_animation.xml │ └── item_scroll_view.xml │ ├── menu │ ├── demo.xml │ ├── menu_in_scroll_view.xml │ ├── menu_with_custom_action_button.xml │ ├── menu_with_custom_animation.xml │ ├── menu_with_fab.xml │ └── sample.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Java ### 4 | *.class 5 | 6 | # Mobile Tools for Java (J2ME) 7 | .mtj.tmp/ 8 | 9 | # Package Files # 10 | !gradle/*.jar 11 | *.war 12 | *.ear 13 | 14 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 15 | hs_err_pid* 16 | 17 | 18 | ### Android ### 19 | # Built application files 20 | *.apk 21 | *.ap_ 22 | 23 | # Files for the Dalvik VM 24 | *.dex 25 | 26 | # Java class files 27 | *.class 28 | 29 | # Generated files 30 | bin/ 31 | gen/ 32 | 33 | # Gradle files 34 | .gradle/ 35 | build/ 36 | 37 | # Local configuration file (sdk path, etc) 38 | local.properties 39 | 40 | # Proguard folder generated by Eclipse 41 | proguard/ 42 | 43 | #Log Files 44 | *.log 45 | 46 | 47 | ### Maven ### 48 | target/ 49 | pom.xml.tag 50 | pom.xml.releaseBackup 51 | pom.xml.versionsBackup 52 | pom.xml.next 53 | release.properties 54 | 55 | 56 | ### Intellij ### 57 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 58 | 59 | ## Directory-based project format 60 | .idea/ 61 | # if you remove the above rule, at least ignore user-specific stuff: 62 | # .idea/workspace.xml 63 | # .idea/tasks.xml 64 | # and these sensitive or high-churn files: 65 | # .idea/dataSources.ids 66 | # .idea/dataSources.xml 67 | # .idea/sqlDataSources.xml 68 | # .idea/dynamic.xml 69 | 70 | ## File-based project format 71 | *.ipr 72 | *.iml 73 | *.iws 74 | 75 | ## Additional for IntelliJ 76 | out/ 77 | 78 | # generated by mpeltonen/sbt-idea plugin 79 | .idea_modules/ 80 | 81 | # generated by JIRA plugin 82 | atlassian-ide-plugin.xml 83 | 84 | # generated by Crashlytics plugin (for Android Studio and Intellij) 85 | com_crashlytics_export_strings.xml 86 | 87 | 88 | ### OSX ### 89 | .DS_Store 90 | .AppleDouble 91 | .LSOverride 92 | 93 | # Icon must end with two \r 94 | Icon 95 | 96 | 97 | # Thumbnails 98 | ._* 99 | 100 | # Files that might appear on external disk 101 | .Spotlight-V100 102 | .Trashes 103 | 104 | # Directories potentially created on remote AFP share 105 | .AppleDB 106 | .AppleDesktop 107 | Network Trash Folder 108 | Temporary Items 109 | .apdisk 110 | 111 | 112 | ### Gradle ### 113 | .gradle 114 | build/ 115 | 116 | # Ignore Gradle GUI config 117 | gradle-app.setting 118 | 119 | 120 | gradle.properties 121 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Oğuz Bilgener 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CircularFloatingActionMenu 2 | 3 | 4 | 5 | An animated, customizable circular floating menu for Android, inspired by Path app. 6 | 7 | ## Getting Started 8 | ### Requirements 9 | - API >= 15 10 | 11 | ### Installation 12 | Grab the AAR from Maven Central by adding it as a dependency in your **build.gradle** file: 13 | 14 | ```groovy 15 | dependencies { 16 | compile 'com.oguzdev:CircularFloatingActionMenu:1.0.2' 17 | } 18 | ``` 19 | Alternatively, clone the repo and add `library` as a module to your project. 20 | 21 | ### Usage 22 | CircularFloatingActionMenu can be attached to **any view** in your layout. A Floating Action Button implementation is available in the library, with a similar look to new Material Design's FAB. 23 | 24 | 1 - Create a button to attach the menu: 25 | 26 | ```java 27 | // in Activity Context 28 | ImageView icon = new ImageView(this); // Create an icon 29 | icon.setImageDrawable( ... ); 30 | 31 | FloatingActionButton actionButton = new FloatingActionButton.Builder(this) 32 | .setContentView(icon) 33 | .build(); 34 | 35 | ``` 36 | 37 | 2 - Create menu items: 38 | 39 | ```java 40 | SubActionButton.Builder itemBuilder = new SubActionButton.Builder(this); 41 | // repeat many times: 42 | ImageView itemIcon = new ImageView(this); 43 | itemIcon.setImageDrawable( ... ); 44 | SubActionButton button1 = itemBuilder.setContentView(itemIcon).build(); 45 | 46 | ``` 47 | 48 | 3 - Create the menu with the items: 49 | 50 | ```java 51 | FloatingActionMenu actionMenu = new FloatingActionMenu.Builder(this) 52 | .addSubActionView(button1) 53 | .addSubActionView(button2) 54 | // ... 55 | .attachTo(actionButton) 56 | .build(); 57 | ``` 58 | And you're ready to go! 59 | 60 | ## Customization 61 | **Animations**, **start angle**, **end angle** and **radius** are customizable via `FloatingActionMenu.Builder`. 62 | 63 | `FloatingActionMenu` is the essential class for the menu. Other two classes, `FloatingActionButton` and `SubActionButton` are just **views** and they can be replaced with any other view. You are completely free to create your own menu button and item views. 64 | 65 | Existing `FloatingActionButton` and `SubActionButton` views are customizable too. These parameters can be changed via Builders of both classes: 66 | 67 | - Theme (Light / Dark) 68 | - Background drawable 69 | - LayoutParams (width & height) 70 | - Content View 71 | 72 | `FloatingActionButton` can be placed to one of **8 predefined positions** on the screen. To place it somewhere else, extend it! 73 | 74 | 75 | 76 | 77 | ### Custom Animations 78 | You can write your own animation handler class by extending from `MenuAnimationHandler` to completely customize menu opening and closing animations. 79 | 80 | Then all you need is to create an instance of your custom animation handler and pass it to `FloatingActionMenu.Builder` via `setAnimationHandler( )` method. 81 | 82 | See `CustomAnimationHandler` in **samples** module for a sample animation handler. 83 | 84 | ## Licence 85 | CircularFloatingActionMenu is released under MIT Licence. See file LICENCE. 86 | -------------------------------------------------------------------------------- /assets/circularfab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/assets/circularfab.gif -------------------------------------------------------------------------------- /assets/customViews.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/assets/customViews.png -------------------------------------------------------------------------------- /assets/customizableButtons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/assets/customizableButtons.png -------------------------------------------------------------------------------- /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 | mavenCentral() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:1.0.0' 10 | 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 | -------------------------------------------------------------------------------- /gradle-mvn-push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | def isReleaseBuild() { 21 | return VERSION_NAME.contains("SNAPSHOT") == false 22 | } 23 | 24 | def getReleaseRepositoryUrl() { 25 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 26 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 27 | } 28 | 29 | def getSnapshotRepositoryUrl() { 30 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 31 | : "https://oss.sonatype.org/content/repositories/snapshots/" 32 | } 33 | 34 | def getRepositoryUsername() { 35 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 36 | } 37 | 38 | def getRepositoryPassword() { 39 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 40 | } 41 | 42 | afterEvaluate { project -> 43 | uploadArchives { 44 | repositories { 45 | mavenDeployer { 46 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 47 | 48 | pom.groupId = GROUP 49 | pom.artifactId = POM_ARTIFACT_ID 50 | pom.version = VERSION_NAME 51 | 52 | repository(url: getReleaseRepositoryUrl()) { 53 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 54 | } 55 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 56 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 57 | } 58 | 59 | pom.project { 60 | name POM_NAME 61 | packaging POM_PACKAGING 62 | description POM_DESCRIPTION 63 | url POM_URL 64 | 65 | scm { 66 | url POM_SCM_URL 67 | connection POM_SCM_CONNECTION 68 | developerConnection POM_SCM_DEV_CONNECTION 69 | } 70 | 71 | licenses { 72 | license { 73 | name POM_LICENCE_NAME 74 | url POM_LICENCE_URL 75 | distribution POM_LICENCE_DIST 76 | } 77 | } 78 | 79 | developers { 80 | developer { 81 | id POM_DEVELOPER_ID 82 | name POM_DEVELOPER_NAME 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | signing { 91 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 92 | sign configurations.archives 93 | } 94 | 95 | task androidJavadocs(type: Javadoc) { 96 | source = android.sourceSets.main.java.getSrcDirs() // source = android.sourceSets.main.allJava 97 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 98 | } 99 | 100 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 101 | classifier = 'javadoc' 102 | from androidJavadocs.destinationDir 103 | } 104 | 105 | task androidSourcesJar(type: Jar) { 106 | classifier = 'sources' 107 | from android.sourceSets.main.java // from android.sourceSets.main.allSource 108 | } 109 | 110 | artifacts { 111 | archives androidSourcesJar 112 | archives androidJavadocsJar 113 | } 114 | } -------------------------------------------------------------------------------- /gradle.properties.sample: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 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 | 20 | VERSION_NAME=1.0 21 | VERSION_CODE=1 22 | GROUP=com.oguzdev 23 | POM_DESCRIPTION=CircularFloatingActionButton 24 | POM_URL=https://github.com/oguzbilgener/CircularFloatingActionMenu 25 | POM_SCM_URL=https://github.com/oguzbilgener/CircularFloatingActionMenu 26 | POM_SCM_CONNECTION=scm:git@github.com:oguzbilgener/CircularFloatingActionMenu.git 27 | POM_SCM_DEV_CONNECTION=scm:git@github.com:oguzbilgener/CircularFloatingActionMenu.git 28 | POM_LICENCE_NAME=The MIT Licence (MIT) 29 | POM_LICENCE_URL=http://opensource.org/licenses/MIT 30 | POM_LICENCE_DIST=repo 31 | POM_DEVELOPER_ID=oguzbilgener 32 | POM_DEVELOPER_NAME=Oguz Bilgener -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-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 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "21.1.1" 6 | 7 | defaultConfig { 8 | // applicationId "com.oguzdev.circularfloatingactionmenu.library" 9 | minSdkVersion 15 10 | targetSdkVersion 21 11 | versionCode 3 12 | versionName "1.0.2" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | repositories { 23 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } 24 | mavenCentral() 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | } 30 | 31 | // apply from: '../gradle-mvn-push.gradle' 32 | -------------------------------------------------------------------------------- /library/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 /Applications/Android Studio.app/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 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/oguzdev/circularfloatingactionmenu/library/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.library; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/java/com/oguzdev/circularfloatingactionmenu/library/FloatingActionButton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Oguz Bilgener 3 | */ 4 | package com.oguzdev.circularfloatingactionmenu.library; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.graphics.PixelFormat; 9 | import android.graphics.drawable.Drawable; 10 | import android.os.Build; 11 | import android.view.Gravity; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.WindowManager; 15 | import android.widget.FrameLayout; 16 | 17 | /** 18 | * An alternative Floating Action Button implementation that can be independently placed in 19 | * one of 8 different places on the screen. 20 | */ 21 | public class FloatingActionButton extends FrameLayout { 22 | 23 | public static final int THEME_LIGHT = 0; 24 | public static final int THEME_DARK = 1; 25 | 26 | public static final int POSITION_TOP_CENTER = 1; 27 | public static final int POSITION_TOP_RIGHT = 2; 28 | public static final int POSITION_RIGHT_CENTER = 3; 29 | public static final int POSITION_BOTTOM_RIGHT = 4; 30 | public static final int POSITION_BOTTOM_CENTER = 5; 31 | public static final int POSITION_BOTTOM_LEFT = 6; 32 | public static final int POSITION_LEFT_CENTER = 7; 33 | public static final int POSITION_TOP_LEFT = 8; 34 | 35 | private View contentView; 36 | 37 | private boolean systemOverlay; 38 | 39 | /** 40 | * Constructor that takes parameters collected using {@link FloatingActionMenu.Builder} 41 | * @param context a reference to the current context 42 | * @param layoutParams 43 | * @param theme 44 | * @param backgroundDrawable 45 | * @param position 46 | * @param contentView 47 | * @param contentParams 48 | */ 49 | public FloatingActionButton(Context context, ViewGroup.LayoutParams layoutParams, int theme, 50 | Drawable backgroundDrawable, int position, View contentView, 51 | FrameLayout.LayoutParams contentParams, 52 | boolean systemOverlay) { 53 | super(context); 54 | this.systemOverlay = systemOverlay; 55 | 56 | if(!systemOverlay && !(context instanceof Activity)) { 57 | throw new RuntimeException("Given context must be an instance of Activity, " 58 | +"since this FAB is not a systemOverlay."); 59 | } 60 | 61 | setPosition(position, layoutParams); 62 | 63 | // If no custom backgroundDrawable is specified, use the background drawable of the theme. 64 | if(backgroundDrawable == null) { 65 | if(theme == THEME_LIGHT) 66 | backgroundDrawable = context.getResources().getDrawable(R.drawable.button_action_selector); 67 | else 68 | backgroundDrawable = context.getResources().getDrawable(R.drawable.button_action_dark_selector); 69 | } 70 | setBackgroundResource(backgroundDrawable); 71 | if(contentView != null) { 72 | setContentView(contentView, contentParams); 73 | } 74 | setClickable(true); 75 | 76 | attach(layoutParams); 77 | } 78 | 79 | /** 80 | * Sets the position of the button by calculating its Gravity from the position parameter 81 | * @param position one of 8 specified positions. 82 | * @param layoutParams should be either FrameLayout.LayoutParams or WindowManager.LayoutParams 83 | */ 84 | public void setPosition(int position, ViewGroup.LayoutParams layoutParams) { 85 | 86 | boolean setDefaultMargin = false; 87 | 88 | int gravity; 89 | switch (position) { 90 | case POSITION_TOP_CENTER: 91 | gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; 92 | break; 93 | case POSITION_TOP_RIGHT: 94 | gravity = Gravity.TOP | Gravity.RIGHT; 95 | break; 96 | case POSITION_RIGHT_CENTER: 97 | gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; 98 | break; 99 | case POSITION_BOTTOM_CENTER: 100 | gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; 101 | break; 102 | case POSITION_BOTTOM_LEFT: 103 | gravity = Gravity.BOTTOM | Gravity.LEFT; 104 | break; 105 | case POSITION_LEFT_CENTER: 106 | gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL; 107 | break; 108 | case POSITION_TOP_LEFT: 109 | gravity = Gravity.TOP | Gravity.LEFT; 110 | break; 111 | case POSITION_BOTTOM_RIGHT: 112 | default: 113 | setDefaultMargin = true; 114 | gravity = Gravity.BOTTOM | Gravity.RIGHT; 115 | break; 116 | } 117 | if(!systemOverlay) { 118 | try { 119 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) layoutParams; 120 | lp.gravity = gravity; 121 | setLayoutParams(lp); 122 | } catch (ClassCastException e) { 123 | throw new ClassCastException("layoutParams must be an instance of " + 124 | "FrameLayout.LayoutParams, since this FAB is not a systemOverlay"); 125 | } 126 | } 127 | else { 128 | try { 129 | WindowManager.LayoutParams lp = (WindowManager.LayoutParams) layoutParams; 130 | lp.gravity = gravity; 131 | if(setDefaultMargin) { 132 | int margin = getContext().getResources().getDimensionPixelSize(R.dimen.action_button_margin); 133 | lp.x = margin; 134 | lp.y = margin; 135 | } 136 | setLayoutParams(lp); 137 | } catch(ClassCastException e) { 138 | throw new ClassCastException("layoutParams must be an instance of " + 139 | "WindowManager.LayoutParams, since this FAB is a systemOverlay"); 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * Sets a content view that will be displayed inside this FloatingActionButton. 146 | * @param contentView 147 | */ 148 | public void setContentView(View contentView, FrameLayout.LayoutParams contentParams) { 149 | this.contentView = contentView; 150 | FrameLayout.LayoutParams params; 151 | if(contentParams == null ){ 152 | params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER); 153 | final int margin = getResources().getDimensionPixelSize(R.dimen.action_button_content_margin); 154 | params.setMargins(margin, margin, margin, margin); 155 | } 156 | else { 157 | params = contentParams; 158 | } 159 | params.gravity = Gravity.CENTER; 160 | 161 | contentView.setClickable(false); 162 | this.addView(contentView, params); 163 | } 164 | 165 | /** 166 | * Attaches it to the content view with specified LayoutParams. 167 | * @param layoutParams 168 | */ 169 | public void attach(ViewGroup.LayoutParams layoutParams) { 170 | if(systemOverlay) { 171 | try { 172 | getWindowManager().addView(this, layoutParams); 173 | } 174 | catch(SecurityException e) { 175 | throw new SecurityException("Your application must have SYSTEM_ALERT_WINDOW " + 176 | "permission to create a system window."); 177 | } 178 | } 179 | else { 180 | ((ViewGroup) getActivityContentView()).addView(this, layoutParams); 181 | } 182 | } 183 | 184 | /** 185 | * Detaches it from the container view. 186 | */ 187 | public void detach() { 188 | if(systemOverlay) { 189 | getWindowManager().removeView(this); 190 | } 191 | else { 192 | ((ViewGroup) getActivityContentView()).removeView(this); 193 | } 194 | } 195 | 196 | /** 197 | * Finds and returns the main content view from the Activity context. 198 | * @return the main content view 199 | */ 200 | public View getActivityContentView() { 201 | try { 202 | return ((Activity) getContext()).getWindow().getDecorView().findViewById(android.R.id.content); 203 | } 204 | catch(ClassCastException e) { 205 | throw new ClassCastException("Please provide an Activity context for this FloatingActionButton."); 206 | } 207 | } 208 | 209 | public WindowManager getWindowManager() { 210 | return (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 211 | } 212 | 213 | private void setBackgroundResource(Drawable drawable) { 214 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 215 | setBackground(drawable); 216 | } 217 | else { 218 | setBackgroundDrawable(drawable); 219 | } 220 | } 221 | 222 | /** 223 | * A builder for {@link FloatingActionButton} in conventional Java Builder format 224 | */ 225 | public static class Builder { 226 | 227 | private Context context; 228 | private ViewGroup.LayoutParams layoutParams; 229 | private int theme; 230 | private Drawable backgroundDrawable; 231 | private int position; 232 | private View contentView; 233 | private LayoutParams contentParams; 234 | private boolean systemOverlay; 235 | 236 | public Builder(Context context) { 237 | this.context = context; 238 | 239 | // Default FloatingActionButton settings 240 | int size = context.getResources().getDimensionPixelSize(R.dimen.action_button_size); 241 | int margin = context.getResources().getDimensionPixelSize(R.dimen.action_button_margin); 242 | FrameLayout.LayoutParams layoutParams = new LayoutParams(size, size, Gravity.BOTTOM | Gravity.RIGHT); 243 | layoutParams.setMargins(margin, margin, margin, margin); 244 | setLayoutParams(layoutParams); 245 | setTheme(FloatingActionButton.THEME_LIGHT); 246 | setPosition(FloatingActionButton.POSITION_BOTTOM_RIGHT); 247 | setSystemOverlay(false); 248 | } 249 | 250 | public Builder setLayoutParams(ViewGroup.LayoutParams params) { 251 | this.layoutParams = params; 252 | return this; 253 | } 254 | 255 | public Builder setTheme(int theme) { 256 | this.theme = theme; 257 | return this; 258 | } 259 | 260 | public Builder setBackgroundDrawable(Drawable backgroundDrawable) { 261 | this.backgroundDrawable = backgroundDrawable; 262 | return this; 263 | } 264 | 265 | public Builder setBackgroundDrawable(int drawableId) { 266 | return setBackgroundDrawable(context.getResources().getDrawable(drawableId)); 267 | } 268 | 269 | public Builder setPosition(int position) { 270 | this.position = position; 271 | return this; 272 | } 273 | 274 | public Builder setContentView(View contentView) { 275 | return setContentView(contentView, null); 276 | } 277 | 278 | public Builder setContentView(View contentView, LayoutParams contentParams) { 279 | this.contentView = contentView; 280 | this.contentParams = contentParams; 281 | return this; 282 | } 283 | 284 | public Builder setSystemOverlay(boolean systemOverlay) { 285 | this.systemOverlay = systemOverlay; 286 | return this; 287 | } 288 | 289 | public FloatingActionButton build() { 290 | return new FloatingActionButton(context, 291 | layoutParams, 292 | theme, 293 | backgroundDrawable, 294 | position, 295 | contentView, 296 | contentParams, 297 | systemOverlay); 298 | } 299 | 300 | public static WindowManager.LayoutParams getDefaultSystemWindowParams(Context context) { 301 | int size = context.getResources().getDimensionPixelSize(R.dimen.action_button_size); 302 | WindowManager.LayoutParams params = new WindowManager.LayoutParams( 303 | size, 304 | size, 305 | WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, // z-ordering 306 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 307 | PixelFormat.TRANSLUCENT); 308 | params.format = PixelFormat.RGBA_8888; 309 | params.gravity = Gravity.TOP | Gravity.LEFT; 310 | return params; 311 | } 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /library/src/main/java/com/oguzdev/circularfloatingactionmenu/library/FloatingActionMenu.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Oguz Bilgener 3 | */ 4 | package com.oguzdev.circularfloatingactionmenu.library; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.graphics.Path; 9 | import android.graphics.PathMeasure; 10 | import android.graphics.PixelFormat; 11 | import android.graphics.Point; 12 | import android.graphics.Rect; 13 | import android.graphics.RectF; 14 | import android.hardware.SensorManager; 15 | import android.view.Display; 16 | import android.view.Gravity; 17 | import android.view.LayoutInflater; 18 | import android.view.OrientationEventListener; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.view.WindowManager; 22 | import android.widget.FrameLayout; 23 | 24 | import com.oguzdev.circularfloatingactionmenu.library.animation.DefaultAnimationHandler; 25 | import com.oguzdev.circularfloatingactionmenu.library.animation.MenuAnimationHandler; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | /** 31 | * Provides the main structure of the menu. 32 | */ 33 | 34 | public class FloatingActionMenu { 35 | 36 | /** Reference to the view (usually a button) to trigger the menu to show */ 37 | private View mainActionView; 38 | /** The angle (in degrees, modulus 360) which the circular menu starts from */ 39 | private int startAngle; 40 | /** The angle (in degrees, modulus 360) which the circular menu ends at */ 41 | private int endAngle; 42 | /** Distance of menu items from mainActionView */ 43 | private int radius; 44 | /** List of menu items */ 45 | private List subActionItems; 46 | /** Reference to the preferred {@link MenuAnimationHandler} object */ 47 | private MenuAnimationHandler animationHandler; 48 | /** Reference to a listener that listens open/close actions */ 49 | private MenuStateChangeListener stateChangeListener; 50 | /** whether the openings and closings should be animated or not */ 51 | private boolean animated; 52 | /** whether the menu is currently open or not */ 53 | private boolean open; 54 | /** whether the menu is an overlay for all other activities */ 55 | private boolean systemOverlay; 56 | /** a simple layout to contain all the sub action views in the system overlay mode */ 57 | private FrameLayout overlayContainer; 58 | 59 | private OrientationEventListener orientationListener; 60 | 61 | /** 62 | * Constructor that takes the parameters collected using {@link FloatingActionMenu.Builder} 63 | * @param mainActionView 64 | * @param startAngle 65 | * @param endAngle 66 | * @param radius 67 | * @param subActionItems 68 | * @param animationHandler 69 | * @param animated 70 | */ 71 | public FloatingActionMenu(final View mainActionView, 72 | int startAngle, 73 | int endAngle, 74 | int radius, 75 | List subActionItems, 76 | MenuAnimationHandler animationHandler, 77 | boolean animated, 78 | MenuStateChangeListener stateChangeListener, 79 | final boolean systemOverlay) { 80 | this.mainActionView = mainActionView; 81 | this.startAngle = startAngle; 82 | this.endAngle = endAngle; 83 | this.radius = radius; 84 | this.subActionItems = subActionItems; 85 | this.animationHandler = animationHandler; 86 | this.animated = animated; 87 | this.systemOverlay = systemOverlay; 88 | // The menu is initially closed. 89 | this.open = false; 90 | 91 | this.stateChangeListener = stateChangeListener; 92 | 93 | // Listen click events on the main action view 94 | // In the future, touch and drag events could be listened to offer an alternative behaviour 95 | this.mainActionView.setClickable(true); 96 | this.mainActionView.setOnClickListener(new ActionViewClickListener()); 97 | 98 | // Do not forget to set the menu as self to our customizable animation handler 99 | if(animationHandler != null) { 100 | animationHandler.setMenu(this); 101 | } 102 | 103 | if(systemOverlay) { 104 | overlayContainer = new FrameLayout(mainActionView.getContext()); 105 | } 106 | else { 107 | overlayContainer = null; // beware NullPointerExceptions! 108 | } 109 | 110 | // Find items with undefined sizes 111 | for(final Item item : subActionItems) { 112 | if(item.width == 0 || item.height == 0) { 113 | if(systemOverlay) { 114 | throw new RuntimeException("Sub action views cannot be added without " + 115 | "definite width and height."); 116 | } 117 | // Figure out the size by temporarily adding it to the Activity content view hierarchy 118 | // and ask the size from the system 119 | addViewToCurrentContainer(item.view); 120 | // Make item view invisible, just in case 121 | item.view.setAlpha(0); 122 | // Wait for the right time 123 | item.view.post(new ItemViewQueueListener(item)); 124 | } 125 | } 126 | 127 | if(systemOverlay) { 128 | orientationListener = new OrientationEventListener(mainActionView.getContext(), SensorManager.SENSOR_DELAY_UI) { 129 | private int lastState = -1; 130 | 131 | public void onOrientationChanged(int orientation) { 132 | 133 | Display display = getWindowManager().getDefaultDisplay(); 134 | if(display.getRotation() != lastState) { 135 | lastState = display.getRotation(); 136 | 137 | // 138 | if(isOpen()) { 139 | close(false); 140 | } 141 | } 142 | } 143 | }; 144 | orientationListener.enable(); 145 | } 146 | } 147 | 148 | /** 149 | * Simply opens the menu by doing necessary calculations. 150 | * @param animated if true, this action is executed by the current {@link MenuAnimationHandler} 151 | */ 152 | public void open(boolean animated) { 153 | 154 | // Get the center of the action view from the following function for efficiency 155 | // populate destination x,y coordinates of Items 156 | Point center = calculateItemPositions(); 157 | 158 | WindowManager.LayoutParams overlayParams = null; 159 | 160 | if(systemOverlay) { 161 | // If this is a system overlay menu, use the overlay container and place it behind 162 | // the main action button so that all the views will be added into it. 163 | attachOverlayContainer(); 164 | 165 | overlayParams = (WindowManager.LayoutParams) overlayContainer.getLayoutParams(); 166 | } 167 | 168 | if(animated && animationHandler != null) { 169 | // If animations are enabled and we have a MenuAnimationHandler, let it do the heavy work 170 | if(animationHandler.isAnimating()) { 171 | // Do not proceed if there is an animation currently going on. 172 | return; 173 | } 174 | 175 | for (int i = 0; i < subActionItems.size(); i++) { 176 | // It is required that these Item views are not currently added to any parent 177 | // Because they are supposed to be added to the Activity content view, 178 | // just before the animation starts 179 | if (subActionItems.get(i).view.getParent() != null) { 180 | throw new RuntimeException("All of the sub action items have to be independent from a parent."); 181 | } 182 | 183 | // Initially, place all items right at the center of the main action view 184 | // Because they are supposed to start animating from that point. 185 | final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(subActionItems.get(i).width, subActionItems.get(i).height, Gravity.TOP | Gravity.LEFT); 186 | 187 | if(systemOverlay) { 188 | params.setMargins(center.x - overlayParams.x - subActionItems.get(i).width / 2, center.y - overlayParams.y - subActionItems.get(i).height / 2, 0, 0); 189 | } 190 | else { 191 | params.setMargins(center.x - subActionItems.get(i).width / 2, center.y - subActionItems.get(i).height / 2, 0, 0); 192 | } 193 | addViewToCurrentContainer(subActionItems.get(i).view, params); 194 | } 195 | // Tell the current MenuAnimationHandler to animate from the center 196 | animationHandler.animateMenuOpening(center); 197 | } 198 | else { 199 | // If animations are disabled, just place each of the items to their calculated destination positions. 200 | for (int i = 0; i < subActionItems.size(); i++) { 201 | // This is currently done by giving them large margins 202 | 203 | final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(subActionItems.get(i).width, subActionItems.get(i).height, Gravity.TOP | Gravity.LEFT); 204 | if(systemOverlay) { 205 | params.setMargins(subActionItems.get(i).x - overlayParams.x, subActionItems.get(i).y - overlayParams.y, 0, 0); 206 | subActionItems.get(i).view.setLayoutParams(params); 207 | } 208 | else { 209 | params.setMargins(subActionItems.get(i).x, subActionItems.get(i).y, 0, 0); 210 | subActionItems.get(i).view.setLayoutParams(params); 211 | // Because they are placed into the main content view of the Activity, 212 | // which is itself a FrameLayout 213 | } 214 | addViewToCurrentContainer(subActionItems.get(i).view, params); 215 | } 216 | } 217 | // do not forget to specify that the menu is open. 218 | open = true; 219 | 220 | if(stateChangeListener != null) { 221 | stateChangeListener.onMenuOpened(this); 222 | } 223 | 224 | } 225 | 226 | /** 227 | * Closes the menu. 228 | * @param animated if true, this action is executed by the current {@link MenuAnimationHandler} 229 | */ 230 | public void close(boolean animated) { 231 | // If animations are enabled and we have a MenuAnimationHandler, let it do the heavy work 232 | if(animated && animationHandler != null) { 233 | if(animationHandler.isAnimating()) { 234 | // Do not proceed if there is an animation currently going on. 235 | return; 236 | } 237 | animationHandler.animateMenuClosing(getActionViewCenter()); 238 | } 239 | else { 240 | // If animations are disabled, just detach each of the Item views from the Activity content view. 241 | for (int i = 0; i < subActionItems.size(); i++) { 242 | removeViewFromCurrentContainer(subActionItems.get(i).view); 243 | } 244 | detachOverlayContainer(); 245 | } 246 | // do not forget to specify that the menu is now closed. 247 | open = false; 248 | 249 | if(stateChangeListener != null) { 250 | stateChangeListener.onMenuClosed(this); 251 | } 252 | } 253 | 254 | /** 255 | * Toggles the menu 256 | * @param animated if true, the open/close action is executed by the current {@link MenuAnimationHandler} 257 | */ 258 | public void toggle(boolean animated) { 259 | if(open) { 260 | close(animated); 261 | } 262 | else { 263 | open(animated); 264 | } 265 | } 266 | 267 | /** 268 | * @return whether the menu is open or not 269 | */ 270 | public boolean isOpen() { 271 | return open; 272 | } 273 | 274 | /** 275 | * @return whether the menu is a system overlay or not 276 | */ 277 | public boolean isSystemOverlay() { 278 | return systemOverlay; 279 | } 280 | 281 | public FrameLayout getOverlayContainer() { 282 | return overlayContainer; 283 | } 284 | 285 | /** 286 | * Recalculates the positions of each sub action item on demand. 287 | */ 288 | public void updateItemPositions() { 289 | // Only update if the menu is currently open 290 | if(!isOpen()) { 291 | return; 292 | } 293 | // recalculate x,y coordinates of Items 294 | calculateItemPositions(); 295 | 296 | // Simply update layout params for each item 297 | for (int i = 0; i < subActionItems.size(); i++) { 298 | // This is currently done by giving them large margins 299 | final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(subActionItems.get(i).width, subActionItems.get(i).height, Gravity.TOP | Gravity.LEFT); 300 | params.setMargins(subActionItems.get(i).x, subActionItems.get(i).y, 0, 0); 301 | subActionItems.get(i).view.setLayoutParams(params); 302 | } 303 | } 304 | 305 | /** 306 | * Gets the coordinates of the main action view 307 | * This method should only be called after the main layout of the Activity is drawn, 308 | * such as when a user clicks the action button. 309 | * @return a Point containing x and y coordinates of the top left corner of action view 310 | */ 311 | private Point getActionViewCoordinates() { 312 | int[] coords = new int[2]; 313 | // This method returns a x and y values that can be larger than the dimensions of the device screen. 314 | mainActionView.getLocationOnScreen(coords); 315 | 316 | // So, we need to deduce the offsets. 317 | if(systemOverlay) { 318 | coords[1] -= getStatusBarHeight(); 319 | } 320 | else { 321 | Rect activityFrame = new Rect(); 322 | getActivityContentView().getWindowVisibleDisplayFrame(activityFrame); 323 | coords[0] -= (getScreenSize().x - getActivityContentView().getMeasuredWidth()); 324 | coords[1] -= (activityFrame.height() + activityFrame.top - getActivityContentView().getMeasuredHeight()); 325 | } 326 | return new Point(coords[0], coords[1]); 327 | } 328 | 329 | /** 330 | * Returns the center point of the main action view 331 | * @return the action view center point 332 | */ 333 | public Point getActionViewCenter() { 334 | Point point = getActionViewCoordinates(); 335 | point.x += mainActionView.getMeasuredWidth() / 2; 336 | point.y += mainActionView.getMeasuredHeight() / 2; 337 | return point; 338 | } 339 | 340 | /** 341 | * Calculates the desired positions of all items. 342 | * @return getActionViewCenter() 343 | */ 344 | private Point calculateItemPositions() { 345 | // Create an arc that starts from startAngle and ends at endAngle 346 | // in an area that is as large as 4*radius^2 347 | final Point center = getActionViewCenter(); 348 | RectF area = new RectF(center.x - radius, center.y - radius, center.x + radius, center.y + radius); 349 | 350 | Path orbit = new Path(); 351 | orbit.addArc(area, startAngle, endAngle - startAngle); 352 | 353 | PathMeasure measure = new PathMeasure(orbit, false); 354 | 355 | // Prevent overlapping when it is a full circle 356 | int divisor; 357 | if(Math.abs(endAngle - startAngle) >= 360 || subActionItems.size() <= 1) { 358 | divisor = subActionItems.size(); 359 | } 360 | else { 361 | divisor = subActionItems.size() -1; 362 | } 363 | 364 | // Measure this path, in order to find points that have the same distance between each other 365 | for(int i=0; i getSubActionItems() { 386 | return subActionItems; 387 | } 388 | 389 | /** 390 | * Finds and returns the main content view from the Activity context. 391 | * @return the main content view 392 | */ 393 | public View getActivityContentView() { 394 | try { 395 | return ((Activity) mainActionView.getContext()).getWindow().getDecorView().findViewById(android.R.id.content); 396 | } 397 | catch(ClassCastException e) { 398 | throw new ClassCastException("Please provide an Activity context for this FloatingActionMenu."); 399 | } 400 | } 401 | 402 | /** 403 | * Intended to use for systemOverlay mode. 404 | * @return the WindowManager for the current context. 405 | */ 406 | public WindowManager getWindowManager() { 407 | return (WindowManager) mainActionView.getContext().getSystemService(Context.WINDOW_SERVICE); 408 | } 409 | 410 | private void addViewToCurrentContainer(View view, ViewGroup.LayoutParams layoutParams) { 411 | if(systemOverlay) { 412 | overlayContainer.addView(view, layoutParams); 413 | } 414 | else { 415 | try { 416 | if(layoutParams != null) { 417 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) layoutParams; 418 | ((ViewGroup) getActivityContentView()).addView(view, lp); 419 | } 420 | else { 421 | ((ViewGroup) getActivityContentView()).addView(view); 422 | } 423 | } 424 | catch(ClassCastException e) { 425 | throw new ClassCastException("layoutParams must be an instance of " + 426 | "FrameLayout.LayoutParams."); 427 | } 428 | } 429 | } 430 | 431 | public void attachOverlayContainer() { 432 | try { 433 | WindowManager.LayoutParams overlayParams = calculateOverlayContainerParams(); 434 | 435 | overlayContainer.setLayoutParams(overlayParams); 436 | if(overlayContainer.getParent() == null) { 437 | getWindowManager().addView(overlayContainer, overlayParams); 438 | } 439 | getWindowManager().updateViewLayout(mainActionView, mainActionView.getLayoutParams()); 440 | } 441 | catch(SecurityException e) { 442 | throw new SecurityException("Your application must have SYSTEM_ALERT_WINDOW " + 443 | "permission to create a system window."); 444 | } 445 | } 446 | 447 | private WindowManager.LayoutParams calculateOverlayContainerParams() { 448 | // calculate the minimum viable size of overlayContainer 449 | WindowManager.LayoutParams overlayParams = getDefaultSystemWindowParams(); 450 | int left = 9999, right = 0, top = 9999, bottom = 0; 451 | for(int i=0; i < subActionItems.size(); i++) { 452 | int lm = subActionItems.get(i).x; 453 | int tm = subActionItems.get(i).y; 454 | 455 | if(lm < left) { 456 | left = lm; 457 | } 458 | if(tm < top) { 459 | top = tm; 460 | } 461 | if(lm + subActionItems.get(i).width > right) { 462 | right = lm + subActionItems.get(i).width; 463 | } 464 | if(tm + subActionItems.get(i).height > bottom) { 465 | bottom = tm + subActionItems.get(i).height; 466 | } 467 | } 468 | overlayParams.width = right - left; 469 | overlayParams.height = bottom - top; 470 | overlayParams.x = left; 471 | overlayParams.y = top; 472 | overlayParams.gravity = Gravity.TOP | Gravity.LEFT; 473 | return overlayParams; 474 | } 475 | 476 | public void detachOverlayContainer() { 477 | getWindowManager().removeView(overlayContainer); 478 | } 479 | 480 | public int getStatusBarHeight() { 481 | int result = 0; 482 | int resourceId = mainActionView.getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); 483 | if (resourceId > 0) { 484 | result = mainActionView.getContext().getResources().getDimensionPixelSize(resourceId); 485 | } 486 | return result; 487 | } 488 | 489 | public void addViewToCurrentContainer(View view) { 490 | addViewToCurrentContainer(view, null); 491 | } 492 | 493 | public void removeViewFromCurrentContainer(View view) { 494 | if(systemOverlay) { 495 | overlayContainer.removeView(view); 496 | } 497 | else { 498 | ((ViewGroup)getActivityContentView()).removeView(view); 499 | } 500 | } 501 | 502 | /** 503 | * Retrieves the screen size from the Activity context 504 | * @return the screen size as a Point object 505 | */ 506 | private Point getScreenSize() { 507 | Point size = new Point(); 508 | getWindowManager().getDefaultDisplay().getSize(size); 509 | return size; 510 | } 511 | 512 | public void setStateChangeListener(MenuStateChangeListener listener) { 513 | this.stateChangeListener = listener; 514 | } 515 | 516 | /** 517 | * A simple click listener used by the main action view 518 | */ 519 | public class ActionViewClickListener implements View.OnClickListener { 520 | 521 | @Override 522 | public void onClick(View v) { 523 | toggle(animated); 524 | } 525 | } 526 | 527 | /** 528 | * This runnable calculates sizes of Item views that are added to the menu. 529 | */ 530 | private class ItemViewQueueListener implements Runnable { 531 | 532 | private static final int MAX_TRIES = 10; 533 | private Item item; 534 | private int tries; 535 | 536 | public ItemViewQueueListener(Item item) { 537 | this.item = item; 538 | this.tries = 0; 539 | } 540 | 541 | @Override 542 | public void run() { 543 | // Wait until the the view can be measured but do not push too hard. 544 | if(item.view.getMeasuredWidth() == 0 && tries < MAX_TRIES) { 545 | item.view.post(this); 546 | return; 547 | } 548 | // Measure the size of the item view 549 | item.width = item.view.getMeasuredWidth(); 550 | item.height = item.view.getMeasuredHeight(); 551 | 552 | // Revert everything back to normal 553 | item.view.setAlpha(item.alpha); 554 | // Remove the item view from view hierarchy 555 | removeViewFromCurrentContainer(item.view); 556 | } 557 | } 558 | 559 | /** 560 | * A simple structure to put a view and its x, y, width and height values together 561 | */ 562 | public static class Item { 563 | public int x; 564 | public int y; 565 | public int width; 566 | public int height; 567 | 568 | public float alpha; 569 | 570 | public View view; 571 | 572 | public Item(View view, int width, int height) { 573 | this.view = view; 574 | this.width = width; 575 | this.height = height; 576 | alpha = view.getAlpha(); 577 | x = 0; 578 | y = 0; 579 | } 580 | } 581 | 582 | /** 583 | * A listener to listen open/closed state changes of the Menu 584 | */ 585 | public static interface MenuStateChangeListener { 586 | public void onMenuOpened(FloatingActionMenu menu); 587 | public void onMenuClosed(FloatingActionMenu menu); 588 | } 589 | 590 | /** 591 | * A builder for {@link FloatingActionMenu} in conventional Java Builder format 592 | */ 593 | public static class Builder { 594 | 595 | private int startAngle; 596 | private int endAngle; 597 | private int radius; 598 | private View actionView; 599 | private List subActionItems; 600 | private MenuAnimationHandler animationHandler; 601 | private boolean animated; 602 | private MenuStateChangeListener stateChangeListener; 603 | private boolean systemOverlay; 604 | 605 | public Builder(Context context, boolean systemOverlay) { 606 | subActionItems = new ArrayList(); 607 | // Default settings 608 | radius = context.getResources().getDimensionPixelSize(R.dimen.action_menu_radius); 609 | startAngle = 180; 610 | endAngle = 270; 611 | animationHandler = new DefaultAnimationHandler(); 612 | animated = true; 613 | this.systemOverlay = systemOverlay; 614 | } 615 | 616 | public Builder(Context context) { 617 | this(context, false); 618 | } 619 | 620 | public Builder setStartAngle(int startAngle) { 621 | this.startAngle = startAngle; 622 | return this; 623 | } 624 | 625 | public Builder setEndAngle(int endAngle) { 626 | this.endAngle = endAngle; 627 | return this; 628 | } 629 | 630 | public Builder setRadius(int radius) { 631 | this.radius = radius; 632 | return this; 633 | } 634 | 635 | public Builder addSubActionView(View subActionView, int width, int height) { 636 | subActionItems.add(new Item(subActionView, width, height)); 637 | return this; 638 | } 639 | 640 | /** 641 | * Adds a sub action view that is already alive, but not added to a parent View. 642 | * @param subActionView a view for the menu 643 | * @return the builder object itself 644 | */ 645 | public Builder addSubActionView(View subActionView) { 646 | if(systemOverlay) { 647 | throw new RuntimeException("Sub action views cannot be added without " + 648 | "definite width and height. Please use " + 649 | "other methods named addSubActionView"); 650 | } 651 | return this.addSubActionView(subActionView, 0, 0); 652 | } 653 | 654 | /** 655 | * Inflates a new view from the specified resource id and adds it as a sub action view. 656 | * @param resId the resource id reference for the view 657 | * @param context a valid context 658 | * @return the builder object itself 659 | */ 660 | public Builder addSubActionView(int resId, Context context) { 661 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 662 | View view = inflater.inflate(resId, null, false); 663 | view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); 664 | return this.addSubActionView(view, view.getMeasuredWidth(), view.getMeasuredHeight()); 665 | } 666 | 667 | /** 668 | * Sets the current animation handler to the specified MenuAnimationHandler child 669 | * @param animationHandler a MenuAnimationHandler child 670 | * @return the builder object itself 671 | */ 672 | public Builder setAnimationHandler(MenuAnimationHandler animationHandler) { 673 | this.animationHandler = animationHandler; 674 | return this; 675 | } 676 | 677 | public Builder enableAnimations() { 678 | animated = true; 679 | return this; 680 | } 681 | 682 | public Builder disableAnimations() { 683 | animated = false; 684 | return this; 685 | } 686 | 687 | public Builder setStateChangeListener(MenuStateChangeListener listener) { 688 | stateChangeListener = listener; 689 | return this; 690 | } 691 | 692 | public Builder setSystemOverlay(boolean systemOverlay) { 693 | this.systemOverlay = systemOverlay; 694 | return this; 695 | } 696 | 697 | /** 698 | * Attaches the whole menu around a main action view, usually a button. 699 | * All the calculations are made according to this action view. 700 | * @param actionView 701 | * @return the builder object itself 702 | */ 703 | public Builder attachTo(View actionView) { 704 | this.actionView = actionView; 705 | return this; 706 | } 707 | 708 | public FloatingActionMenu build() { 709 | return new FloatingActionMenu(actionView, 710 | startAngle, 711 | endAngle, 712 | radius, 713 | subActionItems, 714 | animationHandler, 715 | animated, 716 | stateChangeListener, 717 | systemOverlay); 718 | } 719 | } 720 | 721 | public static WindowManager.LayoutParams getDefaultSystemWindowParams() { 722 | WindowManager.LayoutParams params = new WindowManager.LayoutParams( 723 | WindowManager.LayoutParams.WRAP_CONTENT, 724 | WindowManager.LayoutParams.WRAP_CONTENT, 725 | WindowManager.LayoutParams.TYPE_PHONE, 726 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 727 | PixelFormat.TRANSLUCENT); 728 | params.format = PixelFormat.RGBA_8888; 729 | params.gravity = Gravity.TOP | Gravity.LEFT; 730 | return params; 731 | } 732 | 733 | } -------------------------------------------------------------------------------- /library/src/main/java/com/oguzdev/circularfloatingactionmenu/library/SubActionButton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Oguz Bilgener 3 | */ 4 | package com.oguzdev.circularfloatingactionmenu.library; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.graphics.drawable.Drawable; 9 | import android.os.Build; 10 | import android.view.Gravity; 11 | import android.view.View; 12 | import android.widget.FrameLayout; 13 | 14 | /** 15 | * A simple button implementation with a similar look an feel to{@link FloatingActionButton}. 16 | */ 17 | public class SubActionButton extends FrameLayout { 18 | 19 | public static final int THEME_LIGHT = 0; 20 | public static final int THEME_DARK = 1; 21 | public static final int THEME_LIGHTER = 2; 22 | public static final int THEME_DARKER = 3; 23 | 24 | public SubActionButton(Context context, FrameLayout.LayoutParams layoutParams, int theme, Drawable backgroundDrawable, View contentView, FrameLayout.LayoutParams contentParams) { 25 | super(context); 26 | setLayoutParams(layoutParams); 27 | // If no custom backgroundDrawable is specified, use the background drawable of the theme. 28 | if(backgroundDrawable == null) { 29 | if(theme == THEME_LIGHT) { 30 | backgroundDrawable = context.getResources().getDrawable(R.drawable.button_sub_action_selector); 31 | } 32 | else if(theme == THEME_DARK) { 33 | backgroundDrawable = context.getResources().getDrawable(R.drawable.button_sub_action_dark_selector); 34 | } 35 | else if(theme == THEME_LIGHTER) { 36 | backgroundDrawable = context.getResources().getDrawable(R.drawable.button_action_selector); 37 | } 38 | else if(theme == THEME_DARKER) { 39 | backgroundDrawable = context.getResources().getDrawable(R.drawable.button_action_dark_selector); 40 | } 41 | else { 42 | throw new RuntimeException("Unknown SubActionButton theme: " + theme); 43 | } 44 | } 45 | else { 46 | backgroundDrawable = backgroundDrawable.mutate().getConstantState().newDrawable(); 47 | } 48 | setBackgroundResource(backgroundDrawable); 49 | if(contentView != null) { 50 | setContentView(contentView, contentParams); 51 | } 52 | setClickable(true); 53 | } 54 | 55 | /** 56 | * Sets a content view with custom LayoutParams that will be displayed inside this SubActionButton. 57 | * @param contentView 58 | * @param params 59 | */ 60 | public void setContentView(View contentView, FrameLayout.LayoutParams params) { 61 | if(params == null) { 62 | params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER); 63 | final int margin = getResources().getDimensionPixelSize(R.dimen.sub_action_button_content_margin); 64 | params.setMargins(margin, margin, margin, margin); 65 | } 66 | 67 | contentView.setClickable(false); 68 | this.addView(contentView, params); 69 | } 70 | 71 | /** 72 | * Sets a content view with default LayoutParams 73 | * @param contentView 74 | */ 75 | public void setContentView(View contentView) { 76 | setContentView(contentView, null); 77 | } 78 | 79 | private void setBackgroundResource(Drawable drawable) { 80 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 81 | setBackground(drawable); 82 | } 83 | else { 84 | setBackgroundDrawable(drawable); 85 | } 86 | } 87 | 88 | /** 89 | * A builder for {@link SubActionButton} in conventional Java Builder format 90 | */ 91 | public static class Builder { 92 | 93 | private Context context; 94 | private FrameLayout.LayoutParams layoutParams; 95 | private int theme; 96 | private Drawable backgroundDrawable; 97 | private View contentView; 98 | private FrameLayout.LayoutParams contentParams; 99 | 100 | public Builder(Context context) { 101 | this.context = context; 102 | 103 | // Default SubActionButton settings 104 | int size = context.getResources().getDimensionPixelSize(R.dimen.sub_action_button_size); 105 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(size, size, Gravity.TOP | Gravity.LEFT); 106 | setLayoutParams(params); 107 | setTheme(SubActionButton.THEME_LIGHT); 108 | } 109 | 110 | public Builder setLayoutParams(FrameLayout.LayoutParams params) { 111 | this.layoutParams = params; 112 | return this; 113 | } 114 | 115 | public Builder setTheme(int theme) { 116 | this.theme = theme; 117 | return this; 118 | } 119 | 120 | public Builder setBackgroundDrawable(Drawable backgroundDrawable) { 121 | this.backgroundDrawable = backgroundDrawable; 122 | return this; 123 | } 124 | 125 | public Builder setContentView(View contentView) { 126 | this.contentView = contentView; 127 | return this; 128 | } 129 | 130 | public Builder setContentView(View contentView, FrameLayout.LayoutParams contentParams) { 131 | this.contentView = contentView; 132 | this.contentParams = contentParams; 133 | return this; 134 | } 135 | 136 | public SubActionButton build() { 137 | return new SubActionButton(context, 138 | layoutParams, 139 | theme, 140 | backgroundDrawable, 141 | contentView, 142 | contentParams); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /library/src/main/java/com/oguzdev/circularfloatingactionmenu/library/animation/DefaultAnimationHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Oguz Bilgener 3 | */ 4 | package com.oguzdev.circularfloatingactionmenu.library.animation; 5 | 6 | import android.animation.Animator; 7 | import android.animation.ObjectAnimator; 8 | import android.animation.PropertyValuesHolder; 9 | import android.graphics.Point; 10 | import android.view.View; 11 | import android.view.animation.AccelerateDecelerateInterpolator; 12 | import android.view.animation.OvershootInterpolator; 13 | 14 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 15 | 16 | /** 17 | * An example animation handler 18 | * Animates translation, rotation, scale and alpha at the same time using Property Animation APIs. 19 | */ 20 | public class DefaultAnimationHandler extends MenuAnimationHandler { 21 | 22 | /** duration of animations, in milliseconds */ 23 | protected static final int DURATION = 500; 24 | /** duration to wait between each of */ 25 | protected static final int LAG_BETWEEN_ITEMS = 20; 26 | /** holds the current state of animation */ 27 | private boolean animating; 28 | 29 | public DefaultAnimationHandler() { 30 | setAnimating(false); 31 | } 32 | 33 | @Override 34 | public void animateMenuOpening(Point center) { 35 | super.animateMenuOpening(center); 36 | 37 | setAnimating(true); 38 | 39 | Animator lastAnimation = null; 40 | for (int i = 0; i < menu.getSubActionItems().size(); i++) { 41 | 42 | menu.getSubActionItems().get(i).view.setScaleX(0); 43 | menu.getSubActionItems().get(i).view.setScaleY(0); 44 | menu.getSubActionItems().get(i).view.setAlpha(0); 45 | 46 | PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, menu.getSubActionItems().get(i).x - center.x + menu.getSubActionItems().get(i).width / 2); 47 | PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, menu.getSubActionItems().get(i).y - center.y + menu.getSubActionItems().get(i).height / 2); 48 | PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, 720); 49 | PropertyValuesHolder pvhsX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1); 50 | PropertyValuesHolder pvhsY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1); 51 | PropertyValuesHolder pvhA = PropertyValuesHolder.ofFloat(View.ALPHA, 1); 52 | 53 | final ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(menu.getSubActionItems().get(i).view, pvhX, pvhY, pvhR, pvhsX, pvhsY, pvhA); 54 | animation.setDuration(DURATION); 55 | animation.setInterpolator(new OvershootInterpolator(0.9f)); 56 | animation.addListener(new SubActionItemAnimationListener(menu.getSubActionItems().get(i), ActionType.OPENING)); 57 | 58 | if(i == 0) { 59 | lastAnimation = animation; 60 | } 61 | 62 | // Put a slight lag between each of the menu items to make it asymmetric 63 | animation.setStartDelay((menu.getSubActionItems().size() - i) * LAG_BETWEEN_ITEMS); 64 | animation.start(); 65 | } 66 | if(lastAnimation != null) { 67 | lastAnimation.addListener(new LastAnimationListener()); 68 | } 69 | 70 | } 71 | 72 | @Override 73 | public void animateMenuClosing(Point center) { 74 | super.animateMenuOpening(center); 75 | 76 | setAnimating(true); 77 | 78 | Animator lastAnimation = null; 79 | for (int i = 0; i < menu.getSubActionItems().size(); i++) { 80 | PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, - (menu.getSubActionItems().get(i).x - center.x + menu.getSubActionItems().get(i).width / 2)); 81 | PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, - (menu.getSubActionItems().get(i).y - center.y + menu.getSubActionItems().get(i).height / 2)); 82 | PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, -720); 83 | PropertyValuesHolder pvhsX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0); 84 | PropertyValuesHolder pvhsY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0); 85 | PropertyValuesHolder pvhA = PropertyValuesHolder.ofFloat(View.ALPHA, 0); 86 | 87 | final ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(menu.getSubActionItems().get(i).view, pvhX, pvhY, pvhR, pvhsX, pvhsY, pvhA); 88 | animation.setDuration(DURATION); 89 | animation.setInterpolator(new AccelerateDecelerateInterpolator()); 90 | animation.addListener(new SubActionItemAnimationListener(menu.getSubActionItems().get(i), ActionType.CLOSING)); 91 | 92 | if(i == 0) { 93 | lastAnimation = animation; 94 | } 95 | 96 | animation.setStartDelay((menu.getSubActionItems().size() - i) * LAG_BETWEEN_ITEMS); 97 | animation.start(); 98 | } 99 | if(lastAnimation != null) { 100 | lastAnimation.addListener(new LastAnimationListener()); 101 | } 102 | } 103 | 104 | @Override 105 | public boolean isAnimating() { 106 | return animating; 107 | } 108 | 109 | @Override 110 | protected void setAnimating(boolean animating) { 111 | this.animating = animating; 112 | } 113 | 114 | protected class SubActionItemAnimationListener implements Animator.AnimatorListener { 115 | 116 | private FloatingActionMenu.Item subActionItem; 117 | private ActionType actionType; 118 | 119 | public SubActionItemAnimationListener(FloatingActionMenu.Item subActionItem, ActionType actionType) { 120 | this.subActionItem = subActionItem; 121 | this.actionType = actionType; 122 | } 123 | 124 | @Override 125 | public void onAnimationStart(Animator animation) { 126 | 127 | } 128 | 129 | @Override 130 | public void onAnimationEnd(Animator animation) { 131 | restoreSubActionViewAfterAnimation(subActionItem, actionType); 132 | } 133 | 134 | @Override 135 | public void onAnimationCancel(Animator animation) { 136 | restoreSubActionViewAfterAnimation(subActionItem, actionType); 137 | } 138 | 139 | @Override public void onAnimationRepeat(Animator animation) {} 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /library/src/main/java/com/oguzdev/circularfloatingactionmenu/library/animation/MenuAnimationHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Oguz Bilgener 3 | */ 4 | package com.oguzdev.circularfloatingactionmenu.library.animation; 5 | 6 | import android.animation.Animator; 7 | import android.graphics.Point; 8 | import android.view.ViewGroup; 9 | import android.view.WindowManager; 10 | import android.widget.FrameLayout; 11 | 12 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 13 | 14 | /** 15 | * An abstract class that is a prototype for the actual animation handlers 16 | */ 17 | public abstract class MenuAnimationHandler { 18 | 19 | // There are only two distinct animations at the moment. 20 | protected enum ActionType {OPENING, CLOSING} 21 | 22 | protected FloatingActionMenu menu; 23 | 24 | public MenuAnimationHandler() { 25 | } 26 | 27 | public void setMenu(FloatingActionMenu menu) { 28 | this.menu = menu; 29 | } 30 | 31 | /** 32 | * Starts the opening animation 33 | * Should be overriden by children 34 | * @param center 35 | */ 36 | public void animateMenuOpening(Point center) { 37 | if(menu == null) { 38 | throw new NullPointerException("MenuAnimationHandler cannot animate without a valid FloatingActionMenu."); 39 | } 40 | 41 | } 42 | 43 | /** 44 | * Ends the opening animation 45 | * Should be overriden by children 46 | * @param center 47 | */ 48 | public void animateMenuClosing(Point center) { 49 | if(menu == null) { 50 | throw new NullPointerException("MenuAnimationHandler cannot animate without a valid FloatingActionMenu."); 51 | } 52 | } 53 | 54 | /** 55 | * Restores the specified sub action view to its final state, according to the current actionType 56 | * Should be called after an animation finishes. 57 | * @param subActionItem 58 | * @param actionType 59 | */ 60 | protected void restoreSubActionViewAfterAnimation(FloatingActionMenu.Item subActionItem, ActionType actionType) { 61 | ViewGroup.LayoutParams params = subActionItem.view.getLayoutParams(); 62 | subActionItem.view.setTranslationX(0); 63 | subActionItem.view.setTranslationY(0); 64 | subActionItem.view.setRotation(0); 65 | subActionItem.view.setScaleX(1); 66 | subActionItem.view.setScaleY(1); 67 | subActionItem.view.setAlpha(1); 68 | if(actionType == ActionType.OPENING) { 69 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) params; 70 | if(menu.isSystemOverlay()) { 71 | WindowManager.LayoutParams overlayParams = (WindowManager.LayoutParams) menu.getOverlayContainer().getLayoutParams(); 72 | lp.setMargins(subActionItem.x - overlayParams.x, subActionItem.y - overlayParams.y, 0, 0); 73 | } 74 | else { 75 | lp.setMargins(subActionItem.x, subActionItem.y, 0, 0); 76 | } 77 | subActionItem.view.setLayoutParams(lp); 78 | } 79 | else if(actionType == ActionType.CLOSING) { 80 | Point center = menu.getActionViewCenter(); 81 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) params; 82 | if(menu.isSystemOverlay()) { 83 | WindowManager.LayoutParams overlayParams = (WindowManager.LayoutParams) menu.getOverlayContainer().getLayoutParams(); 84 | lp.setMargins(center.x - overlayParams.x - subActionItem.width / 2, center.y - overlayParams.y - subActionItem.height / 2, 0, 0); 85 | } 86 | else { 87 | lp.setMargins(center.x - subActionItem.width / 2, center.y - subActionItem.height / 2, 0, 0); 88 | } 89 | subActionItem.view.setLayoutParams(lp); 90 | menu.removeViewFromCurrentContainer(subActionItem.view); 91 | 92 | if(menu.isSystemOverlay()) { 93 | // When all the views are removed from the overlay container, 94 | // we also need to detach it 95 | if (menu.getOverlayContainer().getChildCount() == 0) { 96 | menu.detachOverlayContainer(); 97 | } 98 | } 99 | } 100 | } 101 | 102 | /** 103 | * A special animation listener that is intended to listen the last of the sequential animations. 104 | * Changes the animating property of children. 105 | */ 106 | public class LastAnimationListener implements Animator.AnimatorListener { 107 | 108 | @Override 109 | public void onAnimationStart(Animator animation) { 110 | setAnimating(true); 111 | } 112 | 113 | @Override 114 | public void onAnimationEnd(Animator animation) { 115 | setAnimating(false); 116 | } 117 | 118 | @Override 119 | public void onAnimationCancel(Animator animation) { 120 | setAnimating(false); 121 | } 122 | 123 | @Override 124 | public void onAnimationRepeat(Animator animation) { 125 | setAnimating(true); 126 | } 127 | } 128 | 129 | public abstract boolean isAnimating(); 130 | protected abstract void setAnimating(boolean animating); 131 | } 132 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_sub_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_sub_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_sub_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_sub_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_sub_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_sub_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/button_sub_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-hdpi/button_sub_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_sub_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_sub_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_sub_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_sub_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_sub_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_sub_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/button_sub_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-mdpi/button_sub_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_sub_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_sub_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_sub_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_sub_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_sub_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_sub_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/button_sub_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xhdpi/button_sub_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_sub_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_sub_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_sub_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_sub_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_sub_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_sub_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/button_sub_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxhdpi/button_sub_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_sub_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_sub_action.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_sub_action_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_sub_action_dark.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_sub_action_dark_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_sub_action_dark_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/button_sub_action_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/library/src/main/res/drawable-xxxhdpi/button_sub_action_touch.png -------------------------------------------------------------------------------- /library/src/main/res/drawable/button_action_dark_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/button_action_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/button_sub_action_dark_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/button_sub_action_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 72dp 3 | 16dp 4 | 12dp 5 | 96dp 6 | 7 | 36dp 8 | 6dp 9 | 10 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /samples/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "21.1.1" 6 | 7 | defaultConfig { 8 | applicationId "com.oguzdev.circularfloatingactionmenu.samples" 9 | minSdkVersion 15 10 | targetSdkVersion 21 11 | versionCode 3 12 | versionName "1.0.2" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile project(':library') 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | compile 'com.android.support:support-v4:21.+' 26 | compile 'com.android.support:appcompat-v7:+' 27 | } 28 | -------------------------------------------------------------------------------- /samples/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 /Applications/Android Studio.app/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 | -------------------------------------------------------------------------------- /samples/src/androidTest/java/com/oguzdev/circularfloatingactionmenu/samples/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /samples/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 29 | 30 | 34 | 37 | 38 | 43 | 46 | 47 | 51 | 54 | 55 | 59 | 62 | 63 | 64 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /samples/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/DemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.app.Fragment; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v7.app.ActionBarActivity; 7 | import android.view.LayoutInflater; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.AdapterView; 13 | import android.widget.ArrayAdapter; 14 | import android.widget.ListView; 15 | 16 | public class DemoActivity extends ActionBarActivity { 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_demo); 22 | if (savedInstanceState == null) { 23 | getFragmentManager().beginTransaction() 24 | .add(R.id.container, new ContentFragment()) 25 | .commit(); 26 | } 27 | } 28 | 29 | 30 | @Override 31 | public boolean onCreateOptionsMenu(Menu menu) { 32 | // Inflate the menu; this adds items to the action bar if it is present. 33 | getMenuInflater().inflate(R.menu.demo, menu); 34 | return true; 35 | } 36 | 37 | @Override 38 | public boolean onOptionsItemSelected(MenuItem item) { 39 | // Handle action bar item clicks here. The action bar will 40 | // automatically handle clicks on the Home/Up button, so long 41 | // as you specify a parent activity in AndroidManifest.xml. 42 | int id = item.getItemId(); 43 | if (id == R.id.action_settings) { 44 | return true; 45 | } 46 | return super.onOptionsItemSelected(item); 47 | } 48 | 49 | /** 50 | * A placeholder fragment containing a simple view. 51 | */ 52 | public static class ContentFragment extends Fragment implements AdapterView.OnItemClickListener { 53 | 54 | private ListView demosListView; 55 | 56 | public ContentFragment() { 57 | } 58 | 59 | @Override 60 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 61 | Bundle savedInstanceState) { 62 | View rootView = inflater.inflate(R.layout.fragment_demo, container, false); 63 | 64 | String[] items = { "Menu with FloatingActionButton", 65 | "Menu attached to custom button", 66 | "Menu with custom animation", 67 | "Menu in ScrollView", 68 | "Menu as system overlay" 69 | }; 70 | ArrayAdapter simpleAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, items); 71 | demosListView = (ListView) rootView.findViewById(R.id.demosListView); 72 | demosListView.setAdapter(simpleAdapter); 73 | demosListView.setOnItemClickListener(this); 74 | return rootView; 75 | } 76 | 77 | @Override 78 | public void onItemClick(AdapterView parent, View view, int position, long id) { 79 | switch (position) { 80 | case 0: 81 | startActivity(new Intent(getActivity(), MenuWithFABActivity.class)); 82 | break; 83 | case 1: 84 | startActivity(new Intent(getActivity(), MenuWithCustomActionButtonActivity.class)); 85 | break; 86 | case 2: 87 | startActivity(new Intent(getActivity(), MenuWithCustomAnimationActivity.class)); 88 | break; 89 | case 3: 90 | startActivity(new Intent(getActivity(), MenuInScrollViewActivity.class)); 91 | break; 92 | case 4: 93 | startActivity(new Intent(getActivity(), SystemOverlayMenuActivity.class)); 94 | break; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/MenuInScrollViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.v7.app.ActionBarActivity; 7 | import android.view.LayoutInflater; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.view.ViewTreeObserver; 12 | import android.widget.ImageView; 13 | import android.widget.LinearLayout; 14 | import android.widget.ScrollView; 15 | 16 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 17 | import com.oguzdev.circularfloatingactionmenu.library.SubActionButton; 18 | 19 | import java.util.ArrayList; 20 | 21 | public class MenuInScrollViewActivity extends ActionBarActivity implements FloatingActionMenu.MenuStateChangeListener, ViewTreeObserver.OnScrollChangedListener, View.OnLayoutChangeListener { 22 | 23 | private ArrayList menus; 24 | private FloatingActionMenu currentMenu; 25 | private FloatingActionMenu bottomMenu; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_menu_in_scroll_view); 31 | 32 | LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 33 | 34 | final ScrollView scrollView = (ScrollView) findViewById(R.id.scrollView); 35 | LinearLayout scrollViewBody = (LinearLayout) findViewById(R.id.scrollViewBody); 36 | 37 | menus = new ArrayList(); 38 | 39 | // add 20 views into body, each with a menu attached 40 | for(int i=0; i<20; i++) { 41 | LinearLayout item = (LinearLayout) inflater.inflate(R.layout.item_scroll_view, null, false); 42 | 43 | scrollViewBody.addView(item); 44 | 45 | View mainActionView = item.findViewById(R.id.itemActionView); 46 | 47 | SubActionButton.Builder rLSubBuilder = new SubActionButton.Builder(this); 48 | ImageView rlIcon1 = new ImageView(this); 49 | ImageView rlIcon2 = new ImageView(this); 50 | ImageView rlIcon3 = new ImageView(this); 51 | 52 | rlIcon1.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_chat_light)); 53 | rlIcon2.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_camera_light)); 54 | rlIcon3.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_video_light)); 55 | 56 | FloatingActionMenu itemMenu = new FloatingActionMenu.Builder(this) 57 | .setStartAngle(-45) 58 | .setEndAngle(-135) 59 | .setRadius(getResources().getDimensionPixelSize(R.dimen.radius_small)) 60 | .addSubActionView(rLSubBuilder.setContentView(rlIcon1).build()) 61 | .addSubActionView(rLSubBuilder.setContentView(rlIcon2).build()) 62 | .addSubActionView(rLSubBuilder.setContentView(rlIcon3).build()) 63 | // listen state changes of each menu 64 | .setStateChangeListener(this) 65 | .attachTo(mainActionView) 66 | .build(); 67 | 68 | // 69 | menus.add(itemMenu); 70 | } 71 | 72 | // listen scroll events on root ScrollView 73 | scrollView.getViewTreeObserver().addOnScrollChangedListener(this); 74 | 75 | 76 | findViewById(R.id.buttom_bar_edit_text).clearFocus(); 77 | 78 | // Attach a menu to the button in the bottom bar, just to prove that it works. 79 | View bottomActionButton = findViewById(R.id.bottom_bar_action_button); 80 | SubActionButton.Builder rLSubBuilder = new SubActionButton.Builder(this); 81 | ImageView rlIcon1 = new ImageView(this); 82 | ImageView rlIcon2 = new ImageView(this); 83 | ImageView rlIcon3 = new ImageView(this); 84 | 85 | rlIcon1.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_place_light)); 86 | rlIcon2.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_picture_light)); 87 | rlIcon3.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_camera_light)); 88 | 89 | bottomMenu = new FloatingActionMenu.Builder(this) 90 | .addSubActionView(rLSubBuilder.setContentView(rlIcon1).build()) 91 | .addSubActionView(rLSubBuilder.setContentView(rlIcon2).build()) 92 | .addSubActionView(rLSubBuilder.setContentView(rlIcon3).build()) 93 | .setStartAngle(-40) 94 | .setEndAngle(-90) 95 | .setRadius(getResources().getDimensionPixelSize(R.dimen.radius_medium)) 96 | .attachTo(bottomActionButton) 97 | .build(); 98 | 99 | // Listen layout (size) changes on a main layout so that we could reposition the bottom menu 100 | scrollView.addOnLayoutChangeListener(this); 101 | } 102 | 103 | 104 | @Override 105 | public boolean onCreateOptionsMenu(Menu menu) { 106 | // Inflate the menu; this adds items to the action bar if it is present. 107 | getMenuInflater().inflate(R.menu.menu_in_scroll_view, menu); 108 | return true; 109 | } 110 | 111 | @Override 112 | public boolean onOptionsItemSelected(MenuItem item) { 113 | // Handle action bar item clicks here. The action bar will 114 | // automatically handle clicks on the Home/Up button, so long 115 | // as you specify a parent activity in AndroidManifest.xml. 116 | int id = item.getItemId(); 117 | if (id == R.id.action_settings) { 118 | return true; 119 | } 120 | return super.onOptionsItemSelected(item); 121 | } 122 | 123 | @Override 124 | public void onMenuOpened(FloatingActionMenu menu) { 125 | // Only allow one menu to stay open 126 | for(FloatingActionMenu iMenu : menus) { 127 | iMenu.close(true); 128 | } 129 | // update our current menu reference 130 | currentMenu = menu; 131 | } 132 | 133 | @Override 134 | public void onMenuClosed(FloatingActionMenu menu) { 135 | // remove our current menu reference 136 | currentMenu = null; 137 | } 138 | 139 | @Override 140 | public void onScrollChanged() { 141 | // ScrollView is scrolled, 142 | // coordinates of main action view has changed. 143 | // We need to update item coordinates of the current open menu. 144 | if(currentMenu != null) { 145 | currentMenu.updateItemPositions(); 146 | } 147 | } 148 | 149 | @Override 150 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 151 | // update the position of the menu when the main layout changes size on events like soft keyboard open/close 152 | if(right - left != 0 && bottom - top != 0 && 153 | (oldLeft != left || oldTop != top || oldRight != right || oldBottom != bottom) && bottomMenu != null) { 154 | bottomMenu.updateItemPositions(); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/MenuWithCustomActionButtonActivity.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.app.Activity; 4 | import android.app.Fragment; 5 | import android.os.Bundle; 6 | import android.support.v7.app.ActionBarActivity; 7 | import android.view.LayoutInflater; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.Button; 13 | import android.widget.FrameLayout; 14 | import android.widget.TextView; 15 | 16 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 17 | import com.oguzdev.circularfloatingactionmenu.library.SubActionButton; 18 | 19 | public class MenuWithCustomActionButtonActivity extends ActionBarActivity { 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_menu_with_custom_action_button); 25 | if (savedInstanceState == null) { 26 | getFragmentManager().beginTransaction() 27 | .add(R.id.container, new CustomButtonDemoFragment()) 28 | .commit(); 29 | } 30 | } 31 | 32 | 33 | @Override 34 | public boolean onCreateOptionsMenu(Menu menu) { 35 | // Inflate the menu; this adds items to the action bar if it is present. 36 | getMenuInflater().inflate(R.menu.menu_with_custom_action_button, menu); 37 | return true; 38 | } 39 | 40 | @Override 41 | public boolean onOptionsItemSelected(MenuItem item) { 42 | // Handle action bar item clicks here. The action bar will 43 | // automatically handle clicks on the Home/Up button, so long 44 | // as you specify a parent activity in AndroidManifest.xml. 45 | int id = item.getItemId(); 46 | if (id == R.id.action_settings) { 47 | return true; 48 | } 49 | return super.onOptionsItemSelected(item); 50 | } 51 | 52 | /** 53 | * A placeholder fragment containing a simple view. 54 | */ 55 | public static class CustomButtonDemoFragment extends Fragment { 56 | 57 | public CustomButtonDemoFragment() { 58 | } 59 | 60 | @Override 61 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 62 | Bundle savedInstanceState) { 63 | View rootView = inflater.inflate(R.layout.fragment_menu_with_custom_action_button, container, false); 64 | 65 | // Our action button is this time just a regular view! 66 | Button centerActionButton = (Button) rootView.findViewById(R.id.centerActionButton); 67 | 68 | // Add some items to the menu. They are regular views as well! 69 | TextView a = new TextView(getActivity()); a.setText("a"); a.setBackgroundResource(android.R.drawable.btn_default_small); 70 | TextView b = new TextView(getActivity()); b.setText("b"); b.setBackgroundResource(android.R.drawable.btn_default_small); 71 | TextView c = new TextView(getActivity()); c.setText("c"); c.setBackgroundResource(android.R.drawable.btn_default_small); 72 | TextView d = new TextView(getActivity()); d.setText("d"); d.setBackgroundResource(android.R.drawable.btn_default_small); 73 | TextView e = new TextView(getActivity()); e.setText("e"); e.setBackgroundResource(android.R.drawable.btn_default_small); 74 | TextView f = new TextView(getActivity()); f.setText("f"); f.setBackgroundResource(android.R.drawable.btn_default_small); 75 | TextView g = new TextView(getActivity()); g.setText("g"); g.setBackgroundResource(android.R.drawable.btn_default_small); 76 | TextView h = new TextView(getActivity()); h.setText("h"); h.setBackgroundResource(android.R.drawable.btn_default_small); 77 | FrameLayout.LayoutParams tvParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); 78 | a.setLayoutParams(tvParams); 79 | b.setLayoutParams(tvParams); 80 | c.setLayoutParams(tvParams); 81 | d.setLayoutParams(tvParams); 82 | e.setLayoutParams(tvParams); 83 | f.setLayoutParams(tvParams); 84 | g.setLayoutParams(tvParams); 85 | h.setLayoutParams(tvParams); 86 | 87 | SubActionButton.Builder subBuilder = new SubActionButton.Builder(getActivity()); 88 | 89 | FloatingActionMenu circleMenu = new FloatingActionMenu.Builder(getActivity()) 90 | .setStartAngle(0) // A whole circle! 91 | .setEndAngle(360) 92 | .setRadius(getResources().getDimensionPixelSize(R.dimen.radius_large)) 93 | .addSubActionView(a) 94 | .addSubActionView(b) 95 | .addSubActionView(c) 96 | .addSubActionView(d) 97 | .addSubActionView(e) 98 | .addSubActionView(f) 99 | .addSubActionView(g) 100 | .addSubActionView(h) 101 | .attachTo(centerActionButton) 102 | .build(); 103 | 104 | return rootView; 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/MenuWithCustomAnimationActivity.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.app.Activity; 4 | import android.app.Fragment; 5 | import android.os.Bundle; 6 | import android.support.v7.app.ActionBarActivity; 7 | import android.view.LayoutInflater; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.ImageView; 13 | 14 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionButton; 15 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 16 | import com.oguzdev.circularfloatingactionmenu.library.SubActionButton; 17 | 18 | public class MenuWithCustomAnimationActivity extends ActionBarActivity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_menu_with_custom_animation); 24 | if (savedInstanceState == null) { 25 | getFragmentManager().beginTransaction() 26 | .add(R.id.container, new CustomAnimationDemoFragment()) 27 | .commit(); 28 | } 29 | } 30 | 31 | 32 | @Override 33 | public boolean onCreateOptionsMenu(Menu menu) { 34 | // Inflate the menu; this adds items to the action bar if it is present. 35 | getMenuInflater().inflate(R.menu.menu_with_custom_animation, menu); 36 | return true; 37 | } 38 | 39 | @Override 40 | public boolean onOptionsItemSelected(MenuItem item) { 41 | // Handle action bar item clicks here. The action bar will 42 | // automatically handle clicks on the Home/Up button, so long 43 | // as you specify a parent activity in AndroidManifest.xml. 44 | int id = item.getItemId(); 45 | if (id == R.id.action_settings) { 46 | return true; 47 | } 48 | return super.onOptionsItemSelected(item); 49 | } 50 | 51 | /** 52 | * A placeholder fragment containing a simple view. 53 | */ 54 | public static class CustomAnimationDemoFragment extends Fragment { 55 | 56 | public CustomAnimationDemoFragment() { 57 | } 58 | 59 | @Override 60 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 61 | Bundle savedInstanceState) { 62 | View rootView = inflater.inflate(R.layout.fragment_menu_with_custom_animation, container, false); 63 | 64 | ImageView fabContent = new ImageView(getActivity()); 65 | fabContent.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_settings)); 66 | 67 | FloatingActionButton darkButton = new FloatingActionButton.Builder(getActivity()) 68 | .setTheme(FloatingActionButton.THEME_DARK) 69 | .setContentView(fabContent) 70 | .setPosition(FloatingActionButton.POSITION_BOTTOM_CENTER) 71 | .build(); 72 | 73 | SubActionButton.Builder rLSubBuilder = new SubActionButton.Builder(getActivity()) 74 | .setTheme(SubActionButton.THEME_DARK); 75 | ImageView rlIcon1 = new ImageView(getActivity()); 76 | ImageView rlIcon2 = new ImageView(getActivity()); 77 | ImageView rlIcon3 = new ImageView(getActivity()); 78 | ImageView rlIcon4 = new ImageView(getActivity()); 79 | ImageView rlIcon5 = new ImageView(getActivity()); 80 | 81 | rlIcon1.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_chat)); 82 | rlIcon2.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_camera)); 83 | rlIcon3.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_video)); 84 | rlIcon4.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_place)); 85 | rlIcon5.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_headphones)); 86 | 87 | // Set 4 SubActionButtons 88 | FloatingActionMenu centerBottomMenu = new FloatingActionMenu.Builder(getActivity()) 89 | .setStartAngle(0) 90 | .setEndAngle(-180) 91 | .setAnimationHandler(new SlideInAnimationHandler()) 92 | .addSubActionView(rLSubBuilder.setContentView(rlIcon1).build()) 93 | .addSubActionView(rLSubBuilder.setContentView(rlIcon2).build()) 94 | .addSubActionView(rLSubBuilder.setContentView(rlIcon3).build()) 95 | .addSubActionView(rLSubBuilder.setContentView(rlIcon4).build()) 96 | .addSubActionView(rLSubBuilder.setContentView(rlIcon5).build()) 97 | .attachTo(darkButton) 98 | .build(); 99 | 100 | return rootView; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/MenuWithFABActivity.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.animation.PropertyValuesHolder; 5 | import android.app.Activity; 6 | import android.os.Bundle; 7 | import android.support.v7.app.ActionBarActivity; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.FrameLayout; 12 | import android.widget.ImageView; 13 | 14 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionButton; 15 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 16 | import com.oguzdev.circularfloatingactionmenu.library.SubActionButton; 17 | 18 | public class MenuWithFABActivity extends ActionBarActivity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_menu_with_fab); 24 | 25 | // Set up the white button on the lower right corner 26 | // more or less with default parameter 27 | final ImageView fabIconNew = new ImageView(this); 28 | fabIconNew.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_new_light)); 29 | final FloatingActionButton rightLowerButton = new FloatingActionButton.Builder(this) 30 | .setContentView(fabIconNew) 31 | .build(); 32 | 33 | SubActionButton.Builder rLSubBuilder = new SubActionButton.Builder(this); 34 | ImageView rlIcon1 = new ImageView(this); 35 | ImageView rlIcon2 = new ImageView(this); 36 | ImageView rlIcon3 = new ImageView(this); 37 | ImageView rlIcon4 = new ImageView(this); 38 | 39 | rlIcon1.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_chat_light)); 40 | rlIcon2.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_camera_light)); 41 | rlIcon3.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_video_light)); 42 | rlIcon4.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_place_light)); 43 | 44 | // Build the menu with default options: light theme, 90 degrees, 72dp radius. 45 | // Set 4 default SubActionButtons 46 | final FloatingActionMenu rightLowerMenu = new FloatingActionMenu.Builder(this) 47 | .addSubActionView(rLSubBuilder.setContentView(rlIcon1).build()) 48 | .addSubActionView(rLSubBuilder.setContentView(rlIcon2).build()) 49 | .addSubActionView(rLSubBuilder.setContentView(rlIcon3).build()) 50 | .addSubActionView(rLSubBuilder.setContentView(rlIcon4).build()) 51 | .attachTo(rightLowerButton) 52 | .build(); 53 | 54 | // Listen menu open and close events to animate the button content view 55 | rightLowerMenu.setStateChangeListener(new FloatingActionMenu.MenuStateChangeListener() { 56 | @Override 57 | public void onMenuOpened(FloatingActionMenu menu) { 58 | // Rotate the icon of rightLowerButton 45 degrees clockwise 59 | fabIconNew.setRotation(0); 60 | PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, 45); 61 | ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(fabIconNew, pvhR); 62 | animation.start(); 63 | } 64 | 65 | @Override 66 | public void onMenuClosed(FloatingActionMenu menu) { 67 | // Rotate the icon of rightLowerButton 45 degrees counter-clockwise 68 | fabIconNew.setRotation(45); 69 | PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, 0); 70 | ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(fabIconNew, pvhR); 71 | animation.start(); 72 | } 73 | }); 74 | 75 | // Set up the large red button on the center right side 76 | // With custom button and content sizes and margins 77 | int redActionButtonSize = getResources().getDimensionPixelSize(R.dimen.red_action_button_size); 78 | int redActionButtonMargin = getResources().getDimensionPixelOffset(R.dimen.action_button_margin); 79 | int redActionButtonContentSize = getResources().getDimensionPixelSize(R.dimen.red_action_button_content_size); 80 | int redActionButtonContentMargin = getResources().getDimensionPixelSize(R.dimen.red_action_button_content_margin); 81 | int redActionMenuRadius = getResources().getDimensionPixelSize(R.dimen.red_action_menu_radius); 82 | int blueSubActionButtonSize = getResources().getDimensionPixelSize(R.dimen.blue_sub_action_button_size); 83 | int blueSubActionButtonContentMargin = getResources().getDimensionPixelSize(R.dimen.blue_sub_action_button_content_margin); 84 | 85 | ImageView fabIconStar = new ImageView(this); 86 | fabIconStar.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_important)); 87 | 88 | FloatingActionButton.LayoutParams starParams = new FloatingActionButton.LayoutParams(redActionButtonSize, redActionButtonSize); 89 | starParams.setMargins(redActionButtonMargin, 90 | redActionButtonMargin, 91 | redActionButtonMargin, 92 | redActionButtonMargin); 93 | fabIconStar.setLayoutParams(starParams); 94 | 95 | FloatingActionButton.LayoutParams fabIconStarParams = new FloatingActionButton.LayoutParams(redActionButtonContentSize, redActionButtonContentSize); 96 | fabIconStarParams.setMargins(redActionButtonContentMargin, 97 | redActionButtonContentMargin, 98 | redActionButtonContentMargin, 99 | redActionButtonContentMargin); 100 | 101 | final FloatingActionButton leftCenterButton = new FloatingActionButton.Builder(this) 102 | .setContentView(fabIconStar, fabIconStarParams) 103 | .setBackgroundDrawable(R.drawable.button_action_red_selector) 104 | .setPosition(FloatingActionButton.POSITION_LEFT_CENTER) 105 | .setLayoutParams(starParams) 106 | .build(); 107 | 108 | // Set up customized SubActionButtons for the right center menu 109 | SubActionButton.Builder lCSubBuilder = new SubActionButton.Builder(this); 110 | lCSubBuilder.setBackgroundDrawable(getResources().getDrawable(R.drawable.button_action_blue_selector)); 111 | 112 | FrameLayout.LayoutParams blueContentParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); 113 | blueContentParams.setMargins(blueSubActionButtonContentMargin, 114 | blueSubActionButtonContentMargin, 115 | blueSubActionButtonContentMargin, 116 | blueSubActionButtonContentMargin); 117 | lCSubBuilder.setLayoutParams(blueContentParams); 118 | // Set custom layout params 119 | FrameLayout.LayoutParams blueParams = new FrameLayout.LayoutParams(blueSubActionButtonSize, blueSubActionButtonSize); 120 | lCSubBuilder.setLayoutParams(blueParams); 121 | 122 | ImageView lcIcon1 = new ImageView(this); 123 | ImageView lcIcon2 = new ImageView(this); 124 | ImageView lcIcon3 = new ImageView(this); 125 | ImageView lcIcon4 = new ImageView(this); 126 | ImageView lcIcon5 = new ImageView(this); 127 | 128 | lcIcon1.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_camera)); 129 | lcIcon2.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_picture)); 130 | lcIcon3.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_video)); 131 | lcIcon4.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_location_found)); 132 | lcIcon5.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_headphones)); 133 | 134 | // Build another menu with custom options 135 | final FloatingActionMenu leftCenterMenu = new FloatingActionMenu.Builder(this) 136 | .addSubActionView(lCSubBuilder.setContentView(lcIcon1, blueContentParams).build()) 137 | .addSubActionView(lCSubBuilder.setContentView(lcIcon2, blueContentParams).build()) 138 | .addSubActionView(lCSubBuilder.setContentView(lcIcon3, blueContentParams).build()) 139 | .addSubActionView(lCSubBuilder.setContentView(lcIcon4, blueContentParams).build()) 140 | .addSubActionView(lCSubBuilder.setContentView(lcIcon5, blueContentParams).build()) 141 | .setRadius(redActionMenuRadius) 142 | .setStartAngle(70) 143 | .setEndAngle(-70) 144 | .attachTo(leftCenterButton) 145 | .build(); 146 | 147 | 148 | } 149 | 150 | 151 | @Override 152 | public boolean onCreateOptionsMenu(Menu menu) { 153 | // Inflate the menu; this adds items to the action bar if it is present. 154 | getMenuInflater().inflate(R.menu.menu_with_fab, menu); 155 | return true; 156 | } 157 | 158 | @Override 159 | public boolean onOptionsItemSelected(MenuItem item) { 160 | // Handle action bar item clicks here. The action bar will 161 | // automatically handle clicks on the Home/Up button, so long 162 | // as you specify a parent activity in AndroidManifest.xml. 163 | int id = item.getItemId(); 164 | if (id == R.id.action_settings) { 165 | return true; 166 | } 167 | return super.onOptionsItemSelected(item); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/SlideInAnimationHandler.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ObjectAnimator; 5 | import android.animation.PropertyValuesHolder; 6 | import android.graphics.Point; 7 | import android.view.View; 8 | import android.view.animation.AccelerateInterpolator; 9 | import android.view.animation.DecelerateInterpolator; 10 | import android.widget.FrameLayout; 11 | 12 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 13 | import com.oguzdev.circularfloatingactionmenu.library.animation.MenuAnimationHandler; 14 | 15 | /** 16 | * Created by oguzbilgener on 23/07/14. 17 | */ 18 | public class SlideInAnimationHandler extends MenuAnimationHandler { 19 | /** duration of animations, in milliseconds */ 20 | protected static final int DURATION = 700; 21 | /** duration to wait between each of */ 22 | protected static final int LAG_BETWEEN_ITEMS = 100; 23 | 24 | protected static final int DIST_Y = 1000; 25 | 26 | /** holds the current state of animation */ 27 | 28 | private boolean animating; 29 | 30 | public SlideInAnimationHandler() { 31 | setAnimating(false); 32 | } 33 | 34 | @Override 35 | public void animateMenuOpening(Point center) { 36 | super.animateMenuOpening(center); 37 | 38 | setAnimating(true); 39 | 40 | Animator lastAnimation = null; 41 | for (int i = 0; i < menu.getSubActionItems().size(); i++) { 42 | 43 | menu.getSubActionItems().get(i).view.setAlpha(0); 44 | 45 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) menu.getSubActionItems().get(i).view.getLayoutParams(); 46 | params.setMargins(menu.getSubActionItems().get(i).x, menu.getSubActionItems().get(i).y + DIST_Y, 0, 0); 47 | menu.getSubActionItems().get(i).view.setLayoutParams(params); 48 | 49 | // PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, menu.getSubActionItems().get(i).x/* - center.x + menu.getSubActionItems().get(i).width / 2*/); 50 | PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, -DIST_Y); 51 | // PropertyValuesHolder pvhsX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1); 52 | // PropertyValuesHolder pvhsY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1); 53 | PropertyValuesHolder pvhA = PropertyValuesHolder.ofFloat(View.ALPHA, 1); 54 | 55 | final ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(menu.getSubActionItems().get(i).view, pvhY, pvhA); 56 | animation.setDuration(DURATION); 57 | animation.setInterpolator(new DecelerateInterpolator()); 58 | animation.addListener(new SubActionItemAnimationListener(menu.getSubActionItems().get(i), ActionType.OPENING)); 59 | 60 | if(i == 0) { 61 | lastAnimation = animation; 62 | } 63 | 64 | animation.setStartDelay(Math.abs(menu.getSubActionItems().size()/2-i) * LAG_BETWEEN_ITEMS); 65 | animation.start(); 66 | } 67 | if(lastAnimation != null) { 68 | lastAnimation.addListener(new LastAnimationListener()); 69 | } 70 | 71 | } 72 | 73 | @Override 74 | public void animateMenuClosing(Point center) { 75 | super.animateMenuOpening(center); 76 | 77 | setAnimating(true); 78 | 79 | Animator lastAnimation = null; 80 | for (int i = 0; i < menu.getSubActionItems().size(); i++) { 81 | // PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, - (menu.getSubActionItems().get(i).x - center.x + menu.getSubActionItems().get(i).width / 2)); 82 | PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, DIST_Y); 83 | // PropertyValuesHolder pvhsX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0); 84 | // PropertyValuesHolder pvhsY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0); 85 | PropertyValuesHolder pvhA = PropertyValuesHolder.ofFloat(View.ALPHA, 0); 86 | 87 | final ObjectAnimator animation = ObjectAnimator.ofPropertyValuesHolder(menu.getSubActionItems().get(i).view, pvhY, pvhA); 88 | animation.setDuration(DURATION); 89 | animation.setInterpolator(new AccelerateInterpolator()); 90 | animation.addListener(new SubActionItemAnimationListener(menu.getSubActionItems().get(i), ActionType.CLOSING)); 91 | 92 | if(i == 0) { 93 | lastAnimation = animation; 94 | } 95 | 96 | if(i <= menu.getSubActionItems().size()/2) { 97 | animation.setStartDelay(i * LAG_BETWEEN_ITEMS); 98 | } 99 | else { 100 | animation.setStartDelay((menu.getSubActionItems().size() - i) * LAG_BETWEEN_ITEMS); 101 | } 102 | animation.start(); 103 | } 104 | if(lastAnimation != null) { 105 | lastAnimation.addListener(new LastAnimationListener()); 106 | } 107 | } 108 | 109 | @Override 110 | public boolean isAnimating() { 111 | return animating; 112 | } 113 | 114 | @Override 115 | protected void setAnimating(boolean animating) { 116 | this.animating = animating; 117 | } 118 | 119 | protected class SubActionItemAnimationListener implements Animator.AnimatorListener { 120 | 121 | private FloatingActionMenu.Item subActionItem; 122 | private ActionType actionType; 123 | 124 | public SubActionItemAnimationListener(FloatingActionMenu.Item subActionItem, ActionType actionType) { 125 | this.subActionItem = subActionItem; 126 | this.actionType = actionType; 127 | } 128 | 129 | @Override 130 | public void onAnimationStart(Animator animation) { 131 | 132 | } 133 | 134 | @Override 135 | public void onAnimationEnd(Animator animation) { 136 | restoreSubActionViewAfterAnimation(subActionItem, actionType); 137 | } 138 | 139 | @Override 140 | public void onAnimationCancel(Animator animation) { 141 | restoreSubActionViewAfterAnimation(subActionItem, actionType); 142 | } 143 | 144 | @Override public void onAnimationRepeat(Animator animation) {} 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/SystemOverlayMenuActivity.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.os.Bundle; 8 | import android.os.IBinder; 9 | import android.support.v7.app.ActionBarActivity; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.widget.Button; 14 | import android.widget.FrameLayout; 15 | import android.widget.ImageView; 16 | 17 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionButton; 18 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 19 | import com.oguzdev.circularfloatingactionmenu.library.SubActionButton; 20 | 21 | public class SystemOverlayMenuActivity extends ActionBarActivity { 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_menu_with_overlay); 27 | 28 | Button button = (Button) findViewById(R.id.startOverlayServiceButton); 29 | button.setOnClickListener(new View.OnClickListener() { 30 | 31 | @Override 32 | public void onClick(View v) { 33 | // Start an unbound service. 34 | Intent is = new Intent(SystemOverlayMenuActivity.this, SystemOverlayMenuService.class); 35 | startService(is); 36 | // We need to be able to stop it later though. 37 | // This is currently done by the red button of the topCenterMenu in SystemOverlayMenuService 38 | } 39 | }); 40 | } 41 | 42 | @Override 43 | protected void onStop() { 44 | super.onStop(); 45 | } 46 | 47 | 48 | @Override 49 | public boolean onCreateOptionsMenu(Menu menu) { 50 | // Inflate the menu; this adds items to the action bar if it is present. 51 | getMenuInflater().inflate(R.menu.menu_with_fab, menu); 52 | return true; 53 | } 54 | 55 | @Override 56 | public boolean onOptionsItemSelected(MenuItem item) { 57 | // Handle action bar item clicks here. The action bar will 58 | // automatically handle clicks on the Home/Up button, so long 59 | // as you specify a parent activity in AndroidManifest.xml. 60 | int id = item.getItemId(); 61 | if (id == R.id.action_settings) { 62 | return true; 63 | } 64 | return super.onOptionsItemSelected(item); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /samples/src/main/java/com/oguzdev/circularfloatingactionmenu/samples/SystemOverlayMenuService.java: -------------------------------------------------------------------------------- 1 | package com.oguzdev.circularfloatingactionmenu.samples; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.Binder; 6 | import android.os.IBinder; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.view.WindowManager; 10 | import android.widget.FrameLayout; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionButton; 15 | import com.oguzdev.circularfloatingactionmenu.library.FloatingActionMenu; 16 | import com.oguzdev.circularfloatingactionmenu.library.SubActionButton; 17 | 18 | public class SystemOverlayMenuService extends Service { 19 | 20 | private final IBinder mBinder = new LocalBinder(); 21 | 22 | private FloatingActionButton rightLowerButton; 23 | private FloatingActionButton topCenterButton; 24 | 25 | private FloatingActionMenu rightLowerMenu; 26 | private FloatingActionMenu topCenterMenu; 27 | 28 | private boolean serviceWillBeDismissed; 29 | 30 | public SystemOverlayMenuService() { 31 | } 32 | 33 | public class LocalBinder extends Binder { 34 | SystemOverlayMenuService getService() { 35 | // Return this instance of LocalService so clients can call public methods 36 | return SystemOverlayMenuService.this; 37 | } 38 | } 39 | 40 | @Override 41 | public IBinder onBind(Intent intent) { 42 | return mBinder; 43 | } 44 | 45 | @Override 46 | public void onCreate() { 47 | super.onCreate(); 48 | 49 | serviceWillBeDismissed = false; 50 | 51 | // Set up the white button on the lower right corner 52 | // more or less with default parameter 53 | ImageView fabIconNew = new ImageView(this); 54 | fabIconNew.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_new_light)); 55 | WindowManager.LayoutParams params = FloatingActionButton.Builder.getDefaultSystemWindowParams(this); 56 | 57 | rightLowerButton = new FloatingActionButton.Builder(this) 58 | .setContentView(fabIconNew) 59 | .setSystemOverlay(true) 60 | .setLayoutParams(params) 61 | .build(); 62 | 63 | SubActionButton.Builder rLSubBuilder = new SubActionButton.Builder(this); 64 | ImageView rlIcon1 = new ImageView(this); 65 | ImageView rlIcon2 = new ImageView(this); 66 | ImageView rlIcon3 = new ImageView(this); 67 | ImageView rlIcon4 = new ImageView(this); 68 | 69 | rlIcon1.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_chat_light)); 70 | rlIcon2.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_camera_light)); 71 | rlIcon3.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_video_light)); 72 | rlIcon4.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_place_light)); 73 | 74 | // Build the menu with default options: light theme, 90 degrees, 72dp radius. 75 | // Set 4 default SubActionButtons 76 | SubActionButton rlSub1 = rLSubBuilder.setContentView(rlIcon1).build(); 77 | SubActionButton rlSub2 = rLSubBuilder.setContentView(rlIcon2).build(); 78 | SubActionButton rlSub3 = rLSubBuilder.setContentView(rlIcon3).build(); 79 | SubActionButton rlSub4 = rLSubBuilder.setContentView(rlIcon4).build(); 80 | rightLowerMenu = new FloatingActionMenu.Builder(this, true) 81 | .addSubActionView(rlSub1, rlSub1.getLayoutParams().width, rlSub1.getLayoutParams().height) 82 | .addSubActionView(rlSub2, rlSub2.getLayoutParams().width, rlSub2.getLayoutParams().height) 83 | .addSubActionView(rlSub3, rlSub3.getLayoutParams().width, rlSub3.getLayoutParams().height) 84 | .addSubActionView(rlSub4, rlSub4.getLayoutParams().width, rlSub4.getLayoutParams().height) 85 | .setStartAngle(180) 86 | .setEndAngle(270) 87 | .attachTo(rightLowerButton) 88 | .build(); 89 | 90 | //////////////////////////////////////////////////////// 91 | 92 | // Set up the large red button on the top center side 93 | // With custom button and content sizes and margins 94 | int redActionButtonSize = getResources().getDimensionPixelSize(R.dimen.red_action_button_size); 95 | int redActionButtonMargin = getResources().getDimensionPixelOffset(R.dimen.action_button_margin); 96 | int redActionButtonContentSize = getResources().getDimensionPixelSize(R.dimen.red_action_button_content_size); 97 | int redActionButtonContentMargin = getResources().getDimensionPixelSize(R.dimen.red_action_button_content_margin); 98 | int redActionMenuRadius = getResources().getDimensionPixelSize(R.dimen.red_action_menu_radius); 99 | int blueSubActionButtonSize = getResources().getDimensionPixelSize(R.dimen.blue_sub_action_button_size); 100 | int blueSubActionButtonContentMargin = getResources().getDimensionPixelSize(R.dimen.blue_sub_action_button_content_margin); 101 | 102 | ImageView fabIconStar = new ImageView(this); 103 | fabIconStar.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_important)); 104 | 105 | FloatingActionButton.LayoutParams fabIconStarParams = new FloatingActionButton.LayoutParams(redActionButtonContentSize, redActionButtonContentSize); 106 | fabIconStarParams.setMargins(redActionButtonContentMargin, 107 | redActionButtonContentMargin, 108 | redActionButtonContentMargin, 109 | redActionButtonContentMargin); 110 | 111 | WindowManager.LayoutParams params2 = FloatingActionButton.Builder.getDefaultSystemWindowParams(this); 112 | params2.width = redActionButtonSize; 113 | params2.height = redActionButtonSize; 114 | 115 | topCenterButton = new FloatingActionButton.Builder(this) 116 | .setSystemOverlay(true) 117 | .setContentView(fabIconStar, fabIconStarParams) 118 | .setBackgroundDrawable(R.drawable.button_action_red_selector) 119 | .setPosition(FloatingActionButton.POSITION_TOP_CENTER) 120 | .setLayoutParams(params2) 121 | .build(); 122 | 123 | // Set up customized SubActionButtons for the right center menu 124 | SubActionButton.Builder tCSubBuilder = new SubActionButton.Builder(this); 125 | tCSubBuilder.setBackgroundDrawable(getResources().getDrawable(R.drawable.button_action_blue_selector)); 126 | 127 | SubActionButton.Builder tCRedBuilder = new SubActionButton.Builder(this); 128 | tCRedBuilder.setBackgroundDrawable(getResources().getDrawable(R.drawable.button_action_red_selector)); 129 | 130 | FrameLayout.LayoutParams blueContentParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); 131 | blueContentParams.setMargins(blueSubActionButtonContentMargin, 132 | blueSubActionButtonContentMargin, 133 | blueSubActionButtonContentMargin, 134 | blueSubActionButtonContentMargin); 135 | 136 | // Set custom layout params 137 | FrameLayout.LayoutParams blueParams = new FrameLayout.LayoutParams(blueSubActionButtonSize, blueSubActionButtonSize); 138 | tCSubBuilder.setLayoutParams(blueParams); 139 | tCRedBuilder.setLayoutParams(blueParams); 140 | 141 | ImageView tcIcon1 = new ImageView(this); 142 | ImageView tcIcon2 = new ImageView(this); 143 | ImageView tcIcon3 = new ImageView(this); 144 | ImageView tcIcon4 = new ImageView(this); 145 | ImageView tcIcon5 = new ImageView(this); 146 | ImageView tcIcon6 = new ImageView(this); 147 | 148 | tcIcon1.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_camera)); 149 | tcIcon2.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_picture)); 150 | tcIcon3.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_video)); 151 | tcIcon4.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_location_found)); 152 | tcIcon5.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_headphones)); 153 | tcIcon6.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_cancel)); 154 | 155 | SubActionButton tcSub1 = tCSubBuilder.setContentView(tcIcon1, blueContentParams).build(); 156 | SubActionButton tcSub2 = tCSubBuilder.setContentView(tcIcon2, blueContentParams).build(); 157 | SubActionButton tcSub3 = tCSubBuilder.setContentView(tcIcon3, blueContentParams).build(); 158 | SubActionButton tcSub4 = tCSubBuilder.setContentView(tcIcon4, blueContentParams).build(); 159 | SubActionButton tcSub5 = tCSubBuilder.setContentView(tcIcon5, blueContentParams).build(); 160 | SubActionButton tcSub6 = tCRedBuilder.setContentView(tcIcon6, blueContentParams).build(); 161 | 162 | 163 | // Build another menu with custom options 164 | topCenterMenu = new FloatingActionMenu.Builder(this, true) 165 | .addSubActionView(tcSub1, tcSub1.getLayoutParams().width, tcSub1.getLayoutParams().height) 166 | .addSubActionView(tcSub2, tcSub2.getLayoutParams().width, tcSub2.getLayoutParams().height) 167 | .addSubActionView(tcSub3, tcSub3.getLayoutParams().width, tcSub3.getLayoutParams().height) 168 | .addSubActionView(tcSub4, tcSub4.getLayoutParams().width, tcSub4.getLayoutParams().height) 169 | .addSubActionView(tcSub5, tcSub5.getLayoutParams().width, tcSub5.getLayoutParams().height) 170 | .addSubActionView(tcSub6, tcSub6.getLayoutParams().width, tcSub6.getLayoutParams().height) 171 | .setRadius(redActionMenuRadius) 172 | .setStartAngle(0) 173 | .setEndAngle(180) 174 | .attachTo(topCenterButton) 175 | .build(); 176 | 177 | topCenterMenu.setStateChangeListener(new FloatingActionMenu.MenuStateChangeListener() { 178 | @Override 179 | public void onMenuOpened(FloatingActionMenu menu) { 180 | 181 | } 182 | 183 | @Override 184 | public void onMenuClosed(FloatingActionMenu menu) { 185 | if(serviceWillBeDismissed) { 186 | SystemOverlayMenuService.this.stopSelf(); 187 | serviceWillBeDismissed = false; 188 | } 189 | } 190 | }); 191 | 192 | // make the red button terminate the service 193 | tcSub6.setOnClickListener(new View.OnClickListener() { 194 | @Override 195 | public void onClick(View v) { 196 | serviceWillBeDismissed = true; // the order is important 197 | topCenterMenu.close(true); 198 | } 199 | }); 200 | } 201 | 202 | @Override 203 | public void onDestroy() { 204 | if(rightLowerMenu != null && rightLowerMenu.isOpen()) rightLowerMenu.close(false); 205 | if(topCenterMenu != null && topCenterMenu.isOpen()) topCenterMenu.close(false); 206 | if(rightLowerButton != null) rightLowerButton.detach(); 207 | if(topCenterButton != null) topCenterButton.detach(); 208 | 209 | super.onDestroy(); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /samples/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/button_action_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/button_action_blue.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/button_action_blue_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/button_action_blue_touch.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/button_action_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/button_action_red.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/button_action_red_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/button_action_red_touch.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_camera.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_camera_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_camera_light.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_cancel.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_chat.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_chat_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_chat_light.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_headphones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_headphones.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_important.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_location_found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_location_found.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_new.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_new_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_new_light.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_picture.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_picture_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_picture_light.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_place.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_place.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_place_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_place_light.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_send_now.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_send_now.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_send_now_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_send_now_light.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_settings.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_video.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_action_video_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_action_video_light.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzbilgener/CircularFloatingActionMenu/7614a063f326cbd153ead6b06ba979c6cf40796e/samples/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /samples/src/main/res/drawable/bottom_bar_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/src/main/res/drawable/button_action_blue_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /samples/src/main/res/drawable/button_action_red_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_demo.xml: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_menu_in_scroll_view.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 22 | 27 | 28 | 29 | 37 | 38 | 47 | 48 | 49 | 50 | 56 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_menu_with_custom_action_button.xml: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_menu_with_custom_animation.xml: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_menu_with_fab.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_menu_with_overlay.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 |