├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── ForTheWatch.iml
├── ForTheWatchSource.iml
├── README.md
├── README_ENGLISH.md
├── TimerView.iml
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── pheynix
│ │ └── forthewatch
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── pheynix
│ │ └── forthewatch
│ │ ├── MainActivity.java
│ │ ├── Model.java
│ │ └── MyTimer.java
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── menu
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── forthewatch
├── .gitignore
├── build.gradle
├── forthewatch.iml
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── pheynix
│ │ └── forthewatch
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── pheynix
│ │ └── forthewatch
│ │ ├── Model.java
│ │ └── MyTimer.java
│ └── res
│ └── values
│ └── strings.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── read_me
├── screen_record.gif
└── screen_shot.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | TimerView
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ForTheWatch.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ForTheWatchSource.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TimerView
2 |
3 | [ENGLISH README](README_ENGLISH.md)
4 |
5 |
6 | ## 重要的事情
7 |
8 | 由于使用了MaskFilter,辉光效果必须[关闭硬件加速](http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/)!!
9 |
10 | 由于使用了MakskFilter,辉光效果必须[关闭硬件加速](http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/)!!
11 |
12 | 由于使用了MakskFilter,辉光效果必须[关闭硬件加速](http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/)!!
13 |
14 |
15 |
16 |
17 | ## 简介
18 |
19 | 一个倒计时的View,
20 |
21 | 可以实现最大6小时的倒计时,具体看图~
22 |
23 |
24 |
25 |
26 | ## 展示
27 |
28 | 控件长这样,下方的button不属于这个控件
29 |
30 | 
31 |
32 |
33 |
34 |
35 |
36 |
37 | 👇👇👇👇👇👇👇👇👇👇这儿有个gif图,等一会或者刷新~👇👇👇👇👇👇👇👇👇👇
38 |
39 |
40 | 
41 |
42 | 👆👆👆👆👆👆👆👆👆👆这儿有个gif图,等一会或者刷新~👆👆👆👆👆👆👆👆👆👆
43 |
44 |
45 |
46 | ## 使用
47 |
48 | ### 方法1:
49 |
50 | 添加以下代码到build.gradle(Project:xxxxx):
51 |
52 | ```
53 | repositories {
54 | maven { url "https://jitpack.io" }
55 | }
56 |
57 | ```
58 |
59 | 添加以下代码到build.gradle(Module:xxxxx):
60 |
61 | ```
62 | dependencies {
63 | compile 'com.github.pheynix:TimerView:660400fb64'
64 | }
65 | ```
66 |
67 | ### 方法2:
68 |
69 | 找到[MyTimer.java](/app/src/main/java/com/pheynix/forthewatch/MyTimer.java),复制到你的项目里面,就可以使用或者修改了~
70 |
71 |
72 |
73 |
74 | ## 更新
75 |
76 |
77 | * 20150716 增加listener
78 | * 20150717 整合@wsk900906 fork的版本,增加计时功能,新增listener
79 | * 20150719 增加lib方便使用
80 |
81 | ----
82 |
83 |
84 | 如果对这么控件有啥问题或建议,尽情提issues或者[发邮件](mailto:pheynixdu@gmail.com)给我pheynixdu@gmail.com
85 |
86 |
87 |
88 | 其实我是真的忙...
89 |
90 | 一个人写了好久代码,想找群小伙伴了
91 |
92 | 深圳/广州找工作ing...
93 |
--------------------------------------------------------------------------------
/README_ENGLISH.md:
--------------------------------------------------------------------------------
1 | # TimerView
2 |
3 | [中文README](README.md)
4 |
5 |
6 | ## Something important
7 |
8 |
9 | If you need the glow effect,please [DISABLE](http://developer.android.com/guide/topics/graphics/hardware-accel.html) hardware accelerated
10 |
11 | If you need the glow effect,please [DISABLE](http://developer.android.com/guide/topics/graphics/hardware-accel.html) hardware accelerated
12 |
13 | If you need the glow effect,please [DISABLE](http://developer.android.com/guide/topics/graphics/hardware-accel.html) hardware accelerated
14 |
15 |
16 |
17 |
18 | ## Summary
19 |
20 | a timer view,
21 |
22 | look at the picture below;
23 |
24 |
25 |
26 |
27 |
28 |
29 | ## Screen shot
30 |
31 | It looks like this,buttons doesn't belong to this view
32 |
33 | 
34 |
35 |
36 |
37 |
38 |
39 |
40 | 👇👇👇👇👇👇👇👇👇👇here is a gif picture,wait or refresh👇👇👇👇👇👇👇👇👇👇
41 |
42 |
43 | 
44 |
45 | 👆👆👆👆👆👆👆👆👆👆here is a gif picture,wait or refresh👆👆👆👆👆👆👆👆👆👆
46 |
47 |
48 | ## 使用
49 |
50 | ### Method 1:
51 |
52 | Add the following to your build.gradle(Project:xxxxx):
53 |
54 | ```
55 | repositories {
56 | maven { url "https://jitpack.io" }
57 | }
58 | ```
59 |
60 | Add the following to your build.gradle(Module:xxxxx):
61 |
62 | ```
63 | dependencies {
64 | compile 'com.github.pheynix:TimerView:660400fb64'
65 | }
66 | ```
67 |
68 | ### Method 2:
69 |
70 | Locate [MyTimer.java](/app/src/main/java/com/pheynix/forthewatch/MyTimer.java),copy it,modify it,use it,or just let it lay on you hard disk forever~
71 |
72 |
73 |
74 | ## Change Log
75 |
76 |
77 | * 20150716 add listener,add English remark
78 | * 20150717 merge fork version from @wsk900906 , add timing function,add listener
79 | * 20150719 add lib
80 |
81 |
82 | ----
83 |
84 | If you have problem using this view or want to give me some advice on improving it,feel free to issues or [email me](mailto:pheynixdu@gmail.com),pheynixdu@gmail.com.
85 |
86 |
87 |
88 |
89 | I AM SO SORRY FOR MY POOR ENGLISH...
90 |
--------------------------------------------------------------------------------
/TimerView.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | generateDebugAndroidTestSources
19 | generateDebugSources
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "21.1.2"
6 |
7 | defaultConfig {
8 | applicationId "com.pheynix.forthewatch"
9 | minSdkVersion 16
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile 'com.android.support:appcompat-v7:22.2.0'
25 | }
26 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/pheynix/Documents/coding/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/pheynix/forthewatch/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.pheynix.forthewatch;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pheynix/forthewatch/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.pheynix.forthewatch;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.util.Log;
6 | import android.view.View;
7 | import android.widget.Button;
8 |
9 | public class MainActivity extends AppCompatActivity implements View.OnClickListener, MyTimer.OnTimeChangeListener, MyTimer.OnSecondChangListener,MyTimer.OnMinChangListener,MyTimer.OnHourChangListener {
10 |
11 | MyTimer timer;
12 | Button btn_start,btn_stop,btn_reset;
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_main);
18 |
19 | timer = (MyTimer) findViewById(R.id.timer);
20 | timer.setOnTimeChangeListener(this);
21 | timer.setSecondChangListener(this);
22 | timer.setMinChangListener(this);
23 | timer.setHourChangListener(this);
24 | timer.setModel(Model.Timer);
25 | timer.setStartTime(1,30,30);
26 | btn_start = (Button) findViewById(R.id.btn_start);
27 | btn_stop = (Button) findViewById(R.id.btn_stop);
28 | btn_reset = (Button) findViewById(R.id.btn_reset);
29 | btn_start.setOnClickListener(this);
30 | btn_stop.setOnClickListener(this);
31 | btn_reset.setOnClickListener(this);
32 | }
33 |
34 | @Override
35 | public void onClick(View v) {
36 | switch (v.getId()){
37 | case R.id.btn_start:
38 | timer.start();
39 | break;
40 | case R.id.btn_stop:
41 | timer.stop();
42 | break;
43 | case R.id.btn_reset:
44 | timer.reset();
45 | break;
46 | }
47 | }
48 |
49 | @Override
50 | public void onTimerStart(long timeStart) {
51 | Log.e("pheynix","onTimerStart "+timeStart);
52 | }
53 |
54 | @Override
55 | public void onTimeChange(long timeStart, long timeRemain) {
56 | Log.e("pheynix","onTimeChange timeStart "+timeStart);
57 | Log.e("pheynix","onTimeChange timeRemain "+timeRemain);
58 | }
59 |
60 | @Override
61 | public void onTimeStop(long timeStart, long timeRemain) {
62 | Log.e("pheynix","onTimeStop timeRemain "+timeStart);
63 | Log.e("pheynix","onTimeStop timeRemain "+timeRemain);
64 | }
65 |
66 | @Override
67 | public void onSecondChange(int second) {
68 | Log.e("swifty","second change to "+second);
69 | }
70 |
71 | @Override
72 | public void onHourChange(int hour) {
73 | Log.e("swifty","hour change to "+hour);
74 | }
75 |
76 | @Override
77 | public void onMinChange(int minute) {
78 | Log.e("swifty", "minute change to "+minute);
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pheynix/forthewatch/Model.java:
--------------------------------------------------------------------------------
1 | package com.pheynix.forthewatch;
2 |
3 | /**
4 | * Created by Swifty.Wang on 2015/7/16.
5 | */
6 | public enum Model {
7 | Timer,
8 | StopWatch;
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pheynix/forthewatch/MyTimer.java:
--------------------------------------------------------------------------------
1 | package com.pheynix.forthewatch;
2 |
3 | import android.content.Context;
4 | import android.graphics.BlurMaskFilter;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.graphics.RectF;
9 | import android.os.Handler;
10 | import android.os.Message;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 |
16 | import java.util.Calendar;
17 | import java.util.Timer;
18 | import java.util.TimerTask;
19 |
20 |
21 | /**
22 | * Created by pheynix on 7/12/15.
23 | * Sorry for my English...
24 | *
25 | *Disable hardware accelerate if you need the glow effect,See: http://developer.android.com/guide/topics/graphics/hardware-accel.html
26 | *Disable hardware accelerate if you need the glow effect,See: http://developer.android.com/guide/topics/graphics/hardware-accel.html
27 | *Disable hardware accelerate if you need the glow effect,See: http://developer.android.com/guide/topics/graphics/hardware-accel.html
28 | *如果需要辉光效果,请务必关闭硬件加速,参考: http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/
29 | *如果需要辉光效果,请务必关闭硬件加速,参考: http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/
30 | *如果需要辉光效果,请务必关闭硬件加速,参考: http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/
31 | */
32 | public class MyTimer extends View {
33 |
34 | //use this in Log,saving lots of time;
35 | //在打Log的时候使用,节约时间
36 | private static final String tag = "pheynix";
37 | private static final String msg = ">>>>>>>>>>>>>>>>>>>>>> LOOK AT ME <<<<<<<<<<<<<<<<<<<<<<<<";
38 |
39 |
40 | //flags
41 | private boolean isInitialized = false;
42 | private boolean isStarted = false;// =true if countdown begin //倒计时开始的时候为true
43 | private boolean isInDragButton;
44 | private int whichDragButton;//1==hour 2==minute 3==second
45 |
46 | //store time,calculate result when countdown pause/stop
47 | //保存时间,倒计时结束时计算时间差
48 | private Calendar timeStart;
49 | private Calendar timeRemain;
50 |
51 | //dimension and coordinate
52 | //尺寸和坐标
53 | private float viewWidth;
54 | private float viewHeight;
55 | private float circleRadiusHour;
56 | private float circleRadiusMinute;
57 | private float circleRadiusSecond;
58 | private float circleRadiusDragButton;
59 | private float currentDegreeHour;
60 | private float currentDegreeMinute;
61 | private float currentDegreeSecond;
62 | private float centerXHour;
63 | private float centerYHour;
64 | private float centerXMinute;
65 | private float centerYMinute;
66 | private float centerXSecond;
67 | private float centerYSecond;
68 | private float strokeWidth;
69 | private String displayNumberHour;
70 | private String displayNumberMinute;
71 | private String displayNumberSecond;
72 |
73 | private float[] dragButtonHourPosition;
74 | private float[] dragButtonMinutePosition;
75 | private float[] dragButtonSecondPosition;
76 |
77 | private float[] defaultDragButtonHourPosition;
78 | private float[] defaultDragButtonMinutePosition;
79 | private float[] defaultDragButtonSecondPosition;
80 |
81 | //paint
82 | private Paint paintCircleBackground;
83 | private Paint paintDragButton;
84 | private Paint paintHour;
85 | private Paint paintMinute;
86 | private Paint paintSecond;
87 | private Paint paintNumber;
88 | private Paint paintGlowEffect;
89 |
90 | //color
91 | private static final int colorDefault = 0xFFD6D6D6;
92 | private static final int colorHour = 0xFF9AD13C;
93 | private static final int colorMinute = 0xFFA55F7C;
94 | private static final int colorSecond = 0xFF00BCD4;
95 |
96 | private static final int DEFAULT_VIEW_WIDTH = 720;
97 | private static final int DEFAULT_VIEW_HEIGHT = 720;
98 |
99 | private OnTimeChangeListener timeChangeListener;
100 | private OnMinChangListener minChangListener;
101 | private OnHourChangListener hourChangListener;
102 | private OnSecondChangListener secondChangListener;
103 | private OnInitialFinishListener initialFinishListener;
104 | //add model by Swifty default timer
105 | private Model model = Model.Timer;
106 | private boolean maxTime;
107 |
108 | //initialize every thing
109 | //初始化
110 | private void initialize(Canvas canvas) {
111 | Log.e("aa", "init");
112 | timeRemain = Calendar.getInstance();
113 | timeStart = Calendar.getInstance();
114 | timeStart.clear();
115 | timeRemain.clear();
116 |
117 | viewWidth = canvas.getWidth();
118 | viewHeight = canvas.getHeight();
119 |
120 | //use different dimension in high resolution device
121 | //保证高分辨率屏幕有比较好的显示效果
122 | if (viewWidth > 720) {
123 | strokeWidth = 30;
124 | circleRadiusDragButton = 50;
125 | } else {
126 | strokeWidth = 15;
127 | circleRadiusDragButton = 25;
128 | }
129 |
130 | circleRadiusHour = viewWidth / 6;
131 | circleRadiusMinute = viewWidth / 3;
132 | circleRadiusSecond = viewWidth / 6;
133 | currentDegreeHour = 0;
134 | currentDegreeMinute = 0;
135 | currentDegreeSecond = 0;
136 | displayNumberHour = "0";
137 | displayNumberMinute = "0";
138 | displayNumberSecond = "0";
139 | centerXHour = viewWidth / 6 + strokeWidth/2 + circleRadiusDragButton/2;
140 | centerYHour = viewHeight / 6 + strokeWidth/2 + circleRadiusDragButton/2;
141 | centerXMinute = viewWidth / 2;
142 | centerYMinute = 2*viewHeight / 3 - strokeWidth;
143 | centerXSecond = 5 * viewWidth / 6 - strokeWidth/2 - circleRadiusDragButton/2;
144 | centerYSecond = viewHeight / 6 + strokeWidth/2 + circleRadiusDragButton/2;
145 |
146 | defaultDragButtonHourPosition = new float[]{centerXHour, centerYHour - circleRadiusHour};
147 | defaultDragButtonMinutePosition = new float[]{centerXMinute, centerYMinute - circleRadiusMinute};
148 | defaultDragButtonSecondPosition = new float[]{centerXSecond, centerYSecond - circleRadiusSecond};
149 | dragButtonHourPosition = defaultDragButtonHourPosition;
150 | dragButtonMinutePosition = defaultDragButtonMinutePosition;
151 | dragButtonSecondPosition = defaultDragButtonSecondPosition;
152 |
153 | paintCircleBackground = new Paint();
154 | paintDragButton = new Paint();
155 | paintHour = new Paint();
156 | paintMinute = new Paint();
157 | paintSecond = new Paint();
158 | paintNumber = new Paint();
159 | paintGlowEffect = new Paint();
160 |
161 | paintCircleBackground.setColor(colorDefault);
162 | paintCircleBackground.setStrokeWidth(strokeWidth);
163 | paintCircleBackground.setStyle(Paint.Style.STROKE);
164 | paintCircleBackground.setAntiAlias(true);
165 | paintDragButton.setStrokeWidth(5);
166 | paintDragButton.setStyle(Paint.Style.FILL);
167 | paintDragButton.setAntiAlias(true);
168 | paintHour.setColor(colorHour);
169 | paintHour.setStrokeWidth(strokeWidth);
170 | paintHour.setStyle(Paint.Style.STROKE);
171 | paintHour.setAntiAlias(true);
172 | paintMinute.setColor(colorMinute);
173 | paintMinute.setStrokeWidth(strokeWidth);
174 | paintMinute.setStyle(Paint.Style.STROKE);
175 | paintMinute.setAntiAlias(true);
176 | paintSecond.setColor(colorSecond);
177 | paintSecond.setStrokeWidth(strokeWidth);
178 | paintSecond.setStyle(Paint.Style.STROKE);
179 | paintSecond.setAntiAlias(true);
180 | paintNumber.setStrokeWidth(2);
181 | paintNumber.setStyle(Paint.Style.FILL);
182 | paintNumber.setAntiAlias(true);
183 |
184 | //draw glow effect on the end of arc,glow-effect == dragButton
185 | //用于绘制圆弧尽头的辉光效果,辉光区域就是dragButton的区域
186 | paintGlowEffect.setMaskFilter(new BlurMaskFilter(2 * strokeWidth / 3, BlurMaskFilter.Blur.NORMAL));
187 | paintGlowEffect.setStrokeWidth(strokeWidth);
188 | paintGlowEffect.setAntiAlias(true);
189 | paintGlowEffect.setStyle(Paint.Style.FILL);
190 |
191 | //完成初始化回调
192 | if (initialFinishListener != null) {
193 | initialFinishListener.onInitialFinishListener();
194 | }
195 | }
196 |
197 | public MyTimer(Context context) {
198 | super(context);
199 | }
200 |
201 | //use this view in .xml file will invoke this constructor
202 | //在.xml中使用此控件时调用此构造函数
203 | public MyTimer(Context context, AttributeSet attrs) {
204 | super(context, attrs);
205 | }
206 |
207 | public MyTimer(Context context, AttributeSet attrs, int defStyleAttr) {
208 | super(context, attrs, defStyleAttr);
209 | }
210 |
211 |
212 | @Override
213 | protected void onDraw(Canvas canvas) {
214 | super.onDraw(canvas);
215 |
216 | //initialize dimension and coordinate just for once
217 | //初始化尺寸,只会调用一次
218 | if (!isInitialized) {
219 | initialize(canvas);
220 | isInitialized = true;
221 | }
222 |
223 | //arc and number depending on degree,update before drawing
224 | //角度决定圆弧长度和数字,每次重绘前先更新角度
225 | if (isStarted) {
226 | updateDegree();
227 | }
228 |
229 |
230 | //draw background circle
231 | //画背景的圆圈
232 | canvas.drawCircle(centerXHour, centerYHour, circleRadiusHour, paintCircleBackground);
233 | canvas.drawCircle(centerXMinute, centerYMinute, circleRadiusMinute, paintCircleBackground);
234 | canvas.drawCircle(centerXSecond, centerYSecond, circleRadiusSecond, paintCircleBackground);
235 |
236 | //draw arc
237 | //画弧形
238 | RectF rectFHour = new RectF(centerXHour - circleRadiusHour, centerYHour - circleRadiusHour
239 | , centerXHour + circleRadiusHour, centerYHour + circleRadiusHour);
240 | RectF rectFMinute = new RectF(centerXMinute - circleRadiusMinute, centerYMinute - circleRadiusMinute
241 | , centerXMinute + circleRadiusMinute, centerYMinute + circleRadiusMinute);
242 | RectF rectFSecond = new RectF(centerXSecond - circleRadiusSecond, centerYSecond - circleRadiusSecond
243 | , centerXSecond + circleRadiusSecond, centerYSecond + circleRadiusSecond);
244 |
245 | canvas.drawArc(rectFHour, -90, currentDegreeHour, false, paintHour);
246 | canvas.drawArc(rectFMinute, -90, currentDegreeMinute, false, paintMinute);
247 | canvas.drawArc(rectFSecond, -90, currentDegreeSecond, false, paintSecond);
248 |
249 |
250 | //draw glow effect
251 | //画辉光效果
252 | paintDragButton.setColor(colorHour);
253 | canvas.drawCircle(dragButtonHourPosition[0], dragButtonHourPosition[1], strokeWidth / 2, paintDragButton);
254 | paintGlowEffect.setColor(colorHour);
255 | canvas.drawCircle(dragButtonHourPosition[0], dragButtonHourPosition[1], strokeWidth, paintGlowEffect);
256 |
257 | paintDragButton.setColor(colorMinute);
258 | canvas.drawCircle(dragButtonMinutePosition[0], dragButtonMinutePosition[1], strokeWidth / 2, paintDragButton);
259 | paintGlowEffect.setColor(colorMinute);
260 | canvas.drawCircle(dragButtonMinutePosition[0], dragButtonMinutePosition[1], strokeWidth, paintGlowEffect);
261 |
262 | paintDragButton.setColor(colorSecond);
263 | canvas.drawCircle(dragButtonSecondPosition[0], dragButtonSecondPosition[1], strokeWidth / 2, paintDragButton);
264 | paintGlowEffect.setColor(colorSecond);
265 | canvas.drawCircle(dragButtonSecondPosition[0], dragButtonSecondPosition[1], strokeWidth, paintGlowEffect);
266 |
267 |
268 | //draw letter "H""M""S",point(0,0) of text area is on the bottom-left of this area!
269 | //画"H""M""S"这三个字母,文字区域的(0,0)在左下角!
270 | getDisplayNumber();
271 | Rect rect = new Rect();
272 |
273 | paintNumber.setTextSize(70);
274 | paintNumber.setColor(colorHour);
275 | paintNumber.getTextBounds(displayNumberHour, 0, displayNumberHour.length(), rect);
276 | canvas.drawText(displayNumberHour, centerXHour - rect.width() / 2, centerYHour + rect.height() / 2, paintNumber);
277 | paintNumber.setTextSize(25);
278 | canvas.drawText("H", centerXHour + 30, centerYHour + 25, paintNumber);
279 |
280 | paintNumber.setTextSize(70);
281 | paintNumber.setColor(colorMinute);
282 | paintNumber.getTextBounds(displayNumberMinute, 0, displayNumberMinute.length(), rect);
283 | canvas.drawText(displayNumberMinute, centerXMinute - rect.width() / 2, centerYMinute + rect.height() / 2, paintNumber);
284 | paintNumber.setTextSize(25);
285 | canvas.drawText("M", centerXMinute + 50, centerYMinute + 25, paintNumber);
286 |
287 | paintNumber.setTextSize(70);
288 | paintNumber.setColor(colorSecond);
289 | paintNumber.getTextBounds(displayNumberSecond, 0, displayNumberSecond.length(), rect);
290 | canvas.drawText(displayNumberSecond, centerXSecond - rect.width() / 2, centerYSecond + rect.height() / 2, paintNumber);
291 | paintNumber.setTextSize(25);
292 | canvas.drawText("S", centerXSecond + 50, centerYSecond + 25, paintNumber);
293 | }
294 |
295 |
296 | //handle touch event
297 | //
298 | @Override
299 | public boolean onTouchEvent(MotionEvent event) {
300 | super.onTouchEvent(event);
301 |
302 | switch (event.getAction()) {
303 | //whether touch in the drag button or not
304 | //判断点击是否在dragButton内
305 | case MotionEvent.ACTION_DOWN:
306 | isInDragButton(event.getX(), event.getY());
307 | break;
308 |
309 | //update coordination of dragButton
310 | //更新dragButton的位置
311 | case MotionEvent.ACTION_MOVE:
312 | if (!isStarted) {
313 | if (isInDragButton) {
314 | switch (whichDragButton) {
315 | case 1:
316 | currentDegreeHour = getDegree(event.getX(), event.getY(), centerXHour, centerYHour);
317 | updateTime(1);
318 | updateDragButtonPosition(1);
319 | invalidate();
320 | break;
321 | case 2:
322 | currentDegreeMinute = getDegree(event.getX(), event.getY(), centerXMinute, centerYMinute);
323 | updateTime(2);
324 | updateDragButtonPosition(2);
325 | invalidate();
326 | break;
327 | case 3:
328 | currentDegreeSecond = getDegree(event.getX(), event.getY(), centerXSecond, centerYSecond);
329 | updateTime(3);
330 | updateDragButtonPosition(3);
331 | invalidate();
332 | break;
333 | }
334 | }
335 | }
336 | break;
337 |
338 | case MotionEvent.ACTION_UP:
339 | isInDragButton = false;
340 | break;
341 | }
342 |
343 | return true;
344 | }
345 |
346 |
347 | @Override
348 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
349 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
350 |
351 | int width = getDimension(DEFAULT_VIEW_WIDTH, widthMeasureSpec);
352 | int height = getDimension(width, heightMeasureSpec);
353 |
354 | viewWidth = width;
355 | viewHeight = height;
356 |
357 | setMeasuredDimension(width, height);
358 | }
359 |
360 | private int getDimension(int defaultDimension,int measureSpec){
361 |
362 | int result;
363 |
364 | switch (MeasureSpec.getMode(measureSpec)){
365 | case MeasureSpec.EXACTLY:
366 | result = MeasureSpec.getSize(measureSpec);
367 | break;
368 | case MeasureSpec.AT_MOST:
369 | result = Math.min(defaultDimension,MeasureSpec.getSize(measureSpec));
370 |
371 | break;
372 | default:
373 | result = defaultDimension;
374 | break;
375 | }
376 | return result;
377 | }
378 |
379 |
380 |
381 | //update degree,depend on the path user dragging on the screen
382 | //根据用户在屏幕划过的轨迹更新角度
383 | private float getDegree(float eventX, float eventY, float centerX, float centerY) {
384 |
385 | // http://stackoverflow.com/questions/7926816/calculate-angle-of-touched-point-and-rotate-it-in-android
386 | // Math has defeated me once again.So sad...
387 | // 卧槽...
388 | double tx = eventX - centerX;
389 | double ty = eventY - centerY;
390 | double t_length = Math.sqrt(tx * tx + ty * ty);
391 | double a = Math.acos(ty / t_length);
392 | float degree = 180 - (float) Math.toDegrees(a);
393 |
394 | if (centerX > eventX) {
395 | degree = 180 + (float) Math.toDegrees(a);
396 | }
397 |
398 | return degree;
399 | }
400 |
401 | private void getDisplayNumber() {
402 | if (Integer.valueOf(displayNumberHour) != timeRemain.get(Calendar.HOUR_OF_DAY)) {
403 | displayNumberHour = timeRemain.get(Calendar.HOUR_OF_DAY) + "";
404 | if (hourChangListener != null) {
405 | hourChangListener.onHourChange(timeRemain.get(Calendar.HOUR_OF_DAY));
406 | }
407 | }
408 | if (Integer.valueOf(displayNumberMinute) != timeRemain.get(Calendar.MINUTE)) {
409 | displayNumberMinute = timeRemain.get(Calendar.MINUTE) + "";
410 | if (minChangListener != null) {
411 | minChangListener.onMinChange(timeRemain.get(Calendar.MINUTE));
412 | }
413 | }
414 | if (Integer.valueOf(displayNumberSecond) != timeRemain.get(Calendar.SECOND)) {
415 | displayNumberSecond = timeRemain.get(Calendar.SECOND) + "";
416 | if (secondChangListener != null) {
417 | secondChangListener.onSecondChange(timeRemain.get(Calendar.SECOND));
418 | }
419 | }
420 |
421 | }
422 |
423 |
424 | private void isInDragButton(float eventX, float eventY) {
425 |
426 | if (circleRadiusDragButton > Math.sqrt(Math.pow(eventX - dragButtonHourPosition[0], 2)
427 | + Math.pow(eventY - dragButtonHourPosition[1], 2))) {
428 | //在dragButtonHour中
429 | isInDragButton = true;
430 | whichDragButton = 1;
431 | } else if (circleRadiusDragButton > Math.sqrt(Math.pow(eventX - dragButtonMinutePosition[0], 2)
432 | + Math.pow(eventY - dragButtonMinutePosition[1], 2))) {
433 | //在dragButtonMinute中
434 | isInDragButton = true;
435 | whichDragButton = 2;
436 | } else if (circleRadiusDragButton > Math.sqrt(Math.pow(eventX - dragButtonSecondPosition[0], 2)
437 | + Math.pow(eventY - dragButtonSecondPosition[1], 2))) {
438 | //在dragButtonSecond中
439 | isInDragButton = true;
440 | whichDragButton = 3;
441 | } else {
442 | //不在
443 | isInDragButton = false;
444 | whichDragButton = 0;
445 | }
446 | }
447 |
448 |
449 | //degree depending on timeRemain
450 | //角度由剩余时间决定
451 | private void updateDegree() {
452 |
453 | currentDegreeHour = (float) ((timeRemain.get(Calendar.HOUR_OF_DAY) * 60 + timeRemain.get(Calendar.MINUTE)) / (6.0 * 60)) * 360;
454 | currentDegreeMinute = (float) ((timeRemain.get(Calendar.MINUTE) * 60 + timeRemain.get(Calendar.SECOND)) / (60.0 * 60)) * 360;
455 | currentDegreeSecond = (float) ((timeRemain.get(Calendar.SECOND) * 1000 + timeRemain.get(Calendar.MILLISECOND)) / (60.0 * 1000)) * 360;
456 |
457 | updateDragButtonPosition(0);
458 |
459 | }
460 |
461 | //update drag button position(glow effect area)
462 | //更新拖动按钮中心点(辉光效果区域)
463 | private void updateDragButtonPosition(int flag) {
464 |
465 | switch (flag) {
466 | case 0:
467 | dragButtonHourPosition[0] = (float) (centerXHour + circleRadiusHour * (Math.sin(Math.toRadians(currentDegreeHour))));
468 | dragButtonHourPosition[1] = (float) (centerYHour - circleRadiusHour * (Math.cos(Math.toRadians(currentDegreeHour))));
469 |
470 | dragButtonMinutePosition[0] = (float) (centerXMinute + circleRadiusMinute * Math.sin(Math.toRadians(currentDegreeMinute)));
471 | dragButtonMinutePosition[1] = (float) (centerYMinute - circleRadiusMinute * Math.cos(Math.toRadians(currentDegreeMinute)));
472 |
473 | dragButtonSecondPosition[0] = (float) (centerXSecond + circleRadiusSecond * Math.sin(Math.toRadians(currentDegreeSecond)));
474 | dragButtonSecondPosition[1] = (float) (centerYSecond - circleRadiusSecond * Math.cos(Math.toRadians(currentDegreeSecond)));
475 | break;
476 | case 1:
477 | dragButtonHourPosition[0] = (float) (centerXHour + circleRadiusHour * (Math.sin(Math.toRadians(currentDegreeHour))));
478 | dragButtonHourPosition[1] = (float) (centerYHour - circleRadiusHour * (Math.cos(Math.toRadians(currentDegreeHour))));
479 | break;
480 | case 2:
481 | dragButtonMinutePosition[0] = (float) (centerXMinute + circleRadiusMinute * Math.sin(Math.toRadians(currentDegreeMinute)));
482 | dragButtonMinutePosition[1] = (float) (centerYMinute - circleRadiusMinute * Math.cos(Math.toRadians(currentDegreeMinute)));
483 | break;
484 | case 3:
485 | dragButtonSecondPosition[0] = (float) (centerXSecond + circleRadiusSecond * Math.sin(Math.toRadians(currentDegreeSecond)));
486 | dragButtonSecondPosition[1] = (float) (centerYSecond - circleRadiusSecond * Math.cos(Math.toRadians(currentDegreeSecond)));
487 | break;
488 | }
489 |
490 | }
491 |
492 |
493 | //get the time from currentDegree and store it in timeStart and timeRemain
494 | //从当前的角度获取时间,保存到timeStart和timeRemain
495 | private void updateTime(int flag) {
496 |
497 | switch (flag) {
498 | case 0:
499 | timeStart.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
500 | timeRemain.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
501 |
502 | timeStart.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
503 | timeRemain.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
504 |
505 | timeStart.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
506 | timeRemain.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
507 | break;
508 | case 1:
509 | timeStart.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
510 | timeRemain.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
511 | break;
512 | case 2:
513 | timeStart.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
514 | timeRemain.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
515 | break;
516 | case 3:
517 | timeStart.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
518 | timeRemain.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
519 | break;
520 | }
521 |
522 | }
523 |
524 |
525 | //common Timer-TimerTask-Handler countdown solution
526 | //常见的Timer-TimerTask-Handler倒计时模式
527 | private Handler mHandler = new Handler() {
528 | @Override
529 | public void handleMessage(Message msg) {
530 | super.handleMessage(msg);
531 |
532 | switch (msg.what) {
533 | //countdown running
534 | //可以倒计时
535 | case 1:
536 |
537 | timeRemain.add(Calendar.MILLISECOND, -100);
538 |
539 | if (timeChangeListener != null) {
540 | timeChangeListener.onTimeChange(timeStart.getTimeInMillis(), timeRemain.getTimeInMillis());
541 | }
542 |
543 | invalidate();
544 |
545 | break;
546 | //countdown stop
547 | //时间为空,停止倒计时,提示用户
548 | case 2:
549 | isStarted = false;
550 | timerTask.cancel();
551 | break;
552 |
553 | //StopWatch running
554 | case 11:
555 | timeRemain.add(Calendar.MILLISECOND, 100);
556 | if (timeChangeListener != null) {
557 | timeChangeListener.onTimeChange(timeStart.getTimeInMillis(), timeRemain.getTimeInMillis());
558 | }
559 | invalidate();
560 | break;
561 | //StopWatch stop
562 | //到达MAX TIME
563 | case 12:
564 | isStarted = false;
565 | timerTask.cancel();
566 | break;
567 | }
568 | }
569 | };
570 |
571 | Timer timer = new Timer(true);
572 | TimerTask timerTask;
573 |
574 |
575 | public boolean start() {
576 | if (model == Model.Timer) {
577 | if (!isTimeEmpty() && !isStarted) {
578 |
579 | timerTask = new TimerTask() {
580 | @Override
581 | public void run() {
582 | if (!isTimeEmpty()) {
583 | Message message = new Message();
584 | message.what = 1;
585 | mHandler.sendMessage(message);
586 | } else {
587 | Message message = new Message();
588 | message.what = 2;
589 | mHandler.sendMessage(message);
590 | }
591 |
592 | }
593 | };
594 |
595 | timer.schedule(timerTask, 1000, 100);
596 | isStarted = true;
597 |
598 | if (timeChangeListener != null) {
599 | timeChangeListener.onTimerStart(timeStart.getTimeInMillis());
600 | }
601 | }
602 | } else if (model == Model.StopWatch) {
603 | if (!isMaxTime() && !isStarted) {
604 | timerTask = new TimerTask() {
605 | @Override
606 | public void run() {
607 | if (!isMaxTime()) {
608 | Message message = new Message();
609 | message.what = 11;
610 | mHandler.sendMessage(message);
611 | } else {
612 | Message message = new Message();
613 | message.what = 12;
614 | mHandler.sendMessage(message);
615 | }
616 | }
617 | };
618 |
619 | timer.schedule(timerTask, 1000, 100);
620 | isStarted = true;
621 |
622 | if (timeChangeListener != null) {
623 | timeChangeListener.onTimerStart(timeStart.getTimeInMillis());
624 | }
625 | }
626 | }
627 | return isStarted;
628 | }
629 |
630 | private boolean isTimeEmpty() {
631 | if (timeRemain.get(Calendar.HOUR_OF_DAY) != 0
632 | || timeRemain.get(Calendar.MINUTE) != 0
633 | || timeRemain.get(Calendar.SECOND) != 0
634 | || timeRemain.get(Calendar.MILLISECOND) != 0) {
635 | return false;
636 | } else {
637 | return true;
638 | }
639 | }
640 |
641 |
642 | public long stop() {
643 | timerTask.cancel();
644 | isStarted = false;
645 |
646 | if (timeChangeListener != null) {
647 | timeChangeListener.onTimeStop(timeStart.getTimeInMillis(), timeRemain.getTimeInMillis());
648 | }
649 |
650 | return timeStart.getTimeInMillis() - timeRemain.getTimeInMillis();
651 | }
652 |
653 |
654 | public Calendar getTimeStart() {
655 | return timeStart;
656 | }
657 |
658 |
659 | public Calendar getTimeRemaid() {
660 | return timeRemain;
661 | }
662 |
663 |
664 | public long getTimePass() {
665 | return timeStart.getTimeInMillis() - timeRemain.getTimeInMillis();
666 | }
667 |
668 |
669 | public void setOnTimeChangeListener(OnTimeChangeListener listener) {
670 | if (listener != null) {
671 | timeChangeListener = listener;
672 | }
673 | }
674 |
675 | public void setMinChangListener(OnMinChangListener minChangListener) {
676 | this.minChangListener = minChangListener;
677 | }
678 |
679 | public void setSecondChangListener(OnSecondChangListener secondChangListener) {
680 | this.secondChangListener = secondChangListener;
681 | }
682 |
683 | public void setHourChangListener(OnHourChangListener hourChangListener) {
684 | this.hourChangListener = hourChangListener;
685 | }
686 |
687 | public void reset() {
688 | //先停止计时
689 | stop();
690 | //初始化calendar
691 | isInitialized = false;
692 | invalidate();
693 | }
694 |
695 | public boolean isMaxTime() {
696 | if (timeRemain.get(Calendar.HOUR_OF_DAY) == 5
697 | && timeRemain.get(Calendar.MINUTE) == 59
698 | && timeRemain.get(Calendar.SECOND) == 59) {
699 | return true;
700 | } else {
701 | return false;
702 | }
703 | }
704 |
705 |
706 | //listener
707 | public interface OnTimeChangeListener {
708 | public void onTimerStart(long timeStart);
709 |
710 | public void onTimeChange(long timeStart, long timeRemain);
711 |
712 | public void onTimeStop(long timeStart, long timeRemain);
713 | }
714 |
715 | public interface OnMinChangListener {
716 | public void onMinChange(int minute);
717 | }
718 |
719 | public interface OnHourChangListener {
720 | public void onHourChange(int hour);
721 | }
722 |
723 | public interface OnInitialFinishListener {
724 | public void onInitialFinishListener();
725 | }
726 |
727 | public interface OnSecondChangListener {
728 | public void onSecondChange(int second);
729 | }
730 |
731 | public void setModel(Model model) {
732 | this.model = model;
733 | }
734 |
735 | /**
736 | * set default time
737 | *
738 | * @param h max 5
739 | * @param m max 59
740 | * @param s max 59
741 | */
742 | public void setStartTime(final int h, final int m, final int s) throws NumberFormatException {
743 | initialFinishListener = new OnInitialFinishListener() {
744 | @Override
745 | public void onInitialFinishListener() {
746 | if (h > 5 || m > 59 || s > 69 || h < 0 || m < 0 | s < 0) {
747 | throw new NumberFormatException("hour must in [0-5], minute and second must in [0-59]");
748 | }
749 | timeRemain.set(Calendar.HOUR_OF_DAY, h);
750 | timeRemain.set(Calendar.MINUTE, m);
751 | timeRemain.set(Calendar.SECOND, s);
752 | timeStart.set(Calendar.HOUR_OF_DAY, h);
753 | timeStart.set(Calendar.MINUTE, m);
754 | timeStart.set(Calendar.SECOND, s);
755 | updateDegree();
756 | invalidate();
757 | }
758 | };
759 | }
760 |
761 | }
762 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
18 |
19 |
25 |
26 |
32 |
33 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dujigui/TimerView/cd7b2aa72d39d1389e7631cb8e1964036e619f6d/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dujigui/TimerView/cd7b2aa72d39d1389e7631cb8e1964036e619f6d/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dujigui/TimerView/cd7b2aa72d39d1389e7631cb8e1964036e619f6d/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dujigui/TimerView/cd7b2aa72d39d1389e7631cb8e1964036e619f6d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ForTheWatch
3 |
4 | Hello world!
5 | Settings
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/forthewatch/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/forthewatch/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "23.0.0 rc2"
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 22
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | compile 'com.android.support:appcompat-v7:22.2.0'
24 | }
25 |
--------------------------------------------------------------------------------
/forthewatch/forthewatch.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | generateDebugAndroidTestSources
19 | generateDebugSources
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/forthewatch/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/pheynix/Documents/coding/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/forthewatch/src/androidTest/java/com/pheynix/forthewatch/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.pheynix.forthewatch;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/forthewatch/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/forthewatch/src/main/java/com/pheynix/forthewatch/Model.java:
--------------------------------------------------------------------------------
1 | package com.pheynix.forthewatch;
2 |
3 | /**
4 | * Created by Swifty.Wang on 2015/7/16.
5 | */
6 | public enum Model {
7 | Timer,
8 | StopWatch;
9 | }
10 |
--------------------------------------------------------------------------------
/forthewatch/src/main/java/com/pheynix/forthewatch/MyTimer.java:
--------------------------------------------------------------------------------
1 | package com.pheynix.forthewatch;
2 |
3 | import android.content.Context;
4 | import android.graphics.BlurMaskFilter;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.graphics.RectF;
9 | import android.os.Handler;
10 | import android.os.Message;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 |
16 | import java.util.Calendar;
17 | import java.util.Timer;
18 | import java.util.TimerTask;
19 |
20 |
21 | /**
22 | * Created by pheynix on 7/12/15.
23 | * Sorry for my English...
24 | *
25 | *Disable hardware accelerate if you need the glow effect,See: http://developer.android.com/guide/topics/graphics/hardware-accel.html
26 | *Disable hardware accelerate if you need the glow effect,See: http://developer.android.com/guide/topics/graphics/hardware-accel.html
27 | *Disable hardware accelerate if you need the glow effect,See: http://developer.android.com/guide/topics/graphics/hardware-accel.html
28 | *如果需要辉光效果,请务必关闭硬件加速,参考: http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/
29 | *如果需要辉光效果,请务必关闭硬件加速,参考: http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/
30 | *如果需要辉光效果,请务必关闭硬件加速,参考: http://blog.chenming.info/blog/2012/09/18/android-hardware-accel/
31 | */
32 | public class MyTimer extends View {
33 |
34 | //use this in Log,saving lots of time;
35 | //在打Log的时候使用,节约时间
36 | private static final String tag = "pheynix";
37 | private static final String msg = ">>>>>>>>>>>>>>>>>>>>>> LOOK AT ME <<<<<<<<<<<<<<<<<<<<<<<<";
38 |
39 |
40 | //flags
41 | private boolean isInitialized = false;
42 | private boolean isStarted = false;// =true if countdown begin //倒计时开始的时候为true
43 | private boolean isInDragButton;
44 | private int whichDragButton;//1==hour 2==minute 3==second
45 |
46 | //store time,calculate result when countdown pause/stop
47 | //保存时间,倒计时结束时计算时间差
48 | private Calendar timeStart;
49 | private Calendar timeRemain;
50 |
51 | //dimension and coordinate
52 | //尺寸和坐标
53 | private float viewWidth;
54 | private float viewHeight;
55 | private float circleRadiusHour;
56 | private float circleRadiusMinute;
57 | private float circleRadiusSecond;
58 | private float circleRadiusDragButton;
59 | private float currentDegreeHour;
60 | private float currentDegreeMinute;
61 | private float currentDegreeSecond;
62 | private float centerXHour;
63 | private float centerYHour;
64 | private float centerXMinute;
65 | private float centerYMinute;
66 | private float centerXSecond;
67 | private float centerYSecond;
68 | private float strokeWidth;
69 | private String displayNumberHour;
70 | private String displayNumberMinute;
71 | private String displayNumberSecond;
72 |
73 | private float[] dragButtonHourPosition;
74 | private float[] dragButtonMinutePosition;
75 | private float[] dragButtonSecondPosition;
76 |
77 | private float[] defaultDragButtonHourPosition;
78 | private float[] defaultDragButtonMinutePosition;
79 | private float[] defaultDragButtonSecondPosition;
80 |
81 | //paint
82 | private Paint paintCircleBackground;
83 | private Paint paintDragButton;
84 | private Paint paintHour;
85 | private Paint paintMinute;
86 | private Paint paintSecond;
87 | private Paint paintNumber;
88 | private Paint paintGlowEffect;
89 |
90 | //color
91 | private static final int colorDefault = 0xFFD6D6D6;
92 | private static final int colorHour = 0xFF9AD13C;
93 | private static final int colorMinute = 0xFFA55F7C;
94 | private static final int colorSecond = 0xFF00BCD4;
95 |
96 | private static final int DEFAULT_VIEW_WIDTH = 720;
97 | private static final int DEFAULT_VIEW_HEIGHT = 720;
98 |
99 | private OnTimeChangeListener timeChangeListener;
100 | private OnMinChangListener minChangListener;
101 | private OnHourChangListener hourChangListener;
102 | private OnSecondChangListener secondChangListener;
103 | private OnInitialFinishListener initialFinishListener;
104 | //add model by Swifty default timer
105 | private Model model = Model.Timer;
106 | private boolean maxTime;
107 |
108 | //initialize every thing
109 | //初始化
110 | private void initialize(Canvas canvas) {
111 | Log.e("aa", "init");
112 | timeRemain = Calendar.getInstance();
113 | timeStart = Calendar.getInstance();
114 | timeStart.clear();
115 | timeRemain.clear();
116 |
117 | viewWidth = canvas.getWidth();
118 | viewHeight = canvas.getHeight();
119 |
120 | //use different dimension in high resolution device
121 | //保证高分辨率屏幕有比较好的显示效果
122 | if (viewWidth > 720) {
123 | strokeWidth = 30;
124 | circleRadiusDragButton = 50;
125 | } else {
126 | strokeWidth = 15;
127 | circleRadiusDragButton = 25;
128 | }
129 |
130 | circleRadiusHour = viewWidth / 6;
131 | circleRadiusMinute = viewWidth / 3;
132 | circleRadiusSecond = viewWidth / 6;
133 | currentDegreeHour = 0;
134 | currentDegreeMinute = 0;
135 | currentDegreeSecond = 0;
136 | displayNumberHour = "0";
137 | displayNumberMinute = "0";
138 | displayNumberSecond = "0";
139 | centerXHour = viewWidth / 6 + strokeWidth/2 + circleRadiusDragButton/2;
140 | centerYHour = viewHeight / 6 + strokeWidth/2 + circleRadiusDragButton/2;
141 | centerXMinute = viewWidth / 2;
142 | centerYMinute = 2*viewHeight / 3 - strokeWidth;
143 | centerXSecond = 5 * viewWidth / 6 - strokeWidth/2 - circleRadiusDragButton/2;
144 | centerYSecond = viewHeight / 6 + strokeWidth/2 + circleRadiusDragButton/2;
145 |
146 | defaultDragButtonHourPosition = new float[]{centerXHour, centerYHour - circleRadiusHour};
147 | defaultDragButtonMinutePosition = new float[]{centerXMinute, centerYMinute - circleRadiusMinute};
148 | defaultDragButtonSecondPosition = new float[]{centerXSecond, centerYSecond - circleRadiusSecond};
149 | dragButtonHourPosition = defaultDragButtonHourPosition;
150 | dragButtonMinutePosition = defaultDragButtonMinutePosition;
151 | dragButtonSecondPosition = defaultDragButtonSecondPosition;
152 |
153 | paintCircleBackground = new Paint();
154 | paintDragButton = new Paint();
155 | paintHour = new Paint();
156 | paintMinute = new Paint();
157 | paintSecond = new Paint();
158 | paintNumber = new Paint();
159 | paintGlowEffect = new Paint();
160 |
161 | paintCircleBackground.setColor(colorDefault);
162 | paintCircleBackground.setStrokeWidth(strokeWidth);
163 | paintCircleBackground.setStyle(Paint.Style.STROKE);
164 | paintCircleBackground.setAntiAlias(true);
165 | paintDragButton.setStrokeWidth(5);
166 | paintDragButton.setStyle(Paint.Style.FILL);
167 | paintDragButton.setAntiAlias(true);
168 | paintHour.setColor(colorHour);
169 | paintHour.setStrokeWidth(strokeWidth);
170 | paintHour.setStyle(Paint.Style.STROKE);
171 | paintHour.setAntiAlias(true);
172 | paintMinute.setColor(colorMinute);
173 | paintMinute.setStrokeWidth(strokeWidth);
174 | paintMinute.setStyle(Paint.Style.STROKE);
175 | paintMinute.setAntiAlias(true);
176 | paintSecond.setColor(colorSecond);
177 | paintSecond.setStrokeWidth(strokeWidth);
178 | paintSecond.setStyle(Paint.Style.STROKE);
179 | paintSecond.setAntiAlias(true);
180 | paintNumber.setStrokeWidth(2);
181 | paintNumber.setStyle(Paint.Style.FILL);
182 | paintNumber.setAntiAlias(true);
183 |
184 | //draw glow effect on the end of arc,glow-effect == dragButton
185 | //用于绘制圆弧尽头的辉光效果,辉光区域就是dragButton的区域
186 | paintGlowEffect.setMaskFilter(new BlurMaskFilter(2 * strokeWidth / 3, BlurMaskFilter.Blur.NORMAL));
187 | paintGlowEffect.setStrokeWidth(strokeWidth);
188 | paintGlowEffect.setAntiAlias(true);
189 | paintGlowEffect.setStyle(Paint.Style.FILL);
190 |
191 | //完成初始化回调
192 | if (initialFinishListener != null) {
193 | initialFinishListener.onInitialFinishListener();
194 | }
195 | }
196 |
197 | public MyTimer(Context context) {
198 | super(context);
199 | }
200 |
201 | //use this view in .xml file will invoke this constructor
202 | //在.xml中使用此控件时调用此构造函数
203 | public MyTimer(Context context, AttributeSet attrs) {
204 | super(context, attrs);
205 | }
206 |
207 | public MyTimer(Context context, AttributeSet attrs, int defStyleAttr) {
208 | super(context, attrs, defStyleAttr);
209 | }
210 |
211 |
212 | @Override
213 | protected void onDraw(Canvas canvas) {
214 | super.onDraw(canvas);
215 |
216 | //initialize dimension and coordinate just for once
217 | //初始化尺寸,只会调用一次
218 | if (!isInitialized) {
219 | initialize(canvas);
220 | isInitialized = true;
221 | }
222 |
223 | //arc and number depending on degree,update before drawing
224 | //角度决定圆弧长度和数字,每次重绘前先更新角度
225 | if (isStarted) {
226 | updateDegree();
227 | }
228 |
229 |
230 | //draw background circle
231 | //画背景的圆圈
232 | canvas.drawCircle(centerXHour, centerYHour, circleRadiusHour, paintCircleBackground);
233 | canvas.drawCircle(centerXMinute, centerYMinute, circleRadiusMinute, paintCircleBackground);
234 | canvas.drawCircle(centerXSecond, centerYSecond, circleRadiusSecond, paintCircleBackground);
235 |
236 | //draw arc
237 | //画弧形
238 | RectF rectFHour = new RectF(centerXHour - circleRadiusHour, centerYHour - circleRadiusHour
239 | , centerXHour + circleRadiusHour, centerYHour + circleRadiusHour);
240 | RectF rectFMinute = new RectF(centerXMinute - circleRadiusMinute, centerYMinute - circleRadiusMinute
241 | , centerXMinute + circleRadiusMinute, centerYMinute + circleRadiusMinute);
242 | RectF rectFSecond = new RectF(centerXSecond - circleRadiusSecond, centerYSecond - circleRadiusSecond
243 | , centerXSecond + circleRadiusSecond, centerYSecond + circleRadiusSecond);
244 |
245 | canvas.drawArc(rectFHour, -90, currentDegreeHour, false, paintHour);
246 | canvas.drawArc(rectFMinute, -90, currentDegreeMinute, false, paintMinute);
247 | canvas.drawArc(rectFSecond, -90, currentDegreeSecond, false, paintSecond);
248 |
249 |
250 | //draw glow effect
251 | //画辉光效果
252 | paintDragButton.setColor(colorHour);
253 | canvas.drawCircle(dragButtonHourPosition[0], dragButtonHourPosition[1], strokeWidth / 2, paintDragButton);
254 | paintGlowEffect.setColor(colorHour);
255 | canvas.drawCircle(dragButtonHourPosition[0], dragButtonHourPosition[1], strokeWidth, paintGlowEffect);
256 |
257 | paintDragButton.setColor(colorMinute);
258 | canvas.drawCircle(dragButtonMinutePosition[0], dragButtonMinutePosition[1], strokeWidth / 2, paintDragButton);
259 | paintGlowEffect.setColor(colorMinute);
260 | canvas.drawCircle(dragButtonMinutePosition[0], dragButtonMinutePosition[1], strokeWidth, paintGlowEffect);
261 |
262 | paintDragButton.setColor(colorSecond);
263 | canvas.drawCircle(dragButtonSecondPosition[0], dragButtonSecondPosition[1], strokeWidth / 2, paintDragButton);
264 | paintGlowEffect.setColor(colorSecond);
265 | canvas.drawCircle(dragButtonSecondPosition[0], dragButtonSecondPosition[1], strokeWidth, paintGlowEffect);
266 |
267 |
268 | //draw letter "H""M""S",point(0,0) of text area is on the bottom-left of this area!
269 | //画"H""M""S"这三个字母,文字区域的(0,0)在左下角!
270 | getDisplayNumber();
271 | Rect rect = new Rect();
272 |
273 | paintNumber.setTextSize(70);
274 | paintNumber.setColor(colorHour);
275 | paintNumber.getTextBounds(displayNumberHour, 0, displayNumberHour.length(), rect);
276 | canvas.drawText(displayNumberHour, centerXHour - rect.width() / 2, centerYHour + rect.height() / 2, paintNumber);
277 | paintNumber.setTextSize(25);
278 | canvas.drawText("H", centerXHour + 30, centerYHour + 25, paintNumber);
279 |
280 | paintNumber.setTextSize(70);
281 | paintNumber.setColor(colorMinute);
282 | paintNumber.getTextBounds(displayNumberMinute, 0, displayNumberMinute.length(), rect);
283 | canvas.drawText(displayNumberMinute, centerXMinute - rect.width() / 2, centerYMinute + rect.height() / 2, paintNumber);
284 | paintNumber.setTextSize(25);
285 | canvas.drawText("M", centerXMinute + 50, centerYMinute + 25, paintNumber);
286 |
287 | paintNumber.setTextSize(70);
288 | paintNumber.setColor(colorSecond);
289 | paintNumber.getTextBounds(displayNumberSecond, 0, displayNumberSecond.length(), rect);
290 | canvas.drawText(displayNumberSecond, centerXSecond - rect.width() / 2, centerYSecond + rect.height() / 2, paintNumber);
291 | paintNumber.setTextSize(25);
292 | canvas.drawText("S", centerXSecond + 50, centerYSecond + 25, paintNumber);
293 | }
294 |
295 |
296 | //handle touch event
297 | //
298 | @Override
299 | public boolean onTouchEvent(MotionEvent event) {
300 | super.onTouchEvent(event);
301 |
302 | switch (event.getAction()) {
303 | //whether touch in the drag button or not
304 | //判断点击是否在dragButton内
305 | case MotionEvent.ACTION_DOWN:
306 | isInDragButton(event.getX(), event.getY());
307 | break;
308 |
309 | //update coordination of dragButton
310 | //更新dragButton的位置
311 | case MotionEvent.ACTION_MOVE:
312 | if (!isStarted) {
313 | if (isInDragButton) {
314 | switch (whichDragButton) {
315 | case 1:
316 | currentDegreeHour = getDegree(event.getX(), event.getY(), centerXHour, centerYHour);
317 | updateTime(1);
318 | updateDragButtonPosition(1);
319 | invalidate();
320 | break;
321 | case 2:
322 | currentDegreeMinute = getDegree(event.getX(), event.getY(), centerXMinute, centerYMinute);
323 | updateTime(2);
324 | updateDragButtonPosition(2);
325 | invalidate();
326 | break;
327 | case 3:
328 | currentDegreeSecond = getDegree(event.getX(), event.getY(), centerXSecond, centerYSecond);
329 | updateTime(3);
330 | updateDragButtonPosition(3);
331 | invalidate();
332 | break;
333 | }
334 | }
335 | }
336 | break;
337 |
338 | case MotionEvent.ACTION_UP:
339 | isInDragButton = false;
340 | break;
341 | }
342 |
343 | return true;
344 | }
345 |
346 |
347 | @Override
348 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
349 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
350 |
351 | int width = getDimension(DEFAULT_VIEW_WIDTH, widthMeasureSpec);
352 | int height = getDimension(width, heightMeasureSpec);
353 |
354 | viewWidth = width;
355 | viewHeight = height;
356 |
357 | setMeasuredDimension(width, height);
358 | }
359 |
360 | private int getDimension(int defaultDimension,int measureSpec){
361 |
362 | int result;
363 |
364 | switch (MeasureSpec.getMode(measureSpec)){
365 | case MeasureSpec.EXACTLY:
366 | result = MeasureSpec.getSize(measureSpec);
367 | break;
368 | case MeasureSpec.AT_MOST:
369 | result = Math.min(defaultDimension,MeasureSpec.getSize(measureSpec));
370 |
371 | break;
372 | default:
373 | result = defaultDimension;
374 | break;
375 | }
376 | return result;
377 | }
378 |
379 |
380 |
381 | //update degree,depend on the path user dragging on the screen
382 | //根据用户在屏幕划过的轨迹更新角度
383 | private float getDegree(float eventX, float eventY, float centerX, float centerY) {
384 |
385 | // http://stackoverflow.com/questions/7926816/calculate-angle-of-touched-point-and-rotate-it-in-android
386 | // Math has defeated me once again.So sad...
387 | // 卧槽...
388 | double tx = eventX - centerX;
389 | double ty = eventY - centerY;
390 | double t_length = Math.sqrt(tx * tx + ty * ty);
391 | double a = Math.acos(ty / t_length);
392 | float degree = 180 - (float) Math.toDegrees(a);
393 |
394 | if (centerX > eventX) {
395 | degree = 180 + (float) Math.toDegrees(a);
396 | }
397 |
398 | return degree;
399 | }
400 |
401 | private void getDisplayNumber() {
402 | if (Integer.valueOf(displayNumberHour) != timeRemain.get(Calendar.HOUR_OF_DAY)) {
403 | displayNumberHour = timeRemain.get(Calendar.HOUR_OF_DAY) + "";
404 | if (hourChangListener != null) {
405 | hourChangListener.onHourChange(timeRemain.get(Calendar.HOUR_OF_DAY));
406 | }
407 | }
408 | if (Integer.valueOf(displayNumberMinute) != timeRemain.get(Calendar.MINUTE)) {
409 | displayNumberMinute = timeRemain.get(Calendar.MINUTE) + "";
410 | if (minChangListener != null) {
411 | minChangListener.onMinChange(timeRemain.get(Calendar.MINUTE));
412 | }
413 | }
414 | if (Integer.valueOf(displayNumberSecond) != timeRemain.get(Calendar.SECOND)) {
415 | displayNumberSecond = timeRemain.get(Calendar.SECOND) + "";
416 | if (secondChangListener != null) {
417 | secondChangListener.onSecondChange(timeRemain.get(Calendar.SECOND));
418 | }
419 | }
420 |
421 | }
422 |
423 |
424 | private void isInDragButton(float eventX, float eventY) {
425 |
426 | if (circleRadiusDragButton > Math.sqrt(Math.pow(eventX - dragButtonHourPosition[0], 2)
427 | + Math.pow(eventY - dragButtonHourPosition[1], 2))) {
428 | //在dragButtonHour中
429 | isInDragButton = true;
430 | whichDragButton = 1;
431 | } else if (circleRadiusDragButton > Math.sqrt(Math.pow(eventX - dragButtonMinutePosition[0], 2)
432 | + Math.pow(eventY - dragButtonMinutePosition[1], 2))) {
433 | //在dragButtonMinute中
434 | isInDragButton = true;
435 | whichDragButton = 2;
436 | } else if (circleRadiusDragButton > Math.sqrt(Math.pow(eventX - dragButtonSecondPosition[0], 2)
437 | + Math.pow(eventY - dragButtonSecondPosition[1], 2))) {
438 | //在dragButtonSecond中
439 | isInDragButton = true;
440 | whichDragButton = 3;
441 | } else {
442 | //不在
443 | isInDragButton = false;
444 | whichDragButton = 0;
445 | }
446 | }
447 |
448 |
449 | //degree depending on timeRemain
450 | //角度由剩余时间决定
451 | private void updateDegree() {
452 |
453 | currentDegreeHour = (float) ((timeRemain.get(Calendar.HOUR_OF_DAY) * 60 + timeRemain.get(Calendar.MINUTE)) / (6.0 * 60)) * 360;
454 | currentDegreeMinute = (float) ((timeRemain.get(Calendar.MINUTE) * 60 + timeRemain.get(Calendar.SECOND)) / (60.0 * 60)) * 360;
455 | currentDegreeSecond = (float) ((timeRemain.get(Calendar.SECOND) * 1000 + timeRemain.get(Calendar.MILLISECOND)) / (60.0 * 1000)) * 360;
456 |
457 | updateDragButtonPosition(0);
458 |
459 | }
460 |
461 | //update drag button position(glow effect area)
462 | //更新拖动按钮中心点(辉光效果区域)
463 | private void updateDragButtonPosition(int flag) {
464 |
465 | switch (flag) {
466 | case 0:
467 | dragButtonHourPosition[0] = (float) (centerXHour + circleRadiusHour * (Math.sin(Math.toRadians(currentDegreeHour))));
468 | dragButtonHourPosition[1] = (float) (centerYHour - circleRadiusHour * (Math.cos(Math.toRadians(currentDegreeHour))));
469 |
470 | dragButtonMinutePosition[0] = (float) (centerXMinute + circleRadiusMinute * Math.sin(Math.toRadians(currentDegreeMinute)));
471 | dragButtonMinutePosition[1] = (float) (centerYMinute - circleRadiusMinute * Math.cos(Math.toRadians(currentDegreeMinute)));
472 |
473 | dragButtonSecondPosition[0] = (float) (centerXSecond + circleRadiusSecond * Math.sin(Math.toRadians(currentDegreeSecond)));
474 | dragButtonSecondPosition[1] = (float) (centerYSecond - circleRadiusSecond * Math.cos(Math.toRadians(currentDegreeSecond)));
475 | break;
476 | case 1:
477 | dragButtonHourPosition[0] = (float) (centerXHour + circleRadiusHour * (Math.sin(Math.toRadians(currentDegreeHour))));
478 | dragButtonHourPosition[1] = (float) (centerYHour - circleRadiusHour * (Math.cos(Math.toRadians(currentDegreeHour))));
479 | break;
480 | case 2:
481 | dragButtonMinutePosition[0] = (float) (centerXMinute + circleRadiusMinute * Math.sin(Math.toRadians(currentDegreeMinute)));
482 | dragButtonMinutePosition[1] = (float) (centerYMinute - circleRadiusMinute * Math.cos(Math.toRadians(currentDegreeMinute)));
483 | break;
484 | case 3:
485 | dragButtonSecondPosition[0] = (float) (centerXSecond + circleRadiusSecond * Math.sin(Math.toRadians(currentDegreeSecond)));
486 | dragButtonSecondPosition[1] = (float) (centerYSecond - circleRadiusSecond * Math.cos(Math.toRadians(currentDegreeSecond)));
487 | break;
488 | }
489 |
490 | }
491 |
492 |
493 | //get the time from currentDegree and store it in timeStart and timeRemain
494 | //从当前的角度获取时间,保存到timeStart和timeRemain
495 | private void updateTime(int flag) {
496 |
497 | switch (flag) {
498 | case 0:
499 | timeStart.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
500 | timeRemain.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
501 |
502 | timeStart.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
503 | timeRemain.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
504 |
505 | timeStart.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
506 | timeRemain.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
507 | break;
508 | case 1:
509 | timeStart.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
510 | timeRemain.set(Calendar.HOUR_OF_DAY, (int) Math.floor(6 * currentDegreeHour / 360));
511 | break;
512 | case 2:
513 | timeStart.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
514 | timeRemain.set(Calendar.MINUTE, (int) Math.floor(60 * currentDegreeMinute / 360));
515 | break;
516 | case 3:
517 | timeStart.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
518 | timeRemain.set(Calendar.SECOND, (int) Math.floor(60 * currentDegreeSecond / 360));
519 | break;
520 | }
521 |
522 | }
523 |
524 |
525 | //common Timer-TimerTask-Handler countdown solution
526 | //常见的Timer-TimerTask-Handler倒计时模式
527 | private Handler mHandler = new Handler() {
528 | @Override
529 | public void handleMessage(Message msg) {
530 | super.handleMessage(msg);
531 |
532 | switch (msg.what) {
533 | //countdown running
534 | //可以倒计时
535 | case 1:
536 |
537 | timeRemain.add(Calendar.MILLISECOND, -100);
538 |
539 | if (timeChangeListener != null) {
540 | timeChangeListener.onTimeChange(timeStart.getTimeInMillis(), timeRemain.getTimeInMillis());
541 | }
542 |
543 | invalidate();
544 |
545 | break;
546 | //countdown stop
547 | //时间为空,停止倒计时,提示用户
548 | case 2:
549 | isStarted = false;
550 | timerTask.cancel();
551 | break;
552 |
553 | //StopWatch running
554 | case 11:
555 | timeRemain.add(Calendar.MILLISECOND, 100);
556 | if (timeChangeListener != null) {
557 | timeChangeListener.onTimeChange(timeStart.getTimeInMillis(), timeRemain.getTimeInMillis());
558 | }
559 | invalidate();
560 | break;
561 | //StopWatch stop
562 | //到达MAX TIME
563 | case 12:
564 | isStarted = false;
565 | timerTask.cancel();
566 | break;
567 | }
568 | }
569 | };
570 |
571 | Timer timer = new Timer(true);
572 | TimerTask timerTask;
573 |
574 |
575 | public boolean start() {
576 | if (model == Model.Timer) {
577 | if (!isTimeEmpty() && !isStarted) {
578 |
579 | timerTask = new TimerTask() {
580 | @Override
581 | public void run() {
582 | if (!isTimeEmpty()) {
583 | Message message = new Message();
584 | message.what = 1;
585 | mHandler.sendMessage(message);
586 | } else {
587 | Message message = new Message();
588 | message.what = 2;
589 | mHandler.sendMessage(message);
590 | }
591 |
592 | }
593 | };
594 |
595 | timer.schedule(timerTask, 1000, 100);
596 | isStarted = true;
597 |
598 | if (timeChangeListener != null) {
599 | timeChangeListener.onTimerStart(timeStart.getTimeInMillis());
600 | }
601 | }
602 | } else if (model == Model.StopWatch) {
603 | if (!isMaxTime() && !isStarted) {
604 | timerTask = new TimerTask() {
605 | @Override
606 | public void run() {
607 | if (!isMaxTime()) {
608 | Message message = new Message();
609 | message.what = 11;
610 | mHandler.sendMessage(message);
611 | } else {
612 | Message message = new Message();
613 | message.what = 12;
614 | mHandler.sendMessage(message);
615 | }
616 | }
617 | };
618 |
619 | timer.schedule(timerTask, 1000, 100);
620 | isStarted = true;
621 |
622 | if (timeChangeListener != null) {
623 | timeChangeListener.onTimerStart(timeStart.getTimeInMillis());
624 | }
625 | }
626 | }
627 | return isStarted;
628 | }
629 |
630 | private boolean isTimeEmpty() {
631 | if (timeRemain.get(Calendar.HOUR_OF_DAY) != 0
632 | || timeRemain.get(Calendar.MINUTE) != 0
633 | || timeRemain.get(Calendar.SECOND) != 0
634 | || timeRemain.get(Calendar.MILLISECOND) != 0) {
635 | return false;
636 | } else {
637 | return true;
638 | }
639 | }
640 |
641 |
642 | public long stop() {
643 | timerTask.cancel();
644 | isStarted = false;
645 |
646 | if (timeChangeListener != null) {
647 | timeChangeListener.onTimeStop(timeStart.getTimeInMillis(), timeRemain.getTimeInMillis());
648 | }
649 |
650 | return timeStart.getTimeInMillis() - timeRemain.getTimeInMillis();
651 | }
652 |
653 |
654 | public Calendar getTimeStart() {
655 | return timeStart;
656 | }
657 |
658 |
659 | public Calendar getTimeRemaid() {
660 | return timeRemain;
661 | }
662 |
663 |
664 | public long getTimePass() {
665 | return timeStart.getTimeInMillis() - timeRemain.getTimeInMillis();
666 | }
667 |
668 |
669 | public void setOnTimeChangeListener(OnTimeChangeListener listener) {
670 | if (listener != null) {
671 | timeChangeListener = listener;
672 | }
673 | }
674 |
675 | public void setMinChangListener(OnMinChangListener minChangListener) {
676 | this.minChangListener = minChangListener;
677 | }
678 |
679 | public void setSecondChangListener(OnSecondChangListener secondChangListener) {
680 | this.secondChangListener = secondChangListener;
681 | }
682 |
683 | public void setHourChangListener(OnHourChangListener hourChangListener) {
684 | this.hourChangListener = hourChangListener;
685 | }
686 |
687 | public void reset() {
688 | //先停止计时
689 | stop();
690 | //初始化calendar
691 | isInitialized = false;
692 | invalidate();
693 | }
694 |
695 | public boolean isMaxTime() {
696 | if (timeRemain.get(Calendar.HOUR_OF_DAY) == 5
697 | && timeRemain.get(Calendar.MINUTE) == 59
698 | && timeRemain.get(Calendar.SECOND) == 59) {
699 | return true;
700 | } else {
701 | return false;
702 | }
703 | }
704 |
705 |
706 | //listener
707 | public interface OnTimeChangeListener {
708 | public void onTimerStart(long timeStart);
709 |
710 | public void onTimeChange(long timeStart, long timeRemain);
711 |
712 | public void onTimeStop(long timeStart, long timeRemain);
713 | }
714 |
715 | public interface OnMinChangListener {
716 | public void onMinChange(int minute);
717 | }
718 |
719 | public interface OnHourChangListener {
720 | public void onHourChange(int hour);
721 | }
722 |
723 | public interface OnInitialFinishListener {
724 | public void onInitialFinishListener();
725 | }
726 |
727 | public interface OnSecondChangListener {
728 | public void onSecondChange(int second);
729 | }
730 |
731 | public void setModel(Model model) {
732 | this.model = model;
733 | }
734 |
735 | /**
736 | * set default time
737 | *
738 | * @param h max 5
739 | * @param m max 59
740 | * @param s max 59
741 | */
742 | public void setStartTime(final int h, final int m, final int s) throws NumberFormatException {
743 | initialFinishListener = new OnInitialFinishListener() {
744 | @Override
745 | public void onInitialFinishListener() {
746 | if (h > 5 || m > 59 || s > 69 || h < 0 || m < 0 | s < 0) {
747 | throw new NumberFormatException("hour must in [0-5], minute and second must in [0-59]");
748 | }
749 | timeRemain.set(Calendar.HOUR_OF_DAY, h);
750 | timeRemain.set(Calendar.MINUTE, m);
751 | timeRemain.set(Calendar.SECOND, s);
752 | timeStart.set(Calendar.HOUR_OF_DAY, h);
753 | timeStart.set(Calendar.MINUTE, m);
754 | timeStart.set(Calendar.SECOND, s);
755 | updateDegree();
756 | invalidate();
757 | }
758 | };
759 | }
760 |
761 | }
762 |
--------------------------------------------------------------------------------
/forthewatch/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ForTheWatch
3 |
4 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dujigui/TimerView/cd7b2aa72d39d1389e7631cb8e1964036e619f6d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jul 12 08:43:28 HKT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/read_me/screen_record.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dujigui/TimerView/cd7b2aa72d39d1389e7631cb8e1964036e619f6d/read_me/screen_record.gif
--------------------------------------------------------------------------------
/read_me/screen_shot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dujigui/TimerView/cd7b2aa72d39d1389e7631cb8e1964036e619f6d/read_me/screen_shot.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':forthewatch'
2 |
--------------------------------------------------------------------------------