├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── yongxiang
│ │ └── timerulers
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── yongxiang
│ │ │ └── timerulers
│ │ │ ├── ExampleApplication.java
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── yongxiang
│ └── timerulers
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
├── timebar_scale.gif
└── timerulerslib
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
└── main
├── AndroidManifest.xml
├── java
└── timerulers
│ └── yongxiang
│ └── com
│ └── timerulerslib
│ └── views
│ ├── DeviceUtil.java
│ ├── RecordDataExistTimeSegment.java
│ ├── TimebarTickCriterion.java
│ └── TimebarView.java
└── res
└── values
├── attrs.xml
└── strings.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.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 |
18 |
19 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TimeRulers
2 |
3 | 一个可以缩放的时间轴 类似萤石云的录像进度条
4 |
5 | 
6 |
7 | Usage
8 | -----
9 | 1.在xml布局中引入:
10 | ```
11 |
19 | ```
20 |
21 | - 目前支持的自定义属性:
22 | * `middleCursorColor` 时间尺中间刻度条颜色
23 | - `recordTextColor` 时间刻度文字颜色
24 | - `recordBackgroundColor` 含有录像背景颜色
25 | - `timebarColor` 含有时间尺边框及刻度条颜色
26 |
27 |
28 | - 刻度条移动监听
29 | ```
30 | mTimebarView.setOnBarMoveListener(new TimebarView.OnBarMoveListener() {
31 | @Override
32 | public void onBarMove(long screenLeftTime, long screenRightTime, long currentTime) {
33 | if (currentTime == -1) {
34 | Toast.makeText(MainActivity.this, "当前时刻没有录像", Toast.LENGTH_SHORT).show();
35 | }
36 | currentTimeTextView.setText(zeroTimeFormat.format(currentTime));
37 | }
38 |
39 | @Override
40 | public void OnBarMoveFinish(long screenLeftTime, long screenRightTime, long currentTime) {
41 | currentTimeTextView.setText(zeroTimeFormat.format(currentTime));
42 | }
43 | });
44 |
45 | mTimebarView.setOnBarScaledListener(new TimebarView.OnBarScaledListener() {
46 | @Override
47 | public void onOnBarScaledMode(int mode) {
48 | Log.d(TAG, "onOnBarScaledMode()" + mode);
49 | }
50 |
51 | @Override
52 | public void onBarScaled(long screenLeftTime, long screenRightTime, long currentTime) {
53 | currentTimeTextView.setText(zeroTimeFormat.format(currentTime));
54 | Log.d(TAG, "onBarScaled()");
55 | }
56 |
57 | @Override
58 | public void onBarScaleFinish(long screenLeftTime, long screenRightTime, long currentTime) {
59 | Log.d(TAG, "onBarScaleFinish()");
60 | }
61 | });
62 |
63 | }
64 | ```
65 |
66 | - 刻度条缩放监听
67 | ```
68 | mTimebarView.setOnBarScaledListener(new TimebarView.OnBarScaledListener() {
69 | @Override
70 | public void onOnBarScaledMode(int mode) {
71 | 返回缩放级别
72 | Log.d(TAG, "onOnBarScaledMode()" + mode);
73 | }
74 |
75 | @Override
76 | public void onBarScaled(long screenLeftTime, long screenRightTime, long currentTime) {
77 | currentTimeTextView.setText(zeroTimeFormat.format(currentTime));
78 | Log.d(TAG, "onBarScaled()");
79 | }
80 |
81 | @Override
82 | public void onBarScaleFinish(long screenLeftTime, long screenRightTime, long currentTime) {
83 | Log.d(TAG, "onBarScaleFinish()");
84 | }
85 | });
86 |
87 | }
88 | ```
89 |
90 | - Download
91 | ```
92 | dependencies {
93 | compile 'com.dingyongxiang.library:timebarview:1.9'
94 | }
95 | ```
96 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 | defaultConfig {
7 | applicationId "com.yongxiang.timerulers"
8 | minSdkVersion 19
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
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(include: ['*.jar'], dir: 'libs')
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.3.1'
28 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
29 | testCompile 'junit:junit:4.12'
30 | compile project(':timerulerslib')
31 |
32 | debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
33 | releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
34 | testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
35 | }
36 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Software\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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/yongxiang/timerulers/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.yongxiang.timerulers;
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 | * Instrumentation 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() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.yongxiang.timerulers", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yongxiang/timerulers/ExampleApplication.java:
--------------------------------------------------------------------------------
1 | package com.yongxiang.timerulers;
2 |
3 | import android.app.Application;
4 |
5 | import com.squareup.leakcanary.LeakCanary;
6 |
7 | /**
8 | * Created by dingyongxiang on 2017/6/6.
9 | */
10 |
11 | public class ExampleApplication extends Application {
12 | @Override public void onCreate() {
13 | super.onCreate();
14 | if (LeakCanary.isInAnalyzerProcess(this)) {
15 | // This process is dedicated to LeakCanary for heap analysis.
16 | // You should not init your app in this process.
17 | return;
18 | }
19 | LeakCanary.install(this);
20 | // Normal app init code...
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/yongxiang/timerulers/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.yongxiang.timerulers;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.util.Log;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 | import android.widget.Toast;
11 |
12 | import java.text.SimpleDateFormat;
13 | import java.util.ArrayList;
14 | import java.util.Calendar;
15 | import java.util.List;
16 |
17 | import timerulers.yongxiang.com.timerulerslib.views.RecordDataExistTimeSegment;
18 | import timerulers.yongxiang.com.timerulerslib.views.TimebarView;
19 |
20 | public class MainActivity extends AppCompatActivity implements View.OnClickListener {
21 | private String TAG = MainActivity.class.getSimpleName();
22 | private TextView currentTimeTextView;
23 | private Button zoomInButton, zoomOutButton;
24 | private TimebarView mTimebarView;
25 |
26 | private Button mDayBt;
27 | private Button mHourBt;
28 | private Button mMinuteBt;
29 | private int recordDays = 7;
30 | private long currentRealDateTime = System.currentTimeMillis();
31 | private Calendar calendar;
32 |
33 | private static long ONE_MINUTE_IN_MS = 60 * 1000;
34 | private static long ONE_HOUR_IN_MS = 60 * ONE_MINUTE_IN_MS;
35 | private static long ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS;
36 |
37 | private SimpleDateFormat zeroTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
38 |
39 |
40 | @Override
41 | protected void onCreate(Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 | setContentView(R.layout.activity_main);
44 |
45 | mTimebarView = (TimebarView) findViewById(R.id.my_timebar_view);
46 | currentTimeTextView = (TextView) findViewById(R.id.current_time_tv);
47 | zoomInButton = (Button) findViewById(R.id.timebar_zoom_in_btn);
48 | zoomOutButton = (Button) findViewById(R.id.timebar_zoom_out_btn);
49 | mDayBt = (Button) findViewById(R.id.day);
50 | mHourBt = (Button) findViewById(R.id.hour);
51 | mMinuteBt = (Button) findViewById(R.id.minute);
52 |
53 | zoomInButton.setOnClickListener(this);
54 | zoomOutButton.setOnClickListener(this);
55 | mDayBt.setOnClickListener(this);
56 | mHourBt.setOnClickListener(this);
57 | mMinuteBt.setOnClickListener(this);
58 |
59 | calendar = Calendar.getInstance();
60 | calendar.set(Calendar.HOUR_OF_DAY, 0);
61 | calendar.set(Calendar.MINUTE, 0);
62 | calendar.set(Calendar.SECOND, 0);
63 | //long timebarLeftEndPointTime = currentRealDateTime - 7 * 24 * 3600 * 1000;
64 | long timebarLeftEndPointTime = calendar.getTimeInMillis();
65 |
66 | System.out.println("calendar:" + calendar.getTime() + " currentRealDateTime:" + currentRealDateTime);
67 | calendar = Calendar.getInstance();
68 | calendar.set(Calendar.HOUR_OF_DAY, 0);
69 | calendar.set(Calendar.MINUTE, 0);
70 | calendar.set(Calendar.SECOND, 0);
71 | calendar.add(Calendar.DAY_OF_MONTH, 1);
72 | long timebarRightEndPointTime = calendar.getTimeInMillis();
73 | //long timebarRightEndPointTime = currentRealDateTime + 3 * 3600 * 1000;
74 |
75 | mTimebarView.initTimebarLengthAndPosition(timebarLeftEndPointTime,
76 | timebarRightEndPointTime - 1000, currentRealDateTime);
77 |
78 | final List recordDataList = new ArrayList<>();
79 | recordDataList.add(new RecordDataExistTimeSegment(timebarLeftEndPointTime - ONE_HOUR_IN_MS * 1, timebarLeftEndPointTime + ONE_HOUR_IN_MS * 3));
80 | recordDataList.add(new RecordDataExistTimeSegment(timebarLeftEndPointTime + ONE_HOUR_IN_MS * 4, timebarLeftEndPointTime + ONE_HOUR_IN_MS * 8));
81 | recordDataList.add(new RecordDataExistTimeSegment(timebarLeftEndPointTime + ONE_HOUR_IN_MS * 12, timebarLeftEndPointTime + ONE_HOUR_IN_MS * 19));
82 | recordDataList.add(new RecordDataExistTimeSegment(timebarLeftEndPointTime + ONE_HOUR_IN_MS * 20, timebarRightEndPointTime));
83 |
84 | mTimebarView.setRecordDataExistTimeClipsList(recordDataList);
85 |
86 | mTimebarView.openMove();
87 | mTimebarView.checkVideo(true);
88 |
89 |
90 | mTimebarView.setOnBarMoveListener(new TimebarView.OnBarMoveListener() {
91 | @Override
92 | public void onBarMove(long screenLeftTime, long screenRightTime, long currentTime) {
93 | if (currentTime == -1) {
94 | Toast.makeText(MainActivity.this, "当前时刻没有录像", Toast.LENGTH_SHORT).show();
95 | }
96 | currentTimeTextView.setText(zeroTimeFormat.format(currentTime));
97 | }
98 |
99 | @Override
100 | public void OnBarMoveFinish(long screenLeftTime, long screenRightTime, long currentTime) {
101 | currentTimeTextView.setText(zeroTimeFormat.format(currentTime));
102 | }
103 | });
104 |
105 | mTimebarView.setOnBarScaledListener(new TimebarView.OnBarScaledListener() {
106 | @Override
107 | public void onOnBarScaledMode(int mode) {
108 | Log.d(TAG, "onOnBarScaledMode()" + mode);
109 | }
110 |
111 | @Override
112 | public void onBarScaled(long screenLeftTime, long screenRightTime, long currentTime) {
113 | currentTimeTextView.setText(zeroTimeFormat.format(currentTime));
114 | Log.d(TAG, "onBarScaled()");
115 | }
116 |
117 | @Override
118 | public void onBarScaleFinish(long screenLeftTime, long screenRightTime, long currentTime) {
119 | Log.d(TAG, "onBarScaleFinish()");
120 | }
121 | });
122 |
123 | }
124 |
125 | @Override
126 | protected void onResume() {
127 | super.onResume();
128 | }
129 |
130 |
131 | @Override
132 | public void onClick(View v) {
133 | switch (v.getId()) {
134 | case R.id.timebar_zoom_in_btn:
135 | mTimebarView.scaleByPressingButton(true);
136 | break;
137 | case R.id.timebar_zoom_out_btn:
138 | mTimebarView.scaleByPressingButton(false);
139 | break;
140 | case R.id.day:
141 | mTimebarView.setMode(3);
142 | break;
143 | case R.id.hour:
144 | mTimebarView.setMode(2);
145 | break;
146 | case R.id.minute:
147 | mTimebarView.setMode(1);
148 | break;
149 | default:
150 | break;
151 | }
152 | }
153 |
154 | @Override
155 | protected void onDestroy() {
156 | super.onDestroy();
157 | mTimebarView.recycle();
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
22 |
23 |
37 |
38 |
44 |
45 |
46 |
47 |
48 |
57 |
58 |
63 |
64 |
69 |
70 |
75 |
76 |
77 |
81 |
82 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | "TimeRulers "
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/yongxiang/timerulers/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.yongxiang.timerulers;
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() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.2'
9 | classpath 'com.novoda:bintray-release:0.4.0'
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 | tasks.withType(Javadoc) {
20 | options {
21 | encoding "UTF-8"
22 | charSet 'UTF-8'
23 | links "http://docs.oracle.com/javase/7/docs/api"
24 | }
25 | }
26 | }
27 |
28 | task clean(type: Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/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 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon May 22 16:49:03 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/gradlew
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':timerulerslib'
2 |
--------------------------------------------------------------------------------
/timebar_scale.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dingyongxiang/TimeRulers/b88664b5b7ba720570b3e06efddaa295553d94b1/timebar_scale.gif
--------------------------------------------------------------------------------
/timerulerslib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/timerulerslib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.novoda.bintray-release'
3 |
4 | publish {
5 | userOrg = 'dingyongxiang'
6 | groupId = 'com.dingyongxiang.library'
7 | artifactId = 'timebarview'
8 | publishVersion = '1.9'
9 | desc = 'TimebarView'
10 | website = 'https://github.com/dingyongxiang/TimeRulers'
11 | }
12 | /*7e151f52046e3813f7b318b7d6baf3f34976e864*/
13 | android {
14 | compileSdkVersion 25
15 | buildToolsVersion "25.0.3"
16 |
17 | defaultConfig {
18 | minSdkVersion 14
19 | targetSdkVersion 25
20 | versionCode 1
21 | versionName version
22 |
23 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
24 |
25 | }
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
30 | }
31 | }
32 |
33 | lintOptions {
34 | abortOnError false
35 | }
36 | }
37 |
38 | dependencies {
39 | compile fileTree(include: ['*.jar'], dir: 'libs')
40 | }
41 |
--------------------------------------------------------------------------------
/timerulerslib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Software\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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/timerulerslib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/timerulerslib/src/main/java/timerulers/yongxiang/com/timerulerslib/views/DeviceUtil.java:
--------------------------------------------------------------------------------
1 |
2 | package timerulers.yongxiang.com.timerulerslib.views;
3 |
4 | import android.content.Context;
5 | import android.content.res.Resources;
6 | import android.util.DisplayMetrics;
7 | import android.view.WindowManager;
8 |
9 | public class DeviceUtil {
10 | public static int[] getScreenResolution(Context context) {
11 | int scrennResolution[] = new int[2];
12 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
13 | DisplayMetrics dm = new DisplayMetrics();
14 | android.view.Display display = wm.getDefaultDisplay();
15 | display.getMetrics(dm);
16 | scrennResolution[0] = dm.widthPixels;
17 | scrennResolution[1] = dm.heightPixels;
18 | return scrennResolution;
19 | }
20 |
21 |
22 |
23 | public static float getDensity(Context context) {
24 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
25 | DisplayMetrics dm = new DisplayMetrics();
26 | android.view.Display display = wm.getDefaultDisplay();
27 | display.getMetrics(dm);
28 | return dm.density;
29 | }
30 |
31 |
32 | public static int dip2px(float dipValue) {
33 | final float scale = Resources.getSystem().getDisplayMetrics().density;
34 | return (int) (dipValue * scale + 0.5f);
35 | }
36 |
37 |
38 | public static int px2dip(float pxValue) {
39 | final float scale = Resources.getSystem().getDisplayMetrics().density;
40 | return (int) (pxValue / scale + 0.5f);
41 | }
42 |
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/timerulerslib/src/main/java/timerulers/yongxiang/com/timerulerslib/views/RecordDataExistTimeSegment.java:
--------------------------------------------------------------------------------
1 |
2 | package timerulers.yongxiang.com.timerulerslib.views;
3 |
4 | import java.text.ParseException;
5 | import java.text.SimpleDateFormat;
6 | import java.util.ArrayList;
7 | import java.util.Date;
8 | import java.util.List;
9 |
10 | public class RecordDataExistTimeSegment {
11 |
12 | private static long mostLeftDayZeroTime = Long.MAX_VALUE;
13 |
14 | private static long mostRightDayZeroTime = -1;
15 |
16 | private long startTimeInMillisecond;
17 |
18 | private long endTimeInMillisecond;
19 |
20 | private List coverDateZeroOClockList = new ArrayList<>();
21 |
22 | public RecordDataExistTimeSegment(long startTimeInMillisecond, long endTimeInMillisecond) {
23 | this.startTimeInMillisecond = startTimeInMillisecond;
24 | this.endTimeInMillisecond = endTimeInMillisecond;
25 |
26 | if (startTimeInMillisecond < mostLeftDayZeroTime) {
27 | this.mostLeftDayZeroTime = startTimeInMillisecond;
28 | }
29 |
30 | if (endTimeInMillisecond > mostRightDayZeroTime) {
31 | this.mostRightDayZeroTime = endTimeInMillisecond;
32 | }
33 |
34 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
35 | SimpleDateFormat zeroTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
36 |
37 | String startTimeDateString = dateFormat.format(startTimeInMillisecond);
38 | String startTimeZeroTimeString = startTimeDateString + " 00:00:00";
39 |
40 | String endTimeDateString = dateFormat.format(endTimeInMillisecond);
41 | String endTimeZeroTimeString = endTimeDateString + " 00:00:00";
42 |
43 | try {
44 | Date startTimeZeroDate = zeroTimeFormat.parse(startTimeZeroTimeString);
45 | Date endTimeZeroDate = zeroTimeFormat.parse(endTimeZeroTimeString);
46 |
47 | long loopZeroDateInMilliseconds = startTimeZeroDate.getTime();
48 | while (loopZeroDateInMilliseconds <= endTimeZeroDate.getTime()) {
49 | coverDateZeroOClockList.add(loopZeroDateInMilliseconds);
50 | loopZeroDateInMilliseconds = loopZeroDateInMilliseconds + TimebarView.SECONDS_PER_DAY * 1000;
51 | }
52 | } catch (ParseException e) {
53 | e.printStackTrace();
54 | }
55 | }
56 |
57 | public long getStartTimeInMillisecond() {
58 | return startTimeInMillisecond;
59 | }
60 |
61 | public long getEndTimeInMillisecond() {
62 | return endTimeInMillisecond;
63 | }
64 |
65 | public List getCoverDateZeroOClockList() {
66 | return coverDateZeroOClockList;
67 | }
68 | }
--------------------------------------------------------------------------------
/timerulerslib/src/main/java/timerulers/yongxiang/com/timerulerslib/views/TimebarTickCriterion.java:
--------------------------------------------------------------------------------
1 |
2 | package timerulers.yongxiang.com.timerulerslib.views;
3 |
4 | public class TimebarTickCriterion {
5 |
6 | private int viewLength;
7 |
8 | private int totalSecondsInOneScreen;
9 |
10 | private int keyTickInSecond;
11 |
12 | private int minTickInSecond;
13 |
14 | private String dataPattern;
15 |
16 | public int getViewLength() {
17 | return viewLength;
18 | }
19 |
20 | public void setViewLength(int viewLength) {
21 | this.viewLength = viewLength;
22 | }
23 |
24 | public int getTotalSecondsInOneScreen() {
25 | return totalSecondsInOneScreen;
26 | }
27 |
28 | public void setTotalSecondsInOneScreen(int totalSecondsInOneScreen) {
29 | this.totalSecondsInOneScreen = totalSecondsInOneScreen;
30 | }
31 |
32 | public int getKeyTickInSecond() {
33 | return keyTickInSecond;
34 | }
35 |
36 | public void setKeyTickInSecond(int keyTickInSecond) {
37 | this.keyTickInSecond = keyTickInSecond;
38 | }
39 |
40 | public int getMinTickInSecond() {
41 | return minTickInSecond;
42 | }
43 |
44 | public void setMinTickInSecond(int minTickInSecond) {
45 | this.minTickInSecond = minTickInSecond;
46 | }
47 |
48 | public String getDataPattern() {
49 | return dataPattern;
50 | }
51 |
52 | public void setDataPattern(String dataPattern) {
53 | this.dataPattern = dataPattern;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/timerulerslib/src/main/java/timerulers/yongxiang/com/timerulerslib/views/TimebarView.java:
--------------------------------------------------------------------------------
1 |
2 | package timerulers.yongxiang.com.timerulerslib.views;
3 |
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.RectF;
11 | import android.os.Build;
12 | import android.os.Bundle;
13 | import android.os.Debug;
14 | import android.os.Handler;
15 | import android.os.Message;
16 | import android.text.TextPaint;
17 | import android.util.AttributeSet;
18 | import android.util.Log;
19 | import android.view.MotionEvent;
20 | import android.view.ScaleGestureDetector;
21 | import android.view.VelocityTracker;
22 | import android.view.View;
23 | import android.view.ViewConfiguration;
24 | import android.view.ViewGroup;
25 | import android.widget.Scroller;
26 |
27 | import java.text.ParseException;
28 | import java.text.SimpleDateFormat;
29 | import java.util.ArrayList;
30 | import java.util.Calendar;
31 | import java.util.Date;
32 | import java.util.HashMap;
33 | import java.util.List;
34 | import java.util.Map;
35 |
36 | import timerulers.yongxiang.com.timerulerslib.R;
37 |
38 |
39 | public class TimebarView extends View {
40 |
41 | private float pixelsPerSecond = 0;
42 |
43 |
44 | private OnBarMoveListener mOnBarMoveListener;
45 |
46 | private OnBarScaledListener mOnBarScaledListener;
47 |
48 |
49 | private int screenWidth, screenHeight;
50 |
51 |
52 | private int linesColor = Color.BLACK;
53 |
54 | private int recordBackgroundColor = Color.argb(200, 251, 180, 76);
55 |
56 | private int textColor = Color.BLACK;
57 |
58 | private int middleCursorColor = Color.RED;
59 |
60 | private Paint timebarPaint = new Paint();
61 |
62 | private TextPaint keyTickTextPaint = new TextPaint();
63 |
64 | private int VIEW_HEIGHT_IN_DP = 150;
65 |
66 | private final int KEY_TICK_TEXT_SIZE_IN_SP = 12;
67 |
68 | private final int BIG_TICK_HEIGHT_IN_DP = 15;
69 |
70 | private final int SMALL_TICK_HEIGHT_IN_DP = 12;
71 |
72 | private final int BIG_TICK_HALF_WIDTH_IN_DP = 2;
73 |
74 |
75 | private final int SMALL_TICK_HALF_WIDTH_IN_DP = 1;
76 |
77 | private final int BIG_TICK_HALF_WIDTH = DeviceUtil.dip2px(BIG_TICK_HALF_WIDTH_IN_DP);
78 | private final int TRIANGLE_LENGTH = BIG_TICK_HALF_WIDTH * 4;
79 | private final int BIG_TICK_HEIGHT = DeviceUtil.dip2px(BIG_TICK_HEIGHT_IN_DP);
80 | private final int SMALL_TICK_HALF_WIDTH = DeviceUtil.dip2px(SMALL_TICK_HALF_WIDTH_IN_DP);
81 | private final int SMALL_TICK_HEIGHT = DeviceUtil.dip2px(SMALL_TICK_HEIGHT_IN_DP);
82 | private final int KEY_TICK_TEXT_SIZE = DeviceUtil.dip2px(KEY_TICK_TEXT_SIZE_IN_SP);
83 | private int VIEW_HEIGHT;
84 |
85 | private boolean middleCursorVisible = true;
86 |
87 | private Map timebarTickCriterionMap = new HashMap<>();
88 |
89 | private int timebarTickCriterionCount = 5;
90 |
91 | private int currentTimebarTickCriterionIndex = 3;
92 |
93 | private List recordDataExistTimeClipsList = new ArrayList<>();
94 |
95 | private Map> recordDataExistTimeClipsListMap = new HashMap<>();
96 |
97 | private ScaleGestureDetector scaleGestureDetector;
98 |
99 | private long currentTimeInMillisecond;
100 |
101 | private long mostLeftTimeInMillisecond;
102 |
103 | private long mostRightTimeInMillisecond;
104 |
105 | private long screenLeftTimeInMillisecond;
106 |
107 | private long screenRightTimeInMillisecond;
108 |
109 | private boolean justScaledByPressingButton = false;
110 |
111 | public final static int SECONDS_PER_DAY = 24 * 60 * 60;
112 |
113 | private long WHOLE_TIMEBAR_TOTAL_SECONDS;
114 |
115 | private Path path;
116 |
117 | private Calendar calendar;
118 |
119 |
120 | private SimpleDateFormat zeroTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
121 |
122 |
123 | /*
124 | * 设置最大最小缩放级别
125 | * 0:精度为秒
126 | * 1:精度为一分钟
127 | * 2:精度为6分钟
128 | * 3:精度为30分钟
129 | * 4:精度为2小时
130 | */
131 | private int ZOOMMAX = 3;
132 | private int ZOOMMIN = 1;
133 |
134 | private static final int MOVEING = 0x001;
135 | private static final int ACTION_UP = MOVEING + 1;
136 |
137 |
138 | private int idTag;
139 |
140 |
141 | Handler handler = new Handler(new Handler.Callback() {
142 | @Override
143 | public boolean handleMessage(Message msg) {
144 |
145 | switch (msg.what) {
146 | case MOVEING:
147 | openMove();
148 | break;
149 | case ACTION_UP:
150 | if (checkVideo) {
151 | if (!checkHasVideo()) {
152 | Log.d("ACTION_UP", "NO VIDEO currentTimeInMillisecond:" + currentTimeInMillisecond + " lastcurrentTimeInMillisecond:" + lastcurrentTimeInMillisecond);
153 | currentTimeInMillisecond = lastcurrentTimeInMillisecond;
154 | invalidate();
155 | checkVideo = lastCheckState;
156 | if (mOnBarMoveListener != null) {
157 | mOnBarMoveListener.onBarMove(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), -1);
158 | }
159 | } else {
160 | if (mOnBarMoveListener != null) {
161 | mOnBarMoveListener.OnBarMoveFinish(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), currentTimeInMillisecond);
162 | }
163 | }
164 | } else {
165 | if (mOnBarMoveListener != null) {
166 | mOnBarMoveListener.OnBarMoveFinish(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), currentTimeInMillisecond);
167 | }
168 | }
169 | break;
170 |
171 | }
172 |
173 | return false;
174 | }
175 | });
176 |
177 | public TimebarView(Context context) {
178 | super(context);
179 | init(null, 0);
180 |
181 | }
182 |
183 | public TimebarView(Context context, AttributeSet attrs, int defStyleAttr) {
184 | super(context, attrs, defStyleAttr);
185 | init(attrs, defStyleAttr);
186 | }
187 |
188 | public TimebarView(Context context, AttributeSet attrs) {
189 | super(context, attrs);
190 | init(attrs, 0);
191 | }
192 |
193 |
194 | public List getRecordDataExistTimeClipsList() {
195 | return recordDataExistTimeClipsList;
196 | }
197 |
198 | public void setRecordDataExistTimeClipsList(List recordDataExistTimeClipsList) {
199 | this.recordDataExistTimeClipsList = recordDataExistTimeClipsList;
200 | arrangeRecordDataExistTimeClipsIntoMap(recordDataExistTimeClipsList);
201 | }
202 |
203 | public void setMostLeftTimeInMillisecond(long mostLeftTimeInMillisecond) {
204 | this.mostLeftTimeInMillisecond = mostLeftTimeInMillisecond;
205 | }
206 |
207 | public void setMostRightTimeInMillisecond(long mostRightTimeInMillisecond) {
208 | this.mostRightTimeInMillisecond = mostRightTimeInMillisecond;
209 | }
210 |
211 | public long getMostLeftTimeInMillisecond() {
212 | return mostLeftTimeInMillisecond;
213 | }
214 |
215 |
216 | public long getMostRightTimeInMillisecond() {
217 | return mostRightTimeInMillisecond;
218 | }
219 |
220 | public long getScreenLeftTimeInMillisecond() {
221 | screenLeftTimeInMillisecond = (long) (getCurrentTimeInMillisecond() - (long) ((float) screenWidth * 1000f / 2f / pixelsPerSecond));
222 |
223 | return screenLeftTimeInMillisecond;
224 | }
225 |
226 | public long getScreenRightTimeInMillisecond() {
227 | screenRightTimeInMillisecond = (long) (getCurrentTimeInMillisecond() + (long) (screenWidth * 1000f / 2f / pixelsPerSecond));
228 | return screenRightTimeInMillisecond;
229 | }
230 |
231 | private void arrangeRecordDataExistTimeClipsIntoMap(List clipsList) {
232 | recordDataExistTimeClipsListMap = new HashMap<>();
233 |
234 | if (clipsList != null) {
235 | for (RecordDataExistTimeSegment clipItem : clipsList) {
236 | for (Long dateZeroOClockItem : clipItem.getCoverDateZeroOClockList()) {
237 | List list = null;
238 | if ((list = recordDataExistTimeClipsListMap.get(dateZeroOClockItem)) == null) {
239 | list = new ArrayList<>();
240 | recordDataExistTimeClipsListMap.put(dateZeroOClockItem, list);
241 | }
242 | list.add(clipItem);
243 | }
244 |
245 | }
246 | }
247 | postInvalidate();
248 | }
249 |
250 |
251 | public void initTimebarLengthAndPosition(long mostLeftTime, long mostRightTime, long currentTime) {
252 | this.mostLeftTimeInMillisecond = mostLeftTime;
253 | this.mostRightTimeInMillisecond = mostRightTime;
254 | this.currentTimeInMillisecond = currentTime;
255 | WHOLE_TIMEBAR_TOTAL_SECONDS = (mostRightTime - mostLeftTime) / 1000;
256 | initTimebarTickCriterionMap();
257 | resetToStandardWidth();
258 | }
259 |
260 | public int getCurrentTimebarTickCriterionIndex() {
261 | return currentTimebarTickCriterionIndex;
262 | }
263 |
264 | public void setCurrentTimebarTickCriterionIndex(int currentTimebarTickCriterionIndex) {
265 | this.currentTimebarTickCriterionIndex = currentTimebarTickCriterionIndex;
266 | }
267 |
268 | private void init(AttributeSet attrs, int defStyleAttr) {
269 | path = new Path();
270 | TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.TimebarView, defStyleAttr, 0);
271 | int n = a.getIndexCount();
272 | for (int i = 0; i < n; i++) {
273 | int attr = a.getIndex(i);
274 | if (attr == R.styleable.TimebarView_middleCursorColor) {
275 | middleCursorColor = a.getColor(attr, Color.RED);
276 |
277 | } else if (attr == R.styleable.TimebarView_recordBackgroundColor) {// 默认颜色设置为橘黄色
278 | recordBackgroundColor = a.getColor(attr, Color.argb(200, 251, 180, 76));
279 |
280 | } else if (attr == R.styleable.TimebarView_recordTextColor) {// 默认颜色设置为黑色
281 | textColor = a.getColor(attr, Color.BLACK);
282 |
283 | } else if (attr == R.styleable.TimebarView_timebarColor) {// 默认颜色设置为黑色
284 | linesColor = a.getColor(attr, Color.BLACK);
285 |
286 | }
287 |
288 | }
289 | a.recycle();
290 | screenWidth = DeviceUtil.getScreenResolution(getContext())[0];
291 | screenHeight = DeviceUtil.getScreenResolution(getContext())[1];
292 |
293 |
294 | currentTimeInMillisecond = System.currentTimeMillis();
295 |
296 | calendar = Calendar.getInstance();
297 | calendar.set(Calendar.HOUR_OF_DAY, 0);
298 | calendar.set(Calendar.MINUTE, 0);
299 | calendar.set(Calendar.SECOND, 0);
300 | mostLeftTimeInMillisecond = calendar.getTimeInMillis();
301 |
302 |
303 | //mostLeftTimeInMillisecond = currentTimeInMillisecond - 3 * 3600 * 1000;
304 |
305 | calendar = Calendar.getInstance();
306 | calendar.set(Calendar.HOUR_OF_DAY, 0);
307 | calendar.set(Calendar.MINUTE, 0);
308 | calendar.set(Calendar.SECOND, 0);
309 | calendar.add(Calendar.DAY_OF_MONTH, 1);
310 | mostRightTimeInMillisecond = calendar.getTimeInMillis();
311 | //mostRightTimeInMillisecond = currentTimeInMillisecond + 3 * 3600 * 1000;
312 |
313 |
314 | WHOLE_TIMEBAR_TOTAL_SECONDS = (mostRightTimeInMillisecond - mostLeftTimeInMillisecond) / 1000;
315 |
316 | pixelsPerSecond = (float) (getWidth() - screenWidth) / (float) WHOLE_TIMEBAR_TOTAL_SECONDS;
317 |
318 | initTimebarTickCriterionMap();
319 | setCurrentTimebarTickCriterionIndex(3);
320 |
321 | //resetToStandardWidth();
322 |
323 | keyTickTextPaint.setAntiAlias(true);
324 | keyTickTextPaint.setTextSize(KEY_TICK_TEXT_SIZE);
325 | keyTickTextPaint.setColor(textColor);
326 |
327 | ScaleGestureDetector.OnScaleGestureListener scaleGestureListener = new ScaleGestureDetector.OnScaleGestureListener() {
328 | @Override
329 | public boolean onScale(ScaleGestureDetector detector) {
330 | if (lastMoveState) {
331 | if (handler.hasMessages(MOVEING))
332 | handler.removeMessages(MOVEING);
333 | handler.sendEmptyMessageDelayed(MOVEING, 1100);
334 | }
335 | scaleTimebarByFactor(detector.getScaleFactor(), false);
336 | return true;
337 | }
338 |
339 | @Override
340 | public boolean onScaleBegin(ScaleGestureDetector detector) {
341 | return true;
342 | }
343 |
344 | @Override
345 | public void onScaleEnd(ScaleGestureDetector detector) {
346 | justScaledByPressingButton = true;
347 | }
348 |
349 |
350 | };
351 | scaleGestureDetector = new ScaleGestureDetector(getContext(), scaleGestureListener);
352 |
353 | }
354 |
355 |
356 | public void scaleTimebarByFactor(float scaleFactor, boolean scaleByClickButton) {
357 |
358 | int newWidth = (int) ((getWidth() - screenWidth) * scaleFactor);
359 |
360 | if (newWidth > timebarTickCriterionMap.get(ZOOMMIN).getViewLength() || newWidth < timebarTickCriterionMap.get(ZOOMMAX).getViewLength())
361 | return;
362 |
363 | if (newWidth > timebarTickCriterionMap.get(0).getViewLength()) {
364 | setCurrentTimebarTickCriterionIndex(0);
365 | newWidth = timebarTickCriterionMap.get(0).getViewLength();
366 | if (mOnBarScaledListener != null) {
367 | mOnBarScaledListener.onOnBarScaledMode(0);
368 | }
369 |
370 | } else if (newWidth < timebarTickCriterionMap.get(0).getViewLength()
371 | && newWidth >= getAverageWidthForTwoCriterion(0, 1)) {
372 | setCurrentTimebarTickCriterionIndex(0);
373 | if (mOnBarScaledListener != null) {
374 | mOnBarScaledListener.onOnBarScaledMode(0);
375 | }
376 |
377 | } else if (newWidth < getAverageWidthForTwoCriterion(0, 1)
378 | && newWidth >= getAverageWidthForTwoCriterion(1, 2)) {
379 | setCurrentTimebarTickCriterionIndex(1);
380 | if (mOnBarScaledListener != null) {
381 | mOnBarScaledListener.onOnBarScaledMode(1);
382 | }
383 |
384 | } else if (newWidth < getAverageWidthForTwoCriterion(1, 2)
385 | && newWidth >= getAverageWidthForTwoCriterion(2, 3)) {
386 | setCurrentTimebarTickCriterionIndex(2);
387 | if (mOnBarScaledListener != null) {
388 | mOnBarScaledListener.onOnBarScaledMode(2);
389 | }
390 |
391 | } else if (newWidth < getAverageWidthForTwoCriterion(2, 3)
392 | && newWidth >= getAverageWidthForTwoCriterion(3, 4)) {
393 | setCurrentTimebarTickCriterionIndex(3);
394 | if (mOnBarScaledListener != null) {
395 | mOnBarScaledListener.onOnBarScaledMode(3);
396 | }
397 |
398 | } else if (newWidth < getAverageWidthForTwoCriterion(3, 4)
399 | && newWidth >= timebarTickCriterionMap.get(4).getViewLength()) {
400 | setCurrentTimebarTickCriterionIndex(4);
401 | if (mOnBarScaledListener != null) {
402 | mOnBarScaledListener.onOnBarScaledMode(4);
403 | }
404 |
405 | } else if (newWidth < timebarTickCriterionMap.get(4).getViewLength()) {
406 | setCurrentTimebarTickCriterionIndex(4);
407 | newWidth = timebarTickCriterionMap.get(4).getViewLength();
408 | if (mOnBarScaledListener != null) {
409 | mOnBarScaledListener.onOnBarScaledMode(4);
410 | }
411 |
412 | }
413 |
414 | if (scaleByClickButton) {
415 | justScaledByPressingButton = true;
416 | }
417 |
418 |
419 | ViewGroup.LayoutParams params = getLayoutParams();
420 | params.width = newWidth;
421 | setLayoutParams(params);
422 |
423 | }
424 |
425 | public void setMode(int scalMode) {
426 | if (scalMode < ZOOMMIN || scalMode > ZOOMMAX || scalMode == currentTimebarTickCriterionIndex)
427 | return;
428 |
429 | switch (scalMode) {
430 | case 0:
431 | setCurrentTimebarTickCriterionIndex(0);
432 | int newWidth = timebarTickCriterionMap.get(0).getViewLength();
433 | justScaledByPressingButton = true;
434 | ViewGroup.LayoutParams params = getLayoutParams();
435 | params.width = newWidth;
436 | setLayoutParams(params);
437 | break;
438 | case 1:
439 | setCurrentTimebarTickCriterionIndex(1);
440 | int newWidth1 = timebarTickCriterionMap.get(1).getViewLength();
441 | justScaledByPressingButton = true;
442 | ViewGroup.LayoutParams params1 = getLayoutParams();
443 | params1.width = newWidth1;
444 | setLayoutParams(params1);
445 | break;
446 | case 2:
447 | setCurrentTimebarTickCriterionIndex(2);
448 | int newWidth2 = timebarTickCriterionMap.get(2).getViewLength();
449 | justScaledByPressingButton = true;
450 | ViewGroup.LayoutParams params2 = getLayoutParams();
451 | params2.width = newWidth2;
452 | setLayoutParams(params2);
453 | break;
454 | case 3:
455 | setCurrentTimebarTickCriterionIndex(3);
456 | int newWidth3 = timebarTickCriterionMap.get(3).getViewLength();
457 | justScaledByPressingButton = true;
458 | ViewGroup.LayoutParams params3 = getLayoutParams();
459 | params3.width = newWidth3;
460 | setLayoutParams(params3);
461 | break;
462 | case 4:
463 | setCurrentTimebarTickCriterionIndex(4);
464 | int newWidth4 = timebarTickCriterionMap.get(4).getViewLength();
465 | justScaledByPressingButton = true;
466 | ViewGroup.LayoutParams params4 = getLayoutParams();
467 | params4.width = newWidth4;
468 | setLayoutParams(params4);
469 | break;
470 |
471 | }
472 | }
473 |
474 | private float getAverageWidthForTwoCriterion(int criterion1Index, int criterion2Index) {
475 | int width1 = timebarTickCriterionMap.get(criterion1Index).getViewLength();
476 | int width2 = timebarTickCriterionMap.get(criterion2Index).getViewLength();
477 | return (width1 + width2) / 2;
478 | }
479 |
480 |
481 | private void initTimebarTickCriterionMap() {
482 | TimebarTickCriterion t0 = new TimebarTickCriterion();
483 | t0.setTotalSecondsInOneScreen(10 * 60);
484 | t0.setKeyTickInSecond(1 * 60);
485 | t0.setMinTickInSecond(6);
486 | t0.setDataPattern("HH:mm");
487 | t0.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t0.getTotalSecondsInOneScreen()));
488 | timebarTickCriterionMap.put(0, t0);
489 |
490 | /*TimebarTickCriterion t1 = new TimebarTickCriterion();
491 | t1.setTotalSecondsInOneScreen(60 * 60);
492 | t1.setKeyTickInSecond(10 * 60);
493 | t1.setMinTickInSecond(60);
494 | t1.setDataPattern("HH:mm");
495 | t1.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t1.getTotalSecondsInOneScreen()));
496 | timebarTickCriterionMap.put(1, t1);*/
497 |
498 | TimebarTickCriterion t1 = new TimebarTickCriterion();
499 | t1.setTotalSecondsInOneScreen(6 * 60);
500 | t1.setKeyTickInSecond(60);
501 | t1.setMinTickInSecond(6);
502 | t1.setDataPattern("HH:mm");
503 | t1.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t1.getTotalSecondsInOneScreen()));
504 | timebarTickCriterionMap.put(1, t1);
505 |
506 | /*TimebarTickCriterion t2 = new TimebarTickCriterion();
507 | t2.setTotalSecondsInOneScreen(6 * 60 * 60);
508 | t2.setKeyTickInSecond(60 * 60);
509 | t2.setMinTickInSecond(5 * 60);
510 | t2.setDataPattern("HH:mm");
511 | t2.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t2.getTotalSecondsInOneScreen()));
512 | timebarTickCriterionMap.put(2, t2);*/
513 | TimebarTickCriterion t2 = new TimebarTickCriterion();
514 | t2.setTotalSecondsInOneScreen(1 * 60 * 60);
515 | t2.setKeyTickInSecond(10 * 60);
516 | t2.setMinTickInSecond(1 * 60);
517 | t2.setDataPattern("HH:mm");
518 | t2.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t2.getTotalSecondsInOneScreen()));
519 | timebarTickCriterionMap.put(2, t2);
520 |
521 | /* TimebarTickCriterion t3 = new TimebarTickCriterion();
522 | t3.setTotalSecondsInOneScreen(36 * 60 * 60);
523 | t3.setKeyTickInSecond(6 * 60 * 60);
524 | t3.setMinTickInSecond(30 * 60);
525 | t3.setDataPattern("HH:mm");
526 | t3.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t3.getTotalSecondsInOneScreen()));
527 | timebarTickCriterionMap.put(3, t3);*/
528 |
529 | TimebarTickCriterion t3 = new TimebarTickCriterion();
530 | t3.setTotalSecondsInOneScreen(30 * 60 * 60);
531 | t3.setKeyTickInSecond(6 * 60 * 60);
532 | t3.setMinTickInSecond(60 * 60);
533 | t3.setDataPattern("HH:mm");
534 | t3.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t3.getTotalSecondsInOneScreen()));
535 | timebarTickCriterionMap.put(3, t3);
536 |
537 | TimebarTickCriterion t4 = new TimebarTickCriterion();
538 | t4.setTotalSecondsInOneScreen(6 * 24 * 60 * 60);
539 | t4.setKeyTickInSecond(24 * 60 * 60);
540 | t4.setMinTickInSecond(2 * 60 * 60);
541 | t4.setDataPattern("MM.dd");
542 | // t4.dataPattern = "MM.dd HH:mm:ss";
543 | t4.setViewLength((int) ((float) screenWidth * WHOLE_TIMEBAR_TOTAL_SECONDS / (float) t4.getTotalSecondsInOneScreen()));
544 | timebarTickCriterionMap.put(4, t4);
545 |
546 | timebarTickCriterionCount = timebarTickCriterionMap.size();
547 | }
548 |
549 |
550 | private void resetToStandardWidth() {
551 | setCurrentTimebarTickCriterionIndex(3);
552 | ViewGroup.LayoutParams params = getLayoutParams();
553 | params.width = timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getViewLength();
554 | setLayoutParams(params);
555 | }
556 |
557 |
558 | public long getCurrentTimeInMillisecond() {
559 | return currentTimeInMillisecond;
560 | }
561 |
562 | public void setCurrentTimeInMillisecond(long currentTimeInMillisecond) {
563 | this.currentTimeInMillisecond = currentTimeInMillisecond;
564 | invalidate();
565 | }
566 |
567 | @Override
568 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
569 |
570 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
571 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
572 | if (heightMode == MeasureSpec.AT_MOST) {
573 | VIEW_HEIGHT = DeviceUtil.dip2px(VIEW_HEIGHT_IN_DP);
574 | } else {
575 | VIEW_HEIGHT = heightSize;
576 | }
577 |
578 | setMeasuredDimension(measureWidth(widthMeasureSpec), VIEW_HEIGHT);
579 |
580 | if (justScaledByPressingButton && mOnBarScaledListener != null) {
581 | justScaledByPressingButton = false;
582 | mOnBarScaledListener.onBarScaleFinish(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), currentTimeInMillisecond);
583 | }
584 |
585 |
586 | }
587 |
588 | @Override
589 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
590 | super.onSizeChanged(w, h, oldw, oldh);
591 | Log.d("onSizeChanged", " w:" + w + " h:" + h + " oldw:" + oldh + " w:" + oldh);
592 | }
593 |
594 | @Override
595 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
596 | Log.d("onLayout", "changed:" + changed + " left:" + left + " top:" + top + " right:" + right + " bottom:" + bottom);
597 |
598 | /* if (currentTimeInMillisecond != System.currentTimeMillis() && left == 0)
599 | layout((int) (0 - (currentTimeInMillisecond - mostLeftTimeInMillisecond) / 1000 * pixelsPerSecond),
600 | getTop(),
601 | getWidth() - (int) ((currentTimeInMillisecond - mostLeftTimeInMillisecond) / 1000 * pixelsPerSecond),
602 | getTop() + getHeight());*/
603 | super.onLayout(changed, left, top, right, bottom);
604 |
605 | }
606 |
607 | private int measureWidth(int widthMeasureSpec) {
608 | int measureMode = MeasureSpec.getMode(widthMeasureSpec);
609 | int measureSize = MeasureSpec.getSize(widthMeasureSpec);
610 | int result = getSuggestedMinimumWidth();
611 | switch (measureMode) {
612 | case MeasureSpec.AT_MOST:
613 | case MeasureSpec.EXACTLY:
614 | result = measureSize + screenWidth;
615 | pixelsPerSecond = measureSize / (float) WHOLE_TIMEBAR_TOTAL_SECONDS;
616 | if (mOnBarScaledListener != null) {
617 | mOnBarScaledListener.onBarScaled(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), currentTimeInMillisecond);
618 | }
619 | break;
620 | default:
621 | break;
622 | }
623 | Log.d("measureWidth", "measureMode:" + measureMode + "measureSize:" + measureSize + " result" + result);
624 | return result;
625 | }
626 |
627 |
628 | private String getTimeStringFromLong(long value) {
629 | SimpleDateFormat timeFormat = new SimpleDateFormat(timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getDataPattern());
630 | return timeFormat.format(value);
631 | }
632 |
633 |
634 | public void setMiddleCursorVisible(boolean middleCursorVisible) {
635 | this.middleCursorVisible = middleCursorVisible;
636 | invalidate();
637 | }
638 |
639 | int lastMmiddlecursor = 0;
640 | long firstTickToSeeInSecondUTC = -1;
641 | int zoneOffsetInSeconds;
642 |
643 | @Override
644 | protected void onDraw(Canvas canvas) {
645 | super.onDraw(canvas);
646 |
647 |
648 | /*if (notInited) {
649 | notInited = false;
650 | resetToStandardWidth();
651 | return;
652 | }*/
653 |
654 | Log.d("onDraw", "onDraw");
655 |
656 | pixelsPerSecond = (float) (getWidth() - screenWidth) / (float) WHOLE_TIMEBAR_TOTAL_SECONDS;
657 |
658 |
659 | Calendar cal = Calendar.getInstance();
660 | zoneOffsetInSeconds = cal.get(Calendar.ZONE_OFFSET) / 1000;
661 | long forStartUTC = (long) (currentTimeInMillisecond / 1000 - screenWidth / pixelsPerSecond / 2 - timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond());
662 | long forEndUTC = (long) (currentTimeInMillisecond / 1000 + screenWidth / pixelsPerSecond / 2 + timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond());
663 |
664 | long forStartLocalTimezone = forStartUTC + zoneOffsetInSeconds;
665 | long forEndLocalTimezone = forEndUTC + zoneOffsetInSeconds;
666 |
667 |
668 | for (long i = forStartLocalTimezone; i <= forEndLocalTimezone; i++) {
669 | if (i % timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond() == 0) {
670 | firstTickToSeeInSecondUTC = i - zoneOffsetInSeconds;
671 | break;
672 |
673 | }
674 | }
675 |
676 |
677 | // 画刻度及时间
678 | drawTick(canvas);
679 |
680 | // 画录像条
681 | drawRecord(canvas);
682 |
683 |
684 | // 画中间刻度
685 | drawmiddleCursor(canvas);
686 |
687 | layout((int) (0 - (currentTimeInMillisecond - mostLeftTimeInMillisecond) / 1000 * pixelsPerSecond),
688 | getTop(),
689 | getWidth() - (int) ((currentTimeInMillisecond - mostLeftTimeInMillisecond) / 1000 * pixelsPerSecond),
690 | getTop() + getHeight());
691 |
692 |
693 | }
694 |
695 | private void drawTick(Canvas canvas) {
696 | int totalTickToDrawInOneScreen = (int) (screenWidth / pixelsPerSecond / timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond()) + 2;
697 | float keytextY = getHeight() / 2;
698 | for (int i = -20; i <= totalTickToDrawInOneScreen + 10; i++) {
699 | long drawTickTimeInSecondUTC = firstTickToSeeInSecondUTC + i * timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond();
700 | long drawTickTimeInSecondLocalTimezone = drawTickTimeInSecondUTC + zoneOffsetInSeconds;
701 |
702 | if (drawTickTimeInSecondLocalTimezone % timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getKeyTickInSecond() == 0) {//关键刻度
703 | //画大刻度
704 | timebarPaint.setColor(linesColor);
705 | timebarPaint.setAntiAlias(true);
706 | timebarPaint.setStyle(Paint.Style.FILL);
707 | float startX = pixelsPerSecond * (drawTickTimeInSecondUTC - mostLeftTimeInMillisecond / 1000) + screenWidth / 2f;
708 | RectF largeTickRect = new RectF(startX - BIG_TICK_HALF_WIDTH / 2, getHeight() - BIG_TICK_HEIGHT, (startX + BIG_TICK_HALF_WIDTH / 2), getHeight());
709 | canvas.drawRect(largeTickRect, timebarPaint);
710 | RectF largeTickRect1 = new RectF(startX - BIG_TICK_HALF_WIDTH / 2, 0, (startX + BIG_TICK_HALF_WIDTH / 2), BIG_TICK_HEIGHT);
711 | canvas.drawRect(largeTickRect1, timebarPaint);
712 |
713 | //画时间文字
714 | String keytext = getTimeStringFromLong(drawTickTimeInSecondUTC * 1000);
715 | float keyTextWidth = keyTickTextPaint.measureText(keytext);
716 | float keytextX = startX - keyTextWidth / 2;
717 | canvas.drawText(keytext, keytextX, keytextY, keyTickTextPaint);
718 | } else if (drawTickTimeInSecondLocalTimezone % timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond() == 0) {
719 | //小刻度
720 | timebarPaint.setColor(linesColor);
721 | timebarPaint.setAntiAlias(true);
722 | timebarPaint.setStyle(Paint.Style.FILL);
723 | float startX = pixelsPerSecond * (drawTickTimeInSecondUTC - mostLeftTimeInMillisecond / 1000) + screenWidth / 2f;
724 | RectF smallTickRect = new RectF(startX - SMALL_TICK_HALF_WIDTH / 2, getHeight() - SMALL_TICK_HEIGHT, (startX + SMALL_TICK_HALF_WIDTH / 2), getHeight());
725 | canvas.drawRect(smallTickRect, timebarPaint);
726 |
727 | RectF smallTickRect1 = new RectF(startX - SMALL_TICK_HALF_WIDTH / 2, 0, (startX + SMALL_TICK_HALF_WIDTH / 2), SMALL_TICK_HEIGHT);
728 | canvas.drawRect(smallTickRect1, timebarPaint);
729 | }
730 |
731 | }
732 |
733 | canvas.drawLine(0, 0, getWidth(), 0, timebarPaint);
734 | canvas.drawLine(0, VIEW_HEIGHT, getWidth(), VIEW_HEIGHT, timebarPaint);
735 | }
736 |
737 | private void drawRecord(Canvas canvas) {
738 | //录像从哪个时间点开始,单位是毫秒
739 | long startDrawTimeInSeconds = firstTickToSeeInSecondUTC + (-20) * timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond();
740 |
741 | if (recordDataExistTimeClipsList != null && recordDataExistTimeClipsList.size() > 0) {
742 |
743 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
744 | String startDrawTimeDateString = dateFormat.format(startDrawTimeInSeconds * 1000);
745 | String zeroTimeString = startDrawTimeDateString + " 00:00:00";
746 |
747 | long screenLastSecondToSee = (long) (startDrawTimeInSeconds + screenWidth / pixelsPerSecond + 30 * timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond()) * 1000L;
748 |
749 | Date startDate;
750 | try {
751 |
752 | startDate = zeroTimeFormat.parse(zeroTimeString);
753 | List startList = recordDataExistTimeClipsListMap.get(startDate.getTime());
754 | if (startList == null) {
755 | int afterFindDays = 1;
756 | long findTimeInMilliseconds = startDate.getTime();
757 | long newFindStartMilliseconds = findTimeInMilliseconds;
758 | while (startList == null && newFindStartMilliseconds < screenLastSecondToSee) {
759 | newFindStartMilliseconds = findTimeInMilliseconds + (long) SECONDS_PER_DAY * 1000L * (long) afterFindDays;
760 | startList = recordDataExistTimeClipsListMap.get(newFindStartMilliseconds);
761 | afterFindDays++;
762 | }
763 | }
764 |
765 | if (startList != null && startList.size() > 0) {
766 | int thisDateFirstClipStartIndex = recordDataExistTimeClipsList.indexOf(startList.get(0));
767 |
768 | long endDrawTimeInSeconds = (long) (startDrawTimeInSeconds
769 | + screenWidth / pixelsPerSecond
770 | + timebarTickCriterionMap.get(currentTimebarTickCriterionIndex).getMinTickInSecond() * 30);
771 |
772 | timebarPaint.setColor(recordBackgroundColor);
773 | timebarPaint.setStyle(Paint.Style.FILL);
774 |
775 | for (int i = thisDateFirstClipStartIndex; i < recordDataExistTimeClipsList.size(); i++) {
776 | float leftX = pixelsPerSecond * (recordDataExistTimeClipsList.get(i).getStartTimeInMillisecond() - mostLeftTimeInMillisecond) / 1000 + screenWidth / 2f;
777 | float rightX = pixelsPerSecond * (recordDataExistTimeClipsList.get(i).getEndTimeInMillisecond() - mostLeftTimeInMillisecond) / 1000 + screenWidth / 2f;
778 | RectF rectF = new RectF(leftX, 0, rightX, getHeight());
779 | canvas.drawRect(rectF, timebarPaint);
780 | if (recordDataExistTimeClipsList.get(i).getEndTimeInMillisecond() >= endDrawTimeInSeconds * 1000) {
781 | break;
782 | }
783 | }
784 | }
785 |
786 |
787 | } catch (ParseException e) {
788 | e.printStackTrace();
789 | }
790 | }
791 | }
792 |
793 |
794 | private void drawmiddleCursor(Canvas canvas) {
795 | if (middleCursorVisible) {
796 | timebarPaint.setStyle(Paint.Style.FILL);
797 | timebarPaint.setColor(middleCursorColor);
798 | int currentCursor = (int) ((currentTimeInMillisecond / 1000L - mostLeftTimeInMillisecond / 1000L) * pixelsPerSecond + screenWidth / 2f - TRIANGLE_LENGTH / 2);
799 | lastMmiddlecursor = currentCursor;
800 | // Log.d("TIMEBARVIEW", "currentCursor" + currentCursor + " viewWidth:" + getWidth());
801 | //path.rMoveTo(currentCursor, 0);
802 | // 画三角形
803 | path = new Path();
804 | path.moveTo(currentCursor, 0);
805 | path.lineTo(currentCursor + TRIANGLE_LENGTH, 0);
806 | // 求三角形高
807 | float length = (float) Math.sqrt(3d) * TRIANGLE_LENGTH / 2;
808 | path.lineTo(currentCursor + TRIANGLE_LENGTH / 2, length);
809 | path.lineTo(currentCursor, 0);
810 | canvas.drawPath(path, timebarPaint);
811 | // 画三角形下面的线条
812 | canvas.drawLine(currentCursor + TRIANGLE_LENGTH / 2, 0, currentCursor + TRIANGLE_LENGTH / 2, VIEW_HEIGHT, timebarPaint);
813 | }
814 | }
815 |
816 |
817 | float lastX, lastY;
818 |
819 | private int mode = NONE;
820 | private static final int NONE = 0;
821 | private static final int DRAG = 1;
822 | private static final int ZOOM = 2;
823 | long lastcurrentTimeInMillisecond = 0;
824 | boolean lastMoveState;
825 | boolean lastCheckState;
826 |
827 | @Override
828 | public boolean onTouchEvent(MotionEvent event) {
829 |
830 | scaleGestureDetector.onTouchEvent(event);
831 |
832 | if (scaleGestureDetector.isInProgress()) {
833 | return true;
834 | }
835 | switch (event.getAction() & MotionEvent.ACTION_MASK) {
836 | case MotionEvent.ACTION_DOWN:
837 |
838 | if (handler.hasMessages(ACTION_UP))
839 | handler.removeMessages(ACTION_UP);
840 |
841 | // 先记录进度条移动状态 如果进度条正在移动 先停止
842 | lastMoveState = moveFlag;
843 | lastCheckState = checkVideo;
844 | checkVideo = readyCheck;
845 | closeMove();
846 | lastcurrentTimeInMillisecond = currentTimeInMillisecond;
847 | mode = DRAG;
848 | lastX = event.getRawX();
849 | lastY = event.getRawY();
850 | break;
851 | case MotionEvent.ACTION_POINTER_DOWN:
852 | mode = ZOOM;
853 | break;
854 | case MotionEvent.ACTION_MOVE:
855 | if (mode == DRAG && mDrag) {
856 | int dx = (int) (event.getRawX() - lastX);
857 | if (dx == 0) {
858 | return false;
859 | }
860 | int top = getTop();
861 | Log.d("*****onTouchEvent", " dx" + dx + " left" + getLeft() + " right" + getLeft() + getWidth());
862 | int left = getLeft() + dx;
863 | int right = left + getWidth();
864 |
865 | if (left >= 0) {
866 | left = 0;
867 | right = getWidth();
868 | }
869 |
870 | if (right < screenWidth) {
871 | right = screenWidth;
872 | left = right - getWidth();
873 | }
874 | layout(left, top, right, top + getHeight());
875 | invalidate();
876 |
877 | lastX = event.getRawX();
878 | lastY = event.getRawY();
879 |
880 | int deltaX = (0 - left);
881 | int timeBarLength = getWidth() - screenWidth;
882 | currentTimeInMillisecond = mostLeftTimeInMillisecond + deltaX * WHOLE_TIMEBAR_TOTAL_SECONDS * 1000 / timeBarLength;
883 |
884 | if (mOnBarMoveListener != null) {
885 | mOnBarMoveListener.onBarMove(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), currentTimeInMillisecond);
886 | }
887 |
888 | }
889 | break;
890 | case MotionEvent.ACTION_CANCEL:
891 | currentTimeInMillisecond = lastcurrentTimeInMillisecond;
892 | checkVideo = lastCheckState;
893 | if (mOnBarMoveListener != null) {
894 | mOnBarMoveListener.onBarMove(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), currentTimeInMillisecond);
895 | }
896 | invalidate();
897 | /* if (lastMoveState) {
898 | if (handler.hasMessages(MOVEING))
899 | handler.removeMessages(MOVEING);
900 | handler.sendEmptyMessageDelayed(MOVEING, 1100);
901 | }*/
902 | mode = NONE;
903 | break;
904 | case MotionEvent.ACTION_UP:
905 | if (mode == DRAG) {
906 | int deltaX_up = (0 - getLeft());
907 | int timeBarLength_up = getWidth() - screenWidth;
908 | currentTimeInMillisecond = mostLeftTimeInMillisecond + deltaX_up * WHOLE_TIMEBAR_TOTAL_SECONDS * 1000 / timeBarLength_up;
909 | //invalidate();
910 | if (handler.hasMessages(ACTION_UP))
911 | handler.removeMessages(ACTION_UP);
912 | handler.sendEmptyMessageDelayed(ACTION_UP, 1100);
913 | /*if (lastMoveState) {
914 | if (handler.hasMessages(MOVEING))
915 | handler.removeMessages(MOVEING);
916 | handler.sendEmptyMessageDelayed(MOVEING, 1100);
917 | }*/
918 |
919 | }
920 | mode = NONE;
921 | break;
922 | }
923 |
924 |
925 | return true;
926 | }
927 |
928 | public void scaleByPressingButton(boolean zoomIn) {
929 |
930 | //当前所在刻度标准的默认长度(不含两端空出的screenWidth)
931 | int currentCriterionViewLength = timebarTickCriterionMap.get(getCurrentTimebarTickCriterionIndex()).getViewLength();
932 |
933 | int currentViewLength = getWidth() - screenWidth;
934 |
935 | if (currentViewLength == currentCriterionViewLength) {
936 | if (zoomIn) {
937 | int newCriteriaIndex = getCurrentTimebarTickCriterionIndex() - 1;
938 | if (newCriteriaIndex < ZOOMMIN || newCriteriaIndex > ZOOMMAX) {
939 | return;
940 | } else {
941 | setCurrentTimebarTickCriterionIndex(newCriteriaIndex);
942 | int newWidth = timebarTickCriterionMap.get(newCriteriaIndex).getViewLength();
943 | justScaledByPressingButton = true;
944 |
945 | ViewGroup.LayoutParams params = getLayoutParams();
946 | params.width = newWidth;
947 | setLayoutParams(params);
948 | }
949 | } else {
950 | int newCriteriaIndex = getCurrentTimebarTickCriterionIndex() + 1;
951 | // Log.d("newCriteriaIndex", newCriteriaIndex + "");
952 | if (newCriteriaIndex > ZOOMMAX || newCriteriaIndex >= timebarTickCriterionCount) {
953 | return;
954 | } else {
955 | setCurrentTimebarTickCriterionIndex(newCriteriaIndex);
956 | int newWidth = timebarTickCriterionMap.get(newCriteriaIndex).getViewLength();
957 | justScaledByPressingButton = true;
958 |
959 | ViewGroup.LayoutParams params = getLayoutParams();
960 | params.width = newWidth;
961 | setLayoutParams(params);
962 | }
963 | }
964 | } else {
965 | if (currentViewLength > currentCriterionViewLength) {
966 |
967 | if (zoomIn) {
968 | int newCriteriaIndex = getCurrentTimebarTickCriterionIndex() - 1;
969 | if (newCriteriaIndex < 0) {
970 | return;
971 | } else {
972 | setCurrentTimebarTickCriterionIndex(newCriteriaIndex);
973 | int newWidth = timebarTickCriterionMap.get(newCriteriaIndex).getViewLength();
974 | justScaledByPressingButton = true;
975 |
976 | ViewGroup.LayoutParams params = getLayoutParams();
977 | params.width = newWidth;
978 | setLayoutParams(params);
979 | }
980 | } else {
981 | int newWidth = timebarTickCriterionMap.get(getCurrentTimebarTickCriterionIndex()).getViewLength();
982 | justScaledByPressingButton = true;
983 |
984 | ViewGroup.LayoutParams params = getLayoutParams();
985 | params.width = newWidth;
986 | setLayoutParams(params);
987 | }
988 |
989 | } else {
990 |
991 | if (zoomIn) {
992 | int newWidth = timebarTickCriterionMap.get(getCurrentTimebarTickCriterionIndex()).getViewLength();
993 | justScaledByPressingButton = true;
994 |
995 | ViewGroup.LayoutParams params = getLayoutParams();
996 | params.width = newWidth;
997 | setLayoutParams(params);
998 |
999 |
1000 | } else {
1001 | int newCriteriaIndex = getCurrentTimebarTickCriterionIndex() + 1;
1002 | if (newCriteriaIndex >= timebarTickCriterionCount) {
1003 | return;
1004 | } else {
1005 | setCurrentTimebarTickCriterionIndex(newCriteriaIndex);
1006 |
1007 | int newWidth = timebarTickCriterionMap.get(newCriteriaIndex).getViewLength();
1008 | justScaledByPressingButton = true;
1009 |
1010 | ViewGroup.LayoutParams params = getLayoutParams();
1011 | params.width = newWidth;
1012 | setLayoutParams(params);
1013 | }
1014 | }
1015 |
1016 | }
1017 | }
1018 |
1019 |
1020 | }
1021 |
1022 |
1023 | public interface OnBarMoveListener {
1024 |
1025 | void onBarMove(long screenLeftTime, long screenRightTime, long currentTime);
1026 |
1027 | void OnBarMoveFinish(long screenLeftTime, long screenRightTime, long currentTime);
1028 | }
1029 |
1030 | public void setOnBarMoveListener(OnBarMoveListener onBarMoveListener) {
1031 | mOnBarMoveListener = onBarMoveListener;
1032 | }
1033 |
1034 | public interface OnBarScaledListener {
1035 |
1036 | void onOnBarScaledMode(int mode);
1037 |
1038 | void onBarScaled(long screenLeftTime, long screenRightTime, long currentTime);
1039 |
1040 |
1041 | void onBarScaleFinish(long screenLeftTime, long screenRightTime, long currentTime);
1042 | }
1043 |
1044 | public void setOnBarScaledListener(OnBarScaledListener onBarScaledListener) {
1045 | mOnBarScaledListener = onBarScaledListener;
1046 | }
1047 |
1048 | // 设置进度条是否自动滚动
1049 | private boolean moveFlag = false;
1050 | // 进度条滚动状态
1051 | private boolean moveIng = false;
1052 | // 是否检查录像标志位
1053 | private boolean checkVideo = false;
1054 |
1055 | private MoveThread moThread;
1056 |
1057 | private class MoveThread extends Thread {
1058 | @Override
1059 | public void run() {
1060 | Log.d("MOVETHREAD", "thread is start");
1061 | moveIng = true;
1062 | while (moveFlag) {
1063 | try {
1064 | Thread.sleep(1000);
1065 | Log.d("MOVETHREAD", "thread is running");
1066 | currentTimeInMillisecond += 1000;
1067 | if (checkVideo) {
1068 | if (!checkHasVideo()) {
1069 | long nextStartTime = locationVideo();
1070 | if (nextStartTime != -1) {
1071 | currentTimeInMillisecond = nextStartTime;
1072 | } else {
1073 | currentTimeInMillisecond -= 1000;
1074 | moveFlag = false;
1075 | moveIng = false;
1076 | break;
1077 | }
1078 | }
1079 | }
1080 | postInvalidate();
1081 | post(new Runnable() {
1082 | @Override
1083 | public void run() {
1084 | if (mOnBarMoveListener != null) {
1085 | mOnBarMoveListener.onBarMove(getScreenLeftTimeInMillisecond(), getScreenRightTimeInMillisecond(), currentTimeInMillisecond);
1086 | }
1087 | }
1088 | });
1089 | } catch (InterruptedException e) {
1090 | moveIng = false;
1091 | e.printStackTrace();
1092 | }
1093 | }
1094 | moveIng = false;
1095 | Log.d("MOVETHREAD", "thread is stop");
1096 | }
1097 | }
1098 |
1099 |
1100 | public void openMove() {
1101 | if (!moveIng) {
1102 | moveFlag = true;
1103 | moThread = null;
1104 | moThread = new MoveThread();
1105 | moThread.start();
1106 | }
1107 | }
1108 |
1109 | public void closeMove() {
1110 | moveFlag = false;
1111 | moThread = null;
1112 | }
1113 |
1114 | public boolean isMoveing() {
1115 | return moveFlag;
1116 | }
1117 |
1118 | public void setMoveFlag(boolean moveFlag) {
1119 | this.moveFlag = moveFlag;
1120 | }
1121 |
1122 | private boolean readyCheck = false;
1123 |
1124 | /*
1125 | *
1126 | * 设置是否检查有录像
1127 | *
1128 | * */
1129 | public void checkVideo(boolean check) {
1130 | readyCheck = check;
1131 | }
1132 |
1133 | /*
1134 | * 返回下一个录像开始点
1135 | * */
1136 | private long locationVideo() {
1137 | if (recordDataExistTimeClipsList == null)
1138 | return -1;
1139 | int size = recordDataExistTimeClipsList.size();
1140 | for (int i = 0; i < size - 1; i++) {
1141 | long lastEndTime = recordDataExistTimeClipsList.get(i).getEndTimeInMillisecond();
1142 | long nextStartTime = recordDataExistTimeClipsList.get(i + 1).getStartTimeInMillisecond();
1143 | if (currentTimeInMillisecond > lastEndTime && currentTimeInMillisecond < nextStartTime) {
1144 | return nextStartTime;
1145 | }
1146 | }
1147 | return -1;
1148 | }
1149 |
1150 | /*判断是否有录像*/
1151 | private boolean checkHasVideo() {
1152 | if (recordDataExistTimeClipsList != null && recordDataExistTimeClipsList.size() > 0) {
1153 | for (RecordDataExistTimeSegment recordInfo : recordDataExistTimeClipsList) {
1154 | if (recordInfo.getStartTimeInMillisecond() <= currentTimeInMillisecond
1155 | && currentTimeInMillisecond <= recordInfo.getEndTimeInMillisecond())
1156 | return true;
1157 | }
1158 | }
1159 | return false;
1160 | }
1161 |
1162 | public void recycle() {
1163 | closeMove();
1164 | if (recordDataExistTimeClipsList != null) {
1165 | recordDataExistTimeClipsList.clear();
1166 | recordDataExistTimeClipsList = null;
1167 | }
1168 | if (recordDataExistTimeClipsListMap != null) {
1169 | recordDataExistTimeClipsListMap.clear();
1170 | recordDataExistTimeClipsListMap = null;
1171 | }
1172 | mOnBarMoveListener = null;
1173 | mOnBarScaledListener = null;
1174 | timebarPaint = null;
1175 | scaleGestureDetector = null;
1176 | }
1177 |
1178 | public int getIdTag() {
1179 | return idTag;
1180 | }
1181 |
1182 | public void setIdTag(int idTag) {
1183 | this.idTag = idTag;
1184 | }
1185 |
1186 | private boolean mDrag = true;
1187 |
1188 | // 设置是否允许拖动
1189 | public void setDrag(boolean mDrag) {
1190 | this.mDrag = mDrag;
1191 | }
1192 | }
1193 |
1194 |
--------------------------------------------------------------------------------
/timerulerslib/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 |
--------------------------------------------------------------------------------
/timerulerslib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TimeRulersLib
3 |
4 |
--------------------------------------------------------------------------------