├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── cn
│ │ └── wang
│ │ └── refresh
│ │ └── ptc
│ │ └── scrollrulerview
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── cn
│ │ │ └── wang
│ │ │ └── refresh
│ │ │ └── ptc
│ │ │ └── scrollrulerview
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── icon_center_pointer.png
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── cn
│ └── wang
│ └── refresh
│ └── ptc
│ └── scrollrulerview
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── ruler
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── cn
│ │ └── wang
│ │ └── ruler
│ │ ├── RulerActivity.java
│ │ ├── RulerHelper.java
│ │ ├── RulerView.java
│ │ ├── ScrollChange.java
│ │ ├── ScrollRulerLayout.java
│ │ └── ScrollSelected.java
│ └── res
│ ├── drawable
│ └── icon_center_pointer.png
│ ├── layout
│ └── activity_ruler.xml
│ └── values
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/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 | 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 | # Android Studio captures folder
33 | captures/
34 |
35 | # IntelliJ
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/assetWizardSettings.xml
41 | .idea/dictionaries
42 | .idea/libraries
43 | .idea/caches
44 |
45 | # Keystore files
46 | # Uncomment the following line if you do not want to check your keystore files in.
47 | #*.jks
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidScrollRuler
2 | 自定义View实现跟随手指滚动的刻度尺,实现了类似SeekBar的滑动选中效果。[项目解析]()
3 | 需要的可以直接下载demo运行看效果,不上传Maven仓库是因为这个效果是我想要的,但是并不是你们都想要的,这里只是提供一个基础模版,需要的话还是要使用者自己修改代码。如有碰到问题随时issues联系我~
4 |
5 | **UI图:**![]()
6 |
7 | **功能:**
8 |
9 | - 通过设置最小值跟最大值的范围,以及offset值。View将根据这些数据去计算出需要几个小刻度和几个长刻度,和每个长刻度上面显示的数值。
10 | - 指针可以随意的定制。
11 | - 当滑动停止后,刻度尺会根据四舍五入将距离指针最近的长刻度滑动到指针的位置。
12 | - 支持范围越界回弹。
13 | - 支持设置默认值。
14 |
15 | 
16 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'cn.werouter.plugin'
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "cn.wang.refresh.ptc.scrollrulerview"
7 | minSdkVersion 15
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | javaCompileOptions {
13 | annotationProcessorOptions {
14 | arguments = [WEROUTER_MODULE_NAME: project.getName()]
15 | }
16 | }
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(include: ['*.jar'], dir: 'libs')
28 | implementation 'com.android.support:appcompat-v7:28.0.0'
29 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
33 | //WeRouter
34 | annotationProcessor 'cn.wang.werouter:compiler:1.0.0'
35 | implementation 'cn.wang.werouter:api:1.0.0'
36 | implementation project(':ruler')
37 | }
38 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/cn/wang/refresh/ptc/scrollrulerview/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package cn.wang.refresh.ptc.scrollrulerview;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("cn.wang.refresh.ptc.scrollrulerview", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/wang/refresh/ptc/scrollrulerview/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.wang.refresh.ptc.scrollrulerview;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | import cn.router.api.router.WeRouter;
9 | import cn.wang.ruler.RulerActivity;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 | WeRouter.init(getApplication());
18 | findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
19 | @Override
20 | public void onClick(View v) {
21 | Intent intent = new Intent(MainActivity.this,RulerActivity.class);
22 | startActivity(intent);
23 | }
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_center_pointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/drawable/icon_center_pointer.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ScrollRulerView
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/cn/wang/refresh/ptc/scrollrulerview/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cn.wang.refresh.ptc.scrollrulerview;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | maven {
8 | url 'https://dl.bintray.com/wangchaochao/WeRouter'
9 | }
10 | jcenter()
11 | }
12 | dependencies {
13 | classpath 'com.android.tools.build:gradle:3.2.0'
14 | classpath 'cn.wang.werouter:plugin:1.0.0'
15 | classpath 'com.novoda:bintray-release:0.9'
16 |
17 | // NOTE: Do not place your application dependencies here; they belong
18 | // in the individual module build.gradle files
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | google()
25 | maven {
26 | url 'https://dl.bintray.com/wangchaochao/WeRouter'
27 | }
28 | jcenter()
29 | }
30 | }
31 |
32 | task clean(type: Delete) {
33 | delete rootProject.buildDir
34 | }
35 |
--------------------------------------------------------------------------------
/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 |
15 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
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 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/ruler/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/ruler/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.novoda.bintray-release'
3 |
4 | android {
5 | compileSdkVersion 28
6 |
7 |
8 |
9 | defaultConfig {
10 | minSdkVersion 15
11 | targetSdkVersion 28
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 | javaCompileOptions {
17 | annotationProcessorOptions {
18 | arguments = [WEROUTER_MODULE_NAME: project.getName()]
19 | }
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 |
35 | implementation 'com.android.support:appcompat-v7:28.0.0'
36 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
37 | }
38 |
39 | publish {
40 | repoName = 'AndroidScrollRuler'//不指明,默认是上传到maven,指明的话这里的名字需要和你创建的仓库的名字一样
41 | userOrg = 'wangchaochao'//bintray.com你的用户名
42 | groupId = 'cn.wang.view'//jcenter上的路径
43 | artifactId = 'ruler'//项目名称
44 | publishVersion = '1.0.0'//版本号
45 | desc = 'Android自定义View实现滑动的刻度尺'//描述,不重要
46 | website = 'https://github.com/WangcWj/AndroidScrollRuler'//网站,不重要;尽量模拟github上的地址,例如我这样的;当然你有地址最好了
47 | }
48 |
49 | tasks.withType(Javadoc) {
50 | options.addStringOption('Xdoclint:none', '-quiet')
51 | options.addStringOption('encoding', 'UTF-8')
52 | options.addStringOption('charSet', 'UTF-8')
53 | }
54 |
--------------------------------------------------------------------------------
/ruler/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/ruler/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ruler/src/main/java/cn/wang/ruler/RulerActivity.java:
--------------------------------------------------------------------------------
1 | package cn.wang.ruler;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 |
6 | /**
7 | * @author WANG
8 | * GitHub -> https://github.com/WangcWj/AndroidScrollRuler
9 | *
10 | * 提交issues联系作者.
11 | */
12 | public class RulerActivity extends AppCompatActivity {
13 | ScrollRulerLayout rulerView;
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_ruler);
18 | rulerView = findViewById(R.id.ruler_view);
19 | rulerView.setScope(5000,15001,500);
20 | rulerView.setCurrentItem("10000");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ruler/src/main/java/cn/wang/ruler/RulerHelper.java:
--------------------------------------------------------------------------------
1 | package cn.wang.ruler;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Created to : {@link RulerView}的帮助类
8 | *
9 | * GitHub -> https://github.com/WangcWj/AndroidScrollRuler
10 | * 提交issues联系作者.
11 | *
12 | * @author WANG
13 | * @date 2019/3/21
14 | */
15 | public class RulerHelper {
16 |
17 | private int currentLine = -1;
18 | private int offSet = 500;
19 | private int lineNumbers;
20 | private List mPoints;
21 | private int mCenterPointX;
22 | private ScrollChange scrollChange;
23 |
24 | private String currentText;
25 |
26 | private List texts;
27 |
28 | public RulerHelper(ScrollChange scrollChange) {
29 | texts = new ArrayList<>();
30 | mPoints = new ArrayList<>();
31 | this.scrollChange = scrollChange;
32 | }
33 |
34 | public int getCounts() {
35 | return lineNumbers;
36 | }
37 |
38 | public String getCurrentText() {
39 | return currentText;
40 | }
41 |
42 | public void setCenterPoint(int mCenterPoint) {
43 | this.mCenterPointX = mCenterPoint;
44 | }
45 |
46 | public boolean isLongLine(int index) {
47 | int lineIndex = index / 10;
48 | if (currentLine != lineIndex) {
49 | currentLine = lineIndex;
50 | return true;
51 | }
52 | return false;
53 | }
54 |
55 | public void setScope(int start, int count,int offSet) {
56 | if(offSet != 0) {
57 | this.offSet = offSet;
58 | }
59 | lineNumbers = (count - start) / (this.offSet / 10);
60 | for (int i = start; i <= count; i += this.offSet) {
61 | texts.add(String.valueOf(i));
62 | }
63 | }
64 |
65 | public String getTextByIndex(int index) {
66 | if (index < 0 || index >= texts.size()) {
67 | return "";
68 | }
69 | return texts.get(index);
70 | }
71 |
72 | public int getCenterPointX() {
73 | return mCenterPointX;
74 | }
75 |
76 | public void setCurrentItem(String text) {
77 | setCurrentText(text);
78 | }
79 |
80 | public void setCurrentText(int index) {
81 | if (index >= 0 && index < texts.size()) {
82 | this.currentText = texts.get(index);
83 | }
84 | }
85 |
86 |
87 | public void setCurrentText(String currentText) {
88 | this.currentText = currentText;
89 | }
90 |
91 | public int getScrollDistance(int x) {
92 | for (int i = 0; i < mPoints.size(); i++) {
93 | int pointX = mPoints.get(i);
94 | if (0 == i && x < pointX) {
95 | //当前的x比第一个位置的x坐标都小 也就是需要往右移动到第一个长线的位置.
96 | setCurrentText(0);
97 | return x - pointX;
98 | } else if (i == mPoints.size() - 1 && x > pointX) {
99 | //当前的x比最后一个左边的x都大,也就是需要往左移动到最后一个长线位置.
100 | setCurrentText(texts.size() - 1);
101 | return x - pointX;
102 | } else {
103 | if (i + 1 < mPoints.size()) {
104 | int nextX = mPoints.get(i + 1);
105 | if (x > pointX && x <= nextX) {
106 | int distance = (nextX - pointX) / 2;
107 | int dis = x - pointX;
108 | if (dis > distance) {
109 | //说明往下一个移动
110 | setCurrentText(i + 1);
111 | return x - nextX;
112 | } else {
113 | setCurrentText(i);
114 | //往前一个移动
115 | return x - pointX;
116 | }
117 | }
118 | }
119 | }
120 | }
121 | return 0;
122 | }
123 |
124 | public void addPoint(int x) {
125 | mPoints.add(x);
126 | if (mPoints.size() == texts.size() && null != scrollChange) {
127 | int index = texts.indexOf(currentText);
128 | if (index < 0) {
129 | return;
130 | }
131 | int currentX = mPoints.get(index);
132 | if (currentX < 0) {
133 | return;
134 | }
135 | scrollChange.startScroll(mCenterPointX - currentX);
136 | }
137 | }
138 |
139 | public boolean isFull() {
140 | return texts.size() == mPoints.size();
141 | }
142 |
143 | public void destroy(){
144 | mPoints.clear();
145 | texts.clear();
146 | mPoints = null;
147 | texts = null;
148 | scrollChange = null;
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/ruler/src/main/java/cn/wang/ruler/RulerView.java:
--------------------------------------------------------------------------------
1 | package cn.wang.ruler;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.support.annotation.Nullable;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.VelocityTracker;
12 | import android.view.View;
13 | import android.view.ViewConfiguration;
14 | import android.view.ViewGroup;
15 | import android.widget.Scroller;
16 |
17 | /**
18 | * Created to :
19 | * 1.遇到的问题是怎么让他滚动?
20 | * 2.滚动的时候绘制的内容不会错乱掉?
21 | * 3.怎么判断滑动的边界?
22 | * 4.怎么让它Fling?
23 | * 5.fling之后怎么去判断中间坐标的位置?
24 | *
25 | * GitHub -> https://github.com/WangcWj/AndroidScrollRuler
26 | * 提交issues联系作者.
27 | *
28 | * @author WANG
29 | * @date 2019/3/21
30 | */
31 | public class RulerView extends View implements ScrollChange {
32 |
33 | /**
34 | * 帮助类,帮助计算或者存储一些数据.
35 | */
36 | private RulerHelper mRulerHelper;
37 |
38 | /**
39 | * 这个 负责平缓滑动
40 | */
41 | private Scroller mScroller;
42 |
43 | /**
44 | * 每次滑到长刻度的时候 会调用一次
45 | */
46 | private ScrollSelected scrollSelected;
47 |
48 | /**
49 | * ...自行百度一下
50 | */
51 | private VelocityTracker velocityTracker;
52 |
53 | /**
54 | * 花刻度的Paint
55 | */
56 | private Paint mLinePaint;
57 |
58 | /**
59 | * 画文本的
60 | */
61 | private Paint mTextPaint;
62 |
63 | /**
64 | * 每个刻度之间的间距 dp
65 | */
66 | private int mLineSpace;
67 |
68 | /**
69 | * 小刻度的高度 dp
70 | */
71 | private int mSmallLineHeight;
72 |
73 | /**
74 | * 长刻度的高度 dp
75 | */
76 | private int mLongLineHeight;
77 |
78 | /**
79 | * 每根线的宽度 dp
80 | */
81 | private int mLineWidth;
82 |
83 | /**
84 | * 每个矩形用一个Rect去画,因为之前用line去画的话发现宽度会减少一半.
85 | */
86 | private Rect mRect;
87 |
88 | /**
89 | * 一个下标计数器,从集合中取出每个下标对应的Text值.
90 | */
91 | private int mTextIndex = 0;
92 |
93 | /**
94 | * 所有刻度+padding+margin的总px长度.
95 | */
96 | private int mCountWidth;
97 |
98 | /**
99 | * 出去当前屏幕之外的剩余的px长度.
100 | */
101 | private int mCountRange = -1;
102 |
103 | /**
104 | * 也就是过滤一下 不会那么频繁的去调用而已.
105 | */
106 | private float mPreDistance = -1;
107 |
108 | /**
109 | * 说明现在的操作是手指抬起之后.
110 | */
111 | private boolean mPressUp = false;
112 |
113 | /**
114 | * 表示目前处于fling的滑动中.
115 | */
116 | private boolean isFling = false;
117 |
118 | /**
119 | * x轴的最小速率.
120 | */
121 | private int mMinVelocity;
122 |
123 | /**
124 | * 点击事件的初始x坐标.
125 | */
126 | float startX;
127 |
128 | private int mMarginLeft = -1;
129 | private int mPaddingRight = -1;
130 | private int dpFor05;
131 | private int dpFor14;
132 |
133 | public RulerView(Context context) {
134 | super(context);
135 | init(context);
136 | }
137 |
138 | public RulerView(Context context, @Nullable AttributeSet attrs) {
139 | super(context, attrs);
140 | init(context);
141 | }
142 |
143 | public RulerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
144 | super(context, attrs, defStyleAttr);
145 | init(context);
146 | }
147 |
148 | private void init(Context context) {
149 | initDistanceForDp();
150 | mRulerHelper = new RulerHelper(this);
151 | mTextPaint = new Paint();
152 | mTextPaint.setAntiAlias(true);
153 | mTextPaint.setTextSize(dp2px(10));
154 | mTextPaint.setColor(Color.parseColor("#cccccc"));
155 | mTextPaint.setTextAlign(Paint.Align.CENTER);
156 |
157 | mLinePaint = new Paint();
158 | mLinePaint.setAntiAlias(true);
159 | mLinePaint.setStrokeWidth(mLineWidth);
160 | mLinePaint.setStyle(Paint.Style.FILL);
161 | mLinePaint.setColor(Color.parseColor("#dddddd"));
162 |
163 | mRect = new Rect();
164 | mScroller = new Scroller(context);
165 |
166 | velocityTracker = VelocityTracker.obtain();
167 | ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
168 | mMinVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
169 |
170 | }
171 |
172 | private void initDistanceForDp() {
173 | mLineWidth = dp2px(1);
174 | mLineSpace = dp2px(5);
175 | mSmallLineHeight = dp2px(4);
176 | mLongLineHeight = dp2px(9);
177 | dpFor05 = dp2px(0.5f);
178 | dpFor14 = dp2px(14);
179 | }
180 |
181 | /**
182 | * 设置刻度尺的范围, 比如: 100 - 2000
183 | * @param start
184 | * @param end
185 | */
186 | public void setScope(int start, int end,int offSet) {
187 | mRulerHelper.setScope(start, end,offSet);
188 | int counts = mRulerHelper.getCounts();
189 | mCountWidth = counts * mLineSpace + counts * mLineWidth;
190 | invalidate();
191 | }
192 |
193 | /**
194 | * 设置当前刻度初始化的位置 你也可以自定义
195 | * @param text
196 | */
197 | public void setCurrentItem(String text) {
198 | mRulerHelper.setCurrentItem(text);
199 | }
200 |
201 | /**
202 | * 获取当前指针停留的那个长刻度的位置.
203 | * @return
204 | */
205 | public String getCurrentText() {
206 | return mRulerHelper.getCurrentText();
207 | }
208 |
209 | /**
210 | * 设置滑动选中监听,滑动中不会有监听,有需要可以自行添加.
211 | * @param scrollSelected
212 | */
213 | public void setScrollSelected(ScrollSelected scrollSelected) {
214 | this.scrollSelected = scrollSelected;
215 | }
216 |
217 | @Override
218 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
219 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
220 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
221 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
222 | int mode = MeasureSpec.getMode(heightMeasureSpec);
223 | if (mode == MeasureSpec.AT_MOST) {
224 | heightSize = dp2px(54);
225 | }
226 | setMeasuredDimension(widthSize, heightSize);
227 | }
228 |
229 | @Override
230 | protected void onDraw(Canvas canvas) {
231 | super.onDraw(canvas);
232 | if (mRulerHelper.getCounts() > 0) {
233 | ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
234 | if (mMarginLeft == -1 || mCountRange == -1) {
235 | if (null != layoutParams) {
236 | mMarginLeft = layoutParams.leftMargin;
237 | mPaddingRight = layoutParams.rightMargin;
238 | }
239 | mCountRange = mCountWidth - getWidth() + mMarginLeft + mPaddingRight;
240 | mRulerHelper.setCenterPoint(getWidth() / 2);
241 | }
242 | drawRuler(canvas);
243 | }
244 | }
245 |
246 | /**
247 | * 画刻度
248 | * @param canvas
249 | */
250 | private void drawRuler(Canvas canvas) {
251 | mTextIndex = 0;
252 | for (int index = 0; index <= mRulerHelper.getCounts(); index++) {
253 | boolean longLine = mRulerHelper.isLongLine(index);
254 | int lineCount = mLineWidth * index;
255 | mRect.left = index * mLineSpace + lineCount + mMarginLeft;
256 | mRect.top = getStartY(longLine);
257 | mRect.right = mRect.left + mLineWidth;
258 | mRect.bottom = getEndY();
259 | if (longLine) {
260 | if (!mRulerHelper.isFull()) {
261 | mRulerHelper.addPoint(mRect.left);
262 | }
263 | String text = mRulerHelper.getTextByIndex(mTextIndex);
264 | mTextIndex++;
265 | canvas.drawText(text, mRect.centerX(), getMeasuredHeight() - dpFor14, mTextPaint);
266 | }
267 | canvas.drawRect(mRect, mLinePaint);
268 | mRect.setEmpty();
269 | }
270 | }
271 |
272 | /**
273 | * 首先你得知道矩形是怎么画的,left right top bottom.
274 | * 了解之后就明白这个值是怎么计算的了.
275 | * @param isLong
276 | * @return
277 | */
278 | private int getStartY(boolean isLong) {
279 | if (isLong) {
280 | return getMeasuredHeight() - mLongLineHeight - dpFor05;
281 | } else {
282 | return getMeasuredHeight() - mSmallLineHeight - dpFor05;
283 | }
284 | }
285 |
286 | /**
287 | * 跟上面一样的道理
288 | * @return
289 | */
290 | private int getEndY() {
291 | return getMeasuredHeight() - dpFor05;
292 | }
293 |
294 | /**
295 | * event.getRawX() 是点击的view相对于屏幕左上角(0,0)的x坐标.
296 | * event.getX() 是点击的view相对于view本身的左上角(0,0)的x坐标.
297 | *
298 | * @param event
299 | * @return
300 | */
301 | @Override
302 | public boolean onTouchEvent(MotionEvent event) {
303 | velocityTracker.addMovement(event);
304 | switch (event.getAction()) {
305 | case MotionEvent.ACTION_DOWN:
306 | mScroller.forceFinished(true);
307 | mPressUp = false;
308 | isFling = false;
309 | startX = event.getX();
310 | break;
311 | case MotionEvent.ACTION_MOVE:
312 | mPressUp = false;
313 | float distance = event.getX() - startX;
314 | if (mPreDistance != distance) {
315 | doScroll((int) -distance, 0, 0);
316 | invalidate();
317 | }
318 | startX = event.getX();
319 | break;
320 | case MotionEvent.ACTION_UP:
321 | case MotionEvent.ACTION_CANCEL:
322 | mPressUp = true;
323 | velocityTracker.computeCurrentVelocity(1000);
324 | float xVelocity = velocityTracker.getXVelocity();
325 | if (Math.abs(xVelocity) >= mMinVelocity) {
326 | isFling = true;
327 | int finalX = mScroller.getCurrX();
328 | int centerPointX = mRulerHelper.getCenterPointX();
329 | int velocityX = (int) (xVelocity * 0.35);
330 | mScroller.fling(finalX, 0, -velocityX, 0, -centerPointX, mCountRange + centerPointX, 0, 0);
331 | invalidate();
332 | } else {
333 | isFling = false;
334 | scrollFinish();
335 | }
336 | velocityTracker.clear();
337 | break;
338 | default:
339 | break;
340 | }
341 | return true;
342 | }
343 |
344 | /**
345 | * 滑动停止之后重新定位
346 | */
347 | public void scrollFinish() {
348 | int finalX = mScroller.getFinalX();
349 | int centerPointX = mRulerHelper.getCenterPointX();
350 | int currentX = centerPointX + finalX;
351 | int scrollDistance = mRulerHelper.getScrollDistance(currentX);
352 | if (0 != scrollDistance) {
353 | //第一个参数是滚动开始时的x的坐标
354 | //第二个参数是滚动开始时的y的坐标
355 | //第三个参数是在X轴上滚动的距离, 负数向右滚动.
356 | //第四个参数是在Y轴上滚动的距离,负数向下滚动.
357 | mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -scrollDistance, 0, 300);
358 | invalidate();
359 | if (scrollSelected != null) {
360 | scrollSelected.selected(getCurrentText());
361 | }
362 | }
363 | }
364 |
365 | private void doScroll(int dx, int dy, int duration) {
366 | mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, duration);
367 | }
368 |
369 | @Override
370 | public void computeScroll() {
371 | if (mScroller.computeScrollOffset()) {
372 | if (mScroller.getCurrX() == mScroller.getFinalX() && mPressUp && isFling) {
373 | mPressUp = false;
374 | isFling = false;
375 | scrollFinish();
376 | }
377 | scrollTo(mScroller.getCurrX(), 0);
378 | invalidate();
379 | }
380 | super.computeScroll();
381 | }
382 |
383 | /**
384 | * 回收一些资源吧,在{@link View#onDetachedFromWindow()}
385 | */
386 | public void destroy(){
387 | velocityTracker.recycle();
388 | velocityTracker = null;
389 | mRulerHelper.destroy();
390 | scrollSelected = null;
391 | }
392 |
393 | private int dp2px(float dp) {
394 | return (int) (getContext().getResources().getDisplayMetrics().density * dp + 0.5f);
395 | }
396 |
397 | /**
398 | * 因为设置网络数据的时候有个延迟,然后设置范围数值之后View需要重新绘制一下,
399 | * 等绘制结束之后才能做一些滑动的操作,所以这里面的监听就是绘制结束之后做的监听.
400 | *
401 | * @param distance
402 | */
403 | @Override
404 | public void startScroll(int distance) {
405 | doScroll(-distance, 0, 300);
406 | postInvalidate();
407 | }
408 | }
409 |
--------------------------------------------------------------------------------
/ruler/src/main/java/cn/wang/ruler/ScrollChange.java:
--------------------------------------------------------------------------------
1 | package cn.wang.ruler;
2 |
3 | /**
4 | * Created to : 给{@link RulerHelper}使用的方法,来让{@link RulerView}滑动.
5 | * GitHub -> https://github.com/WangcWj/AndroidScrollRuler
6 | * 提交issues联系作者.
7 | * @author WANG
8 | * @date 2019/3/25
9 | */
10 | public interface ScrollChange {
11 | void startScroll(int distance);
12 | }
13 |
--------------------------------------------------------------------------------
/ruler/src/main/java/cn/wang/ruler/ScrollRulerLayout.java:
--------------------------------------------------------------------------------
1 | package cn.wang.ruler;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.util.AttributeSet;
8 | import android.view.ViewGroup;
9 | import android.widget.ImageView;
10 | import android.widget.Toast;
11 |
12 | /**
13 | * Created to : RulerView的载体.
14 | * GitHub -> https://github.com/WangcWj/AndroidScrollRuler
15 | * 提交issues联系作者.
16 | * @author WANG
17 | * @date 2019/3/21
18 | */
19 | public class ScrollRulerLayout extends ViewGroup implements ScrollSelected {
20 |
21 | private RulerView mRulerView;
22 | private ImageView mCenterPointer;
23 | private Paint mLinePaint;
24 | private int mLineWidth;
25 | private int mPadding;
26 |
27 | public ScrollRulerLayout(Context context) {
28 | this(context, null);
29 | }
30 |
31 | public ScrollRulerLayout(Context context, AttributeSet attrs) {
32 | this(context, attrs, 0);
33 | }
34 |
35 | public ScrollRulerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
36 | super(context, attrs, defStyleAttr);
37 | init(context);
38 | }
39 |
40 | private void init(Context context) {
41 | mLinePaint = new Paint();
42 | mLinePaint.setAntiAlias(true);
43 | mLineWidth = dp2px(1);
44 | mLinePaint.setStrokeWidth(mLineWidth);
45 | mLinePaint.setStrokeWidth(mLineWidth);
46 | mLinePaint.setStyle(Paint.Style.FILL);
47 | mLinePaint.setColor(Color.parseColor("#dddddd"));
48 | mPadding = dp2px(10);
49 | }
50 |
51 | @Override
52 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
53 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
54 | if (null != mRulerView) {
55 | mRulerView.measure(widthMeasureSpec, heightMeasureSpec);
56 | }
57 | if (null != mCenterPointer) {
58 | LayoutParams layoutParams = mCenterPointer.getLayoutParams();
59 | mCenterPointer.measure(layoutParams.width, heightMeasureSpec);
60 | }
61 | }
62 |
63 | @Override
64 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
65 | if (null != mRulerView) {
66 | MarginLayoutParams lp = (MarginLayoutParams) mRulerView.getLayoutParams();
67 | int left = getPaddingLeft();
68 | int top = getPaddingTop();
69 | int right = getPaddingRight() + getMeasuredWidth();
70 | int bottom = getPaddingBottom() + getMeasuredHeight() - mLineWidth;
71 | mRulerView.layout(left, top, right, bottom);
72 | }
73 | if (null != mCenterPointer) {
74 | int measuredWidth = mCenterPointer.getMeasuredWidth();
75 | int width = getWidth() + getPaddingLeft() + getPaddingRight();
76 | int left = width / 2 - measuredWidth / 2;
77 | int right = width / 2 + measuredWidth / 2;
78 | mCenterPointer.layout(left, 0, right, getHeight());
79 | }
80 | }
81 |
82 | @Override
83 | protected void dispatchDraw(Canvas canvas) {
84 | super.dispatchDraw(canvas);
85 | canvas.drawLine(0, getHeight(), getWidth(), getHeight(), mLinePaint);
86 | }
87 |
88 | @Override
89 | protected void onFinishInflate() {
90 | super.onFinishInflate();
91 | mRulerView = new RulerView(getContext());
92 | mCenterPointer = new ImageView(getContext());
93 | mCenterPointer.setImageResource(R.drawable.icon_center_pointer);
94 | MarginLayoutParams layoutParams = new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
95 | layoutParams.leftMargin = mPadding;
96 | layoutParams.rightMargin = mPadding;
97 | mRulerView.setLayoutParams(layoutParams);
98 | layoutParams.width = dp2px(12);
99 | layoutParams.height = LayoutParams.MATCH_PARENT;
100 | mCenterPointer.setLayoutParams(layoutParams);
101 | mRulerView.setScrollSelected(this);
102 | addView(mRulerView);
103 | addView(mCenterPointer);
104 | }
105 |
106 | public void setScope(int start, int end,int offSet) {
107 | if (null != mRulerView) {
108 | mRulerView.setScope(start, end,offSet);
109 | }
110 | }
111 |
112 | public void setRulerViewMargin(int left, int top, int right, int bottom) {
113 | MarginLayoutParams layoutParams = (MarginLayoutParams) mRulerView.getLayoutParams();
114 | setRulerViewMargin(layoutParams, left, top, right, bottom);
115 | }
116 |
117 | public void setCurrentItem(String text) {
118 | if (null != mRulerView) {
119 | mRulerView.setCurrentItem(text);
120 | }
121 | }
122 |
123 | public void setRulerViewMargin(MarginLayoutParams layoutParams, int left, int top, int right, int bottom) {
124 | layoutParams.leftMargin = left;
125 | layoutParams.topMargin = top;
126 | layoutParams.rightMargin = right;
127 | layoutParams.bottomMargin = bottom;
128 | mRulerView.setLayoutParams(layoutParams);
129 | }
130 |
131 | @Override
132 | protected void onDetachedFromWindow() {
133 | mRulerView.destroy();
134 | mRulerView = null;
135 | mCenterPointer = null;
136 | super.onDetachedFromWindow();
137 | }
138 |
139 | private int dp2px(float dp) {
140 | return (int) (getContext().getResources().getDisplayMetrics().density * dp + 0.5f);
141 | }
142 |
143 |
144 | @Override
145 | public void selected(String selected) {
146 | Toast.makeText(getContext(), selected, Toast.LENGTH_SHORT).show();
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/ruler/src/main/java/cn/wang/ruler/ScrollSelected.java:
--------------------------------------------------------------------------------
1 | package cn.wang.ruler;
2 |
3 | /**
4 | * Created to : 滑动停止之后选中项的监听.
5 | * GitHub -> https://github.com/WangcWj/AndroidScrollRuler
6 | * 提交issues联系作者.
7 | * @author WANG
8 | * @date 2019/3/25
9 | */
10 | public interface ScrollSelected {
11 |
12 | void selected(String selected);
13 | }
14 |
--------------------------------------------------------------------------------
/ruler/src/main/res/drawable/icon_center_pointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangcWj/AndroidScrollRuler/0f33d77da809b3c33f72d642f63941e3fc925519/ruler/src/main/res/drawable/icon_center_pointer.png
--------------------------------------------------------------------------------
/ruler/src/main/res/layout/activity_ruler.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/ruler/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ruler
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':ruler'
2 |
--------------------------------------------------------------------------------