├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── attrs.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
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable
│ │ │ │ ├── myshape.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── activity_land.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── admin
│ │ │ └── mytimewheelclock
│ │ │ ├── utils
│ │ │ ├── SizeUtils.java
│ │ │ ├── DateUtils.java
│ │ │ └── NumberToChineseUtils.java
│ │ │ ├── MainActivity.java
│ │ │ ├── LandActivity.java
│ │ │ ├── AutoRotateMinuteView.java
│ │ │ ├── AutoRotateHoursView.java
│ │ │ ├── AutoRotateSecondView.java
│ │ │ ├── AutoRotateDayView.java
│ │ │ ├── AutoRotateWeekView.java
│ │ │ └── AutoRotateMonthView.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── admin
│ │ │ └── mytimewheelclock
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── admin
│ │ └── mytimewheelclock
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── Files
├── test.jpg
├── app-debug.apk
├── app-release.apk
└── S90528-10265422.mp4
├── .gitignore
├── README.md
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/Files/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/Files/test.jpg
--------------------------------------------------------------------------------
/Files/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/Files/app-debug.apk
--------------------------------------------------------------------------------
/Files/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/Files/app-release.apk
--------------------------------------------------------------------------------
/Files/S90528-10265422.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/Files/S90528-10265422.mp4
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MyTimeWheelClock
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zylgg/MyTimeWheelClock/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/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/drawable/myshape.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MyTimeWheelClock
2 | ###(注:以下效果图和视频为首次上传版本,最新的以源码为准。apk有问题已更新)
3 | ## 一个仿抖音的时间钟轮盘
4 | 
5 |
6 | # 如下可下载apk查看
7 | [apk](https://github.com/zylgg/MyTimeWheelClock/blob/master/Files/app-release.apk)
8 | # 如下可查看效果(由于录制gif有卡顿)
9 | [时间转动效果的视频](https://github.com/zylgg/MyTimeWheelClock/blob/master/Files/S90528-10265422.mp4)
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/admin/mytimewheelclock/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
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 | }
--------------------------------------------------------------------------------
/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 |
16 |
--------------------------------------------------------------------------------
/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/com/example/admin/mytimewheelclock/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
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("com.example.admin.mytimewheelclock", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.example.admin.mytimewheelclock"
7 | minSdkVersion 15
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:28.0.0'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/utils/SizeUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock.utils;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.util.DisplayMetrics;
6 | import android.view.View;
7 |
8 | /**
9 | * 尺寸转换工具类
10 | */
11 | public class SizeUtils {
12 | public static int dp2px(Context context, float dp) {
13 | final float density = context.getResources().getDisplayMetrics().density;
14 | return (int) (dp * density + 0.5);
15 | }
16 |
17 | public static int px2dp(Context context, float px) {
18 | final float density = context.getResources().getDisplayMetrics().density;
19 | return (int) (px / density + 0.5);
20 | }
21 |
22 | public static int getScreenWidth(Activity context){
23 | DisplayMetrics dm = new DisplayMetrics();
24 | context.getWindow().getWindowManager().getDefaultDisplay().getMetrics(dm);
25 | int screenWidth = dm.widthPixels;
26 | int screenHeight = dm.heightPixels;
27 | return screenWidth;
28 | }
29 |
30 | /**
31 | * 测量控件尺寸
32 | *
33 | * @param measureSpec
34 | * @return
35 | */
36 | public static int measureSize(Context mContext,int measureSpec) {
37 | int result;
38 | int specSize = View.MeasureSpec.getSize(measureSpec);
39 | int specMode = View.MeasureSpec.getMode(measureSpec);
40 | if (specMode == View.MeasureSpec.EXACTLY) {
41 | result = specSize;
42 | } else {
43 | result = SizeUtils.dp2px(mContext, 300);
44 | if (specMode == View.MeasureSpec.AT_MOST) {
45 | result = Math.min(result, specSize);
46 | }
47 | }
48 | return result;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
22 |
23 |
27 |
28 |
32 |
33 |
37 |
38 |
47 |
48 |
55 |
56 |
57 |
58 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/utils/DateUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock.utils;
2 |
3 |
4 | import java.text.SimpleDateFormat;
5 | import java.util.Calendar;
6 | import java.util.Date;
7 |
8 | /**
9 | * 日期工具类 by hpf
10 | */
11 | public class DateUtils {
12 | //返回当前年月日
13 | public static String getNowDate() {
14 | Date date = new Date();
15 | String nowDate = new SimpleDateFormat("yyyy年MM月dd日").format(date);
16 | return nowDate;
17 | }
18 |
19 | //返回当前年份
20 | public static int getYear() {
21 | Date date = new Date();
22 | String year = new SimpleDateFormat("yyyy").format(date);
23 | return Integer.parseInt(year);
24 | }
25 |
26 | //返回当前月份
27 | public static int getMonth() {
28 | Date date = new Date();
29 | String month = new SimpleDateFormat("MM").format(date);
30 | return Integer.parseInt(month);
31 | }
32 |
33 | //判断闰年
34 | private static boolean isLeap(int year) {
35 | if (((year % 100 == 0) && year % 400 == 0) || ((year % 100 != 0) && year % 4 == 0))
36 | return true;
37 | else
38 | return false;
39 | }
40 |
41 | //返回当月天数
42 | public static int getDays(int year, int month) {
43 | int days;
44 | int FebDay = 28;
45 | if (isLeap(year))
46 | FebDay = 29;
47 | switch (month) {
48 | case 1:
49 | case 3:
50 | case 5:
51 | case 7:
52 | case 8:
53 | case 10:
54 | case 12:
55 | days = 31;
56 | break;
57 | case 4:
58 | case 6:
59 | case 9:
60 | case 11:
61 | days = 30;
62 | break;
63 | case 2:
64 | days = FebDay;
65 | break;
66 | default:
67 | days = 0;
68 | break;
69 | }
70 | return days;
71 | }
72 |
73 | //返回当月星期天数
74 | public static int getSundays(int year, int month) {
75 | int sundays = 0;
76 | SimpleDateFormat sdf = new SimpleDateFormat("EEEE");
77 | Calendar setDate = Calendar.getInstance();
78 | //从第一天开始
79 | int day;
80 | for (day = 1; day <= getDays(year, month); day++) {
81 | setDate.set(Calendar.DATE, day);
82 | String str = sdf.format(setDate.getTime());
83 | if (str.equals("星期日")) {
84 | sundays++;
85 | }
86 | }
87 | return sundays;
88 | }
89 |
90 | public static void main(String[] args) {
91 | DateUtils du = new DateUtils();
92 | System.out.println("今天日期是:" + du.getNowDate());
93 | System.out.println("本月有" + du.getDays(du.getYear(), du.getMonth()) + "天");
94 | System.out.println("本月有" + du.getSundays(du.getYear(), du.getMonth()) + "个星期天");
95 | }
96 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_land.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
23 |
24 |
30 |
31 |
37 |
38 |
44 |
45 |
51 |
52 |
61 |
62 |
69 |
70 |
71 |
72 |
79 |
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/utils/NumberToChineseUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock.utils;
2 |
3 | import android.text.SpannableStringBuilder;
4 |
5 | public class NumberToChineseUtils {
6 | /**
7 | * 将数字转换为汉字
8 | *
9 | * @param number
10 | * @return
11 | */
12 | public static String intToChinese(int number) {
13 | String value = String.valueOf(number);
14 | SpannableStringBuilder spannable = new SpannableStringBuilder();
15 | int length = value.length();
16 | if (length > 1) {
17 | /**
18 | * 对于长度大于1的数,对首位进行赋值;
19 | * 对于两位数: 如果首位为“1”,则拼接的字符串为“”;
20 | */
21 | spannable.append(getChinese(number / (int) Math.pow(10, length - 1), length))
22 | .append(getUnitChinese(length));
23 | // 如果该数值取余数为0,则直接返回已有字符(例如:100,直接返回一百)
24 | if (number % (int) Math.pow(10, length - 1) == 0) {
25 | return spannable.toString();
26 | }
27 | }
28 | // 数字为一位数
29 | if (length == 1) {
30 | spannable.append(getChinese(number, 1));
31 | }
32 | // 数字为两位数
33 | if (length == 2) {
34 | // 拼接个位的数值: 如果各位为“0”,则拼接的字符串为“”;
35 | spannable.append(getChinese(number % 10, 0));
36 | }
37 | // 数字为三位数
38 | if (length == 3) {
39 | if (number % 100 < 10) {
40 | spannable.append("零")
41 | .append(getChinese(number % 100, 3));
42 | } else {
43 | spannable.append(getChinese(number % 100 / 10, 3))
44 | .append("十")
45 | .append(getChinese(number % 10, 0));
46 | }
47 | }
48 | // 数字为四位数
49 | if (length == 4) {
50 | if (number % 1000 < 10) {
51 | spannable.append("零").append(getChinese(number % 1000, 3));
52 | } else if (number % 1000 < 100) {
53 | spannable.append("零")
54 | .append(getChinese(number % 1000 / 10, 3))
55 | .append("十")
56 | .append(getChinese(number % 10, 0));
57 | } else {
58 | number = number % 1000;
59 | spannable.append(intToChinese(number));
60 | }
61 | }
62 | return spannable.toString();
63 | }
64 |
65 | /**
66 | * 根据不同的情况获取对应的中文
67 | *
68 | * @param key
69 | * @param length
70 | * @return
71 | */
72 |
73 | public static String getChinese(int key, int length) {
74 | switch (key) {
75 | case 1:
76 | if (length == 2) {
77 | return "";
78 | }
79 | return "一";
80 | case 2:
81 | return "二";
82 | case 3:
83 | return "三";
84 | case 4:
85 | return "四";
86 | case 5:
87 | return "五";
88 | case 6:
89 | return "六";
90 | case 7:
91 | return "七";
92 | case 8:
93 | return "八";
94 | case 9:
95 | return "九";
96 | case 0:
97 | if (length == 1) {
98 | return "零";
99 | }
100 | return "";
101 |
102 | }
103 | return "";
104 | }
105 |
106 | /**
107 | * 根据数字的位数返回最大位数的单位
108 | *
109 | * @param length
110 | * @return
111 | */
112 | public static String getUnitChinese(int length) {
113 | switch (length) {
114 | case 2:
115 | return "十";
116 | case 3:
117 | return "百";
118 | case 4:
119 | return "千";
120 | }
121 | return "";
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.ViewTreeObserver;
10 | import android.widget.Button;
11 | import android.widget.TextView;
12 |
13 | import com.example.admin.mytimewheelclock.utils.DateUtils;
14 |
15 | import java.util.Calendar;
16 |
17 | public class MainActivity extends AppCompatActivity {
18 |
19 |
20 | private AutoRotateSecondView timeView_second;
21 | private AutoRotateMinuteView timeView_minute;
22 | private AutoRotateHoursView timeView_hours;
23 | private AutoRotateWeekView timeView_week;
24 | private AutoRotateDayView timeView_day;
25 | private AutoRotateMonthView timeView_month;
26 | private TextView timeView_year;
27 | private View v_dividing2;
28 | private Button bt_skip_land;
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_main);
34 |
35 | timeView_second = (AutoRotateSecondView) findViewById(R.id.timeView_second);
36 | timeView_minute = (AutoRotateMinuteView) findViewById(R.id.timeView_minute);
37 | timeView_hours = (AutoRotateHoursView) findViewById(R.id.timeView_hours);
38 | timeView_week = findViewById(R.id.timeView_week);
39 | timeView_day = (AutoRotateDayView) findViewById(R.id.timeView_day);
40 | timeView_month = (AutoRotateMonthView) findViewById(R.id.timeView_month);
41 | timeView_year = (TextView) findViewById(R.id.timeView_year);
42 | v_dividing2 = findViewById(R.id.v_dividing2);
43 | bt_skip_land=findViewById(R.id.bt_skip_land);
44 |
45 | initListener();
46 | setData();
47 | }
48 |
49 | private void initListener() {
50 | bt_skip_land.setOnClickListener(new View.OnClickListener() {
51 | @Override
52 | public void onClick(View view) {
53 | startActivity(new Intent(MainActivity.this,LandActivity.class));
54 | }
55 | });
56 | //设置因月份的改变,改变年和天的监听
57 | timeView_month.setChangeTimeListener(new AutoRotateMonthView.OnChangeTimeListener() {
58 | @Override
59 | public void changeYear(final int year) {
60 | timeView_year.post(new Runnable() {
61 | @Override
62 | public void run() {
63 | timeView_year.setText("" + year);
64 | }
65 | });
66 | }
67 |
68 | @Override
69 | public void changeDay(final int count) {
70 | timeView_day.post(new Runnable() {
71 | @Override
72 | public void run() {
73 | timeView_day.setTime(1, count);
74 | }
75 | });
76 | }
77 | });
78 | //秒钟绘制完后,设置水平渐变条的宽度
79 | final ViewTreeObserver viewTreeObserver = timeView_second.getViewTreeObserver();
80 | viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
81 | @Override
82 | public boolean onPreDraw() {
83 | if (viewTreeObserver.isAlive()) {
84 | viewTreeObserver.removeOnPreDrawListener(this);
85 | }
86 | ViewGroup.LayoutParams layoutParams = v_dividing2.getLayoutParams();
87 | layoutParams.width = timeView_second.getViewMaxWidth();
88 | v_dividing2.setLayoutParams(layoutParams);
89 | return true;
90 | }
91 | });
92 | }
93 |
94 | private void setData() {
95 | Calendar calendar = Calendar.getInstance();
96 | int year = calendar.get(Calendar.YEAR);
97 | int month = calendar.get(Calendar.MONTH) + 1;
98 | int week = calendar.get(Calendar.DAY_OF_WEEK) - 1;
99 |
100 | int day = calendar.get(Calendar.DAY_OF_MONTH);
101 | int hours = calendar.get(Calendar.HOUR_OF_DAY);
102 | int minutes = calendar.get(Calendar.MINUTE);
103 | int seconds = calendar.get(Calendar.SECOND);
104 |
105 | // Log.i("Calendar_years", "-" + year);
106 | // Log.i("Calendar_month", "-" + month);
107 | Log.i("Calendar_week", "-" + week);
108 | // Log.i("Calendar_day", "-" + day);
109 | // Log.i("Calendar_hours", "-" + hours);
110 | // Log.i("Calendar_minutes", "-" + minutes);
111 | // Log.i("Calendar_seconds", "-" + seconds);
112 |
113 | timeView_second.setTime(seconds).start();
114 | timeView_minute.setTime(minutes).start();
115 | timeView_hours.setTime(hours).start();
116 | timeView_week.setTime(week).start();
117 | timeView_day.setTime(day, DateUtils.getDays(year, month)).start();
118 | timeView_month.setTime(month, DateUtils.getDays(year, month)).start();
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/LandActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.os.Build;
8 | import android.support.annotation.RequiresApi;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.os.Bundle;
11 | import android.util.Log;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.view.ViewTreeObserver;
15 | import android.widget.Button;
16 | import android.widget.TextView;
17 |
18 | import com.example.admin.mytimewheelclock.utils.DateUtils;
19 | import com.example.admin.mytimewheelclock.utils.SizeUtils;
20 |
21 | import java.util.Calendar;
22 |
23 | public class LandActivity extends AppCompatActivity {
24 |
25 |
26 | private AutoRotateSecondView timeView_second;
27 | private AutoRotateMinuteView timeView_minute;
28 | private AutoRotateHoursView timeView_hours;
29 | private AutoRotateWeekView timeView_week;
30 | private AutoRotateDayView timeView_day;
31 | private AutoRotateMonthView timeView_month;
32 | private TextView timeView_year;
33 | private View v_dividing2;
34 | private Button bt_skip_portrait;
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_land);
40 |
41 | timeView_second = (AutoRotateSecondView) findViewById(R.id.timeView_second);
42 | timeView_minute = (AutoRotateMinuteView) findViewById(R.id.timeView_minute);
43 | timeView_hours = (AutoRotateHoursView) findViewById(R.id.timeView_hours);
44 | timeView_week = findViewById(R.id.timeView_week);
45 | timeView_day = (AutoRotateDayView) findViewById(R.id.timeView_day);
46 | timeView_month = (AutoRotateMonthView) findViewById(R.id.timeView_month);
47 | timeView_year = (TextView) findViewById(R.id.timeView_year);
48 | v_dividing2 = findViewById(R.id.v_dividing2);
49 | bt_skip_portrait=findViewById(R.id.bt_skip_portrait);
50 |
51 | initListener();
52 | setData();
53 | }
54 |
55 | @Override
56 | protected void onResume() {
57 | super.onResume();
58 | timeView_second.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
59 | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
60 | @Override
61 | public void onGlobalLayout() {
62 | timeView_second.getViewTreeObserver().removeOnGlobalLayoutListener(this);
63 | }
64 | });
65 |
66 | }
67 |
68 | private int getMaxTextWidth(String maxWText) {
69 | Paint mPaint=new Paint();
70 | mPaint.setColor(Color.parseColor("#999999"));
71 | mPaint.setStyle(Paint.Style.FILL);
72 | mPaint.setStrokeWidth(1);
73 | mPaint.setTextSize( SizeUtils.dp2px(this, 12));
74 | Rect textBound = new Rect();//创建一个矩形
75 | mPaint.getTextBounds(maxWText, 0, maxWText.length(), textBound);
76 | return textBound.width();
77 | }
78 |
79 | private void initListener() {
80 | bt_skip_portrait.setOnClickListener(new View.OnClickListener() {
81 | @Override
82 | public void onClick(View view) {
83 | startActivity(new Intent(LandActivity.this,MainActivity.class));
84 | }
85 | });
86 | //设置因月份的改变,改变年和天的监听
87 | timeView_month.setChangeTimeListener(new AutoRotateMonthView.OnChangeTimeListener() {
88 | @Override
89 | public void changeYear(final int year) {
90 | timeView_year.post(new Runnable() {
91 | @Override
92 | public void run() {
93 | timeView_year.setText("" + year);
94 | }
95 | });
96 | }
97 |
98 | @Override
99 | public void changeDay(final int count) {
100 | timeView_day.post(new Runnable() {
101 | @Override
102 | public void run() {
103 | timeView_day.setTime(1, count);
104 | }
105 | });
106 | }
107 | });
108 | //秒钟绘制完后,设置水平渐变条的宽度
109 | final ViewTreeObserver viewTreeObserver = timeView_second.getViewTreeObserver();
110 | viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
111 | @Override
112 | public boolean onPreDraw() {
113 | if (viewTreeObserver.isAlive()) {
114 | viewTreeObserver.removeOnPreDrawListener(this);
115 | }
116 | ViewGroup.LayoutParams layoutParams = v_dividing2.getLayoutParams();
117 | layoutParams.width = timeView_second.getViewMaxWidth();
118 | v_dividing2.setLayoutParams(layoutParams);
119 | return true;
120 | }
121 | });
122 | }
123 |
124 | private void setData() {
125 | Calendar calendar = Calendar.getInstance();
126 | int year = calendar.get(Calendar.YEAR);
127 | int month = calendar.get(Calendar.MONTH) + 1;
128 | int week = calendar.get(Calendar.DAY_OF_WEEK) - 1;
129 |
130 | int day = calendar.get(Calendar.DAY_OF_MONTH);
131 | int hours = calendar.get(Calendar.HOUR_OF_DAY);
132 | int minutes = calendar.get(Calendar.MINUTE);
133 | int seconds = calendar.get(Calendar.SECOND);
134 |
135 | // Log.i("Calendar_years", "-" + year);
136 | // Log.i("Calendar_month", "-" + month);
137 | Log.i("Calendar_week", "-" + week);
138 | // Log.i("Calendar_day", "-" + day);
139 | // Log.i("Calendar_hours", "-" + hours);
140 | // Log.i("Calendar_minutes", "-" + minutes);
141 | // Log.i("Calendar_seconds", "-" + seconds);
142 |
143 | timeView_second.setTime(seconds).start();
144 | timeView_minute.setTime(minutes).start();
145 | timeView_hours.setTime(hours).start();
146 | timeView_week.setTime(week).start();
147 | timeView_day.setTime(day, DateUtils.getDays(year, month)).start();
148 | timeView_month.setTime(month, DateUtils.getDays(year, month)).start();
149 | }
150 |
151 | }
--------------------------------------------------------------------------------
/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/java/com/example/admin/mytimewheelclock/AutoRotateMinuteView.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 |
4 | import android.animation.Animator;
5 | import android.animation.AnimatorListenerAdapter;
6 | import android.animation.AnimatorSet;
7 | import android.animation.ObjectAnimator;
8 | import android.animation.ValueAnimator;
9 | import android.content.Context;
10 | import android.content.res.TypedArray;
11 | import android.graphics.Canvas;
12 | import android.graphics.Color;
13 | import android.graphics.Paint;
14 | import android.graphics.Rect;
15 | import android.os.Handler;
16 | import android.os.Message;
17 | import android.text.TextUtils;
18 | import android.util.AttributeSet;
19 | import android.view.View;
20 | import android.view.animation.AccelerateInterpolator;
21 | import android.widget.Toast;
22 |
23 | import com.example.admin.mytimewheelclock.utils.SizeUtils;
24 |
25 | import java.util.Calendar;
26 | import java.util.Timer;
27 | import java.util.TimerTask;
28 |
29 | public class AutoRotateMinuteView extends View {
30 |
31 | private Context mContext;
32 | private Paint mPaint, mSelectedPaint;
33 | private float mMinDegree;
34 | private int borderColor;
35 | private int textColor;
36 | private float textSize;
37 | private int centerPointColor;
38 | private float centerPointSize;
39 | private float borderWidth;
40 | private float centerPointRadiu;
41 | private String centerPointType = "circle";
42 | private int sleepTime;
43 | private boolean isDrawText;
44 | private String minuteArray[] = new String[60];
45 | private Timer mTimer = new Timer();
46 | private int circleRadius = 0;
47 | /**
48 | * 是否显示一半
49 | */
50 | private boolean isShowHalf=true;
51 | private Handler mhandler = new Handler() {
52 | @Override
53 | public void handleMessage(Message msg) {
54 | super.handleMessage(msg);
55 | startRotation();
56 | }
57 | };
58 |
59 | /**
60 | * 是否设置选中文字,
61 | */
62 | private boolean isSetSelectedText = true;
63 |
64 | private void startRotation() {
65 | AnimatorSet animatorSet = new AnimatorSet();
66 | ValueAnimator rotation_15A = ObjectAnimator.ofFloat(this, "rotation", 360 - startDegrees, 360 - mMinDegree);
67 | rotation_15A.setInterpolator(new AccelerateInterpolator());
68 | rotation_15A.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
69 | @Override
70 | public void onAnimationUpdate(ValueAnimator animation) {
71 | float animatedFraction = animation.getAnimatedFraction();
72 | //滚动一段后,就将上一个选中的文字置灰
73 | if (animatedFraction < 0.5 && animatedFraction >= 0.4) {
74 | isSetSelectedText = false;
75 | invalidate();
76 | }
77 | }
78 | });
79 | rotation_15A.addListener(new AnimatorListenerAdapter() {
80 | @Override
81 | public void onAnimationEnd(Animator animation) {
82 | //结束滚动后,当前的文字设置选中
83 | isSetSelectedText = true;
84 | invalidate();
85 | }
86 |
87 | });
88 | animatorSet.play(rotation_15A);
89 | animatorSet.start();
90 | }
91 |
92 | public AutoRotateMinuteView(Context context) {
93 | super(context);
94 | this.mContext = context;
95 | initPainter();
96 | }
97 |
98 | public AutoRotateMinuteView(Context context, AttributeSet attrs) {
99 | super(context, attrs);
100 | this.mContext = context;
101 | init(context, attrs);
102 | initPainter();
103 | }
104 |
105 | /**
106 | * 初始化各参数
107 | *
108 | * @param context
109 | * @param attrs
110 | */
111 | private void init(Context context, AttributeSet attrs) {
112 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimeView);
113 | borderColor = ta.getColor(R.styleable.TimeView_borderColor, Color.WHITE);
114 | borderWidth = ta.getDimension(R.styleable.TimeView_borderWidth,
115 | SizeUtils.dp2px(context, 1));
116 | textColor = ta.getColor(R.styleable.TimeView_textColor, Color.parseColor("#999999"));
117 | textSize = ta.getDimension(R.styleable.TimeView_textSize,
118 | SizeUtils.dp2px(context, 12));
119 | isDrawText = ta.getBoolean(R.styleable.TimeView_isDrawText, true);
120 | centerPointColor = ta.getColor(R.styleable.TimeView_centerPointColor, Color.WHITE);
121 | centerPointSize = ta.getDimension(R.styleable.TimeView_centerPointSize,
122 | SizeUtils.dp2px(context, 5));
123 | centerPointRadiu = ta.getDimension(R.styleable.TimeView_centerPointRadiu,
124 | SizeUtils.dp2px(context, 2));
125 | centerPointType = ta.getString(R.styleable.TimeView_centerPointType);
126 | if (TextUtils.isEmpty(centerPointType)) {
127 | centerPointType = "circle";
128 | }
129 | isShowHalf=ta.getBoolean(R.styleable.TimeView_isShowHalf,true);
130 | sleepTime = 1000;
131 | ta.recycle();
132 | }
133 |
134 | /**
135 | * 初始化画笔
136 | */
137 | private void initPainter() {
138 | mPaint = new Paint();
139 | mPaint.setAntiAlias(true);
140 | mPaint.setColor(Color.BLACK);
141 | mPaint.setStyle(Paint.Style.STROKE);
142 | mPaint.setStrokeWidth(2);
143 | //文字选中的画笔
144 | mSelectedPaint = new Paint();
145 | mSelectedPaint.setAntiAlias(true);
146 | mSelectedPaint.setColor(Color.WHITE);
147 | mSelectedPaint.setStyle(Paint.Style.FILL);
148 | mSelectedPaint.setStrokeWidth(1);
149 | mSelectedPaint.setTextSize(textSize);
150 | }
151 |
152 | @Override
153 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
154 | int size = SizeUtils.measureSize(mContext, widthMeasureSpec);
155 | int sizeH = SizeUtils.measureSize(mContext, heightMeasureSpec);
156 | setMeasuredDimension(sizeH, sizeH);
157 | if (isShowHalf)setTranslationX(-size / 2);
158 | }
159 |
160 | /**
161 | * 绘制(时、分、秒)数字之间的间隔
162 | */
163 | private static int drawNumberSpace = 0;
164 | @Override
165 | protected void onDraw(Canvas canvas) {
166 | drawNumberSpace = SizeUtils.dp2px(getContext(), 12);
167 | circleRadius = getWidth() / 3;
168 | //画外边框
169 | // mPaint.setColor(borderColor);
170 | // mPaint.setStrokeWidth(2);
171 | // mPaint.setStyle(Paint.Style.STROKE);
172 | // Path path = new Path();
173 | // path.lineTo(getWidth(), 0);
174 | // path.lineTo(getWidth(), getHeight());
175 | // path.lineTo(0, getHeight());
176 | // path.close();
177 | // canvas.drawPath(path, mPaint);
178 | //外圆边界
179 | // drawCircleOut(canvas);
180 | //圆心
181 | // drawCirclePoint(canvas);
182 | //画时、分、秒针数字
183 | drawMinuteNumber(canvas);
184 | }
185 |
186 | /**
187 | * 应用打开初始化时间(例如1:30:30)
188 | *
189 | * @param min
190 | */
191 | public AutoRotateMinuteView setTime(int min) {
192 | if (min >= 60 || min < 0) {
193 | Toast.makeText(getContext(), "分-不合法", Toast.LENGTH_SHORT).show();
194 | return this;
195 | }
196 |
197 | for (int i = 0; i < minuteArray.length; i++) {
198 | int currentMinute = min + i;
199 | currentMinute = currentMinute >= 60 ? currentMinute - 60 : currentMinute;
200 | minuteArray[i] = (currentMinute + "分");
201 | }
202 | invalidate();
203 | return this;
204 | }
205 |
206 | /**
207 | * 时钟走起
208 | */
209 | public void start() {
210 | mTimer.schedule(task, 0, sleepTime);
211 | }
212 |
213 | public float startDegrees = 0;
214 | private TimerTask task = new TimerTask() {
215 | @Override
216 | public void run() {
217 | if (mMinDegree == 360) {
218 | mMinDegree = 0;
219 | }
220 |
221 | Calendar calendar = Calendar.getInstance();
222 | int seconds = calendar.getTime().getSeconds();
223 | if (seconds == 59) {//到达了一分钟了
224 | startDegrees = mMinDegree;
225 | mMinDegree = mMinDegree + 6;
226 | mhandler.sendEmptyMessage(0);
227 | }
228 | }
229 | };
230 |
231 |
232 | /**
233 | * 画分钟 数字
234 | *
235 | * @param canvas
236 | */
237 | private void drawMinuteNumber(Canvas canvas) {
238 | if (isDrawText) {
239 | if (TextUtils.isEmpty(minuteArray[0])) return;
240 | //旋转圆盘
241 | canvas.save();
242 | mPaint.setColor(textColor);
243 | mPaint.setStyle(Paint.Style.FILL);
244 | mPaint.setStrokeWidth(1);
245 | mPaint.setTextSize(textSize);
246 | canvas.translate(getWidth() / 2, getHeight() / 2);
247 | int currentDegree = 0;
248 | for (int i = 0; i < minuteArray.length; i++) {
249 | Rect textBound = new Rect();//创建一个矩形
250 | String text = minuteArray[i];
251 | //将文字装在上面创建的矩形中,即这个矩形就是文字的边框
252 | mPaint.getTextBounds(text, 0, text.length(), textBound);
253 | if (isSetSelectedText && (currentDegree + 360 - mMinDegree) % 360 == 0) {
254 | canvas.drawText(text, circleRadius, textBound.height() / 2, mSelectedPaint);
255 | } else {
256 | canvas.drawText(text, circleRadius, textBound.height() / 2, mPaint);
257 | }
258 | canvas.rotate(6);
259 | currentDegree = currentDegree + 6;
260 | }
261 | canvas.restore();
262 | }
263 | }
264 |
265 | private int getMaxTextWidth(String maxWText) {
266 | mPaint.setColor(textColor);
267 | mPaint.setStyle(Paint.Style.FILL);
268 | mPaint.setStrokeWidth(1);
269 | mPaint.setTextSize(textSize);
270 | Rect textBound = new Rect();//创建一个矩形
271 | mPaint.getTextBounds(maxWText, 0, maxWText.length(), textBound);
272 | return textBound.width();
273 | }
274 |
275 |
276 | /**
277 | * 画圆圈
278 | *
279 | * @param canvas
280 | */
281 | private void drawCircleOut(Canvas canvas) {
282 | //半径是宽度的1/3
283 | mPaint.setColor(borderColor);
284 | mPaint.setStrokeWidth(borderWidth);
285 | mPaint.setStyle(Paint.Style.STROKE);
286 | mPaint.setTextSize(textSize);
287 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, circleRadius, mPaint);
288 | }
289 |
290 |
291 | /**
292 | * 画圆心
293 | *
294 | * @param canvas
295 | */
296 | private void drawCirclePoint(Canvas canvas) {
297 | mPaint.setColor(centerPointColor);
298 | mPaint.setStrokeWidth(centerPointSize);
299 | if (centerPointType.equals("rect")) {
300 | canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);
301 | } else if (centerPointType.equals("circle")) {
302 | mPaint.setStyle(Paint.Style.FILL);
303 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, centerPointRadiu, mPaint);
304 | }
305 | }
306 |
307 |
308 | }
309 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/AutoRotateHoursView.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 |
4 | import android.animation.Animator;
5 | import android.animation.AnimatorListenerAdapter;
6 | import android.animation.AnimatorSet;
7 | import android.animation.ObjectAnimator;
8 | import android.animation.ValueAnimator;
9 | import android.content.Context;
10 | import android.content.res.TypedArray;
11 | import android.graphics.Canvas;
12 | import android.graphics.Color;
13 | import android.graphics.Paint;
14 | import android.graphics.Rect;
15 | import android.os.Handler;
16 | import android.os.Message;
17 | import android.text.TextUtils;
18 | import android.util.AttributeSet;
19 | import android.view.View;
20 | import android.view.animation.AccelerateInterpolator;
21 | import android.widget.Toast;
22 |
23 | import com.example.admin.mytimewheelclock.utils.SizeUtils;
24 |
25 | import java.util.Calendar;
26 | import java.util.Timer;
27 | import java.util.TimerTask;
28 |
29 | public class AutoRotateHoursView extends View {
30 |
31 | private Context mContext;
32 | private Paint mPaint, mSelectedPaint;
33 | private float mHourDegree;
34 | private int borderColor;
35 | private int textColor;
36 | private float textSize;
37 | private int centerPointColor;
38 | private float centerPointSize;
39 | private float borderWidth;
40 | private float centerPointRadiu;
41 | private String centerPointType = "circle";
42 | private int sleepTime;
43 | private boolean isDrawText;
44 | private String hourArray[] = new String[12];
45 | private Timer mTimer = new Timer();
46 | private int circleRadius = 0;
47 | private Handler mhandler = new Handler() {
48 | @Override
49 | public void handleMessage(Message msg) {
50 | super.handleMessage(msg);
51 | startRotation();
52 | }
53 | };
54 | /**
55 | * 是否设置选中文字,
56 | */
57 | private boolean isSetSelectedText = true;
58 |
59 | private void startRotation() {
60 | AnimatorSet animatorSet = new AnimatorSet();
61 | ValueAnimator rotation_15A = ObjectAnimator.ofFloat(this, "rotation", 360 - startDegrees, 360 - mHourDegree);
62 | rotation_15A.setInterpolator(new AccelerateInterpolator());
63 | rotation_15A.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
64 | @Override
65 | public void onAnimationUpdate(ValueAnimator animation) {
66 | float animatedFraction = animation.getAnimatedFraction();
67 | //滚动一段后,就将上一个选中的文字置灰
68 | if (animatedFraction < 0.5 && animatedFraction >= 0.4) {
69 | isSetSelectedText = false;
70 | invalidate();
71 | }
72 | }
73 | });
74 | rotation_15A.addListener(new AnimatorListenerAdapter() {
75 | @Override
76 | public void onAnimationEnd(Animator animation) {
77 | //结束滚动后,当前的文字设置选中
78 | isSetSelectedText = true;
79 | invalidate();
80 | }
81 |
82 | });
83 | animatorSet.play(rotation_15A);
84 | animatorSet.start();
85 | }
86 |
87 | public AutoRotateHoursView(Context context) {
88 | super(context);
89 | this.mContext = context;
90 | initPainter();
91 | }
92 |
93 | public AutoRotateHoursView(Context context, AttributeSet attrs) {
94 | super(context, attrs);
95 | this.mContext = context;
96 | init(context, attrs);
97 | initPainter();
98 | }
99 |
100 | /**
101 | * 初始化各参数
102 | *
103 | * @param context
104 | * @param attrs
105 | */
106 | private void init(Context context, AttributeSet attrs) {
107 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimeView);
108 | borderColor = ta.getColor(R.styleable.TimeView_borderColor, Color.WHITE);
109 | borderWidth = ta.getDimension(R.styleable.TimeView_borderWidth,
110 | SizeUtils.dp2px(context, 1));
111 | textColor = ta.getColor(R.styleable.TimeView_textColor, Color.parseColor("#999999"));
112 | textSize = ta.getDimension(R.styleable.TimeView_textSize,
113 | SizeUtils.dp2px(context, 12));
114 | isDrawText = ta.getBoolean(R.styleable.TimeView_isDrawText, true);
115 | centerPointColor = ta.getColor(R.styleable.TimeView_centerPointColor, Color.WHITE);
116 | centerPointSize = ta.getDimension(R.styleable.TimeView_centerPointSize,
117 | SizeUtils.dp2px(context, 5));
118 | centerPointRadiu = ta.getDimension(R.styleable.TimeView_centerPointRadiu,
119 | SizeUtils.dp2px(context, 2));
120 | centerPointType = ta.getString(R.styleable.TimeView_centerPointType);
121 | if (TextUtils.isEmpty(centerPointType)) {
122 | centerPointType = "circle";
123 | }
124 | isShowHalf=ta.getBoolean(R.styleable.TimeView_isShowHalf,true);
125 | sleepTime = 1000;
126 | ta.recycle();
127 | }
128 | private boolean isShowHalf;
129 |
130 | /**
131 | * 初始化画笔
132 | */
133 | private void initPainter() {
134 | mPaint = new Paint();
135 | mPaint.setAntiAlias(true);
136 | mPaint.setColor(Color.BLACK);
137 | mPaint.setStyle(Paint.Style.STROKE);
138 | mPaint.setStrokeWidth(2);
139 | //文字选中的画笔
140 | mSelectedPaint = new Paint();
141 | mSelectedPaint.setAntiAlias(true);
142 | mSelectedPaint.setColor(Color.WHITE);
143 | mSelectedPaint.setStyle(Paint.Style.FILL);
144 | mSelectedPaint.setStrokeWidth(1);
145 | mSelectedPaint.setTextSize(textSize);
146 | }
147 |
148 | @Override
149 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
150 | int size = SizeUtils.measureSize(mContext, widthMeasureSpec);
151 | int sizeH = SizeUtils.measureSize(mContext, heightMeasureSpec);
152 | setMeasuredDimension(sizeH, sizeH);
153 | if (isShowHalf)setTranslationX(-size / 2);
154 | }
155 |
156 | /**
157 | * 绘制(时、分、秒)数字之间的间隔
158 | */
159 | private static int drawNumberSpace = 0;
160 |
161 | @Override
162 | protected void onDraw(Canvas canvas) {
163 | drawNumberSpace = SizeUtils.dp2px(getContext(), 12);
164 | circleRadius = getWidth() / 3;
165 | //画外边框
166 | // mPaint.setColor(borderColor);
167 | // mPaint.setStrokeWidth(2);
168 | // mPaint.setStyle(Paint.Style.STROKE);
169 | // Path path = new Path();
170 | // path.lineTo(getWidth(), 0);
171 | // path.lineTo(getWidth(), getHeight());
172 | // path.lineTo(0, getHeight());
173 | // path.close();
174 | // canvas.drawPath(path, mPaint);
175 | //外圆边界
176 | // drawCircleOut(canvas);
177 | //圆心
178 | // drawCirclePoint(canvas);
179 | //画时、分、秒针数字
180 | drawHourNumber(canvas);
181 | }
182 |
183 | /**
184 | * 应用打开初始化时间(例如1:30:30)
185 | *
186 | * @param hour
187 | */
188 | public AutoRotateHoursView setTime(int hour) {
189 | if (hour >= 24 || hour < 0) {
190 | Toast.makeText(getContext(), "小时-不合法", Toast.LENGTH_SHORT).show();
191 | return this;
192 | }
193 |
194 | for (int i = 0; i < hourArray.length; i++) {
195 | hour = hour > 12 ? hour - 12 : hour;
196 | int currentHour = hour + i;
197 | currentHour = currentHour > 12 ? currentHour - 12 : currentHour;
198 | hourArray[i] = (currentHour + "时");
199 | }
200 | invalidate();
201 | return this;
202 | }
203 |
204 | /**
205 | * 时钟走起
206 | */
207 | public void start() {
208 | mTimer.schedule(task, 0, sleepTime);
209 | }
210 |
211 | public float startDegrees = 0;
212 | private TimerTask task = new TimerTask() {
213 | @Override
214 | public void run() {
215 | if (mHourDegree == 360) {
216 | mHourDegree = 0;
217 | }
218 | Calendar calendar = Calendar.getInstance();
219 | int seconds = calendar.getTime().getSeconds();
220 | int minutes = calendar.getTime().getMinutes();
221 | if (seconds == 59 && minutes == 59) {//到达了一秒了
222 | startDegrees = mHourDegree;
223 | mHourDegree = mHourDegree + 30;
224 | mhandler.sendEmptyMessage(0);
225 | }
226 | }
227 | };
228 |
229 |
230 | /**
231 | * 画小时 数字
232 | *
233 | * @param canvas
234 | */
235 | private void drawHourNumber(Canvas canvas) {
236 | if (isDrawText) {
237 | if (TextUtils.isEmpty(hourArray[0])) return;
238 | //旋转圆盘
239 | canvas.save();
240 | mPaint.setColor(textColor);
241 | mPaint.setStyle(Paint.Style.FILL);
242 | mPaint.setStrokeWidth(1);
243 | mPaint.setTextSize(textSize);
244 | canvas.translate(getWidth() / 2, getHeight() / 2);
245 | int currentDegree = 0;
246 | for (int i = 0; i < 12; i++) {
247 | Rect textBound = new Rect();//创建一个矩形
248 | String text = hourArray[i];
249 | //将文字装在上面创建的矩形中,即这个矩形就是文字的边框
250 | mPaint.getTextBounds(text, 0, text.length(), textBound);
251 | if (isSetSelectedText && (currentDegree + 360 - mHourDegree) % 360 == 0) {
252 | canvas.drawText(text, circleRadius - getMaxTextWidth("60分") , textBound.height() / 2, mSelectedPaint);
253 | } else {
254 | canvas.drawText(text, circleRadius - getMaxTextWidth("60分") , textBound.height() / 2, mPaint);
255 | }
256 | canvas.rotate(30);
257 | currentDegree = currentDegree + 30;
258 | }
259 | canvas.restore();
260 | }
261 | }
262 |
263 | private int getMaxTextWidth(String maxWText) {
264 | mPaint.setColor(textColor);
265 | mPaint.setStyle(Paint.Style.FILL);
266 | mPaint.setStrokeWidth(1);
267 | mPaint.setTextSize(textSize);
268 | Rect textBound = new Rect();//创建一个矩形
269 | mPaint.getTextBounds(maxWText, 0, maxWText.length(), textBound);
270 | return textBound.width();
271 | }
272 |
273 |
274 | /**
275 | * 画圆圈
276 | *
277 | * @param canvas
278 | */
279 | private void drawCircleOut(Canvas canvas) {
280 | //半径是宽度的1/3
281 | mPaint.setColor(borderColor);
282 | mPaint.setStrokeWidth(borderWidth);
283 | mPaint.setStyle(Paint.Style.STROKE);
284 | mPaint.setTextSize(textSize);
285 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, circleRadius, mPaint);
286 | }
287 |
288 |
289 | /**
290 | * 画圆心
291 | *
292 | * @param canvas
293 | */
294 | private void drawCirclePoint(Canvas canvas) {
295 | mPaint.setColor(centerPointColor);
296 | mPaint.setStrokeWidth(centerPointSize);
297 | if (centerPointType.equals("rect")) {
298 | canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);
299 | } else if (centerPointType.equals("circle")) {
300 | mPaint.setStyle(Paint.Style.FILL);
301 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, centerPointRadiu, mPaint);
302 | }
303 | }
304 |
305 |
306 | }
307 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/AutoRotateSecondView.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 |
4 | import android.animation.Animator;
5 | import android.animation.AnimatorListenerAdapter;
6 | import android.animation.AnimatorSet;
7 | import android.animation.ObjectAnimator;
8 | import android.animation.ValueAnimator;
9 | import android.content.Context;
10 | import android.content.res.TypedArray;
11 | import android.graphics.Canvas;
12 | import android.graphics.Color;
13 | import android.graphics.Paint;
14 | import android.graphics.Rect;
15 | import android.os.Handler;
16 | import android.os.Message;
17 | import android.text.TextUtils;
18 | import android.util.AttributeSet;
19 | import android.view.View;
20 | import android.view.animation.AccelerateInterpolator;
21 | import android.widget.Toast;
22 |
23 | import com.example.admin.mytimewheelclock.utils.SizeUtils;
24 |
25 | import java.util.Timer;
26 | import java.util.TimerTask;
27 |
28 | public class AutoRotateSecondView extends View {
29 |
30 | private Context mContext;
31 | private Paint mPaint, mSelectedPaint;
32 | private float mSecondDegree;
33 | private int borderColor;
34 | private int textColor;
35 | private float textSize;
36 | private int centerPointColor;
37 | private float centerPointSize;
38 | private float borderWidth;
39 | private float centerPointRadiu;
40 | private String centerPointType = "circle";
41 | private int sleepTime;
42 | private boolean isDrawText;
43 | private String secondArray[] = new String[60];
44 | private Timer mTimer = new Timer();
45 | private int circleRadius = 0;
46 | /**
47 | * 是否显示一半
48 | */
49 | private boolean isShowHalf=true;
50 | private Handler mHandler = new Handler() {
51 | @Override
52 | public void handleMessage(Message msg) {
53 | super.handleMessage(msg);
54 | startRotation();
55 | }
56 | };
57 |
58 | /**
59 | * 是否设置选中文字,
60 | */
61 | private boolean isSetSelectedText = true;
62 |
63 | private void startRotation() {
64 | AnimatorSet animatorSet = new AnimatorSet();
65 | ValueAnimator rotation_15A = ObjectAnimator.ofFloat(this, "rotation", 360 - startDegrees, 360 - mSecondDegree);
66 | rotation_15A.setInterpolator(new AccelerateInterpolator());
67 | rotation_15A.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
68 | @Override
69 | public void onAnimationUpdate(ValueAnimator animation) {
70 | float animatedFraction = animation.getAnimatedFraction();
71 | //滚动一段后,就将上一个选中的文字置灰
72 | if (animatedFraction < 0.5 && animatedFraction >= 0.4) {
73 | isSetSelectedText = false;
74 | invalidate();
75 | }
76 | }
77 | });
78 | rotation_15A.addListener(new AnimatorListenerAdapter() {
79 | @Override
80 | public void onAnimationEnd(Animator animation) {
81 | //结束滚动后,当前的文字设置选中
82 | isSetSelectedText = true;
83 | invalidate();
84 | }
85 |
86 | });
87 | animatorSet.play(rotation_15A);
88 | animatorSet.start();
89 | }
90 |
91 | public AutoRotateSecondView(Context context) {
92 | super(context);
93 | this.mContext = context;
94 | initPainter();
95 | }
96 |
97 | public AutoRotateSecondView(Context context, AttributeSet attrs) {
98 | super(context, attrs);
99 | this.mContext = context;
100 | init(context, attrs);
101 | initPainter();
102 | }
103 |
104 | /**
105 | * 初始化各参数
106 | *
107 | * @param context
108 | * @param attrs
109 | */
110 | private void init(Context context, AttributeSet attrs) {
111 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimeView);
112 | borderColor = ta.getColor(R.styleable.TimeView_borderColor, Color.WHITE);
113 | borderWidth = ta.getDimension(R.styleable.TimeView_borderWidth,
114 | SizeUtils.dp2px(context, 1));
115 | textColor = ta.getColor(R.styleable.TimeView_textColor, Color.parseColor("#999999"));
116 | textSize = ta.getDimension(R.styleable.TimeView_textSize,
117 | SizeUtils.dp2px(context, 12));
118 | isDrawText = ta.getBoolean(R.styleable.TimeView_isDrawText, true);
119 | centerPointColor = ta.getColor(R.styleable.TimeView_centerPointColor, Color.WHITE);
120 | centerPointSize = ta.getDimension(R.styleable.TimeView_centerPointSize,
121 | SizeUtils.dp2px(context, 5));
122 | centerPointRadiu = ta.getDimension(R.styleable.TimeView_centerPointRadiu,
123 | SizeUtils.dp2px(context, 2));
124 | centerPointType = ta.getString(R.styleable.TimeView_centerPointType);
125 | if (TextUtils.isEmpty(centerPointType)) {
126 | centerPointType = "circle";
127 | }
128 | isShowHalf=ta.getBoolean(R.styleable.TimeView_isShowHalf,true);
129 | sleepTime = 1000;
130 | ta.recycle();
131 | }
132 |
133 | /**
134 | * 初始化画笔
135 | */
136 | private void initPainter() {
137 | mPaint = new Paint();
138 | mPaint.setAntiAlias(true);
139 | mPaint.setColor(Color.BLACK);
140 | mPaint.setStyle(Paint.Style.STROKE);
141 | mPaint.setStrokeWidth(2);
142 | //文字选中的画笔
143 | mSelectedPaint = new Paint();
144 | mSelectedPaint.setAntiAlias(true);
145 | mSelectedPaint.setColor(Color.WHITE);
146 | mSelectedPaint.setStyle(Paint.Style.FILL);
147 | mSelectedPaint.setStrokeWidth(1);
148 | mSelectedPaint.setTextSize(textSize);
149 | }
150 |
151 | @Override
152 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
153 | int size = SizeUtils.measureSize(mContext, widthMeasureSpec);
154 | int sizeH = SizeUtils.measureSize(mContext, heightMeasureSpec);
155 | setMeasuredDimension(sizeH, sizeH);
156 | if (isShowHalf)setTranslationX(-size / 2);
157 | }
158 |
159 |
160 | /**
161 | * 绘制(时、分、秒)数字之间的间隔
162 | */
163 | private static int drawNumberSpace = 0;
164 |
165 | @Override
166 | protected void onDraw(Canvas canvas) {
167 | drawNumberSpace = SizeUtils.dp2px(getContext(), 12);
168 | circleRadius = getWidth() / 3;
169 | //画外边框
170 | // mPaint.setColor(borderColor);
171 | // mPaint.setStrokeWidth(2);
172 | // mPaint.setStyle(Paint.Style.STROKE);
173 | // Path path = new Path();
174 | // path.lineTo(getWidth(), 0);
175 | // path.lineTo(getWidth(), getHeight());
176 | // path.lineTo(0, getHeight());
177 | // path.close();
178 | // canvas.drawPath(path, mPaint);
179 | //外圆边界
180 | // drawCircleOut(canvas);
181 | //圆心
182 | // drawCirclePoint(canvas);
183 | //画秒针数字
184 | drawSecondNumber(canvas);
185 | }
186 |
187 |
188 | /**
189 | * 应用打开初始化时间(例如1:30:30)
190 | *
191 | * @param second
192 | */
193 | public AutoRotateSecondView setTime(int second) {
194 | if (second >= 60 || second < 0) {
195 | Toast.makeText(getContext(), "秒数-不合法", Toast.LENGTH_SHORT).show();
196 | return this;
197 | }
198 | for (int i = 0; i < secondArray.length; i++) {
199 | int currentSecond = second + i;
200 | currentSecond = currentSecond >= 60 ? currentSecond - 60 : currentSecond;
201 | secondArray[i] = (currentSecond + "秒");
202 | }
203 | invalidate();
204 | return this;
205 | }
206 |
207 | /**
208 | * 时钟走起
209 | */
210 | public void start() {
211 | mTimer.schedule(task, 0, sleepTime);
212 | }
213 |
214 | public float startDegrees = 0;
215 | private TimerTask task = new TimerTask() {
216 | @Override
217 | public void run() {
218 | if (mSecondDegree == 360) {
219 | mSecondDegree = 0;
220 | }
221 | startDegrees = mSecondDegree;
222 | //秒针:1秒(1000ms)走6°,即50ms走0.3°
223 | mSecondDegree = mSecondDegree + 0.3f * sleepTime / 50;
224 |
225 | mHandler.sendEmptyMessage(0);
226 | }
227 | };
228 |
229 |
230 | /**
231 | * 画秒钟 数字
232 | *
233 | * @param canvas
234 | */
235 | private void drawSecondNumber(Canvas canvas) {
236 | if (isDrawText) {
237 | if (TextUtils.isEmpty(secondArray[0])) return;
238 | //旋转圆盘
239 | // canvas.rotate(360 - mSecondDegree, getWidth() / 2, getHeight() / 2);
240 |
241 | canvas.save();
242 | mPaint.setColor(textColor);
243 | mPaint.setStyle(Paint.Style.FILL);
244 | mPaint.setStrokeWidth(1);
245 | mPaint.setTextSize(textSize);
246 | canvas.translate(getWidth() / 2, getHeight() / 2);
247 | int currentDegree = 0;
248 | for (int i = 0; i < secondArray.length; i++) {
249 | Rect textBound = new Rect();//创建一个矩形
250 | String text = secondArray[i];
251 | //将文字装在上面创建的矩形中,即这个矩形就是文字的边框
252 | mPaint.getTextBounds(text, 0, text.length(), textBound);
253 | if (isSetSelectedText && (currentDegree + 360 - mSecondDegree) % 360 == 0) {
254 | canvas.drawText(text, circleRadius + getMaxTextWidth("60秒") + drawNumberSpace, textBound.height() / 2, mSelectedPaint);
255 | } else {
256 | canvas.drawText(text, circleRadius + getMaxTextWidth("60秒") + drawNumberSpace, textBound.height() / 2, mPaint);
257 | }
258 | canvas.rotate(6);
259 | currentDegree = currentDegree + 6;
260 | }
261 | canvas.restore();
262 | }
263 | }
264 |
265 | public int getViewMaxWidth() {
266 | return (circleRadius + getMaxTextWidth("60秒") + drawNumberSpace + getMaxTextWidth("60秒"));
267 | }
268 |
269 | /**
270 | * @param maxWText
271 | * @return 根据最长的字,算出最大的宽度
272 | */
273 | private int getMaxTextWidth(String maxWText) {
274 | mPaint.setColor(textColor);
275 | mPaint.setStyle(Paint.Style.FILL);
276 | mPaint.setStrokeWidth(1);
277 | mPaint.setTextSize(textSize);
278 | Rect textBound = new Rect();//创建一个矩形
279 | mPaint.getTextBounds(maxWText, 0, maxWText.length(), textBound);
280 | return textBound.width();
281 | }
282 |
283 |
284 | /**
285 | * 画圆圈
286 | *
287 | * @param canvas
288 | */
289 | private void drawCircleOut(Canvas canvas) {
290 | //半径是宽度的1/3
291 | mPaint.setColor(borderColor);
292 | mPaint.setStrokeWidth(borderWidth);
293 | mPaint.setStyle(Paint.Style.STROKE);
294 | mPaint.setTextSize(textSize);
295 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, circleRadius, mPaint);
296 |
297 | }
298 |
299 |
300 | /**
301 | * 画圆心
302 | *
303 | * @param canvas
304 | */
305 | private void drawCirclePoint(Canvas canvas) {
306 | mPaint.setColor(centerPointColor);
307 | mPaint.setStrokeWidth(centerPointSize);
308 | if (centerPointType.equals("rect")) {
309 | canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);
310 | } else if (centerPointType.equals("circle")) {
311 | mPaint.setStyle(Paint.Style.FILL);
312 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, centerPointRadiu, mPaint);
313 | }
314 | }
315 |
316 |
317 | }
318 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/AutoRotateDayView.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 |
4 | import android.animation.Animator;
5 | import android.animation.AnimatorListenerAdapter;
6 | import android.animation.AnimatorSet;
7 | import android.animation.ObjectAnimator;
8 | import android.animation.ValueAnimator;
9 | import android.content.Context;
10 | import android.content.res.TypedArray;
11 | import android.graphics.Canvas;
12 | import android.graphics.Color;
13 | import android.graphics.Paint;
14 | import android.graphics.Rect;
15 | import android.os.Handler;
16 | import android.os.Message;
17 | import android.text.TextUtils;
18 | import android.util.AttributeSet;
19 | import android.view.View;
20 | import android.view.animation.AccelerateInterpolator;
21 | import android.widget.Toast;
22 |
23 | import com.example.admin.mytimewheelclock.utils.SizeUtils;
24 |
25 | import java.text.DecimalFormat;
26 | import java.util.Calendar;
27 | import java.util.Timer;
28 | import java.util.TimerTask;
29 |
30 | public class AutoRotateDayView extends View {
31 |
32 | private Context mContext;
33 | private Paint mPaint, mSelectedPaint;
34 | private float mDayDegree;
35 | private int borderColor;
36 | private int textColor;
37 | private float textSize;
38 | private int centerPointColor;
39 | private float centerPointSize;
40 | private float borderWidth;
41 | private float centerPointRadiu;
42 | private String centerPointType = "circle";
43 | private int sleepTime;
44 | private boolean isDrawText;
45 | private String dayArray[] = null;
46 | private Timer mTimer = new Timer();
47 | private int circleRadius = 0;
48 | private DecimalFormat format = new DecimalFormat("#");
49 | private DecimalFormat decimalformat = new DecimalFormat("#0.00");
50 | private Handler mhandler = new Handler() {
51 | @Override
52 | public void handleMessage(Message msg) {
53 | super.handleMessage(msg);
54 | startRotation();
55 | }
56 | };
57 | /**
58 | * 是否设置选中文字,
59 | */
60 | private boolean isSetSelectedText = true;
61 |
62 | private void startRotation() {
63 | AnimatorSet animatorSet = new AnimatorSet();
64 | ValueAnimator rotation_15A = ObjectAnimator.ofFloat(this, "rotation", 360 - startDegrees, 360 - mDayDegree);
65 | rotation_15A.setInterpolator(new AccelerateInterpolator());
66 | rotation_15A.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
67 | @Override
68 | public void onAnimationUpdate(ValueAnimator animation) {
69 | float animatedFraction = animation.getAnimatedFraction();
70 | //滚动一段后,就将上一个选中的文字置灰
71 | if (animatedFraction < 0.5 && animatedFraction >= 0.4) {
72 | isSetSelectedText = false;
73 | invalidate();
74 | }
75 | }
76 | });
77 | rotation_15A.addListener(new AnimatorListenerAdapter() {
78 | @Override
79 | public void onAnimationEnd(Animator animation) {
80 | //结束滚动后,当前的文字设置选中
81 | isSetSelectedText = true;
82 | invalidate();
83 | }
84 |
85 | });
86 | animatorSet.play(rotation_15A);
87 | animatorSet.start();
88 | }
89 |
90 | public AutoRotateDayView(Context context) {
91 | super(context);
92 | this.mContext = context;
93 | initPainter();
94 | }
95 |
96 | public AutoRotateDayView(Context context, AttributeSet attrs) {
97 | super(context, attrs);
98 | this.mContext = context;
99 | init(context, attrs);
100 | initPainter();
101 | }
102 |
103 | /**
104 | * 初始化各参数
105 | *
106 | * @param context
107 | * @param attrs
108 | */
109 | private void init(Context context, AttributeSet attrs) {
110 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimeView);
111 | borderColor = ta.getColor(R.styleable.TimeView_borderColor, Color.WHITE);
112 | borderWidth = ta.getDimension(R.styleable.TimeView_borderWidth,
113 | SizeUtils.dp2px(context, 1));
114 | textColor = ta.getColor(R.styleable.TimeView_textColor, Color.parseColor("#999999"));
115 | textSize = ta.getDimension(R.styleable.TimeView_textSize,
116 | SizeUtils.dp2px(context, 12));
117 | isDrawText = ta.getBoolean(R.styleable.TimeView_isDrawText, true);
118 | centerPointColor = ta.getColor(R.styleable.TimeView_centerPointColor, Color.WHITE);
119 | centerPointSize = ta.getDimension(R.styleable.TimeView_centerPointSize,
120 | SizeUtils.dp2px(context, 5));
121 | centerPointRadiu = ta.getDimension(R.styleable.TimeView_centerPointRadiu,
122 | SizeUtils.dp2px(context, 2));
123 | centerPointType = ta.getString(R.styleable.TimeView_centerPointType);
124 | if (TextUtils.isEmpty(centerPointType)) {
125 | centerPointType = "circle";
126 | }
127 | isShowHalf=ta.getBoolean(R.styleable.TimeView_isShowHalf,true);
128 | sleepTime = 1000;
129 | ta.recycle();
130 | }
131 |
132 | /**
133 | * 初始化画笔
134 | */
135 | private void initPainter() {
136 | mPaint = new Paint();
137 | mPaint.setAntiAlias(true);
138 | mPaint.setColor(Color.BLACK);
139 | mPaint.setStyle(Paint.Style.STROKE);
140 | mPaint.setStrokeWidth(2);
141 | //文字选中的画笔
142 | mSelectedPaint = new Paint();
143 | mSelectedPaint.setAntiAlias(true);
144 | mSelectedPaint.setColor(Color.WHITE);
145 | mSelectedPaint.setStyle(Paint.Style.FILL);
146 | mSelectedPaint.setStrokeWidth(1);
147 | mSelectedPaint.setTextSize(textSize);
148 | }
149 | /**
150 | * 是否显示一半
151 | */
152 | private boolean isShowHalf=true;
153 | @Override
154 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
155 | int size = SizeUtils.measureSize(mContext, widthMeasureSpec);
156 | int sizeH = SizeUtils.measureSize(mContext, heightMeasureSpec);
157 | setMeasuredDimension(sizeH, sizeH);
158 | if (isShowHalf)setTranslationX(-size / 2);
159 | }
160 |
161 | /**
162 | * 绘制(时、分、秒)数字之间的间隔
163 | */
164 | private static int drawNumberSpace = 0;
165 |
166 | @Override
167 | protected void onDraw(Canvas canvas) {
168 | drawNumberSpace = SizeUtils.dp2px(getContext(), 12);
169 | circleRadius = getWidth() / 3;
170 | //画外边框
171 | // mPaint.setColor(borderColor);
172 | // mPaint.setStrokeWidth(2);
173 | // mPaint.setStyle(Paint.Style.STROKE);
174 | // Path path = new Path();
175 | // path.lineTo(getWidth(), 0);
176 | // path.lineTo(getWidth(), getHeight());
177 | // path.lineTo(0, getHeight());
178 | // path.close();
179 | // canvas.drawPath(path, mPaint);
180 | //外圆边界
181 | // drawCircleOut(canvas);
182 | //圆心
183 | // drawCirclePoint(canvas);
184 | //画时、分、秒针数字
185 | drawHourNumber(canvas);
186 | }
187 |
188 |
189 | /**
190 | * 月的时间轴一次转动的角度
191 | */
192 | private float mDayFirstDegree = 12.0f;
193 |
194 | /**
195 | * @param day 第几天
196 | * @param currentMonthAllDayCount 当月有多少天
197 | */
198 | public AutoRotateDayView setTime(int day, int currentMonthAllDayCount) {
199 | if (day < 1 || day > 31 || currentMonthAllDayCount > 31 || currentMonthAllDayCount < 28) {
200 | Toast.makeText(getContext(), "天数-不合法", Toast.LENGTH_SHORT).show();
201 | return this;
202 | }
203 | dayArray = new String[currentMonthAllDayCount];
204 |
205 | for (int i = 0; i < currentMonthAllDayCount; i++) {
206 | int currentDay = day + i;
207 | currentDay = currentDay > currentMonthAllDayCount ? currentDay - currentMonthAllDayCount : currentDay;
208 | dayArray[i] = (currentDay + "");
209 | }
210 | mDayFirstDegree = Float.parseFloat(decimalformat.format((360.0f / dayArray.length)));
211 | invalidate();
212 | return this;
213 | }
214 |
215 | /**
216 | * 时钟走起
217 | */
218 | public void start() {
219 | mTimer.schedule(task, 0, sleepTime);
220 | }
221 |
222 | public float startDegrees = 0;
223 | private TimerTask task = new TimerTask() {
224 | @Override
225 | public void run() {
226 | if (mDayDegree == 360) {
227 | mDayDegree = 0;
228 | }
229 | Calendar calendar = Calendar.getInstance();
230 | int hours = calendar.getTime().getHours();
231 | int seconds = calendar.getTime().getSeconds();
232 | int minutes = calendar.getTime().getMinutes();
233 | // 23点0分0秒
234 | if (hours == 23 && seconds == 59 && minutes == 59) {//到达了一小时了
235 | startDegrees = mDayDegree;
236 | mDayDegree = mDayDegree + mDayFirstDegree;
237 | mhandler.sendEmptyMessage(0);
238 | }
239 | }
240 | };
241 |
242 |
243 | /**
244 | * 画小时 数字
245 | *
246 | * @param canvas
247 | */
248 | private void drawHourNumber(Canvas canvas) {
249 | if (isDrawText) {
250 | if (dayArray==null||TextUtils.isEmpty(dayArray[0])) return;
251 | //旋转圆盘
252 | canvas.save();
253 | mPaint.setColor(textColor);
254 | mPaint.setStyle(Paint.Style.FILL);
255 | mPaint.setStrokeWidth(1);
256 | mPaint.setTextSize(textSize);
257 | canvas.translate(getWidth() / 2, getHeight() / 2);
258 | float currentDegree = 0;
259 | for (int i = 0; i < dayArray.length; i++) {
260 | Rect textBound = new Rect();//创建一个矩形
261 | String text = dayArray[i];
262 | //将文字装在上面创建的矩形中,即这个矩形就是文字的边框
263 | mPaint.getTextBounds(text, 0, text.length(), textBound);
264 | //当前文字 绘制的水平角度
265 | float curAllDegree = Float.parseFloat(format.format((currentDegree + 360 - mDayDegree)));
266 | //绘制文字的开始坐标
267 | int drawTStartX = getMaxTextWidth("2019年") + drawNumberSpace + getMaxTextWidth("12月") + drawNumberSpace;
268 | if (isSetSelectedText && curAllDegree % 360 == 0) {
269 | canvas.drawText(text, drawTStartX, textBound.height() / 2, mSelectedPaint);
270 | } else {
271 | canvas.drawText(text, drawTStartX, textBound.height() / 2, mPaint);
272 | }
273 | canvas.rotate(mDayFirstDegree);
274 | currentDegree = currentDegree + mDayFirstDegree;
275 | }
276 | canvas.restore();
277 | }
278 | }
279 |
280 | private int getMaxTextWidth(String maxWText) {
281 | mPaint.setColor(textColor);
282 | mPaint.setStyle(Paint.Style.FILL);
283 | mPaint.setStrokeWidth(1);
284 | mPaint.setTextSize(textSize);
285 | Rect textBound = new Rect();//创建一个矩形
286 | mPaint.getTextBounds(maxWText, 0, maxWText.length(), textBound);
287 | return textBound.width();
288 | }
289 |
290 |
291 | /**
292 | * 画圆圈
293 | *
294 | * @param canvas
295 | */
296 | private void drawCircleOut(Canvas canvas) {
297 | //半径是宽度的1/3
298 | mPaint.setColor(borderColor);
299 | mPaint.setStrokeWidth(borderWidth);
300 | mPaint.setStyle(Paint.Style.STROKE);
301 | mPaint.setTextSize(textSize);
302 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, circleRadius, mPaint);
303 | }
304 |
305 |
306 | /**
307 | * 画圆心
308 | *
309 | * @param canvas
310 | */
311 | private void drawCirclePoint(Canvas canvas) {
312 | mPaint.setColor(centerPointColor);
313 | mPaint.setStrokeWidth(centerPointSize);
314 | if (centerPointType.equals("rect")) {
315 | canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);
316 | } else if (centerPointType.equals("circle")) {
317 | mPaint.setStyle(Paint.Style.FILL);
318 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, centerPointRadiu, mPaint);
319 | }
320 | }
321 |
322 | }
323 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/AutoRotateWeekView.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 |
4 | import android.animation.Animator;
5 | import android.animation.AnimatorListenerAdapter;
6 | import android.animation.AnimatorSet;
7 | import android.animation.ObjectAnimator;
8 | import android.animation.ValueAnimator;
9 | import android.content.Context;
10 | import android.content.res.TypedArray;
11 | import android.graphics.Canvas;
12 | import android.graphics.Color;
13 | import android.graphics.Paint;
14 | import android.graphics.Rect;
15 | import android.os.Handler;
16 | import android.os.Message;
17 | import android.text.TextUtils;
18 | import android.util.AttributeSet;
19 | import android.view.View;
20 | import android.view.animation.AccelerateInterpolator;
21 | import android.widget.Toast;
22 |
23 | import com.example.admin.mytimewheelclock.utils.NumberToChineseUtils;
24 | import com.example.admin.mytimewheelclock.utils.SizeUtils;
25 |
26 | import java.text.DecimalFormat;
27 | import java.util.Calendar;
28 | import java.util.Timer;
29 | import java.util.TimerTask;
30 |
31 | public class AutoRotateWeekView extends View {
32 |
33 | private Context mContext;
34 | private Paint mPaint, mSelectedPaint;
35 | private float mWeekDegree;
36 | private int borderColor;
37 | private int textColor;
38 | private float textSize;
39 | private int centerPointColor;
40 | private float centerPointSize;
41 | private float borderWidth;
42 | private float centerPointRadiu;
43 | private String centerPointType = "circle";
44 | private int sleepTime;
45 | private boolean isDrawText;
46 | private String weekArray[] = new String[7];
47 | private Timer mTimer = new Timer();
48 | private int circleRadius = 0;
49 | private DecimalFormat format = new DecimalFormat("#");
50 | private DecimalFormat decimalformat = new DecimalFormat("#0.00");
51 | private Handler mhandler = new Handler() {
52 | @Override
53 | public void handleMessage(Message msg) {
54 | super.handleMessage(msg);
55 | startRotation();
56 | }
57 | };
58 | /**
59 | * 是否设置选中文字,
60 | */
61 | private boolean isSetSelectedText = true;
62 | /**
63 | * 是否显示一半
64 | */
65 | private boolean isShowHalf=true;
66 |
67 | private void startRotation() {
68 | AnimatorSet animatorSet = new AnimatorSet();
69 | ValueAnimator rotation_15A = ObjectAnimator.ofFloat(this, "rotation", 360 - startDegrees, 360 - mWeekDegree);
70 | rotation_15A.setInterpolator(new AccelerateInterpolator());
71 | rotation_15A.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
72 | @Override
73 | public void onAnimationUpdate(ValueAnimator animation) {
74 | float animatedFraction = animation.getAnimatedFraction();
75 | //滚动一段后,就将上一个选中的文字置灰
76 | if (animatedFraction < 0.5 && animatedFraction >= 0.4) {
77 | isSetSelectedText = false;
78 | invalidate();
79 | }
80 | }
81 | });
82 | rotation_15A.addListener(new AnimatorListenerAdapter() {
83 | @Override
84 | public void onAnimationEnd(Animator animation) {
85 | //结束滚动后,当前的文字设置选中
86 | isSetSelectedText = true;
87 | invalidate();
88 | }
89 |
90 | });
91 | animatorSet.play(rotation_15A);
92 | animatorSet.start();
93 | }
94 |
95 | public AutoRotateWeekView(Context context) {
96 | super(context);
97 | this.mContext = context;
98 | initPainter();
99 | }
100 |
101 | public AutoRotateWeekView(Context context, AttributeSet attrs) {
102 | super(context, attrs);
103 | this.mContext = context;
104 | init(context, attrs);
105 | initPainter();
106 | }
107 |
108 | /**
109 | * 初始化各参数
110 | *
111 | * @param context
112 | * @param attrs
113 | */
114 | private void init(Context context, AttributeSet attrs) {
115 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimeView);
116 | borderColor = ta.getColor(R.styleable.TimeView_borderColor, Color.WHITE);
117 | borderWidth = ta.getDimension(R.styleable.TimeView_borderWidth,
118 | SizeUtils.dp2px(context, 1));
119 | textColor = ta.getColor(R.styleable.TimeView_textColor, Color.parseColor("#999999"));
120 | textSize = ta.getDimension(R.styleable.TimeView_textSize,
121 | SizeUtils.dp2px(context, 12));
122 | isDrawText = ta.getBoolean(R.styleable.TimeView_isDrawText, true);
123 | centerPointColor = ta.getColor(R.styleable.TimeView_centerPointColor, Color.WHITE);
124 | centerPointSize = ta.getDimension(R.styleable.TimeView_centerPointSize,
125 | SizeUtils.dp2px(context, 5));
126 | centerPointRadiu = ta.getDimension(R.styleable.TimeView_centerPointRadiu,
127 | SizeUtils.dp2px(context, 2));
128 | centerPointType = ta.getString(R.styleable.TimeView_centerPointType);
129 | if (TextUtils.isEmpty(centerPointType)) {
130 | centerPointType = "circle";
131 | }
132 | isShowHalf=ta.getBoolean(R.styleable.TimeView_isShowHalf,true);
133 | sleepTime = 1000;
134 | ta.recycle();
135 | }
136 |
137 | /**
138 | * 初始化画笔
139 | */
140 | private void initPainter() {
141 | mPaint = new Paint();
142 | mPaint.setAntiAlias(true);
143 | mPaint.setColor(Color.BLACK);
144 | mPaint.setStyle(Paint.Style.STROKE);
145 | mPaint.setStrokeWidth(2);
146 | //文字选中的画笔
147 | mSelectedPaint = new Paint();
148 | mSelectedPaint.setAntiAlias(true);
149 | mSelectedPaint.setColor(Color.WHITE);
150 | mSelectedPaint.setStyle(Paint.Style.FILL);
151 | mSelectedPaint.setStrokeWidth(1);
152 | mSelectedPaint.setTextSize(textSize);
153 | }
154 |
155 | @Override
156 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
157 | int size = SizeUtils.measureSize(mContext, widthMeasureSpec);
158 | int sizeH = SizeUtils.measureSize(mContext, heightMeasureSpec);
159 | setMeasuredDimension(sizeH, sizeH);
160 | if (isShowHalf)setTranslationX(-size / 2);
161 | }
162 |
163 | /**
164 | * 绘制(时、分、秒)数字之间的间隔
165 | */
166 | private static int drawNumberSpace = 0;
167 |
168 | @Override
169 | protected void onDraw(Canvas canvas) {
170 | drawNumberSpace = SizeUtils.dp2px(getContext(), 12);
171 | circleRadius = getWidth() / 3;
172 | //画外边框
173 | // mPaint.setColor(borderColor);
174 | // mPaint.setStrokeWidth(2);
175 | // mPaint.setStyle(Paint.Style.STROKE);
176 | // Path path = new Path();
177 | // path.lineTo(getWidth(), 0);
178 | // path.lineTo(getWidth(), getHeight());
179 | // path.lineTo(0, getHeight());
180 | // path.close();
181 | // canvas.drawPath(path, mPaint);
182 | //外圆边界
183 | // drawCircleOut(canvas);
184 | //圆心
185 | // drawCirclePoint(canvas);
186 | //画时、分、秒针数字
187 | drawHourNumber(canvas);
188 | }
189 |
190 |
191 | /**
192 | * 月的时间轴一次转动的角度
193 | */
194 | private float mWeekFirstDegree = 12.0f;
195 |
196 | /**
197 | * @param week
198 | */
199 | public AutoRotateWeekView setTime(int week) {
200 | if (week < 1 || week > 7) {
201 | Toast.makeText(getContext(), "星期-不合法", Toast.LENGTH_SHORT).show();
202 | return this;
203 | }
204 | for (int i = 0; i < weekArray.length; i++) {
205 | int currentWeek = week + i;
206 | currentWeek = currentWeek > 7 ? currentWeek - 7 : currentWeek;
207 | //把时间字符小写转大写
208 | weekArray[i] = ("周" + NumberToChineseUtils.intToChinese(currentWeek));
209 | }
210 | mWeekFirstDegree = Float.parseFloat(decimalformat.format((360.0f / weekArray.length)));
211 | invalidate();
212 | return this;
213 | }
214 |
215 | /**
216 | * 时钟走起
217 | */
218 | public void start() {
219 | mTimer.schedule(task, 0, sleepTime);
220 | }
221 |
222 | public float startDegrees = 0;
223 | private TimerTask task = new TimerTask() {
224 | @Override
225 | public void run() {
226 | if (mWeekDegree == 360) {
227 | mWeekDegree = 0;
228 | }
229 | Calendar calendar = Calendar.getInstance();
230 | int hours = calendar.getTime().getHours();
231 | int seconds = calendar.getTime().getSeconds();
232 | int minutes = calendar.getTime().getMinutes();
233 | // 23点0分0秒
234 | if (hours == 23 && seconds == 59 && minutes == 59) {//到达了一小时了
235 | startDegrees = mWeekDegree;
236 | mWeekDegree = mWeekDegree + mWeekFirstDegree;
237 | mhandler.sendEmptyMessage(0);
238 | }
239 | }
240 | };
241 |
242 |
243 | /**
244 | * 画小时 数字
245 | *
246 | * @param canvas
247 | */
248 | private void drawHourNumber(Canvas canvas) {
249 | if (isDrawText) {
250 | if (weekArray == null || TextUtils.isEmpty(weekArray[0])) return;
251 | //旋转圆盘
252 | canvas.save();
253 | mPaint.setColor(textColor);
254 | mPaint.setStyle(Paint.Style.FILL);
255 | mPaint.setStrokeWidth(1);
256 | mPaint.setTextSize(textSize);
257 | canvas.translate(getWidth() / 2, getHeight() / 2);
258 | float currentDegree = 0;
259 | for (int i = 0; i < weekArray.length; i++) {
260 | Rect textBound = new Rect();//创建一个矩形
261 | String text = weekArray[i];
262 | //将文字装在上面创建的矩形中,即这个矩形就是文字的边框
263 | mPaint.getTextBounds(text, 0, text.length(), textBound);
264 | //当前文字 绘制的水平角度
265 | float curAllDegree = Float.parseFloat(format.format((currentDegree + 360 - mWeekDegree)));
266 | //绘制文字的开始坐标
267 | int drawTStartX = getMaxTextWidth("2019年") + drawNumberSpace +
268 | getMaxTextWidth("12月") + drawNumberSpace +
269 | getMaxTextWidth("31号") + drawNumberSpace;
270 | if (!isShowHalf){
271 | drawTStartX = getMaxTextWidth("2019")-drawNumberSpace/2;
272 | }
273 | if (isSetSelectedText && curAllDegree % 360 == 0) {
274 | canvas.drawText(text, drawTStartX, textBound.height() / 2, mSelectedPaint);
275 | } else {
276 | canvas.drawText(text, drawTStartX, textBound.height() / 2, mPaint);
277 | }
278 | canvas.rotate(mWeekFirstDegree);
279 | currentDegree = currentDegree + mWeekFirstDegree;
280 | }
281 | canvas.restore();
282 | }
283 | }
284 |
285 | private int getMaxTextWidth(String maxWText) {
286 | mPaint.setColor(textColor);
287 | mPaint.setStyle(Paint.Style.FILL);
288 | mPaint.setStrokeWidth(1);
289 | mPaint.setTextSize(textSize);
290 | Rect textBound = new Rect();//创建一个矩形
291 | mPaint.getTextBounds(maxWText, 0, maxWText.length(), textBound);
292 | return textBound.width();
293 | }
294 |
295 |
296 | /**
297 | * 画圆圈
298 | *
299 | * @param canvas
300 | */
301 | private void drawCircleOut(Canvas canvas) {
302 | //半径是宽度的1/3
303 | mPaint.setColor(borderColor);
304 | mPaint.setStrokeWidth(borderWidth);
305 | mPaint.setStyle(Paint.Style.STROKE);
306 | mPaint.setTextSize(textSize);
307 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, circleRadius, mPaint);
308 | }
309 |
310 |
311 | /**
312 | * 画圆心
313 | *
314 | * @param canvas
315 | */
316 | private void drawCirclePoint(Canvas canvas) {
317 | mPaint.setColor(centerPointColor);
318 | mPaint.setStrokeWidth(centerPointSize);
319 | if (centerPointType.equals("rect")) {
320 | canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);
321 | } else if (centerPointType.equals("circle")) {
322 | mPaint.setStyle(Paint.Style.FILL);
323 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, centerPointRadiu, mPaint);
324 | }
325 | }
326 |
327 | }
328 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/admin/mytimewheelclock/AutoRotateMonthView.java:
--------------------------------------------------------------------------------
1 | package com.example.admin.mytimewheelclock;
2 |
3 |
4 | import android.animation.Animator;
5 | import android.animation.AnimatorListenerAdapter;
6 | import android.animation.AnimatorSet;
7 | import android.animation.ObjectAnimator;
8 | import android.animation.ValueAnimator;
9 | import android.annotation.SuppressLint;
10 | import android.content.Context;
11 | import android.content.res.TypedArray;
12 | import android.graphics.Canvas;
13 | import android.graphics.Color;
14 | import android.graphics.Paint;
15 | import android.graphics.Rect;
16 | import android.os.Handler;
17 | import android.os.Message;
18 | import android.text.TextUtils;
19 | import android.util.AttributeSet;
20 | import android.view.View;
21 | import android.view.animation.AccelerateInterpolator;
22 | import android.widget.Toast;
23 |
24 | import com.example.admin.mytimewheelclock.utils.DateUtils;
25 | import com.example.admin.mytimewheelclock.utils.SizeUtils;
26 |
27 | import java.text.DecimalFormat;
28 | import java.util.Calendar;
29 | import java.util.Timer;
30 | import java.util.TimerTask;
31 |
32 | public class AutoRotateMonthView extends View {
33 |
34 | private Context mContext;
35 | private Paint mPaint, mSelectedPaint;
36 | private float mDayDegree;
37 | private int borderColor;
38 | private int textColor;
39 | private float textSize;
40 | private int centerPointColor;
41 | private float centerPointSize;
42 | private float borderWidth;
43 | private float centerPointRadiu;
44 | private String centerPointType = "circle";
45 | private int sleepTime;
46 | private boolean isDrawText;
47 | private String monthArray[] = new String[12];
48 | private Timer mTimer = new Timer();
49 | private int circleRadius = 0;
50 | private DecimalFormat format = new DecimalFormat("#");
51 | private DecimalFormat decimalformat = new DecimalFormat("#0.00");
52 | @SuppressLint("HandlerLeak")
53 | private Handler mhandler = new Handler() {
54 | @Override
55 | public void handleMessage(Message msg) {
56 | super.handleMessage(msg);
57 | startRotation();
58 | }
59 | };
60 | /**
61 | * 是否设置选中文字,
62 | */
63 | private boolean isSetSelectedText = true;
64 |
65 | private void startRotation() {
66 | AnimatorSet animatorSet = new AnimatorSet();
67 | ValueAnimator rotation_15A = ObjectAnimator.ofFloat(this, "rotation", 360 - startDegrees, 360 - mDayDegree);
68 | rotation_15A.setInterpolator(new AccelerateInterpolator());
69 | rotation_15A.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
70 | @Override
71 | public void onAnimationUpdate(ValueAnimator animation) {
72 | float animatedFraction = animation.getAnimatedFraction();
73 | //滚动一段后,就将上一个选中的文字置灰
74 | if (animatedFraction < 0.5 && animatedFraction >= 0.4) {
75 | isSetSelectedText = false;
76 | invalidate();
77 | }
78 | }
79 | });
80 | rotation_15A.addListener(new AnimatorListenerAdapter() {
81 | @Override
82 | public void onAnimationEnd(Animator animation) {
83 | //结束滚动后,当前的文字设置选中
84 | isSetSelectedText = true;
85 | invalidate();
86 | }
87 |
88 | });
89 | animatorSet.play(rotation_15A);
90 | animatorSet.start();
91 | }
92 |
93 | public AutoRotateMonthView(Context context) {
94 | super(context);
95 | this.mContext = context;
96 | initPainter();
97 | }
98 |
99 | public AutoRotateMonthView(Context context, AttributeSet attrs) {
100 | super(context, attrs);
101 | this.mContext = context;
102 | init(context, attrs);
103 | initPainter();
104 | }
105 |
106 | /**
107 | * 初始化各参数
108 | *
109 | * @param context
110 | * @param attrs
111 | */
112 | private void init(Context context, AttributeSet attrs) {
113 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimeView);
114 | borderColor = ta.getColor(R.styleable.TimeView_borderColor, Color.WHITE);
115 | borderWidth = ta.getDimension(R.styleable.TimeView_borderWidth,
116 | SizeUtils.dp2px(context, 1));
117 | textColor = ta.getColor(R.styleable.TimeView_textColor, Color.parseColor("#999999"));
118 | textSize = ta.getDimension(R.styleable.TimeView_textSize,
119 | SizeUtils.dp2px(context, 12));
120 | isDrawText = ta.getBoolean(R.styleable.TimeView_isDrawText, true);
121 | centerPointColor = ta.getColor(R.styleable.TimeView_centerPointColor, Color.WHITE);
122 | centerPointSize = ta.getDimension(R.styleable.TimeView_centerPointSize,
123 | SizeUtils.dp2px(context, 5));
124 | centerPointRadiu = ta.getDimension(R.styleable.TimeView_centerPointRadiu,
125 | SizeUtils.dp2px(context, 2));
126 | centerPointType = ta.getString(R.styleable.TimeView_centerPointType);
127 | if (TextUtils.isEmpty(centerPointType)) {
128 | centerPointType = "circle";
129 | }
130 | isShowHalf=ta.getBoolean(R.styleable.TimeView_isShowHalf,true);
131 | sleepTime = 1000;
132 | ta.recycle();
133 | }
134 |
135 | /**
136 | * 初始化画笔
137 | */
138 | private void initPainter() {
139 | mPaint = new Paint();
140 | mPaint.setAntiAlias(true);
141 | mPaint.setColor(Color.BLACK);
142 | mPaint.setStyle(Paint.Style.STROKE);
143 | mPaint.setStrokeWidth(2);
144 | //文字选中的画笔
145 | mSelectedPaint = new Paint();
146 | mSelectedPaint.setAntiAlias(true);
147 | mSelectedPaint.setColor(Color.WHITE);
148 | mSelectedPaint.setStyle(Paint.Style.FILL);
149 | mSelectedPaint.setStrokeWidth(1);
150 | mSelectedPaint.setTextSize(textSize);
151 | }
152 |
153 | /**
154 | * 是否显示一半
155 | */
156 | private boolean isShowHalf=true;
157 | @Override
158 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
159 | int size = SizeUtils.measureSize(mContext, widthMeasureSpec);
160 | int sizeH = SizeUtils.measureSize(mContext, heightMeasureSpec);
161 | setMeasuredDimension(sizeH, sizeH);
162 | if (isShowHalf)setTranslationX(-size / 2);
163 | }
164 |
165 | /**
166 | * 绘制(时、分、秒)数字之间的间隔
167 | */
168 | private static int drawNumberSpace = 0;
169 |
170 | @Override
171 | protected void onDraw(Canvas canvas) {
172 | drawNumberSpace = SizeUtils.dp2px(getContext(), 12);
173 | circleRadius = getWidth() / 3;
174 | //画外边框
175 | // mPaint.setColor(borderColor);
176 | // mPaint.setStrokeWidth(2);
177 | // mPaint.setStyle(Paint.Style.STROKE);
178 | // Path path = new Path();
179 | // path.lineTo(getWidth(), 0);
180 | // path.lineTo(getWidth(), getHeight());
181 | // path.lineTo(0, getHeight());
182 | // path.close();
183 | // canvas.drawPath(path, mPaint);
184 | //外圆边界
185 | // drawCircleOut(canvas);
186 | //圆心
187 | if (isShowHalf)drawCirclePoint(canvas);
188 | //画时、分、秒针数字
189 | drawHourNumber(canvas);
190 | }
191 |
192 |
193 | /**
194 | * 月的时间轴一次转动的角度
195 | */
196 | private float mDayFirstDegree = 12.0f;
197 |
198 | /**
199 | * 当月有多少天
200 | */
201 | private int currentMonthAllDayCount = 30;
202 |
203 | /**
204 | * @param day 第几天
205 | * @param currentMonthAllDayCount 当月有多少天
206 | */
207 | public AutoRotateMonthView setTime(int day, int currentMonthAllDayCount) {
208 | if (currentMonthAllDayCount > 31 || currentMonthAllDayCount < 28) {
209 | Toast.makeText(getContext(), "月份-不合法", Toast.LENGTH_SHORT).show();
210 | return this;
211 | }
212 | this.currentMonthAllDayCount = currentMonthAllDayCount;
213 | for (int i = 0; i < monthArray.length; i++) {
214 | int currentDay = day + i;
215 | currentDay = currentDay > 12 ? currentDay - 12 : currentDay;
216 | monthArray[i] = (currentDay + "月");
217 | }
218 | mDayFirstDegree = Float.parseFloat(decimalformat.format((360.0f / monthArray.length)));
219 | invalidate();
220 | return this;
221 | }
222 |
223 | /**
224 | * 时钟走起
225 | */
226 | public void start() {
227 | mTimer.schedule(task, 0, sleepTime);
228 | }
229 |
230 | public float startDegrees = 0;
231 | private TimerTask task = new TimerTask() {
232 | @Override
233 | public void run() {
234 | if (mDayDegree == 360) {
235 | mDayDegree = 0;
236 | }
237 | Calendar calendar = Calendar.getInstance();
238 | int year = calendar.get(Calendar.YEAR);
239 | int month = calendar.get(Calendar.MONTH) + 1;
240 | int day = calendar.get(Calendar.DAY_OF_MONTH);
241 | int hours = calendar.get(Calendar.HOUR_OF_DAY);
242 | int minutes = calendar.get(Calendar.MINUTE);
243 | int seconds = calendar.get(Calendar.SECOND);
244 | // 最后一天例如 2019年12月31日23点0分0秒
245 | if (day == currentMonthAllDayCount && hours == 23 && seconds == 59 && minutes == 59) {//到达了一天了
246 | startDegrees = mDayDegree;
247 | if (month == 12) {
248 | //改变年
249 | if (changeTimeListener != null) {
250 | changeTimeListener.changeYear(year + 1);
251 | }
252 | }
253 | //判断下一个月的共有多少天,和本月的不同就更新天数时间轴
254 | if (month < 12) {
255 | int count0 = DateUtils.getDays(year, month);
256 | int count1 = DateUtils.getDays(year, month + 1);
257 | if (count0 != count1) {
258 | //改变年
259 | if (changeTimeListener != null) {
260 | changeTimeListener.changeDay(count1);
261 | }
262 | }
263 | }
264 | mDayDegree = mDayDegree + mDayFirstDegree;
265 | mhandler.sendEmptyMessage(0);
266 | }
267 |
268 | }
269 | };
270 |
271 |
272 | /**
273 | * 画小时 数字
274 | *
275 | * @param canvas
276 | */
277 | private void drawHourNumber(Canvas canvas) {
278 | if (isDrawText) {
279 | if (TextUtils.isEmpty(monthArray[0])) return;
280 | //旋转圆盘
281 | canvas.save();
282 | mPaint.setColor(textColor);
283 | mPaint.setStyle(Paint.Style.FILL);
284 | mPaint.setStrokeWidth(1);
285 | mPaint.setTextSize(textSize);
286 | canvas.translate(getWidth() / 2, getHeight() / 2);
287 | float currentDegree = 0;
288 | for (int i = 0; i < monthArray.length; i++) {
289 | Rect textBound = new Rect();//创建一个矩形
290 | String text = monthArray[i];
291 | //将文字装在上面创建的矩形中,即这个矩形就是文字的边框
292 | mPaint.getTextBounds(text, 0, text.length(), textBound);
293 | //当前文字 绘制的水平角度
294 | float curAllDegree = Float.parseFloat(format.format((currentDegree + 360 - mDayDegree)));
295 | if (isSetSelectedText && curAllDegree % 360 == 0) {
296 | canvas.drawText(text, getMaxTextWidth("2019年") + drawNumberSpace, textBound.height() / 2, mSelectedPaint);
297 | } else {
298 | canvas.drawText(text, getMaxTextWidth("2019年") + drawNumberSpace, textBound.height() / 2, mPaint);
299 | }
300 | canvas.rotate(mDayFirstDegree);
301 | currentDegree = currentDegree + mDayFirstDegree;
302 | }
303 | canvas.restore();
304 | }
305 | }
306 |
307 | private int getMaxTextWidth(String maxWText) {
308 | mPaint.setColor(textColor);
309 | mPaint.setStyle(Paint.Style.FILL);
310 | mPaint.setStrokeWidth(1);
311 | mPaint.setTextSize(textSize);
312 | Rect textBound = new Rect();//创建一个矩形
313 | mPaint.getTextBounds(maxWText, 0, maxWText.length(), textBound);
314 | return textBound.width();
315 | }
316 |
317 |
318 | /**
319 | * 画圆圈
320 | *
321 | * @param canvas
322 | */
323 | private void drawCircleOut(Canvas canvas) {
324 | //半径是宽度的1/3
325 | mPaint.setColor(borderColor);
326 | mPaint.setStrokeWidth(borderWidth);
327 | mPaint.setStyle(Paint.Style.STROKE);
328 | mPaint.setTextSize(textSize);
329 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, circleRadius, mPaint);
330 | }
331 |
332 |
333 | /**
334 | * 画圆心
335 | *
336 | * @param canvas
337 | */
338 | private void drawCirclePoint(Canvas canvas) {
339 | mPaint.setColor(centerPointColor);
340 | mPaint.setStrokeWidth(centerPointSize);
341 | if (centerPointType.equals("rect")) {
342 | canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);
343 | } else if (centerPointType.equals("circle")) {
344 | mPaint.setStyle(Paint.Style.FILL);
345 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, centerPointRadiu, mPaint);
346 | }
347 | }
348 |
349 | //----------------------------对外开放的接口---------------------------------
350 | public interface OnChangeTimeListener {
351 | void changeYear(int year);
352 |
353 | void changeDay(int count);
354 | }
355 |
356 | public OnChangeTimeListener changeTimeListener;
357 |
358 | public void setChangeTimeListener(OnChangeTimeListener changeTimeListener) {
359 | this.changeTimeListener = changeTimeListener;
360 | }
361 | }
362 |
--------------------------------------------------------------------------------