├── .gitattributes
├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── dictionaries
│ └── wangchenlong.xml
├── encodings.xml
├── gradle.xml
├── libraries
│ ├── animated_vector_drawable_25_1_0.xml
│ ├── appcompat_v7_25_1_0.xml
│ ├── espresso_core_2_2_2.xml
│ ├── espresso_idling_resource_2_2_2.xml
│ ├── exposed_instrumentation_api_publish_0_5.xml
│ ├── hamcrest_core_1_3.xml
│ ├── hamcrest_integration_1_3.xml
│ ├── hamcrest_library_1_3.xml
│ ├── javawriter_2_1_1.xml
│ ├── javax_annotation_api_1_2.xml
│ ├── javax_inject_1.xml
│ ├── jsr305_2_0_1.xml
│ ├── junit_4_12.xml
│ ├── rules_0_5.xml
│ ├── runner_0_5.xml
│ ├── support_annotations_25_1_0.xml
│ ├── support_compat_25_1_0.xml
│ ├── support_core_ui_25_1_0.xml
│ ├── support_core_utils_25_1_0.xml
│ ├── support_fragment_25_1_0.xml
│ ├── support_media_compat_25_1_0.xml
│ ├── support_v4_25_1_0.xml
│ └── support_vector_drawable_25_1_0.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
├── vcs.xml
└── workspace.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── org
│ │ └── wangchenlong
│ │ └── datescroller
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── org
│ │ │ └── wangchenlong
│ │ │ └── datescroller
│ │ │ ├── MainActivity.java
│ │ │ └── widget
│ │ │ ├── DateScrollerDialog.java
│ │ │ ├── TimeWheel.java
│ │ │ ├── adapters
│ │ │ ├── AbstractWheelAdapter.java
│ │ │ ├── AbstractWheelTextAdapter.java
│ │ │ ├── NumericWheelAdapter.java
│ │ │ └── WheelViewAdapter.java
│ │ │ ├── config
│ │ │ ├── DefaultConfig.java
│ │ │ └── ScrollerConfig.java
│ │ │ ├── data
│ │ │ ├── Type.java
│ │ │ ├── WheelCalendar.java
│ │ │ └── source
│ │ │ │ ├── TimeDataSource.java
│ │ │ │ └── TimeRepository.java
│ │ │ ├── listener
│ │ │ └── OnDateSetListener.java
│ │ │ ├── utils
│ │ │ ├── DateConstants.java
│ │ │ └── Utils.java
│ │ │ └── wheel
│ │ │ ├── ItemsRange.java
│ │ │ ├── OnWheelChangedListener.java
│ │ │ ├── OnWheelClickedListener.java
│ │ │ ├── OnWheelScrollListener.java
│ │ │ ├── WheelRecycle.java
│ │ │ ├── WheelScroller.java
│ │ │ └── WheelView.java
│ └── res
│ │ ├── anim
│ │ ├── slide_in_bottom.xml
│ │ └── slide_out_bottom.xml
│ │ ├── drawable
│ │ ├── date_wheel_bg.xml
│ │ ├── timepicker_divider_line.xml
│ │ └── timepicker_sel_text_item.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── time_scroller_toolbar.xml
│ │ ├── timepicker_layout.xml
│ │ ├── timepicker_line.xml
│ │ └── tv_item.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── org
│ └── wangchenlong
│ └── datescroller
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── 效果图.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.apk
2 | *.ap_
3 |
4 | # Files for the Dalvik VM
5 | *.dex
6 |
7 | # Java class files
8 | *.class
9 |
10 | # Generated files
11 | bin/
12 | gen/
13 | out/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/
33 |
34 | # Intellij
35 | *.iml
36 |
37 | # Keystore files
38 | *.jks
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/dictionaries/wangchenlong.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | dataset
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/libraries/animated_vector_drawable_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/appcompat_v7_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/espresso_core_2_2_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/espresso_idling_resource_2_2_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/exposed_instrumentation_api_publish_0_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/hamcrest_integration_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/hamcrest_library_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/javawriter_2_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/javax_annotation_api_1_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/javax_inject_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/jsr305_2_0_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/junit_4_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/rules_0_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/runner_0_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/support_annotations_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/support_compat_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/support_core_ui_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/support_core_utils_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/support_fragment_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/support_media_compat_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/libraries/support_v4_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/libraries/support_vector_drawable_25_1_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.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 | Android
46 |
47 |
48 | Android Lint
49 |
50 |
51 | Java
52 |
53 |
54 | Java language level migration aidsJava
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 | 1.8
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.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 | # DateRangePicker
2 | 自定义的时间双滚轮控件,可以选择时间范围,结束时间会自动调整到开始时间之后。
3 |
4 | ## 效果图
5 |
6 | 
7 |
8 | ## 参考
9 | [**探索日期滚轮控件的源码**](https://juejin.im/post/5858a8f3b123db00658b1191) \
10 | [**DateScroller**](https://github.com/SpikeKing/DateScroller)
11 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 | defaultConfig {
7 | applicationId "org.wangchenlong.datescroller"
8 | minSdkVersion 16
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(dir: 'libs', include: ['*.jar'])
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.1.0'
28 | testCompile 'junit:junit:4.12'
29 | }
30 |
--------------------------------------------------------------------------------
/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/wangchenlong/Installations/android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/wangchenlong/datescroller/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller;
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("org.wangchenlong.datescroller", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/MainActivity.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import org.wangchenlong.datescroller.widget.DateScrollerDialog;
9 | import org.wangchenlong.datescroller.widget.data.Type;
10 | import org.wangchenlong.datescroller.widget.listener.OnDateSetListener;
11 |
12 | import java.text.SimpleDateFormat;
13 | import java.util.Date;
14 | import java.util.Locale;
15 |
16 | public class MainActivity extends AppCompatActivity {
17 | private static final long HUNDRED_YEARS = 100L * 365 * 1000 * 60 * 60 * 24L; // 100年
18 |
19 | private TextView mTvTime;
20 | private SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
21 | private long mLastTime = System.currentTimeMillis(); // 上次设置的开始时间
22 | private long mLastFinishTime = System.currentTimeMillis(); // 上次设置的结束时间
23 |
24 | // 数据的回调
25 | private OnDateSetListener mOnDateSetListener = new OnDateSetListener() {
26 | @Override public void onDateSet(DateScrollerDialog timePickerView, long milliseconds, long milliFinishseconds) {
27 | mLastTime = milliseconds;
28 | mLastFinishTime = milliFinishseconds;
29 | String text = getDateToString(milliseconds);
30 | String text2 = getDateToString(milliFinishseconds);
31 | mTvTime.setText(text + "-" + text2);
32 | }
33 | };
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 | setContentView(R.layout.activity_main);
39 | initView(); // 初始化View
40 | }
41 |
42 | /**
43 | * 显示日期
44 | *
45 | * @param view 视图
46 | */
47 | public void showDate(View view) {
48 | // 选择日期
49 | DateScrollerDialog dialog = new DateScrollerDialog.Builder()
50 | .setType(Type.YEAR_MONTH_DAY)
51 | .setTitleStringId("请选择日期")
52 | .setMinMilliseconds(System.currentTimeMillis() - HUNDRED_YEARS)
53 | .setMaxMilliseconds(System.currentTimeMillis() + HUNDRED_YEARS)
54 | // 上次设置的开始时间,结束时间
55 | .setCurMilliseconds(mLastTime, mLastFinishTime)
56 | .setCallback(mOnDateSetListener)
57 | .build();
58 |
59 | if (dialog != null) {
60 | if (!dialog.isAdded()) {
61 | dialog.show(getSupportFragmentManager(), "year_month_day");
62 | }
63 | }
64 | }
65 |
66 | // 初始化视图
67 | private void initView() {
68 | mTvTime = (TextView) findViewById(R.id.tv_time);
69 | }
70 |
71 | public String getDateToString(long time) {
72 | Date d = new Date(time);
73 | return sf.format(d);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/DateScrollerDialog.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget;
2 |
3 | import android.app.Dialog;
4 | import android.os.Bundle;
5 | import android.support.annotation.ColorRes;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.app.DialogFragment;
9 | import android.view.Gravity;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.Window;
14 | import android.view.WindowManager;
15 | import android.widget.TextView;
16 |
17 | import org.wangchenlong.datescroller.R;
18 | import org.wangchenlong.datescroller.widget.config.ScrollerConfig;
19 | import org.wangchenlong.datescroller.widget.data.Type;
20 | import org.wangchenlong.datescroller.widget.data.WheelCalendar;
21 | import org.wangchenlong.datescroller.widget.listener.OnDateSetListener;
22 |
23 | import java.util.Calendar;
24 |
25 | /**
26 | * 时间选择框, 设置若干参数
27 | *
28 | * @author C.L.Wang
29 | */
30 | public class DateScrollerDialog extends DialogFragment implements View.OnClickListener {
31 | private ScrollerConfig mScrollerConfig;
32 | private TimeWheel mTimeWheel;
33 | private long mCurrentMilliseconds;
34 | private long mCurrentFinishMilliseconds;
35 |
36 | // 实例化参数, 传入数据
37 | private static DateScrollerDialog newInstance(ScrollerConfig scrollerConfig) {
38 | DateScrollerDialog dateScrollerDialog = new DateScrollerDialog();
39 | dateScrollerDialog.initialize(scrollerConfig);
40 | return dateScrollerDialog;
41 | }
42 |
43 | @Override
44 | public void onCreate(@Nullable Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | Window window = getActivity().getWindow();
47 | // 隐藏软键盘
48 | window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
49 | }
50 |
51 | @Override
52 | public void onResume() {
53 | super.onResume();
54 |
55 | // Dialog的位置置底
56 | Window window = getDialog().getWindow();
57 | if (window != null) {
58 | window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
59 | window.setGravity(Gravity.BOTTOM);
60 | }
61 | }
62 |
63 | /**
64 | * 初始化参数, 来源{@link #newInstance(ScrollerConfig)}
65 | *
66 | * @param scrollerConfig 滚动参数
67 | */
68 | private void initialize(ScrollerConfig scrollerConfig) {
69 | mScrollerConfig = scrollerConfig;
70 | }
71 |
72 | @NonNull @Override
73 | public Dialog onCreateDialog(Bundle savedInstanceState) {
74 | Dialog dialog = new Dialog(getActivity(), R.style.Dialog_NoTitle);
75 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
76 | dialog.setCancelable(true); // 后退键取消
77 | dialog.setCanceledOnTouchOutside(true); // 点击外面被取消
78 | dialog.setContentView(initView()); // 设置View
79 | return dialog;
80 | }
81 |
82 | /**
83 | * 初始化视图
84 | *
85 | * @return 当前视图
86 | */
87 | private View initView() {
88 | LayoutInflater inflater = LayoutInflater.from(getContext());
89 | final ViewGroup nullParent = null;
90 | View view = inflater.inflate(R.layout.timepicker_layout, nullParent);
91 |
92 | TextView cancel = (TextView) view.findViewById(R.id.tv_cancel);
93 | cancel.setOnClickListener(this); // 设置取消按钮
94 | TextView sure = (TextView) view.findViewById(R.id.tv_sure);
95 | sure.setOnClickListener(this); // 设置确认按钮
96 | TextView title = (TextView) view.findViewById(R.id.tv_title);
97 |
98 | // 设置顶部栏
99 | View toolbar = view.findViewById(R.id.toolbar); // 头部视图
100 | toolbar.setBackgroundResource(mScrollerConfig.mToolbarBkgColor);
101 | title.setText(mScrollerConfig.mTitleString); // 设置文字
102 | cancel.setText(mScrollerConfig.mCancelString);
103 | sure.setText(mScrollerConfig.mSureString);
104 |
105 | mTimeWheel = new TimeWheel(view, mScrollerConfig); // 设置滚动参数
106 | return view;
107 | }
108 |
109 | @Override
110 | public void onClick(View v) {
111 | int i = v.getId();
112 | if (i == R.id.tv_cancel) {
113 | dismiss(); // 取消
114 | } else if (i == R.id.tv_sure) {
115 | onSureClicked();
116 | }
117 | }
118 |
119 | /**
120 | * 返回当前的选择秒数
121 | *
122 | * @return 当前秒数
123 | */
124 | public long getCurrentMilliseconds() {
125 | if (mCurrentMilliseconds == 0)
126 | return System.currentTimeMillis();
127 |
128 | return mCurrentMilliseconds;
129 | }
130 |
131 | /**
132 | * 确认按钮, 回调秒数
133 | */
134 | private void onSureClicked() {
135 | Calendar calendar = Calendar.getInstance();
136 | calendar.clear();
137 |
138 | calendar.set(Calendar.YEAR, mTimeWheel.getCurrentYear());
139 | calendar.set(Calendar.MONTH, mTimeWheel.getCurrentMonth() - 1);
140 | calendar.set(Calendar.DAY_OF_MONTH, mTimeWheel.getCurrentDay());
141 | calendar.set(Calendar.HOUR_OF_DAY, mTimeWheel.getCurrentHour());
142 | calendar.set(Calendar.MINUTE, mTimeWheel.getCurrentMinute());
143 |
144 | mCurrentMilliseconds = calendar.getTimeInMillis();
145 |
146 | Calendar calendar2 = Calendar.getInstance();
147 | calendar2.clear();
148 |
149 | calendar2.set(Calendar.YEAR, mTimeWheel.getCurrentFinishYear());
150 | calendar2.set(Calendar.MONTH, mTimeWheel.getCurrentFinishMonth() - 1);
151 | calendar2.set(Calendar.DAY_OF_MONTH, mTimeWheel.getCurrentFinishDay());
152 |
153 | mCurrentFinishMilliseconds = calendar2.getTimeInMillis();
154 |
155 | if (mScrollerConfig.mCallback != null) {
156 | mScrollerConfig.mCallback.onDateSet(this, mCurrentMilliseconds, mCurrentFinishMilliseconds);
157 | }
158 | dismiss();
159 | }
160 |
161 | @SuppressWarnings("unused")
162 | public static class Builder {
163 | ScrollerConfig mScrollerConfig;
164 |
165 | public Builder() {
166 | mScrollerConfig = new ScrollerConfig();
167 | }
168 |
169 | public Builder setType(Type type) {
170 | mScrollerConfig.mType = type;
171 | return this;
172 | }
173 |
174 | public Builder setThemeColor(@ColorRes int color) {
175 | mScrollerConfig.mToolbarBkgColor = color;
176 | return this;
177 | }
178 |
179 | public Builder setCancelStringId(String left) {
180 | mScrollerConfig.mCancelString = left;
181 | return this;
182 | }
183 |
184 | public Builder setSureStringId(String right) {
185 | mScrollerConfig.mSureString = right;
186 | return this;
187 | }
188 |
189 | public Builder setTitleStringId(String title) {
190 | mScrollerConfig.mTitleString = title;
191 | return this;
192 | }
193 |
194 | public Builder setToolBarTextColor(int color) {
195 | mScrollerConfig.mToolBarTVColor = color;
196 | return this;
197 | }
198 |
199 | public Builder setWheelItemTextNormalColor(int color) {
200 | mScrollerConfig.mWheelTVNormalColor = color;
201 | return this;
202 | }
203 |
204 | public Builder setWheelItemTextSelectorColor(int color) {
205 | mScrollerConfig.mWheelTVSelectorColor = color;
206 | return this;
207 | }
208 |
209 | public Builder setWheelItemTextSize(int size) {
210 | mScrollerConfig.mWheelTVSize = size;
211 | return this;
212 | }
213 |
214 | public Builder setCyclic(boolean cyclic) {
215 | mScrollerConfig.cyclic = cyclic;
216 | return this;
217 | }
218 |
219 | public Builder setMinMilliseconds(long milliseconds) {
220 | mScrollerConfig.mMinCalendar = new WheelCalendar(milliseconds);
221 | return this;
222 | }
223 |
224 | public Builder setMaxMilliseconds(long milliseconds) {
225 | mScrollerConfig.mMaxCalendar = new WheelCalendar(milliseconds);
226 | return this;
227 | }
228 |
229 | public Builder setCurMilliseconds(long milliseconds, long milliFinishSeconds) {
230 | mScrollerConfig.mCurCalendar = new WheelCalendar(milliseconds);
231 | mScrollerConfig.mCurFinishCalendar = new WheelCalendar(milliFinishSeconds);
232 | return this;
233 | }
234 |
235 | public Builder setYearText(String year) {
236 | mScrollerConfig.mYear = year;
237 | return this;
238 | }
239 |
240 | public Builder setMonthText(String month) {
241 | mScrollerConfig.mMonth = month;
242 | return this;
243 | }
244 |
245 | public Builder setDayText(String day) {
246 | mScrollerConfig.mDay = day;
247 | return this;
248 | }
249 |
250 | public Builder setHourText(String hour) {
251 | mScrollerConfig.mHour = hour;
252 | return this;
253 | }
254 |
255 | public Builder setMinuteText(String minute) {
256 | mScrollerConfig.mMinute = minute;
257 | return this;
258 | }
259 |
260 | public Builder setCallback(OnDateSetListener listener) {
261 | mScrollerConfig.mCallback = listener;
262 | return this;
263 | }
264 |
265 | public DateScrollerDialog build() {
266 | return newInstance(mScrollerConfig);
267 | }
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/TimeWheel.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import org.wangchenlong.datescroller.R;
7 | import org.wangchenlong.datescroller.widget.adapters.NumericWheelAdapter;
8 | import org.wangchenlong.datescroller.widget.config.ScrollerConfig;
9 | import org.wangchenlong.datescroller.widget.data.source.TimeRepository;
10 | import org.wangchenlong.datescroller.widget.utils.DateConstants;
11 | import org.wangchenlong.datescroller.widget.utils.Utils;
12 | import org.wangchenlong.datescroller.widget.wheel.OnWheelChangedListener;
13 | import org.wangchenlong.datescroller.widget.wheel.WheelView;
14 |
15 | import java.util.Calendar;
16 |
17 | /**
18 | * 时间滚轮, 用于控制时间数据
19 | */
20 | class TimeWheel {
21 | private Context mContext;
22 |
23 | private WheelView mYearView, mMonthView, mDayView, mHourView, mMinuteView
24 | , mFinishYearView, mFinishMonthView, mFinishDayView; // 滚动视图
25 | private NumericWheelAdapter mYearAdapter, mMonthAdapter, mDayAdapter, mHourAdapter, mMinuteAdapter;
26 |
27 | private ScrollerConfig mScrollerConfig;
28 | private TimeRepository mRepository;
29 |
30 | private OnWheelChangedListener mYearListener = new OnWheelChangedListener() {
31 | @Override
32 | public void onChanged(WheelView wheel, int oldValue, int newValue) {
33 | updateMonths();
34 |
35 | // 如果开始年份大于结束年份
36 | if (mYearView.getCurrentItem() > mFinishYearView.getCurrentItem()){
37 | // 则结束年份马上更新到相同年份
38 | mFinishYearView.setCurrentItem(mYearView.getCurrentItem());
39 |
40 | // 当前月份大于结束月份
41 | if (mMonthView.getCurrentItem() > mFinishMonthView.getCurrentItem()){
42 | // 结束月份设置为开始月份
43 | mFinishMonthView.setCurrentItem(mMonthView.getCurrentItem());
44 | }
45 | }
46 | }
47 | };
48 |
49 | private OnWheelChangedListener mFinishYearListener = new OnWheelChangedListener() {
50 | @Override
51 | public void onChanged(WheelView wheel, int oldValue, int newValue) {
52 | updateFinishMonths();
53 |
54 | // 如果结束年份小于结束年份
55 | if (mFinishYearView.getCurrentItem() < mYearView.getCurrentItem()){
56 | // 则开始年份马上更新到相同年份
57 | mYearView.setCurrentItem(mFinishYearView.getCurrentItem());
58 |
59 | // 当前结束月份大于开始月份
60 | if (mFinishMonthView.getCurrentItem() < mMonthView.getCurrentItem()){
61 | // 开始月份设置为结束月份
62 | mMonthView.setCurrentItem(mFinishMonthView.getCurrentItem());
63 |
64 | // 开始日大于结束日
65 | if (mDayView.getCurrentItem() > mFinishDayView.getCurrentItem()){
66 | // 结束日设置为开始日
67 | mFinishDayView.setCurrentItem(mDayView.getCurrentItem());
68 | }
69 | }
70 | }
71 | }
72 | };
73 | private OnWheelChangedListener mMonthListener = new OnWheelChangedListener() {
74 | @Override
75 | public void onChanged(WheelView wheel, int oldValue, int newValue) {
76 | updateDays();
77 |
78 | // 开始月份大于结束月份
79 | if (mMonthView.getCurrentItem() > mFinishMonthView.getCurrentItem() && mYearView.getCurrentItem() == mFinishYearView.getCurrentItem()){
80 | // 结束月份设置为开始月份
81 | mFinishMonthView.setCurrentItem(mMonthView.getCurrentItem());
82 | }
83 | }
84 | };
85 | private OnWheelChangedListener mFinishMonthListener = new OnWheelChangedListener() {
86 | @Override
87 | public void onChanged(WheelView wheel, int oldValue, int newValue) {
88 | updateFinishDays();
89 |
90 | // 当前结束月份大于开始月份
91 | if (mFinishMonthView.getCurrentItem() < mMonthView.getCurrentItem() && mYearView.getCurrentItem() == mFinishYearView.getCurrentItem()){
92 | // 开始月份设置为结束月份
93 | mMonthView.setCurrentItem(mFinishMonthView.getCurrentItem());
94 | }
95 | }
96 | };
97 |
98 | // 开始日监听
99 | private OnWheelChangedListener mDayListener = new OnWheelChangedListener() {
100 | @Override
101 | public void onChanged(WheelView wheel, int oldValue, int newValue) {
102 | // updateHours();
103 |
104 | // 开始日大于结束日
105 | if (mDayView.getCurrentItem() > mFinishDayView.getCurrentItem() && mFinishMonthView.getCurrentItem() == mMonthView.getCurrentItem()){
106 | // 结束日设置为开始日
107 | mFinishDayView.setCurrentItem(mDayView.getCurrentItem());
108 | }
109 | }
110 | };
111 |
112 | // 结束日监听
113 | private OnWheelChangedListener mFinishDayListener = new OnWheelChangedListener() {
114 | @Override
115 | public void onChanged(WheelView wheel, int oldValue, int newValue) {
116 | // 开始日大于结束日
117 | if (mFinishDayView.getCurrentItem() < mDayView.getCurrentItem() && mFinishMonthView.getCurrentItem() == mMonthView.getCurrentItem()){
118 | // 结束日设置为开始日
119 | mDayView.setCurrentItem(mFinishDayView.getCurrentItem());
120 | }
121 | }
122 | };
123 |
124 | private OnWheelChangedListener mHourListener = new OnWheelChangedListener() {
125 | @Override
126 | public void onChanged(WheelView wheel, int oldValue, int newValue) {
127 | updateMinutes();
128 | }
129 | };
130 |
131 | /**
132 | * 设置视图与参数
133 | *
134 | * @param view 视图
135 | * @param scrollerConfig 滚动参数
136 | */
137 | TimeWheel(View view, ScrollerConfig scrollerConfig) {
138 | mScrollerConfig = scrollerConfig;
139 | mRepository = new TimeRepository(scrollerConfig);
140 | mContext = view.getContext();
141 | initialize(view);
142 | }
143 |
144 | /**
145 | * 初始化与设置视图
146 | *
147 | * @param view 视图
148 | */
149 | private void initialize(View view) {
150 | initView(view); // 初始化视图
151 |
152 | // 初始化年份和结束年
153 | initYearView();
154 | initMonthView();
155 | initDayView();
156 | initHourView();
157 | initMinuteView();
158 |
159 | // 初始化结束月
160 | initFinishMonthView();
161 | // 初始化结束日
162 | initFinishDayView();
163 | }
164 |
165 | /**
166 | * 初始化视图
167 | *
168 | * @param view 视图
169 | */
170 | private void initView(View view) {
171 | mYearView = (WheelView) view.findViewById(R.id.year);
172 | mMonthView = (WheelView) view.findViewById(R.id.month);
173 | mDayView = (WheelView) view.findViewById(R.id.day);
174 | mHourView = (WheelView) view.findViewById(R.id.hour);
175 | mMinuteView = (WheelView) view.findViewById(R.id.minute);
176 | mFinishYearView = (WheelView) view.findViewById(R.id.finish_year);
177 | mFinishMonthView = (WheelView)view.findViewById(R.id.finish_month);
178 | mFinishDayView= (WheelView)view.findViewById(R.id.finish_day);
179 |
180 | // 根据类型, 设置隐藏位置
181 | switch (mScrollerConfig.mType) {
182 | case ALL:
183 | break;
184 | case YEAR_MONTH_DAY:
185 | Utils.hideViews(mHourView, mMinuteView);
186 | break;
187 | case YEAR_MONTH:
188 | Utils.hideViews(mDayView, mHourView, mMinuteView);
189 | break;
190 | case MONTH_DAY_HOUR_MIN:
191 | Utils.hideViews(mYearView);
192 | break;
193 | case HOURS_MINS:
194 | Utils.hideViews(mYearView, mMonthView, mDayView);
195 | break;
196 | case YEAR:
197 | Utils.hideViews(mMonthView, mDayView, mHourView, mMinuteView);
198 | break;
199 | }
200 |
201 | mYearView.addChangingListener(mYearListener);
202 | mYearView.addChangingListener(mMonthListener);
203 | mYearView.addChangingListener(mDayListener);
204 | mYearView.addChangingListener(mHourListener);
205 |
206 | mFinishYearView.addChangingListener(mFinishYearListener);
207 | mFinishYearView.addChangingListener(mMonthListener);
208 | mFinishYearView.addChangingListener(mDayListener);
209 | mFinishYearView.addChangingListener(mHourListener);
210 |
211 | mMonthView.addChangingListener(mMonthListener);
212 | mMonthView.addChangingListener(mDayListener);
213 | mMonthView.addChangingListener(mHourListener);
214 |
215 | mFinishMonthView.addChangingListener(mFinishMonthListener);
216 |
217 | mDayView.addChangingListener(mDayListener);
218 | mDayView.addChangingListener(mHourListener);
219 |
220 | mFinishDayView.addChangingListener(mDayListener);
221 |
222 | mHourView.addChangingListener(mHourListener);
223 | }
224 |
225 | /**
226 | * 初始化年视图, 年是最高级
227 | */
228 | private void initYearView() {
229 | int minYear = mRepository.getMinYear();
230 | int maxYear = mRepository.getMaxYear();
231 |
232 | mYearAdapter = new NumericWheelAdapter(mContext, minYear, maxYear,
233 | DateConstants.FORMAT, mScrollerConfig.mYear);
234 | mYearAdapter.setConfig(mScrollerConfig);
235 |
236 | mYearView.setViewAdapter(mYearAdapter);
237 | mFinishYearView.setViewAdapter(mYearAdapter);
238 |
239 | mYearView.setCurrentItem(mRepository.getDefaultCalendar().year - minYear);
240 | mFinishYearView.setCurrentItem(mRepository.getDefaultFinishCalendar().year - minYear);
241 | }
242 |
243 | /**
244 | * 初始化月视图
245 | */
246 | private void initMonthView() {
247 | updateMonths();
248 | int curYear = getCurrentYear();
249 | int minMonth = mRepository.getMinMonth(curYear);
250 | mMonthView.setCurrentItem(mRepository.getDefaultCalendar().month - minMonth);
251 | }
252 |
253 | /**
254 | * 初始化结束月视图
255 | */
256 | private void initFinishMonthView() {
257 | updateFinishMonths();
258 | int curYear = getCurrentFinishYear();
259 | int minMonth = mRepository.getMinMonth(curYear);
260 | mFinishMonthView.setCurrentItem(mRepository.getDefaultFinishCalendar().month - minMonth);
261 | }
262 |
263 | /**
264 | * 初始化日视图
265 | */
266 | private void initDayView() {
267 | updateDays();
268 | int curYear = getCurrentYear();
269 | int curMonth = getCurrentMonth();
270 |
271 | int minDay = mRepository.getMinDay(curYear, curMonth);
272 | mDayView.setCurrentItem(mRepository.getDefaultCalendar().day - minDay);
273 | }
274 |
275 | /**
276 | * 初始化结束日视图
277 | */
278 | private void initFinishDayView() {
279 | updateFinishDays();
280 | int curYear = getCurrentFinishYear();
281 | int curMonth = getCurrentFinishMonth();
282 |
283 | int minDay = mRepository.getMinDay(curYear, curMonth);
284 | mFinishDayView.setCurrentItem(mRepository.getDefaultFinishCalendar().day - minDay);
285 | }
286 |
287 | /**
288 | * 初始化小时视图
289 | */
290 | private void initHourView() {
291 | updateHours();
292 | int curYear = getCurrentYear();
293 | int curMonth = getCurrentMonth();
294 | int curDay = getCurrentDay();
295 |
296 | int minHour = mRepository.getMinHour(curYear, curMonth, curDay);
297 | mHourView.setCurrentItem(mRepository.getDefaultCalendar().hour - minHour);
298 | }
299 |
300 | /**
301 | * 初始化分钟视图
302 | */
303 | private void initMinuteView() {
304 | updateMinutes();
305 | int curYear = getCurrentYear();
306 | int curMonth = getCurrentMonth();
307 | int curDay = getCurrentDay();
308 | int curHour = getCurrentHour();
309 | int minMinute = mRepository.getMinMinute(curYear, curMonth, curDay, curHour);
310 |
311 | mMinuteView.setCurrentItem(mRepository.getDefaultCalendar().minute - minMinute);
312 | }
313 |
314 | /**
315 | * 更新月份
316 | */
317 | private void updateMonths() {
318 | if (mMonthView.getVisibility() == View.GONE)
319 | return;
320 |
321 | int curYear = getCurrentYear();
322 | int minMonth = mRepository.getMinMonth(curYear);
323 | int maxMonth = mRepository.getMaxMonth(curYear);
324 | mMonthAdapter = new NumericWheelAdapter(mContext, minMonth, maxMonth,
325 | DateConstants.FORMAT, mScrollerConfig.mMonth);
326 | mMonthAdapter.setConfig(mScrollerConfig);
327 | mMonthView.setViewAdapter(mMonthAdapter);
328 | mMonthView.setCyclic(mScrollerConfig.cyclic);
329 |
330 | // 当滚动数量不足时, 需要避免循环
331 | if (maxMonth - minMonth < mScrollerConfig.mMaxLines) {
332 | mMonthView.setCyclic(false);
333 | }
334 |
335 | if (mRepository.isMinYear(curYear)) {
336 | mMonthView.setCurrentItem(0, false);
337 | }
338 | }
339 |
340 | /**
341 | * 更新结束月份
342 | */
343 | private void updateFinishMonths() {
344 | if (mFinishMonthView.getVisibility() == View.GONE)
345 | return;
346 |
347 | int curYear = getCurrentFinishYear();
348 | int minMonth = mRepository.getMinMonth(curYear);
349 | int maxMonth = mRepository.getMaxMonth(curYear);
350 | mMonthAdapter = new NumericWheelAdapter(mContext, minMonth, maxMonth,
351 | DateConstants.FORMAT, mScrollerConfig.mMonth);
352 | mMonthAdapter.setConfig(mScrollerConfig);
353 | mFinishMonthView.setViewAdapter(mMonthAdapter);
354 | mFinishMonthView.setCyclic(mScrollerConfig.cyclic);
355 |
356 | // 当滚动数量不足时, 需要避免循环
357 | if (maxMonth - minMonth < mScrollerConfig.mMaxLines) {
358 | mFinishMonthView.setCyclic(false);
359 | }
360 |
361 | if (mRepository.isMinYear(curYear)) {
362 | mFinishMonthView.setCurrentItem(0, false);
363 | }
364 | }
365 |
366 | /**
367 | * 更新日
368 | */
369 | private void updateDays() {
370 | if (mDayView.getVisibility() == View.GONE)
371 | return;
372 |
373 | int curYear = getCurrentYear();
374 | int curMonth = getCurrentMonth();
375 |
376 | Calendar calendar = Calendar.getInstance();
377 | calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + mYearView.getCurrentItem());
378 | calendar.set(Calendar.MONTH, curMonth);
379 |
380 | int maxDay = mRepository.getMaxDay(curYear, curMonth);
381 | int minDay = mRepository.getMinDay(curYear, curMonth);
382 | mDayAdapter = new NumericWheelAdapter(mContext, minDay, maxDay, DateConstants.FORMAT, mScrollerConfig.mDay);
383 | mDayAdapter.setConfig(mScrollerConfig);
384 | mDayView.setViewAdapter(mDayAdapter);
385 | mDayView.setCyclic(mScrollerConfig.cyclic); // 是否循环
386 |
387 | // 当滚动数量不足时, 需要避免循环
388 | if (maxDay - minDay < mScrollerConfig.mMaxLines) {
389 | mDayView.setCyclic(false);
390 | }
391 |
392 | if (mRepository.isMinMonth(curYear, curMonth)) {
393 | mDayView.setCurrentItem(0, true);
394 | }
395 |
396 | int dayCount = mDayAdapter.getItemsCount();
397 | if (mDayView.getCurrentItem() >= dayCount) {
398 | mDayView.setCurrentItem(dayCount - 1, true);
399 | }
400 | }
401 |
402 | /**
403 | * 更新结束日
404 | */
405 | private void updateFinishDays() {
406 | if (mFinishDayView.getVisibility() == View.GONE)
407 | return;
408 |
409 | int curYear = getCurrentFinishYear();
410 | int curMonth = getCurrentFinishMonth();
411 |
412 | Calendar calendar = Calendar.getInstance();
413 | calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + mFinishDayView.getCurrentItem());
414 | calendar.set(Calendar.MONTH, curMonth);
415 |
416 | int maxDay = mRepository.getMaxDay(curYear, curMonth);
417 | int minDay = mRepository.getMinDay(curYear, curMonth);
418 | mDayAdapter = new NumericWheelAdapter(mContext, minDay, maxDay, DateConstants.FORMAT, mScrollerConfig.mDay);
419 | mDayAdapter.setConfig(mScrollerConfig);
420 | mFinishDayView.setViewAdapter(mDayAdapter);
421 | mFinishDayView.setCyclic(mScrollerConfig.cyclic); // 是否循环
422 |
423 | // 当滚动数量不足时, 需要避免循环
424 | if (maxDay - minDay < mScrollerConfig.mMaxLines) {
425 | mFinishDayView.setCyclic(false);
426 | }
427 |
428 | if (mRepository.isMinMonth(curYear, curMonth)) {
429 | mFinishDayView.setCurrentItem(0, true);
430 | }
431 |
432 | int dayCount = mDayAdapter.getItemsCount();
433 | if (mFinishDayView.getCurrentItem() >= dayCount) {
434 | mFinishDayView.setCurrentItem(dayCount - 1, true);
435 | }
436 | }
437 |
438 | /**
439 | * 更新小时
440 | */
441 | private void updateHours() {
442 | if (mHourView.getVisibility() == View.GONE)
443 | return;
444 |
445 | int curYear = getCurrentYear();
446 | int curMonth = getCurrentMonth();
447 | int curDay = getCurrentDay();
448 |
449 | int minHour = mRepository.getMinHour(curYear, curMonth, curDay);
450 | int maxHour = mRepository.getMaxHour(curYear, curMonth, curDay);
451 |
452 | mHourAdapter = new NumericWheelAdapter(mContext, minHour, maxHour, DateConstants.FORMAT, mScrollerConfig.mHour);
453 | mHourAdapter.setConfig(mScrollerConfig);
454 | mHourView.setViewAdapter(mHourAdapter);
455 | mHourView.setCyclic(mScrollerConfig.cyclic);
456 |
457 | // 当滚动数量不足时, 需要避免循环
458 | if (maxHour - minHour < mScrollerConfig.mMaxLines) {
459 | mHourView.setCyclic(false);
460 | }
461 |
462 | if (mRepository.isMinDay(curYear, curMonth, curDay))
463 | mHourView.setCurrentItem(0, false);
464 | }
465 |
466 | /**
467 | * 更新分钟
468 | */
469 | private void updateMinutes() {
470 | if (mMinuteView.getVisibility() == View.GONE)
471 | return;
472 |
473 | int curYear = getCurrentYear();
474 | int curMonth = getCurrentMonth();
475 | int curDay = getCurrentDay();
476 | int curHour = getCurrentHour();
477 |
478 | int minMinute = mRepository.getMinMinute(curYear, curMonth, curDay, curHour);
479 | int maxMinute = mRepository.getMaxMinute(curYear, curMonth, curDay, curHour);
480 |
481 | mMinuteAdapter = new NumericWheelAdapter(mContext, minMinute, maxMinute, DateConstants.FORMAT, mScrollerConfig.mMinute);
482 | mMinuteAdapter.setConfig(mScrollerConfig);
483 | mMinuteView.setViewAdapter(mMinuteAdapter);
484 | mMinuteView.setCyclic(mScrollerConfig.cyclic);
485 |
486 | // 当滚动数量不足时, 需要避免循环
487 | if (maxMinute - minMinute < mScrollerConfig.mMaxLines) {
488 | mMinuteView.setCyclic(false);
489 | }
490 |
491 | if (mRepository.isMinHour(curYear, curMonth, curDay, curHour))
492 | mMinuteView.setCurrentItem(0, false);
493 | }
494 |
495 | int getCurrentYear() {
496 | return mYearView.getCurrentItem() + mRepository.getMinYear();
497 | }
498 |
499 | int getCurrentFinishYear() {
500 | return mFinishYearView.getCurrentItem() + mRepository.getMinYear();
501 | }
502 |
503 | int getCurrentMonth() {
504 | int curYear = getCurrentYear();
505 | return mMonthView.getCurrentItem() + +mRepository.getMinMonth(curYear);
506 | }
507 |
508 | int getCurrentFinishMonth() {
509 | int curYear = getCurrentFinishYear();
510 | return mFinishMonthView.getCurrentItem() + +mRepository.getMinMonth(curYear);
511 | }
512 |
513 | int getCurrentDay() {
514 | int curYear = getCurrentYear();
515 | int curMonth = getCurrentMonth();
516 | return mDayView.getCurrentItem() + mRepository.getMinDay(curYear, curMonth);
517 | }
518 |
519 | int getCurrentFinishDay() {
520 | int curYear = getCurrentFinishYear();
521 | int curMonth = getCurrentFinishMonth();
522 | return mFinishDayView.getCurrentItem() + mRepository.getMinDay(curYear, curMonth);
523 | }
524 |
525 | int getCurrentHour() {
526 | int curYear = getCurrentYear();
527 | int curMonth = getCurrentMonth();
528 | int curDay = getCurrentDay();
529 | return mHourView.getCurrentItem() + mRepository.getMinHour(curYear, curMonth, curDay);
530 | }
531 |
532 | int getCurrentMinute() {
533 | int curYear = getCurrentYear();
534 | int curMonth = getCurrentMonth();
535 | int curDay = getCurrentDay();
536 | int curHour = getCurrentHour();
537 |
538 | return mMinuteView.getCurrentItem() + mRepository.getMinMinute(curYear, curMonth, curDay, curHour);
539 | }
540 | }
541 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/adapters/AbstractWheelAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Yuri Kanivets
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.wangchenlong.datescroller.widget.adapters;
18 |
19 | import android.database.DataSetObserver;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 |
23 | import java.util.LinkedList;
24 | import java.util.List;
25 |
26 | /**
27 | * 抽象滚轮适配器
28 | */
29 | abstract class AbstractWheelAdapter implements WheelViewAdapter {
30 | private List mDatasetObservers; // 数据集观察者
31 |
32 | @Override
33 | public View getEmptyItem(View convertView, ViewGroup parent) {
34 | return null;
35 | }
36 |
37 | @Override
38 | public void registerDataSetObserver(DataSetObserver observer) {
39 | if (mDatasetObservers == null) {
40 | mDatasetObservers = new LinkedList<>();
41 | }
42 | mDatasetObservers.add(observer);
43 | }
44 |
45 | @Override
46 | public void unregisterDataSetObserver(DataSetObserver observer) {
47 | if (mDatasetObservers != null) {
48 | mDatasetObservers.remove(observer);
49 | }
50 | }
51 |
52 | /**
53 | * 通知观察者数据改变
54 | */
55 | protected void notifyDataChangedEvent() {
56 | if (mDatasetObservers != null) {
57 | for (DataSetObserver observer : mDatasetObservers) {
58 | observer.onChanged();
59 | }
60 | }
61 | }
62 |
63 | /**
64 | * 通知观察者无效数据
65 | */
66 | protected void notifyDataInvalidatedEvent() {
67 | if (mDatasetObservers != null) {
68 | for (DataSetObserver observer : mDatasetObservers) {
69 | observer.onInvalidated();
70 | }
71 | }
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/adapters/AbstractWheelTextAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Yuri Kanivets
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.wangchenlong.datescroller.widget.adapters;
17 |
18 | import android.content.Context;
19 | import android.util.Log;
20 | import android.view.Gravity;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.TextView;
25 |
26 | import org.wangchenlong.datescroller.R;
27 | import org.wangchenlong.datescroller.widget.config.ScrollerConfig;
28 | import org.wangchenlong.datescroller.widget.utils.Utils;
29 |
30 |
31 | /**
32 | * Abstract wheel adapter provides common functionality for adapters.
33 | */
34 | public abstract class AbstractWheelTextAdapter extends AbstractWheelAdapter {
35 |
36 | /**
37 | * Text view resource. Used as a default view for adapter.
38 | */
39 | public static final int TEXT_VIEW_ITEM_RESOURCE = -1;
40 |
41 | /**
42 | * Default text color
43 | */
44 | public static final int LABEL_COLOR = 0xFF700070;
45 | /**
46 | * Default text size
47 | */
48 | public static final int DEFAULT_TEXT_SIZE = 24;
49 | /**
50 | * No resource constant.
51 | */
52 | protected static final int NO_RESOURCE = 0;
53 | // Current context
54 | protected Context context;
55 | // Layout inflater
56 | protected LayoutInflater inflater;
57 | // Items resources
58 | protected int itemResourceId;
59 | protected int itemTextResourceId;
60 | // Empty items resources
61 | protected int emptyItemResourceId;
62 |
63 | private int padding = 0;
64 |
65 | private ScrollerConfig mScrollerConfig;
66 |
67 | /**
68 | * Constructor
69 | *
70 | * @param context the current context
71 | */
72 | protected AbstractWheelTextAdapter(Context context) {
73 | this(context, TEXT_VIEW_ITEM_RESOURCE);
74 | }
75 |
76 | /**
77 | * Constructor
78 | *
79 | * @param context the current context
80 | * @param itemResource the resource ID for a layout file containing a TextView to use when instantiating items views
81 | */
82 | protected AbstractWheelTextAdapter(Context context, int itemResource) {
83 | this(context, itemResource, NO_RESOURCE);
84 | }
85 |
86 | /**
87 | * Constructor
88 | *
89 | * @param context the current context
90 | * @param itemResource the resource ID for a layout file containing a TextView to use when instantiating items views
91 | * @param itemTextResource the resource ID for a text view in the item layout
92 | */
93 | protected AbstractWheelTextAdapter(Context context, int itemResource, int itemTextResource) {
94 | this.context = context;
95 | itemResourceId = itemResource;
96 | itemTextResourceId = itemTextResource;
97 | padding = context.getResources().getDimensionPixelSize(R.dimen.textview_default_padding);
98 |
99 | inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
100 | }
101 |
102 | /**
103 | * Gets resource Id for items views
104 | *
105 | * @return the item resource Id
106 | */
107 | public int getItemResource() {
108 | return itemResourceId;
109 | }
110 |
111 | /**
112 | * Sets resource Id for items views
113 | *
114 | * @param itemResourceId the resource Id to set
115 | */
116 | public void setItemResource(int itemResourceId) {
117 | this.itemResourceId = itemResourceId;
118 | }
119 |
120 | /**
121 | * Gets resource Id for text view in item layout
122 | *
123 | * @return the item text resource Id
124 | */
125 | public int getItemTextResource() {
126 | return itemTextResourceId;
127 | }
128 |
129 | /**
130 | * Sets resource Id for text view in item layout
131 | *
132 | * @param itemTextResourceId the item text resource Id to set
133 | */
134 | public void setItemTextResource(int itemTextResourceId) {
135 | this.itemTextResourceId = itemTextResourceId;
136 | }
137 |
138 | /**
139 | * Gets resource Id for empty items views
140 | *
141 | * @return the empty item resource Id
142 | */
143 | public int getEmptyItemResource() {
144 | return emptyItemResourceId;
145 | }
146 |
147 | /**
148 | * Sets resource Id for empty items views
149 | *
150 | * @param emptyItemResourceId the empty item resource Id to set
151 | */
152 | public void setEmptyItemResource(int emptyItemResourceId) {
153 | this.emptyItemResourceId = emptyItemResourceId;
154 | }
155 |
156 |
157 | /**
158 | * Returns text for specified item
159 | *
160 | * @param index the item index
161 | * @return the text of specified items
162 | */
163 | protected abstract CharSequence getItemText(int index);
164 |
165 | @Override
166 | public View getItem(int index, View convertView, ViewGroup parent) {
167 | if (index >= 0 && index < getItemsCount()) {
168 | if (convertView == null) {
169 | convertView = getView(itemResourceId, parent);
170 | }
171 | TextView textView = getTextView(convertView, itemTextResourceId);
172 | if (textView != null) {
173 | CharSequence text = getItemText(index);
174 | if (text == null) {
175 | text = "";
176 | }
177 | textView.setText(text);
178 | if (itemResourceId == TEXT_VIEW_ITEM_RESOURCE) {
179 | configureTextView(textView);
180 | }
181 | }
182 | return convertView;
183 | }
184 | return null;
185 | }
186 |
187 | @Override
188 | public View getEmptyItem(View convertView, ViewGroup parent) {
189 | if (convertView == null) {
190 | convertView = getView(emptyItemResourceId, parent);
191 | }
192 | if (emptyItemResourceId == TEXT_VIEW_ITEM_RESOURCE && convertView instanceof TextView) {
193 | configureTextView((TextView) convertView);
194 | }
195 |
196 | return convertView;
197 | }
198 |
199 |
200 | /**
201 | * Configures text view. Is called for the TEXT_VIEW_ITEM_RESOURCE views.
202 | *
203 | * @param view the text view to be configured
204 | */
205 | protected void configureTextView(TextView view) {
206 | if (mScrollerConfig == null)
207 | mScrollerConfig = new ScrollerConfig();
208 | view.setTextColor(mScrollerConfig.mWheelTVNormalColor);
209 |
210 | view.setGravity(Gravity.CENTER);
211 | view.setPadding(0, padding, 0, padding);
212 | view.setTextSize(mScrollerConfig.mWheelTVSize);
213 | view.setLines(1);
214 | // view.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
215 | }
216 |
217 | /**
218 | * Loads a text view from view
219 | *
220 | * @param view the text view or layout containing it
221 | * @param textResource the text resource Id in layout
222 | * @return the loaded text view
223 | */
224 | private TextView getTextView(View view, int textResource) {
225 | TextView text = null;
226 | try {
227 | if (textResource == NO_RESOURCE && view instanceof TextView) {
228 | text = (TextView) view;
229 | } else if (textResource != NO_RESOURCE) {
230 | text = (TextView) view.findViewById(textResource);
231 | }
232 | } catch (ClassCastException e) {
233 | Log.e("AbstractWheelAdapter", "You must supply a resource ID for a TextView");
234 | throw new IllegalStateException(
235 | "AbstractWheelAdapter requires the resource ID to be a TextView", e);
236 | }
237 |
238 | return text;
239 | }
240 |
241 | /**
242 | * Loads view from resources
243 | *
244 | * @param resource the resource Id
245 | * @return the loaded view or null if resource is not set
246 | */
247 | private View getView(int resource, ViewGroup parent) {
248 | switch (resource) {
249 | case NO_RESOURCE:
250 | return null;
251 | case TEXT_VIEW_ITEM_RESOURCE:
252 | return new TextView(context);
253 | default:
254 | return inflater.inflate(resource, parent, false);
255 | }
256 | }
257 |
258 | @Override
259 | public ScrollerConfig getConfig() {
260 | if (mScrollerConfig == null)
261 | mScrollerConfig = new ScrollerConfig();
262 | return mScrollerConfig;
263 | }
264 |
265 | @Override
266 | public void setConfig(ScrollerConfig config) {
267 | mScrollerConfig = config;
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/adapters/NumericWheelAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Yuri Kanivets
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.wangchenlong.datescroller.widget.adapters;
18 |
19 | import android.content.Context;
20 | import android.text.TextUtils;
21 |
22 | import org.wangchenlong.datescroller.R;
23 |
24 | /**
25 | * Numeric Wheel adapter.
26 | */
27 | public class NumericWheelAdapter extends AbstractWheelTextAdapter {
28 |
29 | private static final int DEFAULT_MAX_VALUE = 9; // 默认的数字最大值
30 | private static final int DEFAULT_MIN_VALUE = 0; // 默认的数字最小值
31 |
32 | // Values
33 | private int minValue;
34 | private int maxValue;
35 |
36 | private String format; // format
37 | private String unit; //unit
38 |
39 | /**
40 | * 构造器
41 | *
42 | * @param context 当前上下文
43 | */
44 | public NumericWheelAdapter(Context context) {
45 | this(context, DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE);
46 | }
47 |
48 |
49 | /**
50 | * 构造器
51 | *
52 | * @param context 上下文
53 | * @param minValue 最小值
54 | * @param maxValue 最大值
55 | */
56 | public NumericWheelAdapter(Context context, int minValue, int maxValue) {
57 | this(context, minValue, maxValue, null);
58 | }
59 |
60 | /**
61 | * 构造器
62 | *
63 | * @param context 上下文
64 | * @param minValue 最小值
65 | * @param maxValue 最大值
66 | * @param format 格式
67 | */
68 | public NumericWheelAdapter(Context context, int minValue, int maxValue, String format) {
69 | this(context, minValue, maxValue, format, null);
70 | }
71 |
72 | /**
73 | * 构造器
74 | *
75 | * @param context 上下文
76 | * @param minValue 最小值
77 | * @param maxValue 最大值
78 | * @param format 格式化
79 | * @param unit 滚轮单位
80 | */
81 | public NumericWheelAdapter(Context context, int minValue, int maxValue, String format, String unit) {
82 | super(context);
83 | this.minValue = minValue;
84 | this.maxValue = maxValue;
85 | this.format = format;
86 | this.unit = unit;
87 | this.itemResourceId = R.layout.tv_item; // 默认样式
88 | }
89 |
90 | @Override
91 | public CharSequence getItemText(int index) {
92 | if (index >= 0 && index < getItemsCount()) {
93 | int value = minValue + index;
94 | String text = !TextUtils.isEmpty(format) ? String.format(format, value) : Integer.toString(value);
95 | text = TextUtils.isEmpty(unit) ? text : text + unit;
96 |
97 | return text;
98 | }
99 | return null;
100 | }
101 |
102 | @Override
103 | public int getItemsCount() {
104 | return maxValue - minValue + 1;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/adapters/WheelViewAdapter.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.adapters;
2 |
3 | import android.database.DataSetObserver;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 |
7 | import org.wangchenlong.datescroller.widget.config.ScrollerConfig;
8 |
9 |
10 | /**
11 | * 滚轮列表视图的适配器接口
12 | */
13 | public interface WheelViewAdapter {
14 | /**
15 | * 获取滚轮项目的数量
16 | *
17 | * @return 滚轮项目数量
18 | */
19 | int getItemsCount();
20 |
21 | /**
22 | * 根据索引获取展示项目的视图.
23 | *
24 | * @param index 索引
25 | * @param convertView 复用视图
26 | * @param parent 依附的父视图
27 | * @return 当前视图
28 | */
29 | View getItem(int index, View convertView, ViewGroup parent);
30 |
31 | /**
32 | * 获取一个空的滚轮视图, 在开始或者结尾
33 | *
34 | * @param convertView 复用视图
35 | * @param parent 依附的父视图
36 | * @return 空视图
37 | */
38 | View getEmptyItem(View convertView, ViewGroup parent);
39 |
40 | /**
41 | * 注册数据集的观察者, 供适配器使用
42 | *
43 | * @param observer 被注册观察者
44 | */
45 | void registerDataSetObserver(DataSetObserver observer);
46 |
47 | /**
48 | * 取消已注册数据集的观察者
49 | *
50 | * @param observer 被取消的观察者
51 | */
52 | void unregisterDataSetObserver(DataSetObserver observer);
53 |
54 | /**
55 | * 获取滚轮的配置
56 | *
57 | * @return 配置
58 | */
59 | ScrollerConfig getConfig();
60 |
61 | /**
62 | * 设置滚轮的配置
63 | *
64 | * @param config 配置
65 | */
66 | void setConfig(ScrollerConfig config);
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/config/DefaultConfig.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.config;
2 |
3 |
4 | import org.wangchenlong.datescroller.R;
5 | import org.wangchenlong.datescroller.widget.data.Type;
6 |
7 | /**
8 | * 默认配置, 类型, 颜色, 文字
9 | *
10 | * @author C.L. Wang
11 | */
12 | public class DefaultConfig {
13 | public static final Type TYPE = Type.ALL; // 全部类型
14 |
15 | public static final int TOOLBAR_BKG_COLOR = R.color.toolbar_bg; // 背景颜色
16 | public static final int ITEM_SELECTOR_LINE = R.color.item_selector_line; // 选择线颜色
17 | public static final int ITEM_SELECTOR_RECT = R.color.item_selector_rect; // 选择框颜色
18 |
19 |
20 | public static final int TOOLBAR_TV_COLOR = 0xFFFFFFFF;
21 | public static final int TV_NORMAL_COLOR = R.color.timepicker_dialog_bg; // 默认文字颜色
22 | public static final int TV_SELECTOR_COLOR = R.color.toolbar_text_cancel; // 选中文字颜色
23 |
24 | public static final int TV_SIZE = 12; // 尺寸
25 |
26 | public static final boolean CYCLIC = true; // 循环
27 |
28 | public static final int MAX_LINE = 5; // 最大行数, 依据控件样式
29 |
30 | // 默认文案
31 | public static String CANCEL = "取消";
32 | public static String SURE = "确定";
33 | public static String TITLE = "请选择日期";
34 | public static String YEAR = "年";
35 | public static String MONTH = "月";
36 | public static String DAY = "日";
37 | public static String HOUR = "时";
38 | public static String MINUTE = "分";
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/config/ScrollerConfig.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.config;
2 |
3 |
4 | import android.support.annotation.ColorRes;
5 |
6 | import org.wangchenlong.datescroller.widget.data.Type;
7 | import org.wangchenlong.datescroller.widget.data.WheelCalendar;
8 | import org.wangchenlong.datescroller.widget.listener.OnDateSetListener;
9 |
10 | import static org.wangchenlong.datescroller.widget.config.DefaultConfig.MAX_LINE;
11 |
12 | /**
13 | * 滚动配置
14 | */
15 | public class ScrollerConfig {
16 | public Type mType = DefaultConfig.TYPE;
17 | @ColorRes public int mToolbarBkgColor = DefaultConfig.TOOLBAR_BKG_COLOR; // 背景颜色
18 | @ColorRes public int mItemSelectorLine = DefaultConfig.ITEM_SELECTOR_LINE; // 选中线颜色
19 | @ColorRes public int mItemSelectorRect = DefaultConfig.ITEM_SELECTOR_RECT; // 选中框颜色
20 |
21 | public String mCancelString = DefaultConfig.CANCEL; // 取消
22 | public String mSureString = DefaultConfig.SURE; // 确认
23 | public String mTitleString = DefaultConfig.TITLE; // 标题
24 | public int mToolBarTVColor = DefaultConfig.TOOLBAR_TV_COLOR; // ToolBar的颜色
25 |
26 | public int mWheelTVNormalColor = DefaultConfig.TV_NORMAL_COLOR; // 滚轮默认颜色
27 | public int mWheelTVSelectorColor = DefaultConfig.TV_SELECTOR_COLOR; // 滚轮选中颜色
28 | public int mWheelTVSize = DefaultConfig.TV_SIZE; // 文字默认大小
29 | public boolean cyclic = DefaultConfig.CYCLIC; // 是否循环
30 |
31 | public String mYear = DefaultConfig.YEAR; // 年单位
32 | public String mMonth = DefaultConfig.MONTH; // 月单位
33 | public String mDay = DefaultConfig.DAY; // 日单位
34 | public String mHour = DefaultConfig.HOUR; // 小时单位
35 | public String mMinute = DefaultConfig.MINUTE; // 分钟单位
36 |
37 | public WheelCalendar mMinCalendar = new WheelCalendar(0); // 最小日期
38 | public WheelCalendar mMaxCalendar = new WheelCalendar(0); // 最大日期
39 | public WheelCalendar mCurCalendar = new WheelCalendar(System.currentTimeMillis()); // 当前日期
40 | public WheelCalendar mCurFinishCalendar = new WheelCalendar(System.currentTimeMillis()); // 当前结束日期
41 |
42 | public OnDateSetListener mCallback; // 回调
43 |
44 | public int mMaxLines = MAX_LINE; // 最大行数, 依据控件样式
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/data/Type.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.data;
2 |
3 | public enum Type {
4 | // 五种选择模式,年月日时分,年月日,时分,月日时分,年月
5 | ALL,
6 | YEAR_MONTH_DAY,
7 | HOURS_MINS,
8 | MONTH_DAY_HOUR_MIN,
9 | YEAR_MONTH,
10 | YEAR
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/data/WheelCalendar.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.data;
2 |
3 | import java.util.Calendar;
4 |
5 | /**
6 | * 滚轮日期, 通过秒转换时间数据
7 | *
8 | * @author C.L. Wang
9 | */
10 | public class WheelCalendar {
11 |
12 | public int year, month, day, hour, minute;
13 |
14 | private boolean mNoRange; // 是否含有数据
15 |
16 | public WheelCalendar(long milliseconds) {
17 | initData(milliseconds);
18 | }
19 |
20 | private void initData(long milliseconds) {
21 | if (milliseconds == 0) {
22 | mNoRange = true;
23 | return;
24 | }
25 | Calendar calendar = Calendar.getInstance();
26 | calendar.clear();
27 | calendar.setTimeInMillis(milliseconds);
28 |
29 | year = calendar.get(Calendar.YEAR);
30 | month = calendar.get(Calendar.MONTH) + 1;
31 | day = calendar.get(Calendar.DAY_OF_MONTH);
32 | hour = calendar.get(Calendar.HOUR_OF_DAY);
33 | minute = calendar.get(Calendar.MINUTE);
34 | }
35 |
36 | public boolean isNoRange() {
37 | return mNoRange;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/data/source/TimeDataSource.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.data.source;
2 |
3 |
4 | import org.wangchenlong.datescroller.widget.data.WheelCalendar;
5 |
6 | /**
7 | * 数据源的接口
8 | *
9 | * @author C.L. Wang
10 | */
11 | public interface TimeDataSource {
12 |
13 | int getMinYear();
14 |
15 | int getMaxYear();
16 |
17 | int getMinMonth(int currentYear);
18 |
19 | int getMaxMonth(int currentYear);
20 |
21 | int getMinDay(int year, int month);
22 |
23 | int getMaxDay(int year, int month);
24 |
25 | int getMinHour(int year, int month, int day);
26 |
27 | int getMaxHour(int year, int month, int day);
28 |
29 | int getMinMinute(int year, int month, int day, int hour);
30 |
31 | int getMaxMinute(int year, int month, int day, int hour);
32 |
33 | boolean isMinYear(int year);
34 |
35 | boolean isMinMonth(int year, int month);
36 |
37 | boolean isMinDay(int year, int month, int day);
38 |
39 | boolean isMinHour(int year, int month, int day, int hour);
40 |
41 | WheelCalendar getDefaultCalendar();
42 | WheelCalendar getDefaultFinishCalendar();
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/data/source/TimeRepository.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.data.source;
2 |
3 |
4 | import org.wangchenlong.datescroller.widget.config.ScrollerConfig;
5 | import org.wangchenlong.datescroller.widget.data.WheelCalendar;
6 | import org.wangchenlong.datescroller.widget.utils.DateConstants;
7 | import org.wangchenlong.datescroller.widget.utils.Utils;
8 |
9 | import java.util.Calendar;
10 |
11 | /**
12 | * 时间数据集合, 代理WheelCalendar和DateConstants
13 | *
14 | * @author C.L. Wang
15 | */
16 | public class TimeRepository implements TimeDataSource {
17 | private ScrollerConfig mScrollerConfig;
18 |
19 | private WheelCalendar mCalendarMin, mCalendarMax;
20 |
21 | private boolean mIsMinNoRange, mIsMaxNoRange;
22 |
23 | public TimeRepository(ScrollerConfig scrollerConfig) {
24 | mScrollerConfig = scrollerConfig;
25 | mCalendarMin = scrollerConfig.mMinCalendar;
26 | mCalendarMax = scrollerConfig.mMaxCalendar;
27 |
28 | mIsMinNoRange = mCalendarMin.isNoRange();
29 | mIsMaxNoRange = mCalendarMax.isNoRange();
30 | }
31 |
32 | @Override
33 | public int getMinYear() {
34 | if (mIsMinNoRange)
35 | return DateConstants.DEFAULT_MIN_YEAR;
36 | else
37 | return mCalendarMin.year;
38 | }
39 |
40 | @Override
41 | public int getMaxYear() {
42 | if (mIsMaxNoRange)
43 | return getMinYear() + DateConstants.YEAR_COUNT;
44 |
45 | return mCalendarMax.year;
46 | }
47 |
48 | @Override
49 | public int getMinMonth(int year) {
50 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year))
51 | return mCalendarMin.month;
52 |
53 | return DateConstants.MIN_MONTH;
54 | }
55 |
56 | @Override
57 | public int getMaxMonth(int year) {
58 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year))
59 | return mCalendarMax.month;
60 |
61 | return DateConstants.MAX_MONTH;
62 | }
63 |
64 | @Override
65 | public int getMinDay(int year, int month) {
66 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year, month))
67 | return mCalendarMin.day;
68 |
69 | return DateConstants.MIN_DAY;
70 | }
71 |
72 | @Override
73 | public int getMaxDay(int year, int month) {
74 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year, month))
75 | return mCalendarMax.day;
76 |
77 | Calendar calendar = Calendar.getInstance();
78 | calendar.set(Calendar.YEAR, year);
79 | calendar.set(Calendar.DATE, 1);
80 | calendar.set(Calendar.MONTH, month - 1);
81 |
82 | return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
83 | }
84 |
85 | @Override
86 | public int getMinHour(int year, int month, int day) {
87 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year, month, day))
88 | return mCalendarMin.hour;
89 | else
90 | return DateConstants.MIN_HOUR;
91 | }
92 |
93 | @Override
94 | public int getMaxHour(int year, int month, int day) {
95 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year, month, day))
96 | return mCalendarMax.hour;
97 | else
98 | return DateConstants.MAX_HOUR;
99 | }
100 |
101 | @Override
102 | public int getMinMinute(int year, int month, int day, int hour) {
103 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year, month, day, hour))
104 | return mCalendarMin.minute + 1;
105 | else
106 | return DateConstants.MIN_MINUTE;
107 | }
108 |
109 | @Override
110 | public int getMaxMinute(int year, int month, int day, int hour) {
111 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year, month, day, hour))
112 | return mCalendarMax.minute;
113 |
114 | return DateConstants.MAX_MINUTE;
115 | }
116 |
117 | @Override
118 | public boolean isMinYear(int year) {
119 | return Utils.isTimeEquals(mCalendarMin, year);
120 | }
121 |
122 | @Override
123 | public boolean isMinMonth(int year, int month) {
124 | return Utils.isTimeEquals(mCalendarMin, year, month);
125 | }
126 |
127 | @Override
128 | public boolean isMinDay(int year, int month, int day) {
129 | return Utils.isTimeEquals(mCalendarMin, year, month, day);
130 | }
131 |
132 | @Override
133 | public boolean isMinHour(int year, int month, int day, int hour) {
134 | return Utils.isTimeEquals(mCalendarMin, year, month, day, hour);
135 | }
136 |
137 |
138 | @Override
139 | public WheelCalendar getDefaultCalendar() {
140 | return mScrollerConfig.mCurCalendar;
141 | }
142 |
143 | @Override
144 | public WheelCalendar getDefaultFinishCalendar() {
145 | return mScrollerConfig.mCurFinishCalendar;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/listener/OnDateSetListener.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.listener;
2 |
3 |
4 | import org.wangchenlong.datescroller.widget.DateScrollerDialog;
5 |
6 | /**
7 | * 日期设置的监听器
8 | *
9 | * @author C.L. Wang
10 | */
11 | public interface OnDateSetListener {
12 | void onDateSet(DateScrollerDialog timePickerView, long milliseconds, long milliFinishSeconds);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/utils/DateConstants.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.utils;
2 |
3 | /**
4 | * 时间的若干常量
5 | *
6 | * @author C.L. Wang
7 | */
8 | public class DateConstants {
9 | public static final String FORMAT = "%02d";
10 |
11 | public static final int DEFAULT_MIN_YEAR = 2015;
12 | public static final int YEAR_COUNT = 50;
13 | public static final int MIN_MONTH = 1;
14 | public static final int MAX_MONTH = 12;
15 | public static final int MIN_DAY = MIN_MONTH;
16 | public static final int MIN_HOUR = 0;
17 | public static final int MAX_HOUR = 23;
18 | public static final int MIN_MINUTE = 0;
19 | public static final int MAX_MINUTE = 59;
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.utils;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import org.wangchenlong.datescroller.widget.data.WheelCalendar;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 |
12 | /**
13 | * 工具类
14 | *
15 | * @author C.L.Wang
16 | */
17 | public class Utils {
18 |
19 | /**
20 | * 判断时间是否相等
21 | *
22 | * @param calendar 日期
23 | * @param params 参数
24 | * @return 时间是否相等
25 | */
26 | public static boolean isTimeEquals(WheelCalendar calendar, int... params) {
27 | switch (params.length) {
28 | case 1:
29 | return calendar.year == params[0];
30 | case 2:
31 | return calendar.year == params[0] &&
32 | calendar.month == params[1];
33 | case 3:
34 | return calendar.year == params[0] &&
35 | calendar.month == params[1] &&
36 | calendar.day == params[2];
37 | case 4:
38 | return calendar.year == params[0] &&
39 | calendar.month == params[1] &&
40 | calendar.day == params[2] &&
41 | calendar.hour == params[3];
42 | }
43 | return false;
44 | }
45 |
46 | /**
47 | * 全部隐藏视图
48 | *
49 | * @param views 视图
50 | */
51 | public static void hideViews(View... views) {
52 | for (View view : views) {
53 | view.setVisibility(View.GONE);
54 | }
55 | }
56 |
57 | public static int px2dp(Context context, float pxValue) {
58 | float m = context.getResources().getDisplayMetrics().density;
59 | return (int) (pxValue / m + 0.5f);
60 | }
61 |
62 | public static int dp2px(Context context, float dipValue) {
63 | float m = context.getResources().getDisplayMetrics().density;
64 | return (int) (dipValue * m + 0.5f);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/wheel/ItemsRange.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Android Wheel Control.
3 | * https://code.google.com/p/android-wheel/
4 | *
5 | * Copyright 2011 Yuri Kanivets
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | package org.wangchenlong.datescroller.widget.wheel;
21 |
22 | /**
23 | * Range for visible items.
24 | */
25 | public class ItemsRange {
26 | // First item number
27 | private int first;
28 |
29 | // Items count
30 | private int count;
31 |
32 | /**
33 | * Default constructor. Creates an empty range
34 | */
35 | public ItemsRange() {
36 | this(0, 0);
37 | }
38 |
39 | /**
40 | * Constructor
41 | *
42 | * @param first the number of first item
43 | * @param count the count of items
44 | */
45 | public ItemsRange(int first, int count) {
46 | this.first = first;
47 | this.count = count;
48 | }
49 |
50 | /**
51 | * Gets number of first item
52 | *
53 | * @return the number of the first item
54 | */
55 | public int getFirst() {
56 | return first;
57 | }
58 |
59 | /**
60 | * Gets number of last item
61 | *
62 | * @return the number of last item
63 | */
64 | public int getLast() {
65 | return getFirst() + getCount() - 1;
66 | }
67 |
68 | /**
69 | * Get items count
70 | *
71 | * @return the count of items
72 | */
73 | public int getCount() {
74 | return count;
75 | }
76 |
77 | /**
78 | * Tests whether item is contained by range
79 | *
80 | * @param index the item number
81 | * @return true if item is contained
82 | */
83 | public boolean contains(int index) {
84 | return index >= getFirst() && index <= getLast();
85 | }
86 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/wheel/OnWheelChangedListener.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.wheel;
2 |
3 | /**
4 | * Wheel changed listener interface.
5 | * The onChanged() method is called whenever current wheel positions is changed:
6 | * New Wheel position is set
7 | * Wheel view is scrolled
8 | */
9 | public interface OnWheelChangedListener {
10 | /**
11 | * Callback method to be invoked when current item changed
12 | *
13 | * @param wheel the wheel view whose state has changed
14 | * @param oldValue the old value of current item
15 | * @param newValue the new value of current item
16 | */
17 | void onChanged(WheelView wheel, int oldValue, int newValue);
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/wheel/OnWheelClickedListener.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.wheel;
2 |
3 | /**
4 | * Wheel clicked listener interface.
5 | * The onItemClicked() method is called whenever a wheel item is clicked
6 | * New Wheel position is set
7 | * Wheel view is scrolled
8 | */
9 | public interface OnWheelClickedListener {
10 | /**
11 | * Callback method to be invoked when current item clicked
12 | *
13 | * @param wheel the wheel view
14 | * @param itemIndex the index of clicked item
15 | */
16 | void onItemClicked(WheelView wheel, int itemIndex);
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/wheel/OnWheelScrollListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Yuri Kanivets
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.wangchenlong.datescroller.widget.wheel;
18 |
19 | /**
20 | * Wheel scrolled listener interface.
21 | */
22 | public interface OnWheelScrollListener {
23 | /**
24 | * Callback method to be invoked when scrolling started.
25 | *
26 | * @param wheel the wheel view whose state has changed.
27 | */
28 | void onScrollingStarted(WheelView wheel);
29 |
30 | /**
31 | * Callback method to be invoked when scrolling ended.
32 | *
33 | * @param wheel the wheel view whose state has changed.
34 | */
35 | void onScrollingFinished(WheelView wheel);
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/wheel/WheelRecycle.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.wheel;
2 |
3 | import android.view.View;
4 | import android.widget.LinearLayout;
5 |
6 | import java.util.LinkedList;
7 | import java.util.List;
8 |
9 | /**
10 | * Recycle stores wheel items to reuse.
11 | */
12 | public class WheelRecycle {
13 | // Cached items
14 | private List items;
15 |
16 | // Cached empty items
17 | private List emptyItems;
18 |
19 | // Wheel view
20 | private WheelView wheel;
21 |
22 |
23 | public WheelRecycle(WheelView wheel) {
24 | this.wheel = wheel;
25 | }
26 |
27 | public int recycleItems(LinearLayout layout, int firstItem, ItemsRange range, int currentItem) {
28 | int index = firstItem;
29 | for (int i = 0; i < layout.getChildCount(); ) {
30 | if (!range.contains(index)) {
31 | recycleView(layout.getChildAt(i), index, currentItem);
32 | layout.removeViewAt(i);
33 | if (i == 0) { // first item
34 | firstItem++;
35 | }
36 | } else {
37 | i++; // go to next item
38 | }
39 | index++;
40 | }
41 | return firstItem;
42 | }
43 |
44 | /**
45 | * Gets item view
46 | *
47 | * @return the cached view
48 | */
49 | public View getItem() {
50 | return getCachedView(items);
51 | }
52 |
53 | /**
54 | * Gets empty item view
55 | *
56 | * @return the cached empty view
57 | */
58 | public View getEmptyItem() {
59 | return getCachedView(emptyItems);
60 | }
61 |
62 | /**
63 | * Clears all views
64 | */
65 | public void clearAll() {
66 | if (items != null) {
67 | items.clear();
68 | }
69 | if (emptyItems != null) {
70 | emptyItems.clear();
71 | }
72 | }
73 |
74 | /**
75 | * Adds view to specified cache. Creates a cache list if it is null.
76 | *
77 | * @param view the view to be cached
78 | * @param cache the cache list
79 | * @return the cache list
80 | */
81 | private List addView(View view, List cache) {
82 | if (cache == null) {
83 | cache = new LinkedList();
84 | }
85 |
86 | cache.add(view);
87 | return cache;
88 | }
89 |
90 | /**
91 | * Adds view to cache. Determines view type (item view or empty one) by index.
92 | *
93 | * @param view the view to be cached
94 | * @param index the index of view
95 | */
96 | private void recycleView(View view, int index, int current) {
97 | int count = wheel.getViewAdapter().getItemsCount();
98 |
99 |
100 | if ((index < 0 || index >= count) && !wheel.isCyclic()) {
101 | // empty view
102 | emptyItems = addView(view, emptyItems);
103 | } else {
104 | while (index < 0) {
105 | index = count + index;
106 | }
107 | index %= count;
108 | items = addView(view, items);
109 | }
110 | }
111 |
112 | /**
113 | * Gets view from specified cache.
114 | *
115 | * @param cache the cache
116 | * @return the first view from cache.
117 | */
118 | private View getCachedView(List cache) {
119 | if (cache != null && cache.size() > 0) {
120 | View view = cache.get(0);
121 | cache.remove(0);
122 | return view;
123 | }
124 | return null;
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/wheel/WheelScroller.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller.widget.wheel;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.os.Message;
6 | import android.view.GestureDetector;
7 | import android.view.GestureDetector.SimpleOnGestureListener;
8 | import android.view.MotionEvent;
9 | import android.view.animation.Interpolator;
10 | import android.widget.Scroller;
11 |
12 | public class WheelScroller {
13 |
14 | public static final int MIN_DELTA_FOR_SCROLLING = 1;
15 | private static final int SCROLLING_DURATION = 400;
16 | // Messages
17 | private final int MESSAGE_SCROLL = 0;
18 | private final int MESSAGE_JUSTIFY = 1;
19 | private ScrollingListener listener;
20 | // Context
21 | private Context context;
22 | // Scrolling
23 | private GestureDetector gestureDetector;
24 | private Scroller scroller;
25 | private int lastScrollY;
26 | private float lastTouchedY;
27 | private boolean isScrollingPerformed;
28 | // animation handler
29 | private Handler animationHandler = new Handler() {
30 | public void handleMessage(Message msg) {
31 | scroller.computeScrollOffset();
32 | int currY = scroller.getCurrY();
33 | int delta = lastScrollY - currY;
34 | lastScrollY = currY;
35 | if (delta != 0) {
36 | listener.onScroll(delta);
37 | }
38 |
39 | // scrolling is not finished when it comes to final Y
40 | // so, finish it manually
41 | if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) {
42 | currY = scroller.getFinalY();
43 | scroller.forceFinished(true);
44 | }
45 | if (!scroller.isFinished()) {
46 | animationHandler.sendEmptyMessage(msg.what);
47 | } else if (msg.what == MESSAGE_SCROLL) {
48 | justify();
49 | } else {
50 | finishScrolling();
51 | }
52 | }
53 | };
54 | // gesture listener
55 | private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {
56 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
57 | // Do scrolling in onTouchEvent() since onScroll() are not call immediately
58 | // when user touch and move the wheel
59 | return true;
60 | }
61 |
62 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
63 | lastScrollY = 0;
64 | final int maxY = 0x7FFFFFFF;
65 | final int minY = -maxY;
66 | scroller.fling(0, lastScrollY, 0, (int) -velocityY, 0, 0, minY, maxY);
67 | setNextMessage(MESSAGE_SCROLL);
68 | return true;
69 | }
70 | };
71 |
72 |
73 | public WheelScroller(Context context, ScrollingListener listener) {
74 | gestureDetector = new GestureDetector(context, gestureListener);
75 | gestureDetector.setIsLongpressEnabled(false);
76 |
77 | scroller = new Scroller(context);
78 |
79 | this.listener = listener;
80 | this.context = context;
81 | }
82 |
83 |
84 | public void setInterpolator(Interpolator interpolator) {
85 | scroller.forceFinished(true);
86 | scroller = new Scroller(context, interpolator);
87 | }
88 |
89 | public void scroll(int distance, int time) {
90 | scroller.forceFinished(true);
91 |
92 | lastScrollY = 0;
93 |
94 | scroller.startScroll(0, 0, 0, distance, time != 0 ? time : SCROLLING_DURATION);
95 | setNextMessage(MESSAGE_SCROLL);
96 |
97 | startScrolling();
98 | }
99 |
100 | /**
101 | * Stops scrolling
102 | */
103 | public void stopScrolling() {
104 | scroller.forceFinished(true);
105 | }
106 |
107 | /**
108 | * Handles Touch event
109 | *
110 | * @param event the motion event
111 | * @return
112 | */
113 | public boolean onTouchEvent(MotionEvent event) {
114 | switch (event.getAction()) {
115 | case MotionEvent.ACTION_DOWN:
116 | lastTouchedY = event.getY();
117 | scroller.forceFinished(true);
118 | clearMessages();
119 | break;
120 |
121 | case MotionEvent.ACTION_MOVE:
122 | // perform scrolling
123 | int distanceY = (int) (event.getY() - lastTouchedY);
124 | if (distanceY != 0) {
125 | startScrolling();
126 | listener.onScroll(distanceY);
127 | lastTouchedY = event.getY();
128 | }
129 | break;
130 | }
131 |
132 | if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) {
133 | justify();
134 | }
135 |
136 | return true;
137 | }
138 |
139 | /**
140 | * Set next message to queue. Clears queue before.
141 | *
142 | * @param message the message to set
143 | */
144 | private void setNextMessage(int message) {
145 | clearMessages();
146 | animationHandler.sendEmptyMessage(message);
147 | }
148 |
149 | /**
150 | * Clears messages from queue
151 | */
152 | private void clearMessages() {
153 | animationHandler.removeMessages(MESSAGE_SCROLL);
154 | animationHandler.removeMessages(MESSAGE_JUSTIFY);
155 | }
156 |
157 | /**
158 | * Justifies wheel
159 | */
160 | private void justify() {
161 | listener.onJustify();
162 | setNextMessage(MESSAGE_JUSTIFY);
163 | }
164 |
165 | /**
166 | * Starts scrolling
167 | */
168 | private void startScrolling() {
169 | if (!isScrollingPerformed) {
170 | isScrollingPerformed = true;
171 | listener.onStarted();
172 | }
173 | }
174 |
175 | /**
176 | * Finishes scrolling
177 | */
178 | void finishScrolling() {
179 | if (isScrollingPerformed) {
180 | listener.onFinished();
181 | isScrollingPerformed = false;
182 | }
183 | }
184 |
185 | public interface ScrollingListener {
186 | /**
187 | * Scrolling callback called when scrolling is performed.
188 | *
189 | * @param distance the distance to scroll
190 | */
191 | void onScroll(int distance);
192 |
193 | /**
194 | * Starting callback called when scrolling is started
195 | */
196 | void onStarted();
197 |
198 | /**
199 | * Finishing callback called after justifying
200 | */
201 | void onFinished();
202 |
203 | /**
204 | * Justifying callback called to justify a view when scrolling is ended
205 | */
206 | void onJustify();
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wangchenlong/datescroller/widget/wheel/WheelView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Android Wheel Control.
3 | * https://code.google.com/p/android-wheel/
4 | *
5 | * Copyright 2011 Yuri Kanivets
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | package org.wangchenlong.datescroller.widget.wheel;
21 |
22 | import android.content.Context;
23 | import android.database.DataSetObserver;
24 | import android.graphics.Canvas;
25 | import android.graphics.Paint;
26 | import android.util.AttributeSet;
27 | import android.view.MotionEvent;
28 | import android.view.View;
29 | import android.view.ViewGroup.LayoutParams;
30 | import android.view.animation.Interpolator;
31 | import android.widget.LinearLayout;
32 | import android.widget.TextView;
33 |
34 |
35 | import org.wangchenlong.datescroller.R;
36 | import org.wangchenlong.datescroller.widget.adapters.WheelViewAdapter;
37 | import org.wangchenlong.datescroller.widget.config.DefaultConfig;
38 | import org.wangchenlong.datescroller.widget.config.ScrollerConfig;
39 | import org.wangchenlong.datescroller.widget.utils.Utils;
40 |
41 | import java.util.LinkedList;
42 | import java.util.List;
43 |
44 |
45 | /**
46 | * Numeric wheel view.
47 | */
48 | public class WheelView extends View {
49 |
50 | /**
51 | * Top and bottom items offset (to hide that)
52 | */
53 | private static final int ITEM_OFFSET_PERCENT = 10;
54 |
55 | /**
56 | * Left and right padding value
57 | */
58 | private static final int PADDING = 10;
59 |
60 | /**
61 | * Default count of visible items
62 | */
63 | private static final int DEF_VISIBLE_ITEMS = 5;
64 | // Cyclic
65 | boolean isCyclic = false;
66 | int defaultColor, selectorColor;
67 | // Wheel Values
68 | private int currentItem = 0;
69 | // Count of visible items
70 | private int visibleItems = DEF_VISIBLE_ITEMS;
71 | // Item height
72 | private int mItemHeight = 0;
73 | // Scrolling
74 | private WheelScroller scroller;
75 | private boolean isScrollingPerformed;
76 | private int scrollingOffset;
77 | // Items layout
78 | private LinearLayout itemsLayout;
79 | // The number of first item in layout
80 | private int firstItem;
81 | // View adapter
82 | private WheelViewAdapter viewAdapter;
83 |
84 | // Recycle
85 | private WheelRecycle recycle = new WheelRecycle(this);
86 | private Paint mPaintLineCenter, mPaintLineRight, mPaintRectCenter;
87 | private int mLineRightMar;
88 | // Listeners
89 | private List changingListeners = new LinkedList();
90 | private List scrollingListeners = new LinkedList();
91 | // Scrolling listener
92 | WheelScroller.ScrollingListener scrollingListener = new WheelScroller.ScrollingListener() {
93 | public void onStarted() {
94 | isScrollingPerformed = true;
95 | notifyScrollingListenersAboutStart();
96 | }
97 |
98 | public void onScroll(int distance) {
99 | doScroll(distance);
100 |
101 | int height = getHeight();
102 | if (scrollingOffset > height) {
103 | scrollingOffset = height;
104 | scroller.stopScrolling();
105 | } else if (scrollingOffset < -height) {
106 | scrollingOffset = -height;
107 | scroller.stopScrolling();
108 | }
109 | }
110 |
111 | public void onFinished() {
112 | if (isScrollingPerformed) {
113 | notifyScrollingListenersAboutEnd();
114 | isScrollingPerformed = false;
115 | }
116 |
117 | scrollingOffset = 0;
118 | invalidate();
119 | }
120 |
121 | public void onJustify() {
122 | if (Math.abs(scrollingOffset) > WheelScroller.MIN_DELTA_FOR_SCROLLING) {
123 | scroller.scroll(scrollingOffset, 0);
124 | }
125 | }
126 | };
127 | private List clickingListeners = new LinkedList();
128 | // Adapter listener
129 | private DataSetObserver dataObserver = new DataSetObserver() {
130 | @Override
131 | public void onChanged() {
132 | invalidateWheel(false);
133 | }
134 |
135 | @Override
136 | public void onInvalidated() {
137 | invalidateWheel(true);
138 | }
139 | };
140 |
141 | /**
142 | * Constructor
143 | */
144 | public WheelView(Context context, AttributeSet attrs, int defStyle) {
145 | super(context, attrs, defStyle);
146 | initData(context);
147 | }
148 |
149 | /**
150 | * Constructor
151 | */
152 | public WheelView(Context context, AttributeSet attrs) {
153 | super(context, attrs);
154 | initData(context);
155 | }
156 |
157 | /**
158 | * Constructor
159 | */
160 | public WheelView(Context context) {
161 | super(context);
162 | initData(context);
163 | }
164 |
165 | /**
166 | * Initializes class data
167 | *
168 | * @param context the context
169 | */
170 | private void initData(Context context) {
171 | scroller = new WheelScroller(getContext(), scrollingListener);
172 |
173 | mPaintLineCenter = new Paint();
174 | mPaintLineCenter.setColor(getResources().getColor(DefaultConfig.TOOLBAR_BKG_COLOR));
175 | mPaintLineCenter.setAntiAlias(true);
176 | mPaintLineCenter.setStrokeWidth(1);
177 | mPaintLineCenter.setStyle(Paint.Style.FILL);
178 |
179 | mPaintLineRight = new Paint();
180 | mPaintLineRight.setColor(0xffe8e8e8);
181 | mPaintLineRight.setAntiAlias(true);
182 | // mPaintLineRight.setStrokeWidth(context.getResources().getDimensionPixelSize(R.dimen.picker_line_width));
183 | mPaintLineRight.setStrokeWidth(1);
184 | mPaintLineRight.setStyle(Paint.Style.FILL);
185 |
186 | mPaintRectCenter = new Paint();
187 | mPaintRectCenter.setColor(getResources().getColor(DefaultConfig.TOOLBAR_BKG_COLOR));
188 | mPaintRectCenter.setAlpha((int) (0.1 * 255));
189 | mPaintRectCenter.setAntiAlias(true);
190 | mPaintRectCenter.setStyle(Paint.Style.FILL);
191 |
192 | mLineRightMar = context.getResources().getDimensionPixelSize(R.dimen.picker_line_mar);
193 |
194 | defaultColor = DefaultConfig.TV_NORMAL_COLOR;
195 | selectorColor = DefaultConfig.TV_SELECTOR_COLOR;
196 | }
197 |
198 | public void setConfig(ScrollerConfig config) {
199 | mPaintLineCenter.setColor(getResources().getColor(config.mItemSelectorLine));
200 |
201 | mPaintRectCenter.setColor(getResources().getColor(config.mItemSelectorRect));
202 | mPaintRectCenter.setAlpha((int) (0.1 * 255));
203 |
204 | defaultColor = config.mWheelTVNormalColor;
205 | selectorColor = config.mWheelTVSelectorColor;
206 | }
207 |
208 |
209 | /**
210 | * Set the the specified scrolling interpolator
211 | *
212 | * @param interpolator the interpolator
213 | */
214 | public void setInterpolator(Interpolator interpolator) {
215 | scroller.setInterpolator(interpolator);
216 | }
217 |
218 | /**
219 | * Gets count of visible items
220 | *
221 | * @return the count of visible items
222 | */
223 | public int getVisibleItems() {
224 | return visibleItems;
225 | }
226 |
227 | /**
228 | * Sets the desired count of visible items.
229 | * Actual amount of visible items depends on wheel layout parameters.
230 | * To apply changes and rebuild view call measure().
231 | *
232 | * @param count the desired count for visible items
233 | */
234 | public void setVisibleItems(int count) {
235 | visibleItems = count;
236 | }
237 |
238 | /**
239 | * Gets view adapter
240 | *
241 | * @return the view adapter
242 | */
243 | public WheelViewAdapter getViewAdapter() {
244 | return viewAdapter;
245 | }
246 |
247 | /**
248 | * Sets view adapter. Usually new adapters contain different views, so
249 | * it needs to rebuild view by calling measure().
250 | *
251 | * @param viewAdapter the view adapter
252 | */
253 | public void setViewAdapter(WheelViewAdapter viewAdapter) {
254 | if (this.viewAdapter != null) {
255 | this.viewAdapter.unregisterDataSetObserver(dataObserver);
256 | }
257 | this.viewAdapter = viewAdapter;
258 | if (this.viewAdapter != null) {
259 | this.viewAdapter.registerDataSetObserver(dataObserver);
260 | }
261 |
262 | setConfig(viewAdapter.getConfig());
263 | invalidateWheel(true);
264 | }
265 |
266 | /**
267 | * Adds wheel changing listener
268 | *
269 | * @param listener the listener
270 | */
271 | public void addChangingListener(OnWheelChangedListener listener) {
272 | changingListeners.add(listener);
273 | }
274 |
275 | /**
276 | * Removes wheel changing listener
277 | *
278 | * @param listener the listener
279 | */
280 | public void removeChangingListener(OnWheelChangedListener listener) {
281 | changingListeners.remove(listener);
282 | }
283 |
284 | /**
285 | * Notifies changing listeners
286 | *
287 | * @param oldValue the old wheel value
288 | * @param newValue the new wheel value
289 | */
290 | protected void notifyChangingListeners(int oldValue, int newValue) {
291 | for (OnWheelChangedListener listener : changingListeners) {
292 | listener.onChanged(this, oldValue, newValue);
293 | }
294 |
295 | if (oldValue < 0 || newValue < 0 || itemsLayout == null)
296 | return;
297 |
298 | View oldView = itemsLayout.getChildAt(oldValue - firstItem);
299 | View newView = itemsLayout.getChildAt(newValue - firstItem);
300 |
301 | refreshTextStatus(oldView, oldValue);
302 | refreshTextStatus(newView, newValue);
303 |
304 | }
305 |
306 | /**
307 | * Adds wheel scrolling listener
308 | *
309 | * @param listener the listener
310 | */
311 | public void addScrollingListener(OnWheelScrollListener listener) {
312 | scrollingListeners.add(listener);
313 | }
314 |
315 | /**
316 | * Removes wheel scrolling listener
317 | *
318 | * @param listener the listener
319 | */
320 | public void removeScrollingListener(OnWheelScrollListener listener) {
321 | scrollingListeners.remove(listener);
322 | }
323 |
324 | /**
325 | * Notifies listeners about starting scrolling
326 | */
327 | protected void notifyScrollingListenersAboutStart() {
328 | for (OnWheelScrollListener listener : scrollingListeners) {
329 | listener.onScrollingStarted(this);
330 | }
331 | }
332 |
333 | /**
334 | * Notifies listeners about ending scrolling
335 | */
336 | protected void notifyScrollingListenersAboutEnd() {
337 | for (OnWheelScrollListener listener : scrollingListeners) {
338 | listener.onScrollingFinished(this);
339 | }
340 |
341 |
342 | }
343 |
344 | /**
345 | * Adds wheel clicking listener
346 | *
347 | * @param listener the listener
348 | */
349 | public void addClickingListener(OnWheelClickedListener listener) {
350 | clickingListeners.add(listener);
351 | }
352 |
353 | /**
354 | * Removes wheel clicking listener
355 | *
356 | * @param listener the listener
357 | */
358 | public void removeClickingListener(OnWheelClickedListener listener) {
359 | clickingListeners.remove(listener);
360 | }
361 |
362 | /**
363 | * Notifies listeners about clicking
364 | */
365 | protected void notifyClickListenersAboutClick(int item) {
366 | for (OnWheelClickedListener listener : clickingListeners) {
367 | listener.onItemClicked(this, item);
368 | }
369 | }
370 |
371 | /**
372 | * Gets current value
373 | *
374 | * @return the current value
375 | */
376 | public int getCurrentItem() {
377 | return currentItem;
378 | }
379 |
380 | /**
381 | * Sets the current item w/o animation. Does nothing when index is wrong.
382 | *
383 | * @param index the item index
384 | */
385 | public void setCurrentItem(int index) {
386 | setCurrentItem(index, false);
387 | }
388 |
389 | /**
390 | * Sets the current item. Does nothing when index is wrong.
391 | *
392 | * @param index the item index
393 | * @param animated the animation flag
394 | */
395 | public void setCurrentItem(int index, boolean animated) {
396 | if (viewAdapter == null || viewAdapter.getItemsCount() == 0) {
397 | return; // throw?
398 | }
399 |
400 | int itemCount = viewAdapter.getItemsCount();
401 | if (index < 0 || index >= itemCount) {
402 | if (isCyclic) {
403 | while (index < 0) {
404 | index += itemCount;
405 | }
406 | index %= itemCount;
407 | } else {
408 | return; // throw?
409 | }
410 | }
411 |
412 |
413 | if (index != currentItem) {
414 | if (animated) {
415 | int itemsToScroll = index - currentItem;
416 | if (isCyclic) {
417 | int scroll = itemCount + Math.min(index, currentItem) - Math.max(index, currentItem);
418 | if (scroll < Math.abs(itemsToScroll)) {
419 | itemsToScroll = itemsToScroll < 0 ? scroll : -scroll;
420 | }
421 | }
422 | scroll(itemsToScroll, 0);
423 | } else {
424 | scrollingOffset = 0;
425 |
426 | int old = currentItem;
427 | currentItem = index;
428 |
429 | notifyChangingListeners(old, currentItem);
430 |
431 | invalidate();
432 | }
433 |
434 |
435 | }
436 | }
437 |
438 | /**
439 | * Tests if wheel is cyclic. That means before the 1st item there is shown the last one
440 | *
441 | * @return true if wheel is cyclic
442 | */
443 | public boolean isCyclic() {
444 | return isCyclic;
445 | }
446 |
447 | /**
448 | * Set wheel cyclic flag
449 | *
450 | * @param isCyclic the flag to set
451 | */
452 | public void setCyclic(boolean isCyclic) {
453 | this.isCyclic = isCyclic;
454 | invalidateWheel(false);
455 | }
456 |
457 | /**
458 | * Invalidates wheel
459 | *
460 | * @param clearCaches if true then cached views will be clear
461 | */
462 | public void invalidateWheel(boolean clearCaches) {
463 | if (clearCaches) {
464 | recycle.clearAll();
465 | if (itemsLayout != null) {
466 | itemsLayout.removeAllViews();
467 | }
468 | scrollingOffset = 0;
469 | } else if (itemsLayout != null) {
470 | // cache all items
471 | recycle.recycleItems(itemsLayout, firstItem, new ItemsRange(), currentItem);
472 | }
473 |
474 | invalidate();
475 | }
476 |
477 | /**
478 | * Initializes resources
479 | */
480 | private void initResourcesIfNecessary() {
481 | setBackgroundResource(android.R.color.transparent);
482 | }
483 |
484 | /**
485 | * Calculates desired height for layout
486 | *
487 | * @param layout the source layout
488 | * @return the desired layout height
489 | */
490 | private int getDesiredHeight(LinearLayout layout) {
491 | if (layout != null && layout.getChildAt(0) != null) {
492 | mItemHeight = layout.getChildAt(0).getMeasuredHeight();
493 | }
494 |
495 | int desired = mItemHeight * visibleItems - mItemHeight * ITEM_OFFSET_PERCENT / 50;
496 |
497 | return Math.max(desired, getSuggestedMinimumHeight());
498 | }
499 |
500 | /**
501 | * Returns height of wheel item
502 | *
503 | * @return the item height
504 | */
505 | private int getItemHeight() {
506 | if (mItemHeight != 0) {
507 | return mItemHeight;
508 | }
509 |
510 | if (itemsLayout != null && itemsLayout.getChildAt(0) != null) {
511 | mItemHeight = itemsLayout.getChildAt(0).getHeight();
512 | return mItemHeight;
513 | }
514 |
515 | return getHeight() / visibleItems;
516 | }
517 |
518 | /**
519 | * Calculates control width and creates text layouts
520 | *
521 | * @param widthSize the input layout width
522 | * @param mode the layout mode
523 | * @return the calculated control width
524 | */
525 | private int calculateLayoutWidth(int widthSize, int mode) {
526 | initResourcesIfNecessary();
527 |
528 | // TODO: make it static
529 | itemsLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
530 | itemsLayout.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.UNSPECIFIED),
531 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
532 | int width = itemsLayout.getMeasuredWidth();
533 |
534 | if (mode == MeasureSpec.EXACTLY) {
535 | width = widthSize;
536 | } else {
537 | width += 2 * PADDING;
538 |
539 | // Check against our minimum width
540 | width = Math.max(width, getSuggestedMinimumWidth());
541 |
542 | if (mode == MeasureSpec.AT_MOST && widthSize < width) {
543 | width = widthSize;
544 | }
545 | }
546 |
547 | itemsLayout.measure(MeasureSpec.makeMeasureSpec(width - 2 * PADDING, MeasureSpec.EXACTLY),
548 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
549 |
550 | return width;
551 | }
552 |
553 | @Override
554 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
555 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
556 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
557 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
558 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
559 |
560 | int dp = Utils.px2dp(getContext(), heightSize);
561 |
562 | buildViewForMeasuring();
563 |
564 | int width = calculateLayoutWidth(widthSize, widthMode);
565 |
566 | int height;
567 | if (heightMode == MeasureSpec.EXACTLY) {
568 | height = heightSize;
569 | } else {
570 | height = getDesiredHeight(itemsLayout);
571 |
572 | if (heightMode == MeasureSpec.AT_MOST) {
573 | height = Math.min(height, heightSize);
574 | }
575 | }
576 |
577 | setMeasuredDimension(width, height);
578 | }
579 |
580 | @Override
581 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
582 | layout(r - l, b - t);
583 | }
584 |
585 | /**
586 | * Sets layouts width and height
587 | *
588 | * @param width the layout width
589 | * @param height the layout height
590 | */
591 | private void layout(int width, int height) {
592 | int itemsWidth = width - 2 * PADDING;
593 |
594 | itemsLayout.layout(0, 0, itemsWidth, height);
595 | }
596 |
597 | @Override
598 | protected void onDraw(Canvas canvas) {
599 | super.onDraw(canvas);
600 | if (viewAdapter != null && viewAdapter.getItemsCount() > 0) {
601 | updateView();
602 | drawItems(canvas);
603 | drawCenterRect(canvas);
604 | }
605 | }
606 |
607 |
608 | /**
609 | * Draws items
610 | *
611 | * @param canvas the canvas for drawing
612 | */
613 | private void drawItems(Canvas canvas) {
614 | canvas.save();
615 |
616 | int top = (currentItem - firstItem) * getItemHeight() + (getItemHeight() - getHeight()) / 2;
617 | canvas.translate(PADDING, -top + scrollingOffset);
618 |
619 | itemsLayout.draw(canvas);
620 |
621 | canvas.restore();
622 | }
623 |
624 | /**
625 | * Draws rect for current value
626 | *
627 | * @param canvas the canvas for drawing
628 | */
629 | private void drawCenterRect(Canvas canvas) {
630 | int center = getHeight() / 2;
631 | int offset = (int) (getItemHeight() / 2 * 1.2);
632 | // centerDrawable.setBounds(0, center - offset, getWidth(), center + offset);
633 | // centerDrawable.draw(canvas);
634 | canvas.drawRect(0, center - offset, getWidth(), center + offset, mPaintRectCenter);
635 |
636 | canvas.drawLine(0, center - offset, getWidth(), center - offset, mPaintLineCenter);
637 | canvas.drawLine(0, center + offset, getWidth(), center + offset, mPaintLineCenter);
638 |
639 | // 去掉右侧画线
640 | // int x = getWidth() - 1;
641 | // canvas.drawLine(x, mLineRightMar, x, getHeight() - mLineRightMar, mPaintLineRight);
642 | }
643 |
644 |
645 | @Override
646 | public boolean onTouchEvent(MotionEvent event) {
647 | if (!isEnabled() || getViewAdapter() == null) {
648 | return true;
649 | }
650 |
651 | switch (event.getAction()) {
652 | case MotionEvent.ACTION_MOVE:
653 | if (getParent() != null) {
654 | getParent().requestDisallowInterceptTouchEvent(true);
655 | }
656 | break;
657 |
658 | case MotionEvent.ACTION_UP:
659 | if (!isScrollingPerformed) {
660 | int distance = (int) event.getY() - getHeight() / 2;
661 | if (distance > 0) {
662 | distance += getItemHeight() / 2;
663 | } else {
664 | distance -= getItemHeight() / 2;
665 | }
666 | int items = distance / getItemHeight();
667 | if (items != 0 && isValidItemIndex(currentItem + items)) {
668 | notifyClickListenersAboutClick(currentItem + items);
669 | }
670 | }
671 | break;
672 | }
673 |
674 | return scroller.onTouchEvent(event);
675 | }
676 |
677 | /**
678 | * Scrolls the wheel
679 | *
680 | * @param delta the scrolling value
681 | */
682 | private void doScroll(int delta) {
683 | scrollingOffset += delta;
684 |
685 | int itemHeight = getItemHeight();
686 | int count = scrollingOffset / itemHeight;
687 |
688 | int pos = currentItem - count;
689 | int itemCount = viewAdapter.getItemsCount();
690 |
691 | int fixPos = scrollingOffset % itemHeight;
692 | if (Math.abs(fixPos) <= itemHeight / 2) {
693 | fixPos = 0;
694 | }
695 | if (isCyclic && itemCount > 0) {
696 | if (fixPos > 0) {
697 | pos--;
698 | count++;
699 | } else if (fixPos < 0) {
700 | pos++;
701 | count--;
702 | }
703 | // fix position by rotating
704 | while (pos < 0) {
705 | pos += itemCount;
706 | }
707 | pos %= itemCount;
708 | } else {
709 | //
710 | if (pos < 0) {
711 | count = currentItem;
712 | pos = 0;
713 | } else if (pos >= itemCount) {
714 | count = currentItem - itemCount + 1;
715 | pos = itemCount - 1;
716 | } else if (pos > 0 && fixPos > 0) {
717 | pos--;
718 | count++;
719 | } else if (pos < itemCount - 1 && fixPos < 0) {
720 | pos++;
721 | count--;
722 | }
723 | }
724 |
725 | int offset = scrollingOffset;
726 | if (pos != currentItem) {
727 | setCurrentItem(pos, false);
728 | } else {
729 | invalidate();
730 | }
731 |
732 | // update offset
733 | scrollingOffset = offset - count * itemHeight;
734 | if (scrollingOffset > getHeight()) {
735 | scrollingOffset = scrollingOffset % getHeight() + getHeight();
736 | }
737 | }
738 |
739 | /**
740 | * Scroll the wheel
741 | *
742 | * @param itemsToScroll items to scroll
743 | * @param time scrolling duration
744 | */
745 | public void scroll(int itemsToScroll, int time) {
746 | int distance = itemsToScroll * getItemHeight() - scrollingOffset;
747 | scroller.scroll(distance, time);
748 | }
749 |
750 | /**
751 | * Calculates range for wheel items
752 | *
753 | * @return the items range
754 | */
755 | private ItemsRange getItemsRange() {
756 | if (getItemHeight() == 0) {
757 | return null;
758 | }
759 |
760 | int first = currentItem;
761 | int count = 1;
762 |
763 | while (count * getItemHeight() < getHeight()) {
764 | first--;
765 | count += 2; // top + bottom items
766 | }
767 |
768 | if (scrollingOffset != 0) {
769 | if (scrollingOffset > 0) {
770 | first--;
771 | }
772 | count++;
773 |
774 | // process empty items above the first or below the second
775 | int emptyItems = scrollingOffset / getItemHeight();
776 | first -= emptyItems;
777 | count += Math.asin(emptyItems);
778 | }
779 | return new ItemsRange(first, count);
780 | }
781 |
782 | /**
783 | * Rebuilds wheel items if necessary. Caches all unused items.
784 | *
785 | * @return true if items are rebuilt
786 | */
787 | private boolean rebuildItems() {
788 | boolean updated = false;
789 | ItemsRange range = getItemsRange();
790 | if (itemsLayout != null) {
791 | int first = recycle.recycleItems(itemsLayout, firstItem, range, currentItem);
792 | updated = firstItem != first;
793 | firstItem = first;
794 | } else {
795 | createItemsLayout();
796 | updated = true;
797 | }
798 |
799 | if (!updated) {
800 | updated = firstItem != range.getFirst() || itemsLayout.getChildCount() != range.getCount();
801 | }
802 |
803 | if (firstItem > range.getFirst() && firstItem <= range.getLast()) {
804 | for (int i = firstItem - 1; i >= range.getFirst(); i--) {
805 | if (!addViewItem(i, true)) {
806 | break;
807 | }
808 | firstItem = i;
809 | }
810 | } else {
811 | firstItem = range.getFirst();
812 | }
813 |
814 | int first = firstItem;
815 | for (int i = itemsLayout.getChildCount(); i < range.getCount(); i++) {
816 | if (!addViewItem(firstItem + i, false) && itemsLayout.getChildCount() == 0) {
817 | first++;
818 | }
819 | }
820 | firstItem = first;
821 |
822 | return updated;
823 | }
824 |
825 | /**
826 | * Updates view. Rebuilds items and label if necessary, recalculate items sizes.
827 | */
828 | private void updateView() {
829 | if (rebuildItems()) {
830 | calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
831 | layout(getWidth(), getHeight());
832 | }
833 | }
834 |
835 | /**
836 | * Creates item layouts if necessary
837 | */
838 | private void createItemsLayout() {
839 | if (itemsLayout == null) {
840 | itemsLayout = new LinearLayout(getContext());
841 | itemsLayout.setOrientation(LinearLayout.VERTICAL);
842 | }
843 | }
844 |
845 | /**
846 | * Builds view for measuring
847 | */
848 | private void buildViewForMeasuring() {
849 | // clear all items
850 | if (itemsLayout != null) {
851 | recycle.recycleItems(itemsLayout, firstItem, new ItemsRange(), currentItem);
852 | } else {
853 | createItemsLayout();
854 | }
855 |
856 | // add views
857 | int addItems = visibleItems / 2;
858 | for (int i = currentItem + addItems; i >= currentItem - addItems; i--) {
859 | if (addViewItem(i, true)) {
860 | firstItem = i;
861 | }
862 | }
863 | }
864 |
865 | /**
866 | * Adds view for item to items layout
867 | *
868 | * @param index the item index
869 | * @param first the flag indicates if view should be first
870 | * @return true if corresponding item exists and is added
871 | */
872 | private boolean addViewItem(int index, boolean first) {
873 | View view = getItemView(index);
874 | refreshTextStatus(view, index);
875 | if (view != null) {
876 | if (first) {
877 | itemsLayout.addView(view, 0);
878 | } else {
879 | itemsLayout.addView(view);
880 | }
881 |
882 | return true;
883 | }
884 |
885 | return false;
886 | }
887 |
888 | void refreshTextStatus(View view, int index) {
889 | if (!(view instanceof TextView))
890 | return;
891 | TextView textView = (TextView) view;
892 | if (index == currentItem) {
893 | textView.setTextColor(getResources().getColor(selectorColor));
894 | } else {
895 | textView.setTextColor(getResources().getColor(defaultColor));
896 | }
897 | }
898 |
899 | /**
900 | * Checks whether intem index is valid
901 | *
902 | * @param index the item index
903 | * @return true if item index is not out of bounds or the wheel is cyclic
904 | */
905 | private boolean isValidItemIndex(int index) {
906 | return viewAdapter != null && viewAdapter.getItemsCount() > 0 &&
907 | (isCyclic || index >= 0 && index < viewAdapter.getItemsCount());
908 | }
909 |
910 | /**
911 | * Returns view for specified item
912 | *
913 | * @param index the item index
914 | * @return item view or empty view if index is out of bounds
915 | */
916 | private View getItemView(int index) {
917 | if (viewAdapter == null || viewAdapter.getItemsCount() == 0) {
918 | return null;
919 | }
920 | int count = viewAdapter.getItemsCount();
921 | if (!isValidItemIndex(index)) {
922 | return viewAdapter.getEmptyItem(recycle.getEmptyItem(), itemsLayout);
923 | } else {
924 | while (index < 0) {
925 | index = count + index;
926 | }
927 | }
928 |
929 | index %= count;
930 |
931 | View view = viewAdapter.getItem(index, recycle.getItem(), itemsLayout);
932 |
933 |
934 | return view;
935 | }
936 |
937 | /**
938 | * Stops scrolling
939 | */
940 | public void stopScrolling() {
941 | scroller.stopScrolling();
942 | }
943 | }
944 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/date_wheel_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
6 |
7 |
11 |
12 |
13 |
14 | -
17 |
18 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/timepicker_divider_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/timepicker_sel_text_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
24 |
25 |
33 |
34 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/time_scroller_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
21 |
22 |
31 |
32 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/timepicker_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
11 |
12 |
16 |
17 |
20 |
21 |
28 |
29 |
35 |
36 |
40 |
41 |
46 |
47 |
51 |
52 |
56 |
57 |
63 |
64 |
69 |
70 |
75 |
76 |
80 |
81 |
85 |
86 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/timepicker_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/tv_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huangssh/DateRangePicker/cf5039c7c149506770b9b8539d764886a4ffbba8/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huangssh/DateRangePicker/cf5039c7c149506770b9b8539d764886a4ffbba8/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huangssh/DateRangePicker/cf5039c7c149506770b9b8539d764886a4ffbba8/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huangssh/DateRangePicker/cf5039c7c149506770b9b8539d764886a4ffbba8/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huangssh/DateRangePicker/cf5039c7c149506770b9b8539d764886a4ffbba8/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #999999
8 | #FBFBFB
9 | #323232
10 | #39D167
11 | #E7E7E7
12 |
13 | #C2C2C2
14 | #FFFFFF
15 |
16 | #80000000
17 | #e8e8e8
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 12sp
7 | 48dp
8 | 180dp
9 |
10 | 5dp
11 | 1dp
12 | 15dp
13 | 230dp
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DateScroller
3 |
4 | 确定
5 | 取消
6 | 请选择日期
7 |
8 | 年
9 | 月
10 | 日
11 | 时
12 | 分
13 |
14 | 选择日期
15 | 请选择日期
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
20 |
21 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/test/java/org/wangchenlong/datescroller/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package org.wangchenlong.datescroller;
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.2.2'
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 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/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/huangssh/DateRangePicker/cf5039c7c149506770b9b8539d764886a4ffbba8/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 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.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/效果图.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huangssh/DateRangePicker/cf5039c7c149506770b9b8539d764886a4ffbba8/效果图.png
--------------------------------------------------------------------------------