├── .gitignore
├── .travis.yml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── haohao
│ │ └── switchbuttonsimple
│ │ └── MainActivity.java
│ └── res
│ ├── drawable-xxhdpi
│ ├── switch_bg_1.png
│ └── switch_white_1.png
│ ├── layout
│ └── activity_mian.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
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── bintrayUpload.gradle
├── build.gradle
├── docs
├── show.gif
├── show1.gif
├── switch_bg.png
├── switch_bg_1.png
└── switch_white.png
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── switchbutton
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── project.properties
└── src
└── main
├── AndroidManifest.xml
├── java
└── com
│ └── haohao
│ └── switchbutton
│ ├── IOSSwitchView.java
│ └── SwitchButton.java
└── res
├── drawable-xxhdpi
├── switch_bg.png
└── switch_white.png
└── values
├── attrs.xml
└── strings.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | gradlew.bat
19 | gradlew
20 | build/
21 |
22 | # Mirror files
23 | mirror/
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Intellij project files
31 | *.iws
32 |
33 | # Log Files
34 | *.log
35 |
36 | # Android Studio Navigation editor temp files
37 | .navigation/
38 |
39 | # Android Studio captures folder
40 | captures/
41 |
42 | # Intellij
43 | *.iml
44 | .idea/
45 |
46 |
47 | # OS
48 | .DS_Store
49 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk: oraclejdk8
3 | sudo: required
4 | dist: precise
5 | before_install:
6 | - chmod +x gradlew
7 | android:
8 | components:
9 | - tools
10 | - platform-tools
11 | - build-tools-25.0.3
12 | - android-25
13 | - extra-android-m2repository
14 | - extra-google-m2repository
15 | - extra-android-support
16 | - extra-google-google_play_services
17 | licenses:
18 | - 'android-sdk-preview-license-.+'
19 | - 'android-sdk-license-.+'
20 | - 'google-gdk-license-.+'
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwitchButton [](https://jitpack.io/#ronghao/SwitchButton) [](https://travis-ci.org/ronghao/SwitchButton) [](https://raw.githubusercontent.com/ronghao/CacheManage/master/LICENSE) [  ](https://bintray.com/haohao/maven/SwitchButton/1.0.0/link)
2 | 类似ios效果的滑动按钮,支持自定义背景,动画效果,渐变效果
3 |
4 | # 示例
5 | 
6 |
7 | # 功能
8 | + 可自定义背景和游标图片
9 | + 动画效果:滑动动画,颜色渐变
10 | + 背景变灰
11 | + 可支持手势滑动一半退回效果
12 | + 支持禁用模式
13 |
14 |
15 | # 项目添加方法
16 | 在根 build.gradle中添加
17 |
18 | allprojects {
19 | repositories {
20 | ...
21 | maven { url 'https://jitpack.io' }
22 | }
23 | }
24 | 在项目build.gradle中添加
25 |
26 | dependencies {
27 | compile 'com.github.ronghao:SwitchButton:1.0.5'
28 | }
29 |
30 | # XML代码
31 |
37 |
38 |
39 | # Attributes
40 | 自定义背景图片
41 | 自定义图片
42 |
43 | # 方法简介
44 | |||
45 | |-|-|
46 | |setStatus|设置开启关闭|
47 | |setStatusImmediately|设置开启关闭(无动画)|
48 | |setOnSwitchChangeListener|设置滑动开关监听|
49 | |setSwitchEnabled|设置是否可用|
50 |
51 | # 图片示例
52 | |名称|图片样式|
53 | |-|-|
54 | |背景图片||
55 | |游标图片(图片纯白做了特殊处理)||
56 |
57 |
58 | # 版本
59 | - 1.0.5
60 | - 添加类似IOS 滑动选择效果
61 | - 1.0.1
62 | - 添加不可用设置
63 | - 1.0.0
64 | - 添加基本效果
65 |
66 |
67 | # 关于
68 | + 个人博客:[www.haohaohu.com](http://www.haohaohu.com/)
69 | + 如果你也喜欢这个库,Star一下吧,欢迎Fork
70 |
71 | # License
72 |
73 | Copyright 2016 haohaohu
74 |
75 | Licensed under the Apache License, Version 2.0 (the "License");
76 | you may not use this file except in compliance with the License.
77 | You may obtain a copy of the License at
78 |
79 | http://www.apache.org/licenses/LICENSE-2.0
80 |
81 | Unless required by applicable law or agreed to in writing, software
82 | distributed under the License is distributed on an "AS IS" BASIS,
83 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
84 | See the License for the specific language governing permissions and
85 | limitations under the License.
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion compile_sdk_version
5 | buildToolsVersion build_tools_version
6 |
7 | defaultConfig {
8 | applicationId "com.haohao.switchbutton"
9 | minSdkVersion min_sdk_version
10 | targetSdkVersion compile_sdk_version
11 | versionCode version_code
12 | versionName version_name
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 | lintOptions {
22 | abortOnError false
23 | }
24 |
25 | testOptions.unitTests.all {
26 | testLogging {
27 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | compile fileTree(dir: 'libs', include: ['*.jar'])
34 | compile 'com.android.support:appcompat-v7:25.3.1'
35 | // compile 'com.github.ronghao:SwitchButton:1.0.1'
36 | compile project(path: ':switchbutton')
37 | }
38 |
--------------------------------------------------------------------------------
/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 D:\androidIDE\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/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/haohao/switchbuttonsimple/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.haohao.switchbuttonsimple;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.FragmentActivity;
5 | import android.widget.TextView;
6 | import com.haohao.switchbutton.SwitchButton;
7 |
8 | /**
9 | * 主界面
10 | *
11 | * @author haohao(ronghao3508@gmail.com) on 2018/5/1 15:10
12 | * @version v1.0
13 | */
14 | public class MainActivity extends FragmentActivity {
15 | private SwitchButton mSwitchButton;
16 | private TextView mSwitchText;
17 |
18 | private SwitchButton mSwitchButton1;
19 | private TextView mSwitchText1;
20 |
21 | private SwitchButton mSwitchButton2;
22 | private TextView mSwitchText2;
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_mian);
28 | init();
29 | }
30 |
31 | private void init() {
32 | mSwitchButton = (SwitchButton) findViewById(R.id.switchbutton);
33 | mSwitchButton.setStatusImmediately(true);
34 | mSwitchButton.setOnSwitchChangeListener(new SwitchButton.OnSwitchChangeListener() {
35 | @Override
36 | public void onSwitch(int status) {
37 | mSwitchText.setText(String.valueOf("状态:" + getStateStr(status)));
38 | }
39 | });
40 | mSwitchText = (TextView) findViewById(R.id.switchbutton_text);
41 | mSwitchText.setText(String.valueOf("状态:" + getStateStr(mSwitchButton.getSwitchStatus())));
42 |
43 | mSwitchButton1 = (SwitchButton) findViewById(R.id.switchbutton1);
44 | mSwitchButton1.setStatusImmediately(true);
45 | mSwitchButton1.setOnSwitchChangeListener(new SwitchButton.OnSwitchChangeListener() {
46 | @Override
47 | public void onSwitch(int status) {
48 | mSwitchText1.setText(String.valueOf("状态:" + getStateStr(status)));
49 | mSwitchButton2.setSwitchEnabled(status == 1);
50 | mSwitchText2.setText(
51 | String.valueOf("状态:" + getStateStr(mSwitchButton2.getSwitchStatus())));
52 | }
53 | });
54 | mSwitchText1 = (TextView) findViewById(R.id.switchbutton1_text);
55 | mSwitchText1.setText(String.valueOf("状态:" + getStateStr(mSwitchButton.getSwitchStatus())));
56 |
57 | mSwitchButton2 = (SwitchButton) findViewById(R.id.switchbutton2);
58 | mSwitchButton2.setSwitchEnabled(false);
59 | mSwitchButton2.setStatusImmediately(true);
60 | mSwitchButton2.setOnSwitchChangeListener(new SwitchButton.OnSwitchChangeListener() {
61 | @Override
62 | public void onSwitch(int status) {
63 | mSwitchText2.setText(String.valueOf("状态:" + getStateStr(status)));
64 | }
65 | });
66 |
67 | mSwitchText2 = (TextView) findViewById(R.id.switchbutton2_text);
68 | mSwitchText2.setText(String.valueOf("状态:" + getStateStr(mSwitchButton2.getSwitchStatus())));
69 | }
70 |
71 | private String getStateStr(int status) {
72 | switch (status) {
73 | case SwitchButton.SWITCH_ON:
74 | return "开启";
75 | case SwitchButton.SWITCH_OFF:
76 | return "关闭";
77 | case SwitchButton.SWITCH_UNENABLE:
78 | return "禁用";
79 | default:
80 | }
81 | return "";
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/switch_bg_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/app/src/main/res/drawable-xxhdpi/switch_bg_1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/switch_white_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/app/src/main/res/drawable-xxhdpi/switch_white_1.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_mian.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
24 |
25 |
32 |
33 |
34 |
39 |
40 |
46 |
47 |
54 |
55 |
56 |
61 |
67 |
68 |
75 |
76 |
77 |
82 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SwitchButton
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/bintrayUpload.gradle:
--------------------------------------------------------------------------------
1 | def projectVersionName = android.defaultConfig.versionName
2 |
3 | // load properties
4 | Properties properties = new Properties()
5 | File localPropertiesFile = project.file("local.properties");
6 | if (localPropertiesFile.exists()) {
7 | properties.load(localPropertiesFile.newDataInputStream())
8 | }
9 | File projectPropertiesFile = project.file("project.properties");
10 | if (projectPropertiesFile.exists()) {
11 | properties.load(projectPropertiesFile.newDataInputStream())
12 | }
13 | def javadocName = properties.getProperty("javadoc.name")
14 |
15 | // This generates sources.jar
16 | task sourcesJar(type: Jar) {
17 | from android.sourceSets.main.java.srcDirs
18 | classifier = 'sources'
19 | }
20 |
21 | task javadoc(type: Javadoc) {
22 | source = android.sourceSets.main.java.srcDirs
23 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
24 | }
25 |
26 | // This generates javadoc.jar
27 | task javadocJar(type: Jar, dependsOn: javadoc) {
28 | classifier = 'javadoc'
29 | from javadoc.destinationDir
30 | }
31 |
32 | artifacts {
33 | archives javadocJar
34 | archives sourcesJar
35 | }
36 |
37 | // javadoc configuration
38 | javadoc {
39 | options {
40 | encoding "UTF-8"
41 | charSet 'UTF-8'
42 | author true
43 | version projectVersionName
44 | links "http://docs.oracle.com/javase/7/docs/api"
45 | title javadocName
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.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 | maven { url 'https://jitpack.io' }
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
26 | ext {
27 | // Sdk and tools
28 | build_tools_version = "25.0.3"
29 | compile_sdk_version = 25
30 | min_sdk_version = 14
31 | target_sdk_version = 25
32 |
33 | version_code = 2
34 | version_name = '1.0.1'
35 |
36 | // App dependencies
37 | support_version = '27.1.0'
38 | gson_version = '2.8.2'
39 | }
40 |
--------------------------------------------------------------------------------
/docs/show.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/docs/show.gif
--------------------------------------------------------------------------------
/docs/show1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/docs/show1.gif
--------------------------------------------------------------------------------
/docs/switch_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/docs/switch_bg.png
--------------------------------------------------------------------------------
/docs/switch_bg_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/docs/switch_bg_1.png
--------------------------------------------------------------------------------
/docs/switch_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/docs/switch_white.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jun 27 13:37:29 CST 2017
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-3.3-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 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':switchbutton'
2 |
--------------------------------------------------------------------------------
/switchbutton/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/switchbutton/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion compile_sdk_version
5 | buildToolsVersion build_tools_version
6 |
7 | defaultConfig {
8 | minSdkVersion min_sdk_version
9 | targetSdkVersion compile_sdk_version
10 | versionCode version_code
11 | versionName version_name
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | lintOptions {
21 | abortOnError false
22 | }
23 |
24 | testOptions.unitTests.all {
25 | testLogging {
26 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
27 | }
28 | }
29 | }
30 |
31 | dependencies {
32 | compile fileTree(dir: 'libs', include: ['*.jar'])
33 | }
34 |
35 | apply from: "../bintrayUpload.gradle"
36 |
37 |
--------------------------------------------------------------------------------
/switchbutton/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 D:\androidIDE\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 |
--------------------------------------------------------------------------------
/switchbutton/project.properties:
--------------------------------------------------------------------------------
1 | #project
2 | project.name=SwitchButton
3 | project.groupId=com.haohao
4 | project.artifactId=switchbutton
5 | project.packaging=aar
6 | project.siteUrl=https://github.com/ronghao/SwitchButton
7 | project.gitUrl=https://github.com/ronghao/SwitchButton.git
8 | #javadoc
9 | javadoc.name=SwitchButton
--------------------------------------------------------------------------------
/switchbutton/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/switchbutton/src/main/java/com/haohao/switchbutton/IOSSwitchView.java:
--------------------------------------------------------------------------------
1 | package com.haohao.switchbutton;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.RadialGradient;
11 | import android.graphics.RectF;
12 | import android.graphics.Shader;
13 | import android.os.Build;
14 | import android.os.Parcel;
15 | import android.os.Parcelable;
16 | import android.util.AttributeSet;
17 | import android.util.TypedValue;
18 | import android.view.MotionEvent;
19 | import android.view.View;
20 | import android.view.animation.AccelerateInterpolator;
21 |
22 | /**
23 | * fork on https://github.com/iielse/SwitchButton
24 | */
25 | public class IOSSwitchView extends View {
26 |
27 | private static final int STATE_SWITCH_ON = 4;
28 | private static final int STATE_SWITCH_ON2 = 3;
29 | private static final int STATE_SWITCH_OFF2 = 2;
30 | private static final int STATE_SWITCH_OFF = 1;
31 |
32 | private final AccelerateInterpolator interpolator = new AccelerateInterpolator(2);
33 | private final Paint paint = new Paint();
34 | private final Path sPath = new Path();
35 | private final Path bPath = new Path();
36 | private final RectF bRectF = new RectF();
37 | protected float ratioAspect = 0.68f; // (0,1]
38 | protected float animationSpeed = 0.1f; // (0,1]
39 | protected int colorPrimary;
40 | protected int colorPrimaryDark;
41 | protected int colorOff;
42 | protected int colorOffDark;
43 | protected int colorShadow;
44 | protected boolean hasShadow;
45 | protected boolean isOpened;
46 | protected boolean isEnable = true;
47 | private float sAnim, bAnim;
48 | private RadialGradient shadowGradient;
49 | private int state;
50 | private int lastState;
51 | private boolean isCanVisibleDrawing = false;
52 | private OnClickListener mOnClickListener;
53 | private float sRight;
54 | private float sCenterX, sCenterY;
55 | private float sScale;
56 |
57 | private float bOffset;
58 | private float bRadius, bStrokeWidth;
59 | private float bWidth;
60 | private float bLeft;
61 | private float bRight;
62 | private float bOnLeftX, bOn2LeftX, bOff2LeftX, bOffLeftX;
63 |
64 | private float shadowReservedHeight;
65 | private OnStateChangedListener listener = new OnStateChangedListener() {
66 | @Override
67 | public void toggleToOn(IOSSwitchView view) {
68 | toggleSwitch(true);
69 | }
70 |
71 | @Override
72 | public void toggleToOff(IOSSwitchView view) {
73 | toggleSwitch(false);
74 | }
75 | };
76 |
77 | public IOSSwitchView(Context context) {
78 | this(context, null);
79 | }
80 |
81 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
82 | public IOSSwitchView(Context context, AttributeSet attrs) {
83 | super(context, attrs);
84 | setLayerType(LAYER_TYPE_SOFTWARE, null);
85 |
86 | final int DEFAULT_COLOR_PRIMARY = 0xFF4BD763;
87 | final int DEFAULT_COLOR_PRIMARY_DARK = 0xFF3AC652;
88 |
89 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IOSSwitchView);
90 | colorPrimary = a.getColor(R.styleable.IOSSwitchView_primaryColor, DEFAULT_COLOR_PRIMARY);
91 | colorPrimaryDark =
92 | a.getColor(R.styleable.IOSSwitchView_primaryColorDark, DEFAULT_COLOR_PRIMARY_DARK);
93 | colorOff = a.getColor(R.styleable.IOSSwitchView_offColor, 0xFFE3E3E3);
94 | colorOffDark = a.getColor(R.styleable.IOSSwitchView_offColorDark, 0xFFBFBFBF);
95 | colorShadow = a.getColor(R.styleable.IOSSwitchView_shadowColor, 0xFF333333);
96 | ratioAspect = a.getFloat(R.styleable.IOSSwitchView_ratioAspect, 0.68f);
97 | hasShadow = a.getBoolean(R.styleable.IOSSwitchView_hasShadow, true);
98 | isOpened = a.getBoolean(R.styleable.IOSSwitchView_isOpened, false);
99 | isEnable = a.getBoolean(R.styleable.IOSSwitchView_isEnable, true);
100 |
101 | state = isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
102 | lastState = state;
103 | a.recycle();
104 |
105 | if (colorPrimary == DEFAULT_COLOR_PRIMARY
106 | && colorPrimaryDark == DEFAULT_COLOR_PRIMARY_DARK) {
107 | try {
108 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
109 | TypedValue primaryColorTypedValue = new TypedValue();
110 | context.getTheme()
111 | .resolveAttribute(android.R.attr.colorPrimary, primaryColorTypedValue,
112 | true);
113 | if (primaryColorTypedValue.data > 0) colorPrimary = primaryColorTypedValue.data;
114 | TypedValue primaryColorDarkTypedValue = new TypedValue();
115 | context.getTheme()
116 | .resolveAttribute(android.R.attr.colorPrimaryDark,
117 | primaryColorDarkTypedValue, true);
118 | if (primaryColorDarkTypedValue.data > 0) {
119 | colorPrimaryDark = primaryColorDarkTypedValue.data;
120 | }
121 | }
122 | } catch (Exception ignore) {
123 | }
124 | }
125 | }
126 |
127 | public void setColor(int newColorPrimary, int newColorPrimaryDark) {
128 | setColor(newColorPrimary, newColorPrimaryDark, colorOff, colorOffDark);
129 | }
130 |
131 | public void setColor(int newColorPrimary, int newColorPrimaryDark, int newColorOff,
132 | int newColorOffDark) {
133 | setColor(newColorPrimary, newColorPrimaryDark, newColorOff, newColorOffDark, colorShadow);
134 | }
135 |
136 | public void setColor(int newColorPrimary, int newColorPrimaryDark, int newColorOff,
137 | int newColorOffDark, int newColorShadow) {
138 | colorPrimary = newColorPrimary;
139 | colorPrimaryDark = newColorPrimaryDark;
140 | colorOff = newColorOff;
141 | colorOffDark = newColorOffDark;
142 | colorShadow = newColorShadow;
143 | invalidate();
144 | }
145 |
146 | public void setShadow(boolean shadow) {
147 | hasShadow = shadow;
148 | invalidate();
149 | }
150 |
151 | public boolean isOpened() {
152 | return isOpened;
153 | }
154 |
155 | public void setOpened(boolean isOpened) {
156 | int wishState = isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
157 | if (wishState == state) {
158 | return;
159 | }
160 | refreshState(wishState);
161 | }
162 |
163 | public boolean isEnable() {
164 | return isEnable;
165 | }
166 |
167 | public void setEnable(boolean enable) {
168 | isEnable = enable;
169 | }
170 |
171 | public void toggleSwitch(boolean isOpened) {
172 | int wishState = isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
173 | if (wishState == state) {
174 | return;
175 | }
176 | if ((wishState == STATE_SWITCH_ON && (state == STATE_SWITCH_OFF
177 | || state == STATE_SWITCH_OFF2)) || (wishState == STATE_SWITCH_OFF && (state
178 | == STATE_SWITCH_ON || state == STATE_SWITCH_ON2))) {
179 | sAnim = 1;
180 | }
181 | bAnim = 1;
182 | refreshState(wishState);
183 | }
184 |
185 | private void refreshState(int newState) {
186 | if (!isOpened && newState == STATE_SWITCH_ON) {
187 | isOpened = true;
188 | } else if (isOpened && newState == STATE_SWITCH_OFF) {
189 | isOpened = false;
190 | }
191 | lastState = state;
192 | state = newState;
193 | postInvalidate();
194 | }
195 |
196 | @Override
197 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
198 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
199 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
200 | int resultWidth;
201 | if (widthMode == MeasureSpec.EXACTLY) {
202 | resultWidth = widthSize;
203 | } else {
204 | resultWidth = (int) (56 * getResources().getDisplayMetrics().density + 0.5f)
205 | + getPaddingLeft()
206 | + getPaddingRight();
207 | if (widthMode == MeasureSpec.AT_MOST) {
208 | resultWidth = Math.min(resultWidth, widthSize);
209 | }
210 | }
211 |
212 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
213 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
214 | int resultHeight;
215 | if (heightMode == MeasureSpec.EXACTLY) {
216 | resultHeight = heightSize;
217 | } else {
218 | resultHeight = (int) (resultWidth * ratioAspect) + getPaddingTop() + getPaddingBottom();
219 | if (heightMode == MeasureSpec.AT_MOST) {
220 | resultHeight = Math.min(resultHeight, heightSize);
221 | }
222 | }
223 | setMeasuredDimension(resultWidth, resultHeight);
224 | }
225 |
226 | @Override
227 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
228 | super.onSizeChanged(w, h, oldw, oldh);
229 |
230 | isCanVisibleDrawing = w > getPaddingLeft() + getPaddingRight()
231 | && h > getPaddingTop() + getPaddingBottom();
232 |
233 | if (isCanVisibleDrawing) {
234 | int actuallyDrawingAreaWidth = w - getPaddingLeft() - getPaddingRight();
235 | int actuallyDrawingAreaHeight = h - getPaddingTop() - getPaddingBottom();
236 |
237 | int actuallyDrawingAreaLeft;
238 | int actuallyDrawingAreaRight;
239 | int actuallyDrawingAreaTop;
240 | int actuallyDrawingAreaBottom;
241 | if (actuallyDrawingAreaWidth * ratioAspect < actuallyDrawingAreaHeight) {
242 | actuallyDrawingAreaLeft = getPaddingLeft();
243 | actuallyDrawingAreaRight = w - getPaddingRight();
244 | int heightExtraSize =
245 | (int) (actuallyDrawingAreaHeight - actuallyDrawingAreaWidth * ratioAspect);
246 | actuallyDrawingAreaTop = getPaddingTop() + heightExtraSize / 2;
247 | actuallyDrawingAreaBottom = getHeight() - getPaddingBottom() - heightExtraSize / 2;
248 | } else {
249 | int widthExtraSize =
250 | (int) (actuallyDrawingAreaWidth - actuallyDrawingAreaHeight / ratioAspect);
251 | actuallyDrawingAreaLeft = getPaddingLeft() + widthExtraSize / 2;
252 | actuallyDrawingAreaRight = getWidth() - getPaddingRight() - widthExtraSize / 2;
253 | actuallyDrawingAreaTop = getPaddingTop();
254 | actuallyDrawingAreaBottom = getHeight() - getPaddingBottom();
255 | }
256 |
257 | shadowReservedHeight =
258 | (int) ((actuallyDrawingAreaBottom - actuallyDrawingAreaTop) * 0.07f);
259 | float sLeft = actuallyDrawingAreaLeft;
260 | float sTop = actuallyDrawingAreaTop + shadowReservedHeight;
261 | sRight = actuallyDrawingAreaRight;
262 | float sBottom = actuallyDrawingAreaBottom - shadowReservedHeight;
263 |
264 | float sHeight = sBottom - sTop;
265 | sCenterX = (sRight + sLeft) / 2;
266 | sCenterY = (sBottom + sTop) / 2;
267 |
268 | bLeft = sLeft;
269 | bWidth = sBottom - sTop;
270 | bRight = sLeft + bWidth;
271 | final float halfHeightOfS = bWidth / 2; // OfB
272 | bRadius = halfHeightOfS * 0.95f;
273 | bOffset = bRadius * 0.2f; // offset of switching
274 | bStrokeWidth = (halfHeightOfS - bRadius) * 2;
275 | bOnLeftX = sRight - bWidth;
276 | bOn2LeftX = bOnLeftX - bOffset;
277 | bOffLeftX = sLeft;
278 | bOff2LeftX = bOffLeftX + bOffset;
279 | sScale = 1 - bStrokeWidth / sHeight;
280 |
281 | sPath.reset();
282 | RectF sRectF = new RectF();
283 | sRectF.top = sTop;
284 | sRectF.bottom = sBottom;
285 | sRectF.left = sLeft;
286 | sRectF.right = sLeft + sHeight;
287 | sPath.arcTo(sRectF, 90, 180);
288 | sRectF.left = sRight - sHeight;
289 | sRectF.right = sRight;
290 | sPath.arcTo(sRectF, 270, 180);
291 | sPath.close();
292 |
293 | bRectF.left = bLeft;
294 | bRectF.right = bRight;
295 | bRectF.top = sTop + bStrokeWidth / 2; // bTop = sTop
296 | bRectF.bottom = sBottom - bStrokeWidth / 2; // bBottom = sBottom
297 | float bCenterX = (bRight + bLeft) / 2;
298 | float bCenterY = (sBottom + sTop) / 2;
299 |
300 | int red = colorShadow >> 16 & 0xFF;
301 | int green = colorShadow >> 8 & 0xFF;
302 | int blue = colorShadow & 0xFF;
303 | shadowGradient = new RadialGradient(bCenterX, bCenterY, bRadius,
304 | Color.argb(200, red, green, blue), Color.argb(25, red, green, blue),
305 | Shader.TileMode.CLAMP);
306 | }
307 | }
308 |
309 | private void calcBPath(float percent) {
310 | bPath.reset();
311 | bRectF.left = bLeft + bStrokeWidth / 2;
312 | bRectF.right = bRight - bStrokeWidth / 2;
313 | bPath.arcTo(bRectF, 90, 180);
314 | bRectF.left = bLeft + percent * bOffset + bStrokeWidth / 2;
315 | bRectF.right = bRight + percent * bOffset - bStrokeWidth / 2;
316 | bPath.arcTo(bRectF, 270, 180);
317 | bPath.close();
318 | }
319 |
320 | private float calcBTranslate(float percent) {
321 | float result = 0;
322 | switch (state - lastState) {
323 | case 1:
324 | if (state == STATE_SWITCH_OFF2) {
325 | result = bOffLeftX; // off -> off2
326 | } else if (state == STATE_SWITCH_ON) {
327 | result = bOnLeftX - (bOnLeftX - bOn2LeftX) * percent; // on2 -> on
328 | }
329 | break;
330 | case 2:
331 | if (state == STATE_SWITCH_ON) {
332 | result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent; // off2 -> on
333 | } else if (state == STATE_SWITCH_ON2) {
334 | result = bOn2LeftX - (bOn2LeftX - bOffLeftX) * percent; // off -> on2
335 | }
336 | break;
337 | case 3:
338 | result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent; // off -> on
339 | break;
340 | case -1:
341 | if (state == STATE_SWITCH_ON2) {
342 | result = bOn2LeftX + (bOnLeftX - bOn2LeftX) * percent; // on -> on2
343 | } else if (state == STATE_SWITCH_OFF) {
344 | result = bOffLeftX; // off2 -> off
345 | }
346 | break;
347 | case -2:
348 | if (state == STATE_SWITCH_OFF) {
349 | result = bOffLeftX + (bOn2LeftX - bOffLeftX) * percent; // on2 -> off
350 | } else if (state == STATE_SWITCH_OFF2) {
351 | result = bOff2LeftX + (bOnLeftX - bOff2LeftX) * percent; // on -> off2
352 | }
353 | break;
354 | case -3:
355 | result = bOffLeftX + (bOnLeftX - bOffLeftX) * percent; // on -> off
356 | break;
357 | default: // init
358 | case 0:
359 | if (state == STATE_SWITCH_OFF) {
360 | result = bOffLeftX; // off -> off
361 | } else if (state == STATE_SWITCH_ON) {
362 | result = bOnLeftX; // on -> on
363 | }
364 | break;
365 | }
366 | return result - bOffLeftX;
367 | }
368 |
369 | @Override
370 | protected void onDraw(Canvas canvas) {
371 | super.onDraw(canvas);
372 | if (!isCanVisibleDrawing) return;
373 |
374 | paint.setAntiAlias(true);
375 | final boolean isOn = (state == STATE_SWITCH_ON || state == STATE_SWITCH_ON2);
376 | // Draw background
377 | paint.setStyle(Paint.Style.FILL);
378 | if (isEnable) {
379 | paint.setColor(isOn ? colorPrimary : colorOff);
380 | } else {
381 | paint.setColor(0xffe4e4e4);
382 | }
383 |
384 | canvas.drawPath(sPath, paint);
385 |
386 | sAnim = sAnim - animationSpeed > 0 ? sAnim - animationSpeed : 0;
387 | bAnim = bAnim - animationSpeed > 0 ? bAnim - animationSpeed : 0;
388 |
389 | final float dsAnim = interpolator.getInterpolation(sAnim);
390 | final float dbAnim = interpolator.getInterpolation(bAnim);
391 | // Draw background animation
392 | final float scale = sScale * (isOn ? dsAnim : 1 - dsAnim);
393 | final float scaleOffset = (sRight - sCenterX - bRadius) * (isOn ? 1 - dsAnim : dsAnim);
394 | canvas.save();
395 | canvas.scale(scale, scale, sCenterX + scaleOffset, sCenterY);
396 | paint.setColor(isEnable ? 0xFFFFFFFF : 0xffe4e4e4);
397 | canvas.drawPath(sPath, paint);
398 | canvas.restore();
399 | // To prepare center bar path
400 | canvas.save();
401 | canvas.translate(calcBTranslate(dbAnim), shadowReservedHeight);
402 | final boolean isState2 = (state == STATE_SWITCH_ON2 || state == STATE_SWITCH_OFF2);
403 | calcBPath(isState2 ? 1 - dbAnim : dbAnim);
404 | // Use center bar path to draw shadow
405 | if (isEnable && hasShadow) {
406 | paint.setStyle(Paint.Style.FILL);
407 | paint.setShader(shadowGradient);
408 | canvas.drawPath(bPath, paint);
409 | paint.setShader(null);
410 | }
411 | canvas.translate(0, -shadowReservedHeight);
412 |
413 | // draw bar
414 | canvas.scale(0.98f, 0.98f, bWidth / 2, bWidth / 2);
415 | paint.setStyle(Paint.Style.FILL);
416 | paint.setColor(isEnable ? 0xffffffff : 0xffB3B3B3);
417 | canvas.drawPath(bPath, paint);
418 | paint.setStyle(Paint.Style.STROKE);
419 | paint.setStrokeWidth(bStrokeWidth * 0.5f);
420 | if (isEnable) {
421 | paint.setColor(isOn ? colorPrimaryDark : colorOffDark);
422 | } else {
423 | paint.setColor(0xffe4e4e4);
424 | }
425 |
426 | canvas.drawPath(bPath, paint);
427 | canvas.restore();
428 |
429 | paint.reset();
430 | if (sAnim > 0 || bAnim > 0) invalidate();
431 | }
432 |
433 | @Override
434 | public boolean onTouchEvent(MotionEvent event) {
435 | if (!isEnable) return true;
436 | if ((state == STATE_SWITCH_ON || state == STATE_SWITCH_OFF) && (sAnim * bAnim == 0)) {
437 | switch (event.getAction()) {
438 | case MotionEvent.ACTION_DOWN:
439 | return true;
440 | case MotionEvent.ACTION_UP:
441 | lastState = state;
442 |
443 | bAnim = 1;
444 | if (state == STATE_SWITCH_OFF) {
445 | refreshState(STATE_SWITCH_OFF2);
446 | listener.toggleToOn(this);
447 | } else if (state == STATE_SWITCH_ON) {
448 | refreshState(STATE_SWITCH_ON2);
449 | listener.toggleToOff(this);
450 | }
451 |
452 | if (mOnClickListener != null) {
453 | mOnClickListener.onClick(this);
454 | }
455 | break;
456 | default:
457 | }
458 | }
459 | return super.onTouchEvent(event);
460 | }
461 |
462 | @Override
463 | public void setOnClickListener(OnClickListener l) {
464 | super.setOnClickListener(l);
465 | mOnClickListener = l;
466 | }
467 |
468 | public void setOnStateChangedListener(OnStateChangedListener listener) {
469 | if (listener == null) throw new IllegalArgumentException("empty listener");
470 | this.listener = listener;
471 | }
472 |
473 | @Override
474 | public Parcelable onSaveInstanceState() {
475 | Parcelable superState = super.onSaveInstanceState();
476 | SavedState ss = new SavedState(superState);
477 | ss.isOpened = isOpened;
478 | return ss;
479 | }
480 |
481 | @Override
482 | public void onRestoreInstanceState(Parcelable state) {
483 | SavedState ss = (SavedState) state;
484 | super.onRestoreInstanceState(ss.getSuperState());
485 | this.isOpened = ss.isOpened;
486 | this.state = this.isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
487 | invalidate();
488 | }
489 |
490 | public interface OnStateChangedListener {
491 | void toggleToOn(IOSSwitchView view);
492 |
493 | void toggleToOff(IOSSwitchView view);
494 | }
495 |
496 | private static final class SavedState extends BaseSavedState {
497 | public static final Creator CREATOR = new Creator() {
498 | @Override
499 | public SavedState createFromParcel(Parcel in) {
500 | return new SavedState(in);
501 | }
502 |
503 | @Override
504 | public SavedState[] newArray(int size) {
505 | return new SavedState[size];
506 | }
507 | };
508 | private boolean isOpened;
509 |
510 | SavedState(Parcelable superState) {
511 | super(superState);
512 | }
513 |
514 | private SavedState(Parcel in) {
515 | super(in);
516 | isOpened = 1 == in.readInt();
517 | }
518 |
519 | @Override
520 | public void writeToParcel(Parcel out, int flags) {
521 | super.writeToParcel(out, flags);
522 | out.writeInt(isOpened ? 1 : 0);
523 | }
524 |
525 | // fixed by Night99 https://github.com/g19980115
526 | @Override
527 | public int describeContents() {
528 | return 0;
529 | }
530 | }
531 | }
532 |
--------------------------------------------------------------------------------
/switchbutton/src/main/java/com/haohao/switchbutton/SwitchButton.java:
--------------------------------------------------------------------------------
1 | package com.haohao.switchbutton;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.Canvas;
9 | import android.graphics.ColorMatrix;
10 | import android.graphics.ColorMatrixColorFilter;
11 | import android.graphics.Paint;
12 | import android.util.AttributeSet;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 | import android.view.ViewParent;
16 |
17 | /**
18 | * 滑动按钮
19 | *
20 | * @author haohao(ronghao3508@gmail.com) on 2016/5/1 12:33
21 | * @version v1.0
22 | */
23 | public class SwitchButton extends View {
24 |
25 | //关闭状态
26 | public static final int SWITCH_OFF = 0;
27 | //打开状态
28 | public static final int SWITCH_ON = 1;
29 | //滚动状态
30 | public static final int SWITCH_SCROLING = 2;
31 | //不可用状态
32 | public static final int SWITCH_UNENABLE = 3;
33 | //当前状态
34 | private int switchStatus = SWITCH_OFF;
35 |
36 | //开关状态图
37 | private Bitmap mSwitchBg, mSwitch_thumb;
38 |
39 | private float mSrcX = 0f, mDstX = 0f;
40 | private float mMoveX = 0f;
41 | private int mBgWidth = 0;
42 | private int mBgHeight = 0;
43 | private int mThumbWidth = 0;
44 | private int mThumbHeight = 0;
45 | private int mMinMoveWidth = 0;
46 | private int mMaxMoveWidth = 0;
47 | private int mPadding = 0;
48 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
49 | private Paint mPaintBg = new Paint(Paint.ANTI_ALIAS_FLAG);
50 |
51 | private boolean isChange = false;
52 | private long TIMEOUT = 100;
53 | private boolean isTouchMove;//手按下移动
54 | private boolean isStartAnimation;//手抬起后播放动画
55 | private float MOVESTAP = 2f;
56 |
57 | private OnSwitchChangeListener mListener;
58 | private boolean isImmediately = true;
59 | private int switchBg = R.drawable.switch_bg;
60 | private int switchCursor = R.drawable.switch_white;
61 | private boolean isEnable = true;
62 |
63 | public SwitchButton(Context context) {
64 | this(context, null);
65 | }
66 |
67 | public SwitchButton(Context context, AttributeSet attrs) {
68 | super(context, attrs);
69 | initAttrs(attrs);
70 | init();
71 | }
72 |
73 | public SwitchButton(Context context, AttributeSet attrs, int defStyle) {
74 | super(context, attrs, defStyle);
75 | initAttrs(attrs);
76 | init();
77 | }
78 |
79 | @Override
80 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//方法是用来测量控价所占的大小
81 | setMeasuredDimension(mBgWidth, mBgHeight);//0 0自己定义控件的大小
82 |
83 | //在onMeasure(int, int)中,必须调用setMeasuredDimension(int width, int height)
84 | // 来存储测量得到的宽度和高度值,如果没有这么去做会触发异常IllegalStateException。
85 | }
86 |
87 | private void initAttrs(AttributeSet attrs) {
88 | if (attrs != null) {
89 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.switchbutton);
90 | switchBg = a.getResourceId(R.styleable.switchbutton_switchbackground,
91 | R.drawable.switch_bg);
92 | switchCursor =
93 | a.getResourceId(R.styleable.switchbutton_switchcursor, R.drawable.switch_white);
94 | a.recycle();
95 | }
96 | }
97 |
98 | //初始化三幅图片
99 | private void init() {
100 | Resources res = getResources();
101 | mSwitchBg = BitmapFactory.decodeResource(res, switchBg);
102 | mSwitch_thumb = BitmapFactory.decodeResource(res, switchCursor);
103 |
104 | mBgWidth = mSwitchBg.getWidth();//背景宽度
105 | mBgHeight = mSwitchBg.getHeight();//背景高度
106 | mThumbWidth = mSwitch_thumb.getWidth();
107 | mThumbHeight = mSwitch_thumb.getHeight();
108 |
109 | mPadding = (mBgHeight - mThumbHeight) / 2;
110 | mMinMoveWidth = mPadding;
111 | mMaxMoveWidth = mBgWidth - mThumbWidth - mPadding;
112 | }
113 |
114 | /**
115 | * 设置开关的状态
116 | *
117 | * @param on 是否打开开关 打开为true 关闭为false
118 | */
119 | public void setStatus(boolean on) {
120 | if (!isEnable) return;
121 | isImmediately = false;
122 | switchStatus = (on ? SWITCH_ON : SWITCH_OFF);
123 | mMoveX = mBgWidth - mThumbWidth;
124 | if (switchStatus == 0) {
125 | isStartAnimation = true;
126 | }
127 |
128 | postInvalidate();//更新界面,重新绘制界面
129 | }
130 |
131 | /**
132 | * 设置开关的状态,第一次启动,快速打开无动画
133 | *
134 | * @param on 是否打开开关 打开为true 关闭为false
135 | */
136 | public void setStatusImmediately(boolean on) {
137 | if (!isEnable) return;
138 | isImmediately = true;
139 | switchStatus = (on ? SWITCH_ON : SWITCH_OFF);
140 | mMoveX = mBgWidth - mThumbWidth;
141 | if (switchStatus == SWITCH_OFF) {
142 | isStartAnimation = true;
143 | }
144 |
145 | postInvalidate();//更新界面,重新绘制界面
146 | }
147 |
148 | /**
149 | * 获取开关当前状态
150 | *
151 | * @return 0关闭 1打开 2不可用
152 | */
153 | public int getSwitchStatus() {
154 | if (!isEnable) {
155 | return SWITCH_UNENABLE;
156 | }
157 | return switchStatus;
158 | }
159 |
160 | /**
161 | * 设置事件监听
162 | *
163 | * @param mListener 事件监听接口
164 | */
165 | public void setOnSwitchChangeListener(OnSwitchChangeListener mListener) {
166 | this.mListener = mListener;
167 | }
168 |
169 | /**
170 | * 获取是否可用
171 | *
172 | * @return 可选择为true 不可选择为false
173 | */
174 | public boolean getSwitchEnabled() {
175 | return this.isEnable;
176 | }
177 |
178 | /**
179 | * 设置是否可用
180 | *
181 | * @param isEnable 可选择为true 不可选择为false
182 | */
183 | public void setSwitchEnabled(boolean isEnable) {
184 | this.isEnable = isEnable;
185 | }
186 |
187 | public void toggle() {
188 | setStatus(getSwitchStatus() == SWITCH_OFF);
189 | }
190 |
191 | @Override
192 | public boolean onTouchEvent(MotionEvent event) {
193 | if (!isEnable) return true;
194 | int action = event.getAction();
195 | switch (action) {
196 | case MotionEvent.ACTION_DOWN:
197 | isTouchMove = true;
198 | attemptClaimDrag();
199 | mSrcX = (int) event.getX();
200 | break;
201 | case MotionEvent.ACTION_MOVE:
202 | mDstX = event.getX();
203 | if (mSrcX == mDstX) {
204 | return true;
205 | }
206 | mMoveX += mDstX - mSrcX;
207 | mSrcX = mDstX;
208 | break;
209 | case MotionEvent.ACTION_UP:
210 | isTouchMove = false;
211 | long time = event.getEventTime() - event.getDownTime();
212 | isStartAnimation = true;
213 | if (time < TIMEOUT) {
214 | switchStatus = (switchStatus == SWITCH_OFF ? SWITCH_ON : SWITCH_OFF);
215 | } else {
216 | if (event.getX() < mBgWidth / 2) {
217 | switchStatus = SWITCH_OFF;
218 | } else {
219 | switchStatus = SWITCH_ON;
220 | }
221 | }
222 | if (mListener != null) {
223 | mListener.onSwitch(switchStatus);
224 | }
225 | break;
226 | default:
227 | break;
228 | }
229 | postInvalidate();
230 | return true;
231 | }
232 |
233 | @Override
234 | protected void onDraw(Canvas canvas) {
235 | // 绘制按钮
236 | mMoveX = Math.min(mMoveX, mMaxMoveWidth);
237 | mMoveX = Math.max(mMoveX, mMinMoveWidth);
238 | if (!isTouchMove) {
239 | if (!isImmediately && isStartAnimation) {
240 | if (switchStatus == SWITCH_OFF) {
241 | if (mMoveX > mMinMoveWidth) {
242 | float i = (mMoveX - mMinMoveWidth) / 4;
243 | float move = Math.max(i, MOVESTAP);
244 | mMoveX -= move;
245 | } else {
246 | mMoveX = mMinMoveWidth;
247 | isStartAnimation = false;
248 | }
249 | } else {
250 | if (mMoveX < mMaxMoveWidth) {
251 | float i = (mMaxMoveWidth - mMoveX) / 4;
252 | float move = Math.max(i, MOVESTAP);
253 | mMoveX += move;
254 | } else {
255 | mMoveX = mMaxMoveWidth;
256 | isStartAnimation = false;
257 | }
258 | }
259 | postInvalidate();
260 | } else {
261 | if (switchStatus == SWITCH_OFF) {
262 | mMoveX = mMinMoveWidth;
263 | isStartAnimation = false;
264 | } else {
265 | mMoveX = mMaxMoveWidth;
266 | isStartAnimation = false;
267 | }
268 | isImmediately = false;
269 | postInvalidate();
270 | }
271 | }
272 |
273 | //绘制底部图片
274 | if (isEnable) {
275 | ColorMatrix cm = new ColorMatrix();//颜色矩阵
276 | cm.setSaturation((mMoveX - mMinMoveWidth) / (mMaxMoveWidth - mMinMoveWidth));
277 | ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
278 | mPaintBg.setColorFilter(f);
279 | canvas.drawBitmap(mSwitchBg, 0, 0, mPaintBg);
280 | //Log.i("mMoveX", mMoveX + "");
281 | canvas.drawBitmap(mSwitch_thumb, mMoveX, mPadding, mPaint);
282 | } else {
283 | ColorMatrix cm = new ColorMatrix();//颜色矩阵
284 | cm.setSaturation(0.01f);
285 | ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
286 | mPaintBg.setColorFilter(f);
287 | mPaintBg.setAntiAlias(true);//边缘平滑处理
288 | canvas.drawBitmap(mSwitchBg, 0, 0, mPaintBg);
289 | //Log.i("mMoveX", mMoveX + "");
290 | canvas.drawBitmap(mSwitch_thumb, mMoveX, mPadding, mPaintBg);
291 | }
292 | }
293 |
294 | //阻止父组件对事件的响应,父组件不能响应 action包括action_up action_down action_move
295 | private void attemptClaimDrag() {
296 | ViewParent mParent = getParent();
297 | if (mParent != null) {
298 | mParent.requestDisallowInterceptTouchEvent(true);
299 | }
300 | }
301 |
302 | public interface OnSwitchChangeListener {
303 | /**
304 | * 状态变化监听
305 | *
306 | * @param status 1(SWITCH_ON)开启 0 (SWITCH_OFF)关闭
307 | */
308 | void onSwitch(int status);
309 | }
310 | }
--------------------------------------------------------------------------------
/switchbutton/src/main/res/drawable-xxhdpi/switch_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/switchbutton/src/main/res/drawable-xxhdpi/switch_bg.png
--------------------------------------------------------------------------------
/switchbutton/src/main/res/drawable-xxhdpi/switch_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronghao/SwitchButton/fcb1c2e2b07ef14f9ff21024738d2e403c514e6e/switchbutton/src/main/res/drawable-xxhdpi/switch_white.png
--------------------------------------------------------------------------------
/switchbutton/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 |
--------------------------------------------------------------------------------
/switchbutton/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SwitchButton
3 |
4 |
--------------------------------------------------------------------------------