├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ui │ │ └── ui │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ui │ │ │ └── ui │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── ui │ └── ui │ └── ExampleUnitTest.java ├── build.gradle ├── build └── intermediates │ └── dex-cache │ └── cache.xml ├── gif └── ripple_shadow.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── settings.gradle └── uimodule ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── ui │ └── uimodule │ ├── FloatArrayEvaluator.java │ ├── RippleHelper.java │ ├── ShadowHelper.java │ ├── SquareLinearLayout.java │ ├── SquareRelativeLayout.java │ └── button │ └── RaisedButton.java └── res └── values └── attrs.xml /.gitignore: -------------------------------------------------------------------------------- 1 | ### Android template 2 | # Built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # Files for the Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # IntelliJ files 33 | .idea 34 | *.iml 35 | 36 | # Created by .ignore support plugin (hsz.mobi) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uiModule 2 | Material Design support API 21 above 3 | ![Screenshot](https://github.com/cuber5566/uiModule/blob/master/gif/ripple_shadow.gif) 4 | 5 | 6 | ###SquareLinearLayout, SquareRelativeLayout 7 | ```xml 8 | 13 | ``` 14 | ###RaisedButton 15 | 16 | ```mxl 17 | 32 | ``` 33 | 34 | ###ShadowHelper 35 | ```java 36 | shadowHelper.onDraw(canvas, rectRadius, elevation, focus); 37 | ``` 38 | 39 | ###RippleHelper 40 | ```java 41 | rippleHelper.onTouch(event); 42 | rippleHelper.onDraw(canvas, rippleRadius, rippleColor, padding, rectRadius); 43 | ``` 44 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.ui.ui" 9 | minSdkVersion 16 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 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 'com.android.support:appcompat-v7:23.0.1' 24 | compile 'com.android.support:cardview-v7:23.0.1' 25 | compile project(':uimodule') 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/cuber/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ui/ui/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.ui.ui; 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 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/ui/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ui.ui; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import com.ui.uimodule.button.RaisedButton; 7 | 8 | 9 | public class MainActivity extends AppCompatActivity { 10 | 11 | RaisedButton raisedButton; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | 18 | raisedButton = (RaisedButton) findViewById(R.id.raisedButton); 19 | 20 | // (findViewById(R.id.enable)).setOnClickListener(new View.OnClickListener() { 21 | // boolean enable = true; 22 | // @Override 23 | // public void onClick(View v) { 24 | // enable = !enable; 25 | // raisedButton.setEnabled(enable); 26 | // } 27 | // }); 28 | // 29 | // (findViewById(R.id.animationEnable)).setOnClickListener(new View.OnClickListener() { 30 | // boolean enable = true; 31 | // @Override 32 | // public void onClick(View v) { 33 | // enable = !enable; 34 | // raisedButton.setEnabledWithAnimation(enable); 35 | // } 36 | // }); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 28 | 29 | 44 | 45 | 57 | 58 | 70 | 71 | 83 | 84 | 96 | 97 | 109 | 110 | 122 | 123 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuber5566/uiModule/2e2eb0dcc36c6f8f26d5257792f610e9e013a03b/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuber5566/uiModule/2e2eb0dcc36c6f8f26d5257792f610e9e013a03b/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuber5566/uiModule/2e2eb0dcc36c6f8f26d5257792f610e9e013a03b/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuber5566/uiModule/2e2eb0dcc36c6f8f26d5257792f610e9e013a03b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuber5566/uiModule/2e2eb0dcc36c6f8f26d5257792f610e9e013a03b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ui 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/ui/ui/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.ui.ui; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /build/intermediates/dex-cache/cache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /gif/ripple_shadow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuber5566/uiModule/2e2eb0dcc36c6f8f26d5257792f610e9e013a03b/gif/ripple_shadow.gif -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuber5566/uiModule/2e2eb0dcc36c6f8f26d5257792f610e9e013a03b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Nov 03 11:07:30 CST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-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 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by Android Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # Location of the SDK. This is only used by Gradle. 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | #Fri Nov 06 21:44:51 CST 2015 11 | sdk.dir=/Users/cuber/Desktop/adt-bundle-mac-x86_64-20140702/sdk 12 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':uimodule' 2 | -------------------------------------------------------------------------------- /uimodule/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /uimodule/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile 'com.android.support:appcompat-v7:23.0.1' 23 | } 24 | -------------------------------------------------------------------------------- /uimodule/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/cuber/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /uimodule/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uimodule/src/main/java/com/ui/uimodule/FloatArrayEvaluator.java: -------------------------------------------------------------------------------- 1 | package com.ui.uimodule; 2 | 3 | import android.animation.TypeEvaluator; 4 | 5 | public class FloatArrayEvaluator implements TypeEvaluator { 6 | 7 | private float[] mArray; 8 | 9 | public FloatArrayEvaluator() { 10 | } 11 | 12 | public FloatArrayEvaluator(float[] reuseArray) { 13 | mArray = reuseArray; 14 | } 15 | 16 | @Override 17 | public float[] evaluate(float fraction, float[] startValue, float[] endValue) { 18 | float[] array = mArray; 19 | if (array == null) { 20 | array = new float[startValue.length]; 21 | } 22 | 23 | for (int i = 0; i < array.length; i++) { 24 | float start = startValue[i]; 25 | float end = endValue[i]; 26 | array[i] = start + (fraction * (end - start)); 27 | } 28 | return array; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /uimodule/src/main/java/com/ui/uimodule/RippleHelper.java: -------------------------------------------------------------------------------- 1 | package com.ui.uimodule; 2 | 3 | import android.animation.ArgbEvaluator; 4 | import android.animation.ValueAnimator; 5 | import android.content.res.Resources; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.Path; 10 | import android.graphics.RectF; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.animation.CycleInterpolator; 14 | import android.view.animation.DecelerateInterpolator; 15 | 16 | public class RippleHelper { 17 | 18 | public static final int STATE_DOWN = 0; 19 | public static final int STATE_UP = 1; 20 | public static final int STATE_CANCEL = 2; 21 | 22 | private View view; 23 | private ValueAnimator rippleColorAnimator, rippleRadiusAnimator, rippleFocusAnimator; 24 | 25 | int animationDurationFocus = 2500; 26 | int animationDurationPress = 300; 27 | int animationDurationUp = 350; 28 | 29 | float radius = 48 * Resources.getSystem().getDisplayMetrics().density; 30 | float focusDelta = 4 * Resources.getSystem().getDisplayMetrics().density; 31 | 32 | float width, height; 33 | float x, y; 34 | int color; 35 | 36 | float cur_radius; 37 | 38 | Path clipPath = new Path(); 39 | RectF clipRect = new RectF(); 40 | Paint ripplePaint = new Paint(Paint.ANTI_ALIAS_FLAG) { 41 | { 42 | setStyle(Style.FILL); 43 | } 44 | }; 45 | 46 | public RippleHelper(View view) { 47 | this.view = view; 48 | } 49 | 50 | public RippleHelper animationDurationFocus(int animationDurationFocus){ 51 | this.animationDurationFocus = animationDurationFocus; 52 | return this; 53 | } 54 | 55 | public RippleHelper animationDurationPress(int animationDurationPress){ 56 | this.animationDurationPress = animationDurationPress; 57 | return this; 58 | } 59 | 60 | public RippleHelper animationDurationUp(int animationDurationUp){ 61 | this.animationDurationUp = animationDurationUp; 62 | return this; 63 | } 64 | 65 | public void onTouch(MotionEvent event){ 66 | 67 | x = event.getX(); 68 | y = event.getY(); 69 | 70 | switch (event.getAction()) { 71 | case MotionEvent.ACTION_DOWN: 72 | changeRippleColor(true); 73 | startRippleRadiusFocus(true); 74 | changeRippleRadius(STATE_DOWN); 75 | break; 76 | 77 | case MotionEvent.ACTION_MOVE: 78 | view.invalidate(); 79 | break; 80 | 81 | case MotionEvent.ACTION_UP: 82 | case MotionEvent.ACTION_CANCEL: 83 | changeRippleColor(false); 84 | startRippleRadiusFocus(false); 85 | if (0 < x && x < width && 0 < y && y < height) { 86 | changeRippleRadius(STATE_UP); 87 | } else { 88 | changeRippleRadius(STATE_CANCEL); 89 | view.invalidate(); 90 | } 91 | break; 92 | } 93 | } 94 | 95 | public void onDraw(Canvas canvas, float rippleRadius, int rippleColor, float padding, float rectRadius) { 96 | 97 | width = canvas.getWidth(); 98 | height = canvas.getHeight(); 99 | radius = rippleRadius; 100 | color = rippleColor; 101 | 102 | canvas.save(); 103 | clipRect.set(padding, padding, width - padding, height - padding); 104 | clipPath.addRoundRect(clipRect, rectRadius, rectRadius, Path.Direction.CW); 105 | canvas.clipPath(clipPath); 106 | canvas.clipRect(clipRect); 107 | canvas.drawCircle(x, y, cur_radius, ripplePaint); 108 | canvas.restore(); 109 | } 110 | 111 | private void changeRippleColor(final boolean visible) { 112 | 113 | int normalBackgroundColor = Color.argb((int) (255 * 0.0), Color.red(color), Color.green(color), Color.blue(color)); 114 | int pressBackgroundColor = Color.argb((int) (255 * 0.1), Color.red(color), Color.green(color), Color.blue(color)); 115 | 116 | if (null != rippleColorAnimator) rippleColorAnimator.cancel(); 117 | 118 | if (visible) { 119 | rippleColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), normalBackgroundColor, pressBackgroundColor); 120 | rippleColorAnimator.setDuration(animationDurationPress); 121 | } else { 122 | rippleColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), pressBackgroundColor, normalBackgroundColor); 123 | rippleColorAnimator.setDuration(animationDurationUp); 124 | } 125 | 126 | rippleColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 127 | @Override 128 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 129 | int color = (Integer) valueAnimator.getAnimatedValue(); 130 | ripplePaint.setColor(color); 131 | } 132 | }); 133 | rippleColorAnimator.setInterpolator(new DecelerateInterpolator()); 134 | rippleColorAnimator.start(); 135 | } 136 | 137 | private void changeRippleRadius(int state) { 138 | 139 | if (null != rippleRadiusAnimator) rippleRadiusAnimator.cancel(); 140 | 141 | switch (state) { 142 | 143 | case STATE_DOWN: 144 | rippleRadiusAnimator = ValueAnimator.ofFloat(0, radius); 145 | rippleRadiusAnimator.setDuration(animationDurationPress); 146 | break; 147 | 148 | case STATE_UP: 149 | rippleRadiusAnimator = ValueAnimator.ofFloat(cur_radius, radius * 2); 150 | rippleRadiusAnimator.setDuration(animationDurationUp); 151 | break; 152 | 153 | default: 154 | rippleRadiusAnimator = ValueAnimator.ofFloat(cur_radius, 0); 155 | rippleRadiusAnimator.setDuration(animationDurationUp); 156 | break; 157 | } 158 | rippleRadiusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 159 | @Override 160 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 161 | cur_radius = (float) valueAnimator.getAnimatedValue(); 162 | view.invalidate(); 163 | } 164 | }); 165 | rippleRadiusAnimator.setInterpolator(new DecelerateInterpolator()); 166 | rippleRadiusAnimator.start(); 167 | } 168 | 169 | private void startRippleRadiusFocus(final boolean start) { 170 | 171 | if (null != rippleFocusAnimator) rippleFocusAnimator.cancel(); 172 | rippleFocusAnimator = ValueAnimator.ofFloat(0, focusDelta); 173 | rippleFocusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 174 | @Override 175 | public void onAnimationUpdate(ValueAnimator animation) { 176 | if (start) { 177 | cur_radius = radius + (Float) animation.getAnimatedValue(); 178 | view.invalidate(); 179 | } else { 180 | rippleFocusAnimator.cancel(); 181 | } 182 | } 183 | }); 184 | 185 | rippleFocusAnimator.setInterpolator(new CycleInterpolator(1)); 186 | rippleFocusAnimator.setRepeatCount(Integer.MAX_VALUE); 187 | rippleFocusAnimator.setStartDelay(animationDurationPress); 188 | rippleFocusAnimator.setDuration(animationDurationFocus).start(); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /uimodule/src/main/java/com/ui/uimodule/ShadowHelper.java: -------------------------------------------------------------------------------- 1 | package com.ui.uimodule; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.res.Resources; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.LinearGradient; 8 | import android.graphics.Paint; 9 | import android.graphics.RadialGradient; 10 | import android.graphics.RectF; 11 | import android.graphics.Shader; 12 | import android.view.View; 13 | import android.view.animation.DecelerateInterpolator; 14 | 15 | public class ShadowHelper { 16 | 17 | private LinearGradient verticalLinearGradient, horizontalLinearGradient; 18 | private RadialGradient verticalRadialGradient, horizontalRadialGradient; 19 | 20 | private static final float SHADOW_NORMAL = 1 * Resources.getSystem().getDisplayMetrics().density; 21 | 22 | int startColor, midColor, endColor; 23 | float width, height; 24 | 25 | private float shadowRadius, elevation, radiusCenter, shadowNormal, shadowPress, shadowOffset; 26 | private boolean init, animationStart; 27 | private int animationDuration = 300; 28 | private int[] colors = new int[3]; 29 | private float[] positions = new float[3]; 30 | private float[] start = new float[2], end = new float[2]; 31 | 32 | private View view; 33 | private ValueAnimator shadowAnimator; 34 | private RectF rectF = new RectF(); 35 | private Paint centerPaint = new Paint(Paint.ANTI_ALIAS_FLAG) { 36 | { 37 | setStyle(Style.FILL); 38 | } 39 | }, linearPaint = new Paint(Paint.ANTI_ALIAS_FLAG) { 40 | { 41 | setStyle(Style.FILL); 42 | } 43 | }, radiusPaint = new Paint(Paint.ANTI_ALIAS_FLAG) { 44 | { 45 | setStyle(Style.FILL); 46 | } 47 | }; 48 | 49 | public ShadowHelper(View view) { 50 | this.view = view; 51 | } 52 | 53 | public ShadowHelper duration(int animationDuration) { 54 | this.animationDuration = animationDuration; 55 | return this; 56 | } 57 | 58 | private void init() { 59 | 60 | start[0] = shadowNormal; 61 | start[1] = 0; 62 | end[0] = shadowPress; 63 | end[1] = elevation / 3; 64 | 65 | startColor = Color.argb((int) (255 * 0.7), 0, 0, 0); 66 | midColor = Color.argb((int) (255 * 0.5), 0, 0, 0); 67 | endColor = Color.TRANSPARENT; 68 | 69 | colors[0] = startColor; 70 | colors[1] = midColor; 71 | colors[2] = endColor; 72 | 73 | positions[0] = 0; 74 | positions[1] = 1 - elevation / (shadowRadius - elevation); 75 | positions[2] = 1; 76 | 77 | verticalLinearGradient = new LinearGradient(0, radiusCenter, 0, radiusCenter - shadowNormal, colors, positions, Shader.TileMode.CLAMP); 78 | horizontalLinearGradient = new LinearGradient(width - radiusCenter, 0, width - radiusCenter + shadowNormal, 0, colors, positions, Shader.TileMode.CLAMP); 79 | verticalRadialGradient = new RadialGradient(radiusCenter, radiusCenter, shadowNormal, colors, positions, Shader.TileMode.CLAMP); 80 | horizontalRadialGradient = new RadialGradient(width - radiusCenter, radiusCenter, shadowNormal, colors, positions, Shader.TileMode.CLAMP); 81 | } 82 | 83 | public void onDraw(Canvas canvas, float radius, float e, boolean press) { 84 | 85 | width = canvas.getWidth(); 86 | height = canvas.getHeight(); 87 | elevation = e; 88 | shadowRadius = radius + elevation; 89 | 90 | shadowNormal = shadowRadius + SHADOW_NORMAL - elevation; 91 | shadowPress = shadowRadius + elevation * 2 / 3 - elevation; 92 | radiusCenter = shadowRadius; 93 | 94 | if (!init) { 95 | init(); 96 | init = true; 97 | } 98 | 99 | if (animationStart != press) { 100 | startShadowAnimation(press); 101 | animationStart = press; 102 | } 103 | 104 | canvas.save(); 105 | canvas.translate(0, shadowOffset); 106 | 107 | for (int i = 0; i < 2; i++) { 108 | rectF.set(0, 0, radiusCenter * 2, radiusCenter * 2); 109 | radiusPaint.setShader(verticalRadialGradient); 110 | canvas.drawArc(rectF, 180, 90, true, radiusPaint); 111 | 112 | rectF.set(radiusCenter, 0, width - radiusCenter, radiusCenter); 113 | linearPaint.setShader(verticalLinearGradient); 114 | canvas.drawRect(rectF, linearPaint); 115 | 116 | canvas.rotate(180, width / 2, height / 2); 117 | } 118 | 119 | for (int i = 0; i < 2; i++) { 120 | 121 | rectF.set(width - radiusCenter * 2, 0, width, radiusCenter * 2); 122 | radiusPaint.setShader(horizontalRadialGradient); 123 | canvas.drawArc(rectF, 270, 90, true, radiusPaint); 124 | 125 | rectF.set(width - shadowRadius, shadowRadius, width, height - shadowRadius); 126 | linearPaint.setShader(horizontalLinearGradient); 127 | canvas.drawRect(rectF, linearPaint); 128 | 129 | canvas.rotate(180, width / 2, height / 2); 130 | } 131 | 132 | rectF.set(shadowRadius, shadowRadius, width - shadowRadius, height - shadowRadius); 133 | centerPaint.setColor(startColor); 134 | canvas.drawRect(rectF, centerPaint); 135 | 136 | canvas.restore(); 137 | 138 | } 139 | 140 | private void startShadowAnimation(boolean press) { 141 | 142 | if (null != shadowAnimator) shadowAnimator.cancel(); 143 | 144 | if (press) { 145 | shadowAnimator = ValueAnimator.ofObject(new FloatArrayEvaluator(), start, end); 146 | } else { 147 | shadowAnimator = ValueAnimator.ofObject(new FloatArrayEvaluator(), end, start); 148 | } 149 | 150 | shadowAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 151 | @Override 152 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 153 | float[] value = (float[]) valueAnimator.getAnimatedValue(); 154 | 155 | //offset 156 | shadowOffset = value[1]; 157 | 158 | //T 159 | verticalLinearGradient = new LinearGradient(0, radiusCenter, 0, radiusCenter - value[0], colors, positions, Shader.TileMode.CLAMP); 160 | 161 | //R 162 | horizontalLinearGradient = new LinearGradient(width - radiusCenter, 0, width - radiusCenter + value[0], 0, colors, positions, Shader.TileMode.CLAMP); 163 | 164 | //LT 165 | verticalRadialGradient = new RadialGradient(radiusCenter, radiusCenter, value[0], colors, positions, Shader.TileMode.CLAMP); 166 | 167 | //RT 168 | horizontalRadialGradient = new RadialGradient(width - radiusCenter, radiusCenter, value[0], colors, positions, Shader.TileMode.CLAMP); 169 | 170 | view.invalidate(); 171 | } 172 | }); 173 | shadowAnimator.setInterpolator(new DecelerateInterpolator()); 174 | shadowAnimator.setDuration(animationDuration); 175 | shadowAnimator.start(); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /uimodule/src/main/java/com/ui/uimodule/SquareLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.ui.uimodule; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.LinearLayout; 9 | 10 | public class SquareLinearLayout extends LinearLayout { 11 | 12 | private float widthWeight, heightWeight; 13 | 14 | public SquareLinearLayout(Context context) { 15 | this(context, null); 16 | } 17 | 18 | public SquareLinearLayout(Context context, AttributeSet attrs) { 19 | this(context, attrs, 0); 20 | } 21 | 22 | public SquareLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | setAttributes(context, attrs, defStyleAttr); 25 | } 26 | 27 | private void setAttributes(Context context, AttributeSet attrs, int defStyleAttr) { 28 | 29 | final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SquareLayout, defStyleAttr, 0); 30 | widthWeight = typedArray.getFloat(R.styleable.SquareLayout_sl_widthWeight, 1); 31 | heightWeight = typedArray.getFloat(R.styleable.SquareLayout_sl_heightWeight, 1); 32 | typedArray.recycle(); 33 | 34 | } 35 | 36 | @Override 37 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 38 | int width = MeasureSpec.getSize(widthMeasureSpec); 39 | int measureSpecWidth = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 40 | int measureSpecHeight = MeasureSpec.makeMeasureSpec((int) (width * heightWeight / widthWeight), MeasureSpec.EXACTLY); 41 | super.onMeasure(measureSpecWidth, measureSpecHeight); 42 | } 43 | 44 | @Override 45 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 46 | super.onLayout(changed, l, t, r, b); 47 | 48 | Log.i("layout", String.format("left:%d, top:%d, right:%d, bottom:%d", getPaddingLeft(), ((View) getParent()).getPaddingTop(), ((View) getParent()).getPaddingRight(), ((View) getParent()).getPaddingBottom())); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /uimodule/src/main/java/com/ui/uimodule/SquareRelativeLayout.java: -------------------------------------------------------------------------------- 1 | package com.ui.uimodule; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.util.AttributeSet; 6 | import android.widget.RelativeLayout; 7 | 8 | public class SquareRelativeLayout extends RelativeLayout { 9 | 10 | private float widthWeight, heightWeight; 11 | 12 | public SquareRelativeLayout(Context context) { 13 | this(context, null); 14 | } 15 | 16 | public SquareRelativeLayout(Context context, AttributeSet attrs) { 17 | this(context, attrs, 0); 18 | } 19 | 20 | public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | setAttributes(context, attrs, defStyleAttr); 23 | } 24 | 25 | private void setAttributes(Context context, AttributeSet attrs, int defStyleAttr) { 26 | 27 | final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SquareLayout, defStyleAttr, 0); 28 | widthWeight = typedArray.getFloat(R.styleable.SquareLayout_sl_widthWeight, 1); 29 | heightWeight = typedArray.getFloat(R.styleable.SquareLayout_sl_heightWeight, 1); 30 | typedArray.recycle(); 31 | } 32 | 33 | @Override 34 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 35 | int width = MeasureSpec.getSize(widthMeasureSpec); 36 | int measureSpecWidth = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 37 | int measureSpecHeight = MeasureSpec.makeMeasureSpec((int) (width * heightWeight / widthWeight), MeasureSpec.EXACTLY); 38 | super.onMeasure(measureSpecWidth, measureSpecHeight); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /uimodule/src/main/java/com/ui/uimodule/button/RaisedButton.java: -------------------------------------------------------------------------------- 1 | package com.ui.uimodule.button; 2 | 3 | import android.animation.ArgbEvaluator; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.content.res.Resources; 7 | import android.content.res.TypedArray; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.graphics.RectF; 12 | import android.os.Build; 13 | import android.support.annotation.NonNull; 14 | import android.util.AttributeSet; 15 | import android.view.MotionEvent; 16 | import android.view.View; 17 | import android.view.animation.DecelerateInterpolator; 18 | import android.widget.Button; 19 | 20 | import com.ui.uimodule.R; 21 | import com.ui.uimodule.RippleHelper; 22 | import com.ui.uimodule.ShadowHelper; 23 | 24 | public class RaisedButton extends Button { 25 | 26 | final int ANIMATION_DURATION_DISABLED = 250; 27 | final int ANIMATION_DURATION_PRESS = 300; 28 | final int ANIMATION_DURATION_UP = 350; 29 | 30 | float rippleRadius = 48 * Resources.getSystem().getDisplayMetrics().density; 31 | 32 | float padding = 4 * Resources.getSystem().getDisplayMetrics().density; 33 | float elevation = 4 * Resources.getSystem().getDisplayMetrics().density; 34 | float rectRadius = 2 * Resources.getSystem().getDisplayMetrics().density; 35 | 36 | protected int backgroundColor, rippleColor, enableTextColor, enableBackgroundColor; 37 | private ValueAnimator enableTextColorAnimator, enableBackgroundColorAnimator, backgroundColorAnimator; 38 | 39 | Paint ripplePaint, backgroundPaint; 40 | RectF rectF = new RectF(); 41 | ShadowHelper shadowHelper; 42 | RippleHelper rippleHelper; 43 | 44 | float x, y; 45 | int textColor; 46 | int height, width; 47 | boolean isClicked = false; 48 | boolean focus; 49 | 50 | public RaisedButton(Context context) { 51 | this(context, null); 52 | } 53 | 54 | public RaisedButton(Context context, AttributeSet attrs) { 55 | this(context, attrs, 0); 56 | } 57 | 58 | public RaisedButton(Context context, AttributeSet attrs, int defStyleAttr) { 59 | super(context, attrs, defStyleAttr); 60 | 61 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 62 | setLayerType(View.LAYER_TYPE_SOFTWARE, null); 63 | } 64 | setAttributes(context, attrs); 65 | setPaint(); 66 | shadowHelper = new ShadowHelper(this); 67 | rippleHelper = new RippleHelper(this); 68 | } 69 | 70 | private void setAttributes(Context context, AttributeSet attrs) { 71 | final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RaisedButton, 0, 0); 72 | backgroundColor = typedArray.getColor(R.styleable.RaisedButton_rb_backgroundColor, Color.WHITE); 73 | rippleColor = typedArray.getColor(R.styleable.RaisedButton_rb_rippleColor, Color.WHITE); 74 | enableTextColor = typedArray.getColor(R.styleable.RaisedButton_rb_enableTextColor, Color.WHITE); 75 | enableBackgroundColor = typedArray.getColor(R.styleable.RaisedButton_rb_enableBackgroundColor, Color.LTGRAY); 76 | rippleRadius = typedArray.getDimension(R.styleable.RaisedButton_rb_rippleRadius, rippleRadius); 77 | elevation = typedArray.getDimension(R.styleable.RaisedButton_rb_elevation, elevation); 78 | padding = elevation + 0.5f * Resources.getSystem().getDisplayMetrics().density; 79 | rectRadius = typedArray.getDimension(R.styleable.RaisedButton_rb_rectRadius, rectRadius); 80 | typedArray.recycle(); 81 | } 82 | 83 | private void setPaint() { 84 | ripplePaint = new Paint() { 85 | { 86 | setAntiAlias(true); 87 | } 88 | }; 89 | 90 | backgroundPaint = new Paint() { 91 | { 92 | setAntiAlias(true); 93 | setStyle(Style.FILL); 94 | setColor(backgroundColor); 95 | } 96 | }; 97 | } 98 | 99 | public void setEnabledWithAnimation(boolean enabled) { 100 | 101 | if (enabled == isEnabled()) { 102 | return; 103 | } 104 | 105 | changeEnableBackgroundColor(enabled); 106 | changeEnableTextColor(enabled); 107 | super.setEnabled(enabled); 108 | } 109 | 110 | @Override 111 | public void setEnabled(boolean enabled) { 112 | 113 | if (enabled == isEnabled()) { 114 | return; 115 | } 116 | 117 | if (enabled) { 118 | setTextColor(textColor); 119 | backgroundPaint.setColor(backgroundColor); 120 | } else { 121 | setTextColor(enableTextColor); 122 | backgroundPaint.setColor(enableBackgroundColor); 123 | } 124 | invalidate(); 125 | 126 | super.setEnabled(enabled); 127 | } 128 | 129 | @Override 130 | public boolean onTouchEvent(@NonNull MotionEvent event) { 131 | if (!isEnabled() || isClicked) 132 | return false; 133 | 134 | x = event.getX(); 135 | y = event.getY(); 136 | rippleHelper.onTouch(event); 137 | 138 | switch (event.getAction()) { 139 | case MotionEvent.ACTION_DOWN: 140 | focus = true; 141 | changeBackgroundColor(true); 142 | break; 143 | 144 | case MotionEvent.ACTION_MOVE: 145 | invalidate(); 146 | break; 147 | 148 | case MotionEvent.ACTION_UP: 149 | case MotionEvent.ACTION_CANCEL: 150 | isClicked = true; 151 | focus = false; 152 | changeBackgroundColor(false); 153 | if (0 < x && x < width && 0 < y && y < height) { 154 | postDelayed(clickRunnable, ANIMATION_DURATION_UP - 200); 155 | } else { 156 | isClicked = false; 157 | invalidate(); 158 | } 159 | break; 160 | } 161 | return true; 162 | } 163 | 164 | Runnable clickRunnable = new Runnable() { 165 | @Override 166 | public void run() { 167 | onClick(); 168 | isClicked = false; 169 | } 170 | }; 171 | 172 | @Override 173 | protected void onDraw(@NonNull Canvas canvas) { 174 | 175 | height = canvas.getHeight(); 176 | width = canvas.getWidth(); 177 | 178 | shadowHelper.onDraw(canvas, rectRadius, elevation, focus); 179 | 180 | rectF.set(padding, padding, width - padding, height - padding - 0.5f * Resources.getSystem().getDisplayMetrics().density); 181 | canvas.drawRoundRect(rectF, rectRadius, rectRadius, backgroundPaint); 182 | 183 | rippleHelper.onDraw(canvas, rippleRadius, rippleColor, padding, rectRadius); 184 | 185 | super.onDraw(canvas); 186 | } 187 | 188 | private void changeEnableTextColor(boolean enabled) { 189 | 190 | int disabledTextColor = enableTextColor; 191 | int normalTextColor = textColor; 192 | 193 | if (null != enableTextColorAnimator) enableTextColorAnimator.cancel(); 194 | 195 | if (enabled) { 196 | enableTextColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), disabledTextColor, normalTextColor); 197 | } else { 198 | enableTextColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), normalTextColor, disabledTextColor); 199 | } 200 | 201 | enableTextColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 202 | @Override 203 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 204 | setTextColor((Integer) valueAnimator.getAnimatedValue()); 205 | } 206 | }); 207 | enableTextColorAnimator.setInterpolator(new DecelerateInterpolator()); 208 | enableTextColorAnimator.setDuration(ANIMATION_DURATION_DISABLED); 209 | enableTextColorAnimator.start(); 210 | } 211 | 212 | private void changeEnableBackgroundColor(boolean enabled) { 213 | 214 | int disabledTextColor = Color.argb((int) (255 * 0.3), Color.red(enableBackgroundColor), Color.green(enableBackgroundColor), Color.blue(enableBackgroundColor)); 215 | int normalTextColor = backgroundColor; 216 | 217 | if (null != enableBackgroundColorAnimator) 218 | enableBackgroundColorAnimator.cancel(); 219 | 220 | if (enabled) { 221 | enableBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), disabledTextColor, normalTextColor); 222 | } else { 223 | enableBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), normalTextColor, disabledTextColor); 224 | } 225 | 226 | enableBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 227 | @Override 228 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 229 | 230 | backgroundPaint.setColor((Integer) valueAnimator.getAnimatedValue()); 231 | invalidate(); 232 | } 233 | }); 234 | enableBackgroundColorAnimator.setInterpolator(new DecelerateInterpolator()); 235 | enableBackgroundColorAnimator.setDuration(ANIMATION_DURATION_DISABLED); 236 | enableBackgroundColorAnimator.start(); 237 | } 238 | 239 | private void changeBackgroundColor(boolean focus) { 240 | 241 | if (null != backgroundColorAnimator) backgroundColorAnimator.cancel(); 242 | 243 | if (focus) { 244 | backgroundColorAnimator = ValueAnimator.ofFloat(1.0f, 0.95f); 245 | } else { 246 | backgroundColorAnimator = ValueAnimator.ofFloat(0.95f, 1.0f); 247 | } 248 | 249 | backgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 250 | @Override 251 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 252 | float v = (float) valueAnimator.getAnimatedValue(); 253 | float[] hsv = new float[3]; 254 | Color.colorToHSV(backgroundColor, hsv); 255 | hsv[2] *= v; 256 | backgroundPaint.setColor(Color.HSVToColor(hsv)); 257 | invalidate(); 258 | } 259 | }); 260 | backgroundColorAnimator.setInterpolator(new DecelerateInterpolator()); 261 | backgroundColorAnimator.setDuration(ANIMATION_DURATION_PRESS); 262 | backgroundColorAnimator.start(); 263 | } 264 | 265 | public void onClick() { 266 | performClick(); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /uimodule/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------