├── library
├── .gitignore
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ ├── attrs.xml
│ │ │ ├── styles.xml
│ │ │ └── dimens_font.xml
│ │ ├── anim
│ │ │ ├── slide_in_from_bottom.xml
│ │ │ └── slide_out_to_bottom.xml
│ │ └── layout
│ │ │ └── bottom_sheet_dialog.xml
│ │ └── java
│ │ └── com
│ │ └── wheelpicker
│ │ ├── widget
│ │ ├── PickString.java
│ │ ├── AbstractViewWheelPicker.java
│ │ ├── TextWheelPickerAdapter.java
│ │ ├── ViewBaseAdapter.java
│ │ ├── IPickerView.java
│ │ ├── TextBaseAdapter.java
│ │ ├── AbstractTextWheelPicker.java
│ │ └── TextWheelPicker.java
│ │ ├── core
│ │ ├── OnWheelPickedListener.java
│ │ ├── WheelPickerUtil.java
│ │ ├── WheelScroller.java
│ │ ├── WheelPickerAdapter.java
│ │ ├── WheelPickerImpl.java
│ │ ├── IWheelPicker.java
│ │ ├── ScrollerCompat.java
│ │ ├── OverScrollerCompat.java
│ │ ├── OSUtils.java
│ │ ├── ScrollWheelPicker.java
│ │ └── AbstractWheelPicker.java
│ │ ├── OnDatePickListener.java
│ │ ├── OnDataPickListener.java
│ │ ├── OnCascadeWheelListener.java
│ │ ├── OnMultiDataPickListener.java
│ │ ├── DateTimeItem.java
│ │ ├── PickMode.java
│ │ ├── AdministrativeMap.java
│ │ ├── IDateTimePicker.java
│ │ ├── anim
│ │ └── Animation.java
│ │ ├── SingleTextWheelPicker.java
│ │ ├── WheelPickerData.java
│ │ ├── DataPickerUtils.java
│ │ ├── BottomSheet.java
│ │ ├── AdministrativeUtil.java
│ │ ├── DataPicker.java
│ │ └── MultipleTextWheelPicker.java
├── build.gradle
└── proguard-rules.pro
├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── encodings.xml
├── compiler.xml
├── vcs.xml
├── runConfigurations.xml
├── gradle.xml
├── misc.xml
└── markdown-navigator.xml
├── app
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ └── strings.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ └── layout
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_picker_old.xml
│ │ │ └── activity_picker_new.xml
│ │ ├── java
│ │ └── com
│ │ │ └── wp
│ │ │ └── demo
│ │ │ ├── Student.java
│ │ │ ├── MainActivity.java
│ │ │ └── OldPickerActivity.java
│ │ └── AndroidManifest.xml
├── .gitignore
├── proguard-rules.pro
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 3d_wheepicker
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yijiebuyi/3DWheelPicker/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *.iml
3 | .gradle
4 | /local.properties
5 | /.idea/*
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | libragy/libs/
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/*
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | libragy/libs/
10 | /app/gradlew
11 | /app/gradlew.bat
12 | /app/gradle
13 | .idea/*
--------------------------------------------------------------------------------
/library/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #333333
5 | #0076ff
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 16 10:49:15 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/PickString.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 | /**
4 | * Copyright (C) 2017
5 | * 版权所有
6 | *
7 | * 功能描述:
8 | *
9 | * 作者:yijiebuyi
10 | * 创建时间:2020/8/18
11 | *
12 | * 修改人:
13 | * 修改描述:
14 | * 修改日期
15 | */
16 |
17 | public interface PickString {
18 | public String pickDisplayName();
19 | }
20 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/OnWheelPickedListener.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | /*
4 | * Copyright (C) 2017
5 | * 版权所有
6 | *
7 | * 功能描述:
8 | *
9 | * 作者:yijiebuyi
10 | * 创建时间:2017/11/26
11 | *
12 | * 修改人:
13 | * 修改描述:
14 | * 修改日期
15 | */
16 | public interface OnWheelPickedListener {
17 |
18 | public void onWheelSelected(AbstractWheelPicker wheelPicker, int index, T data, boolean touch);
19 | }
20 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/OnDatePickListener.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | /**
4 | * Copyright (C) 2017
5 | * 版权所有
6 | *
7 | * 功能描述:日期选择器回调
8 | *
9 | * 作者:yijiebuyi
10 | * 创建时间:2020/8/17
11 | *
12 | * 修改人:
13 | * 修改描述:
14 | * 修改日期
15 | */
16 |
17 | public interface OnDatePickListener {
18 | /**
19 | * @param dateTimePicker
20 | */
21 | public void onDatePicked(IDateTimePicker dateTimePicker);
22 | }
23 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/OnDataPickListener.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | /**
4 | * Copyright (C) 2017
5 | * 版权所有
6 | *
7 | * 功能描述:数据选择回调
8 | *
9 | * 作者:yijiebuyi
10 | * 创建时间:2020/8/17
11 | *
12 | * 修改人:
13 | * 修改描述:
14 | * 修改日期
15 | */
16 |
17 | public interface OnDataPickListener {
18 | /**
19 | * @param index 选中项的index
20 | * @param val 选中项的显示字符串
21 | * @param data 选中项的数据
22 | */
23 | public void onDataPicked(int index, String val, T data);
24 | }
25 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/OnCascadeWheelListener.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Copyright (C) 2017
7 | * 版权所有
8 | *
9 | * 功能描述:
10 | *
11 | * 作者:yijiebuyi
12 | * 创建时间:2017/11/26
13 | *
14 | * 修改人:
15 | * 修改描述:
16 | * 修改日期
17 | */
18 | public interface OnCascadeWheelListener {
19 |
20 | /**
21 | *
22 | * @param wheelIndex 滚轮的索引位置
23 | * @param itemIndex 所有滚轮对应数据的索引位置数组
24 | * @return T 级联数据
25 | */
26 | public T onCascade(int wheelIndex, List itemIndex);
27 | }
28 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/OnMultiDataPickListener.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Copyright (C) 2017
7 | * 版权所有
8 | *
9 | * 功能描述:
10 | *
11 | * 作者:yijiebuyi
12 | * 创建时间:2020/8/17
13 | *
14 | * 修改人:
15 | * 修改描述:
16 | * 修改日期
17 | */
18 |
19 | public interface OnMultiDataPickListener {
20 | /**
21 | * @param indexArr 选中项的index数组
22 | * @param val 选中项的显示字符串数组
23 | * @param data 选中项的数据数组
24 | */
25 | public void onDataPicked(List indexArr, List val, List data);
26 | }
27 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 年
3 | 月
4 | 日
5 | 时
6 | 分
7 | 秒
8 | 今天
9 | 明天
10 | 后天
11 | 今年
12 | 明年
13 |
14 |
15 | 取消
16 | 完成
17 |
18 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "28.0.3"
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 19
10 | targetSdkVersion 28
11 | versionCode 7
12 | versionName "1.1.2"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(dir: 'libs', include: ['*.jar'])
26 | implementation 'androidx.appcompat:appcompat:1.0.0'
27 | }
28 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wp/demo/Student.java:
--------------------------------------------------------------------------------
1 | package com.wp.demo;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.wheelpicker.widget.PickString;
6 |
7 | /**
8 | * Copyright (C) 2017
9 | * 版权所有
10 | *
11 | * 功能描述:
12 | *
13 | * 作者:yijiebuyi
14 | * 创建时间:2020/8/18
15 | *
16 | * 修改人:
17 | * 修改描述:
18 | * 修改日期
19 | */
20 |
21 | class Student implements PickString {
22 | public String name;
23 | public int age;
24 |
25 | public Student(String n, int a) {
26 | name = n;
27 | age = a;
28 | }
29 |
30 | @NonNull
31 | @Override
32 | public String toString() {
33 | return age + "岁";
34 | }
35 |
36 | @Override
37 | public String pickDisplayName() {
38 | return name;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/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 | android.enableJetifier=true
13 | android.useAndroidX=true
14 | org.gradle.jvmargs=-Xmx1536m
15 |
16 | # When configured, Gradle will run in incubating parallel mode.
17 | # This option should only be used with decoupled projects. More details, visit
18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
19 | # org.gradle.parallel=true
20 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/AbstractViewWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 | import com.wheelpicker.core.ScrollWheelPicker;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 |
8 | /*
9 | * Copyright (C) 2017
10 | * 版权所有
11 | *
12 | * 功能描述:
13 | *
14 | * 作者:yijiebuyi
15 | * 创建时间:2017/11/26
16 | *
17 | * 修改人:
18 | * 修改描述:
19 | * 修改日期
20 | */
21 | public abstract class AbstractViewWheelPicker extends ScrollWheelPicker {
22 |
23 | public AbstractViewWheelPicker(Context context) {
24 | super(context);
25 | }
26 |
27 | public AbstractViewWheelPicker(Context context, AttributeSet attrs) {
28 | super(context, attrs);
29 | }
30 |
31 | public AbstractViewWheelPicker(Context context, AttributeSet attrs,
32 | int defStyleAttr) {
33 | super(context, attrs, defStyleAttr);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/library/src/main/res/anim/slide_in_from_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
--------------------------------------------------------------------------------
/library/src/main/res/anim/slide_out_to_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\development\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/library/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
12 |
13 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "28.0.3"
6 | defaultConfig {
7 | applicationId "com.wp.demo"
8 | minSdkVersion 19
9 | targetSdkVersion 29
10 | versionCode 1
11 | versionName "1.1.2"
12 | testInstrumentationRunner 'androidx.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('androidx.test.espresso:espresso-core:3.1.0', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'androidx.appcompat:appcompat:1.0.0'
28 | compile 'androidx.constraintlayout:constraintlayout:1.1.3'
29 | testCompile 'junit:junit:4.12'
30 |
31 | compile project(':library')
32 | }
33 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/TextWheelPickerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 | import com.wheelpicker.core.WheelPickerUtil;
4 |
5 | import java.util.List;
6 |
7 | /*
8 | * Copyright (C) 2017
9 | * 版权所有
10 | *
11 | * 功能描述:
12 | *
13 | * 作者:yijiebuyi
14 | * 创建时间:2017/11/26
15 | *
16 | * 修改人:
17 | * 修改描述:
18 | * 修改日期
19 | */
20 | public class TextWheelPickerAdapter extends TextBaseAdapter {
21 | private List mData;
22 |
23 | public TextWheelPickerAdapter() {
24 | //empty constructor
25 | }
26 |
27 | public TextWheelPickerAdapter(List data) {
28 | mData = data;
29 | }
30 |
31 | public List getData() {
32 | return mData;
33 | }
34 |
35 | public void setData(List data) {
36 | mData = data;
37 | notifyDataSetChanged();
38 | }
39 |
40 | @Override
41 | public int getCount() {
42 | return mData == null ? 0 : mData.size();
43 | }
44 |
45 | @Override
46 | public String getItemText(int position) {
47 | T d = mData == null ? null : mData.get(position);
48 | return WheelPickerUtil.formString(d);
49 | }
50 |
51 | @Override
52 | public String getItem(int position) {
53 | return getItemText(position);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/DateTimeItem.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import com.wheelpicker.widget.TextWheelPicker;
4 | import com.wheelpicker.widget.TextWheelPickerAdapter;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * Copyright (C) 2017
10 | * 版权所有
11 | *
12 | * 功能描述:
13 | *
14 | * 作者:yijiebuyi
15 | * 创建时间:2021/11/13
16 | *
17 | * 修改人:
18 | * 修改描述:
19 | * 修改日期
20 | */
21 |
22 | public class DateTimeItem {
23 | public final static int TYPE_YEAR = 1 << 1;
24 | public final static int TYPE_MONTH = 1 << 2;
25 | public final static int TYPE_DAY = 1 << 3;
26 | public final static int TYPE_HOUR = 1 << 4;
27 | public final static int TYPE_MINUTE = 1 << 5;
28 | public final static int TYPE_SECOND = 1 << 6;
29 |
30 | private int type;
31 | private String label;
32 | private TextWheelPicker wheelPicker;
33 |
34 | public DateTimeItem(int type, String label, TextWheelPicker picker) {
35 | this.type = type;
36 | this.label = label;
37 | this.wheelPicker = picker;
38 | }
39 |
40 | public int getType() {
41 | return type;
42 | }
43 |
44 | public String getLabel() {
45 | return label;
46 | }
47 |
48 | public TextWheelPicker getWheelPicker() {
49 | return wheelPicker;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/PickMode.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import androidx.annotation.IntDef;
4 | import androidx.annotation.RestrictTo;
5 |
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 |
9 | /**
10 | * Copyright (C) 2017
11 | * 版权所有
12 | *
13 | * 功能描述:
14 | *
15 | * 作者:yijiebuyi
16 | * 创建时间:2020/8/17
17 | *
18 | * 修改人:
19 | * 修改描述:
20 | * 修改日期
21 | */
22 |
23 | public class PickMode {
24 | /**
25 | * 生日(过去的日期): 年月日
26 | */
27 | public static final int MODE_BIRTHDAY = 1;
28 | /**
29 | * 普通日期选择,在一个时间范围内选择,默认当前日期前后100年
30 | */
31 | public static final int MODE_DATE = 2;
32 | /**
33 | * 未来日期选择, 当前时间往后推100年
34 | */
35 | public static final int MODE_FUTURE_DATE = 3;
36 | /**
37 | * 时间段
38 | */
39 | public static final int MODE_PERIOD_DATE = 4;
40 |
41 | /**
42 | * 普通数据选择(单列表数据)
43 | */
44 | public static final int MODE_DATA = 5;
45 | /**
46 | * 多行数据数据选择
47 | */
48 | public static final int MODE_MULTI_DATA = 6;
49 |
50 | private PickMode() {
51 | }
52 |
53 | @IntDef({MODE_BIRTHDAY, MODE_DATE, MODE_FUTURE_DATE, MODE_DATA, MODE_MULTI_DATA})
54 | @Retention(RetentionPolicy.SOURCE)
55 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
56 | public @interface Mode {
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/WheelPickerUtil.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.text.TextUtils;
4 |
5 | import com.wheelpicker.widget.PickString;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * Copyright (C) 2017
11 | * 版权所有
12 | *
13 | * 功能描述:
14 | *
15 | * 作者:yijiebuyi
16 | * 创建时间:2020/8/18
17 | *
18 | * 修改人:
19 | * 修改描述:
20 | * 修改日期
21 | */
22 |
23 | public class WheelPickerUtil {
24 | public static String formString(T d) {
25 | if (d == null) {
26 | return null;
27 | }
28 |
29 | if (d instanceof String) {
30 | return (String) d;
31 | } else if (d instanceof PickString) {
32 | return ((PickString) d).pickDisplayName();
33 | } else {
34 | return d.toString();
35 | }
36 | }
37 |
38 | public static int indexOf(T targetElement, List data) {
39 | if (data == null || data.isEmpty()) {
40 | return -1;
41 | }
42 |
43 | String targetStr = WheelPickerUtil.formString(targetElement);
44 | for (int i = 0; i < data.size(); i++) {
45 | String str = WheelPickerUtil.formString(data.get(i));
46 | if (TextUtils.equals(targetStr, str)) {
47 | return i;
48 | }
49 | }
50 |
51 | return -1;
52 | }
53 |
54 | public static String getStringVal(int index, List data) {
55 | return WheelPickerUtil.formString(data.get(index));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/ViewBaseAdapter.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 | import com.wheelpicker.core.WheelPickerAdapter;
4 |
5 | import android.database.DataSetObservable;
6 | import android.database.DataSetObserver;
7 |
8 | /*
9 | * Copyright (C) 2017
10 | * 版权所有
11 | *
12 | * 功能描述:
13 | *
14 | * 作者:yijiebuyi
15 | * 创建时间:2017/11/26
16 | *
17 | * 修改人:
18 | * 修改描述:
19 | * 修改日期
20 | */
21 | public abstract class ViewBaseAdapter implements WheelPickerAdapter {
22 | private final DataSetObservable mDataSetObservable = new DataSetObservable();
23 |
24 | @Override
25 | public void registerDataSetObserver(DataSetObserver observer) {
26 | mDataSetObservable.registerObserver(observer);
27 | }
28 |
29 | @Override
30 | public void unregisterDataSetObserver(DataSetObserver observer) {
31 | mDataSetObservable.unregisterObserver(observer);
32 | }
33 |
34 | /**
35 | * Notifies the attached observers that the underlying data has been changed
36 | * and any View reflecting the data set should refresh itself.
37 | */
38 | public void notifyDataSetChanged() {
39 | mDataSetObservable.notifyChanged();
40 | }
41 |
42 | /**
43 | * Notifies the attached observers that the underlying data is no longer valid
44 | * or available. Once invoked this adapter is no longer valid and should
45 | * not report further data set changes.
46 | */
47 | public void notifyDataSetInvalidated() {
48 | mDataSetObservable.notifyInvalidated();
49 | }
50 |
51 | @Override
52 | public boolean isEmpty() {
53 | return getCount() == 0;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/WheelScroller.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | /*
4 | * Copyright (C) 2017
5 | * 版权所有
6 | *
7 | * 功能描述:
8 | *
9 | * 作者:yijiebuyi
10 | * 创建时间:2017/11/26
11 | *
12 | * 修改人:
13 | * 修改描述:
14 | * 修改日期
15 | */
16 | public interface WheelScroller {
17 | void abortAnimation();
18 |
19 | boolean computeScrollOffset();
20 |
21 | void extendDuration(int extend);
22 |
23 | void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY);
24 |
25 | void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY);
26 |
27 | void forceFinished(boolean finished);
28 |
29 | float getCurrVelocity();
30 |
31 | int getCurrX();
32 |
33 | int getCurrY();
34 |
35 | int getDuration();
36 |
37 | int getFinalX();
38 |
39 | int getFinalY();
40 |
41 | int getStartX();
42 |
43 | int getStartY();
44 |
45 | boolean isFinished();
46 |
47 | void setFinalX(int newX);
48 |
49 | void setFinalY(int newY);
50 |
51 | boolean isOverScrolled();
52 |
53 | void notifyHorizontalEdgeReached(int startX, int finalX, int overX);
54 |
55 | void notifyVerticalEdgeReached(int startY, int finalY, int overY);
56 |
57 | void setFriction(float friction);
58 |
59 | boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY);
60 |
61 | void startScroll(int startX, int startY, int dx, int dy);
62 |
63 | void startScroll(int startX, int startY, int dx, int dy, int duration);
64 |
65 | int timePassed();
66 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/AdministrativeMap.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import com.wheelpicker.widget.PickString;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Copyright (C) 2017
9 | * 版权所有
10 | *
11 | * 功能描述:行政区域
12 | *
13 | * 作者:yijiebuyi
14 | * 创建时间:2020/8/19
15 | *
16 | * 修改人:
17 | * 修改描述:
18 | * 修改日期
19 | */
20 |
21 | public class AdministrativeMap {
22 | public int year;
23 | public List provinces;
24 |
25 | /**
26 | * 省
27 | */
28 | public static class Province implements PickString {
29 | public String name;
30 | public String code;
31 | public List city;
32 |
33 | @Override
34 | public String pickDisplayName() {
35 | return name;
36 | }
37 | }
38 |
39 | /**
40 | * 市
41 | */
42 | public static class City implements PickString{
43 | public String name;
44 | public String code;
45 | public List areas;
46 |
47 | @Override
48 | public String pickDisplayName() {
49 | return name;
50 | }
51 | }
52 |
53 | /**
54 | * 区县
55 | */
56 | public static class Area implements PickString{
57 | public String name;
58 | public String code;
59 | public List countries;
60 |
61 | @Override
62 | public String pickDisplayName() {
63 | return name;
64 | }
65 | }
66 |
67 | /**
68 | * 乡镇
69 | */
70 | public static class Country implements PickString{
71 | public String name;
72 | public String code;
73 |
74 | @Override
75 | public String pickDisplayName() {
76 | return name;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/IPickerView.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 | import android.view.View;
4 | import android.view.animation.Interpolator;
5 |
6 | /**
7 | * Copyright (C) 2017
8 | * 版权所有
9 | *
10 | * 功能描述:
11 | *
12 | * 作者:yijiebuyi
13 | * 创建时间:2020/8/17
14 | *
15 | * 修改人:
16 | * 修改描述:
17 | * 修改日期
18 | */
19 |
20 | public interface IPickerView {
21 | /**
22 | * 设置Item的文本大小
23 | * @param textSize
24 | */
25 | public void setTextSize(int textSize);
26 |
27 | /**
28 | * 设置item文本的颜色
29 | * @param textColor
30 | */
31 | public void setTextColor(int textColor);
32 |
33 | /**
34 | * 设置item分割线的颜色
35 | * @param lineColor
36 | */
37 | public void setLineColor(int lineColor);
38 |
39 | /**
40 | * 设置item分割线的宽度
41 | * @param width
42 | */
43 | public void setLineWidth(int width);
44 |
45 | /**
46 | * 设置item的space
47 | * @param
48 | */
49 | public void setItemSpace(int space);
50 |
51 | /**
52 | * 设置显示的item counte
53 | * @param itemCount
54 | */
55 | public void setVisibleItemCount(int itemCount);
56 |
57 | /**
58 | * 设置item的宽高
59 | * @param itemWidth
60 | * @param itemHeight
61 | * @deprecated pickerView自动计算
62 | */
63 | public void setItemSize(int itemWidth, int itemHeight);
64 |
65 | /**
66 | * 设置滚轮偏向,及其偏向因子
67 | * @param gravity
68 | * @param factor [0, 1]
69 | */
70 | public void setShadow(int gravity, float factor);
71 |
72 | /**
73 | * 设置滚动阻尼因子
74 | * @param factor
75 | */
76 | public void setScrollAnimFactor(float factor);
77 |
78 | /**
79 | * 设置回手指滑动因子
80 | * @param factor
81 | */
82 | public void setScrollMoveFactor(float factor);
83 |
84 | /**
85 | * 设置回滚偏移量
86 | * @param offset
87 | */
88 | public void setScrollOverOffset(int offset);
89 |
90 | public View asView();
91 | }
92 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/IDateTimePicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | /**
4 | * Copyright (C) 2017
5 | * 版权所有
6 | *
7 | * 功能描述:日期时间picker
8 | *
9 | * 作者:yijiebuyi
10 | * 创建时间:2020/9/28
11 | *
12 | * 修改人:
13 | * 修改描述:
14 | * 修改日期
15 | */
16 |
17 | public interface IDateTimePicker {
18 | /**
19 | * 设置默认选择日期:年月日
20 | *
21 | * @param year 年
22 | * @param month 月
23 | * @param day 日
24 | */
25 | public void setDefaultSelectedDate(int year, int month, int day);
26 |
27 | /**
28 | * 设置默认选中的时间:时分秒
29 | *
30 | * @param hour 时
31 | * @param minute 分
32 | * @param second 秒
33 | */
34 | public void setDefaultSelectedTime(int hour, int minute, int second);
35 |
36 | /**
37 | * 设置那些picker 显示或隐藏
38 | * 1.若当前设置visibility 设置为VISIBLE,
39 | * wheelType匹配的picker显示,没匹配的就隐藏
40 | *
41 | * 2.若当前设置visibility 设置为GONE
42 | * wheelType匹配的picker隐藏,没匹配的就显示
43 | *
44 | * @param wheelType 哪些picker
45 | * @param visibility 显示或隐藏, 不能取值为INVISIBLE
46 | */
47 | public void setWheelPickerVisibility(int wheelType, int visibility);
48 |
49 | /**
50 | * 获取选择的毫秒,距离1970
51 | *
52 | * @return
53 | */
54 | public long getTime();
55 |
56 | /**
57 | * 选中的年
58 | *
59 | * @return
60 | */
61 | public int getSelectedYear();
62 |
63 | /**
64 | * 选中的月
65 | *
66 | * @return
67 | */
68 | public int getSelectedMonth();
69 |
70 | /**
71 | * 选中的日
72 | *
73 | * @return
74 | */
75 | public int getSelectedDay();
76 |
77 | /**
78 | * 选中的小时
79 | *
80 | * @return
81 | */
82 | public int getSelectedHour();
83 |
84 | /**
85 | * 选择的分
86 | *
87 | * @return
88 | */
89 | public int getSelectedMinute();
90 |
91 | /**
92 | * 选择的秒
93 | *
94 | * @return
95 | */
96 | public int getSelectedSecond();
97 | }
98 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/WheelPickerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.database.DataSetObserver;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 |
7 | /*
8 | * Copyright (C) 2017
9 | * 版权所有
10 | *
11 | * 功能描述:WheelPicker adapter interface
12 | *
13 | * 作者:yijiebuyi
14 | * 创建时间:2017/11/26
15 | *
16 | * 修改人:
17 | * 修改描述:
18 | * 修改日期
19 | */
20 | public interface WheelPickerAdapter {
21 | /**
22 | * Register an observer that is called when changes happen to the data used by this adapter.
23 | *
24 | * @param observer the object that gets notified when the data set changes.
25 | */
26 | void registerDataSetObserver(DataSetObserver observer);
27 |
28 | /**
29 | * Unregister an observer that has previously been registered with this
30 | * adapter via {@link #registerDataSetObserver}.
31 | *
32 | * @param observer the object to unregister.
33 | */
34 | void unregisterDataSetObserver(DataSetObserver observer);
35 |
36 | /**
37 | * How many items are in the data set represented by this Adapter.
38 | *
39 | * @return Count of items.
40 | */
41 | int getCount();
42 |
43 | /**
44 | * Get a View that displays the data at the specified position in the data set
45 | *
46 | * @param index the item index
47 | * @param convertView the old view to reuse if possible
48 | * @param parent the parent that this view will eventually be attached to
49 | * @return the wheel item View
50 | */
51 | View getView(int index, View convertView, ViewGroup parent);
52 |
53 | /**
54 | * Get the data item associated with the specified position in the data set.
55 | *
56 | * @param position Position of the item whose data we want within the adapter's
57 | * data set.
58 | * @return The data at the specified position.
59 | */
60 | Object getItem(int position);
61 |
62 | /**
63 | * @return true if this adapter doesn't contain any data.
64 | */
65 | boolean isEmpty();
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wp/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.wp.demo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.view.View;
8 |
9 | import com.wheelpicker.AdministrativeMap;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Date;
13 | import java.util.List;
14 |
15 | public class MainActivity extends Activity {
16 | public static final String TIME_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
17 | public static final String TIME_YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
18 | public static final String TIME_YYYY_MM_DD = "yyyy-MM-dd";
19 |
20 | private Date mInitBirthday = new Date();
21 | private Date mInitDate = new Date();
22 | private Date mInitFutureDateTime = new Date();
23 | private Date mInitFutureDate = new Date();
24 |
25 | private Student mInitData = null;
26 | private List mMultiInitIndex = null;
27 | private List mCascadeInitIndex = new ArrayList();
28 | private List mCascadeInitIndexNoArea = new ArrayList();
29 |
30 | private AdministrativeMap mAdministrativeMap;
31 | private Context mContext;
32 |
33 | @Override
34 | protected void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 |
37 | setContentView(R.layout.activity_main);
38 | mContext = this;
39 |
40 | findViewById(R.id.new_picker).setOnClickListener(new View.OnClickListener() {
41 | @Override
42 | public void onClick(View view) {
43 | Intent intent = new Intent(MainActivity.this, PickerActivity.class);
44 | startActivity(intent);
45 | }
46 | });
47 |
48 | findViewById(R.id.old_picker).setOnClickListener(new View.OnClickListener() {
49 | @Override
50 | public void onClick(View view) {
51 | Intent intent = new Intent(MainActivity.this, OldPickerActivity.class);
52 | startActivity(intent);
53 | }
54 | });
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/anim/Animation.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.anim;
2 |
3 | import android.view.animation.Interpolator;
4 |
5 | /*
6 | * Copyright (C) 2017
7 | * 版权所有
8 | *
9 | * 功能描述:动画
10 | *
11 | * 作者:yijiebuyi
12 | * 创建时间:2017/11/26
13 | *
14 | * 修改人:
15 | * 修改描述:
16 | * 修改日期
17 | */
18 | abstract public class Animation {
19 | private static final long ANIMATION_START = -1;
20 | private static final long NO_ANIMATION = -2;
21 |
22 | private long mStartTime = NO_ANIMATION;
23 | private int mDuration;
24 | private Interpolator mInterpolator;
25 |
26 | public void setInterpolator(Interpolator interpolator) {
27 | mInterpolator = interpolator;
28 | }
29 |
30 | public void setDuration(int duration) {
31 | mDuration = duration;
32 | }
33 |
34 | public void start() {
35 | mStartTime = ANIMATION_START;
36 | }
37 |
38 | public void setStartTime(long time) {
39 | mStartTime = time;
40 | }
41 |
42 | public boolean isActive() {
43 | return mStartTime != NO_ANIMATION;
44 | }
45 |
46 | public synchronized void forceStop() {
47 | mStartTime = NO_ANIMATION;
48 | }
49 |
50 | public synchronized boolean calculate(long currentTimeMillis) {
51 | if (mStartTime == NO_ANIMATION) {
52 | return false;
53 | }
54 |
55 | if (mStartTime == ANIMATION_START) {
56 | mStartTime = currentTimeMillis;
57 | }
58 | int elapse = (int) (currentTimeMillis - mStartTime);
59 | float x = clamp((float) elapse / mDuration, 0f, 1f);
60 | Interpolator i = mInterpolator;
61 | onCalculate(i != null ? i.getInterpolation(x) : x);
62 | if (elapse >= mDuration) {
63 | mStartTime = NO_ANIMATION;
64 | }
65 |
66 | return mStartTime != NO_ANIMATION;
67 | }
68 |
69 | /**
70 | * Returns the input value x clamped to the range [min, max].
71 | * @param x
72 | * @param min
73 | * @param max
74 | * @return
75 | */
76 | public static float clamp(float x, float min, float max) {
77 | if (x > max) {
78 | return max;
79 | }
80 |
81 | if (x < min) {
82 | return min;
83 | }
84 | return x;
85 | }
86 |
87 | public void reset() {
88 | }
89 |
90 | abstract protected void onCalculate(float progress);
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_picker_old.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
29 |
30 |
36 |
37 |
43 |
44 |
50 |
51 |
57 |
58 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/TextBaseAdapter.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 | import com.wheelpicker.core.WheelPickerAdapter;
4 |
5 | import android.database.DataSetObservable;
6 | import android.database.DataSetObserver;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | /*
11 | * Copyright (C) 2017
12 | * 版权所有
13 | *
14 | * 功能描述:
15 | *
16 | * 作者:yijiebuyi
17 | * 创建时间:2017/11/26
18 | *
19 | * 修改人:
20 | * 修改描述:
21 | * 修改日期
22 | */
23 | public abstract class TextBaseAdapter implements WheelPickerAdapter {
24 | private final DataSetObservable mDataSetObservable = new DataSetObservable();
25 |
26 | @Override
27 | public void registerDataSetObserver(DataSetObserver observer) {
28 | mDataSetObservable.registerObserver(observer);
29 | }
30 |
31 | @Override
32 | public void unregisterDataSetObserver(DataSetObserver observer) {
33 | mDataSetObservable.unregisterObserver(observer);
34 | }
35 |
36 | /**
37 | * Notifies the attached observers that the underlying data has been changed
38 | * and any View reflecting the data set should refresh itself.
39 | */
40 | public void notifyDataSetChanged() {
41 | mDataSetObservable.notifyChanged();
42 | }
43 |
44 | /**
45 | * Notifies the attached observers that the underlying data is no longer valid
46 | * or available. Once invoked this adapter is no longer valid and should
47 | * not report further data set changes.
48 | */
49 | public void notifyDataSetInvalidated() {
50 | mDataSetObservable.notifyInvalidated();
51 | }
52 |
53 | @Override
54 | public View getView(int index, View convertView, ViewGroup parent) {
55 | return null;
56 | }
57 |
58 | @Override
59 | public Object getItem(int position) {
60 | return null;
61 | }
62 |
63 | @Override
64 | public boolean isEmpty() {
65 | return getCount() == 0;
66 | }
67 |
68 | /**
69 | * Get the text of item associated with the specified position in the data set.
70 | *
71 | * @param position Position of the item whose data we want within the adapter's
72 | * data set.
73 | * @return The text at the specified position.
74 | */
75 | abstract String getItemText(int position);
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/WheelPickerImpl.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.graphics.Camera;
4 | import android.graphics.Matrix;
5 | import android.widget.Scroller;
6 |
7 | /*
8 | * Copyright (C) 2017
9 | * 版权所有
10 | *
11 | * 功能描述:
12 | *
13 | * 作者:yijiebuyi
14 | * 创建时间:2017/11/26
15 | *
16 | * 修改人:
17 | * 修改描述:
18 | * 修改日期
19 | */
20 | public class WheelPickerImpl implements IWheelPicker {
21 | private int mOrientation = ScrollWheelPicker.VETTAICL;
22 |
23 | public WheelPickerImpl() {
24 |
25 | }
26 |
27 | public WheelPickerImpl(int orientation) {
28 | mOrientation = orientation;
29 | }
30 |
31 | @Override
32 | public int getUnitDeltaTotal(Scroller scroller) {
33 | return 0;
34 | }
35 |
36 | @Override
37 | public void startScroll(Scroller scroller, int start, int distance) {
38 | if (mOrientation == ScrollWheelPicker.HORIZENTAL) {
39 | scroller.startScroll(start, 0, start, 0);
40 | } else {
41 | scroller.startScroll(0, start, 0, distance);
42 | }
43 | }
44 |
45 | @Override
46 | public int computeRadius(int count, int space, int width, int height) {
47 | if (mOrientation == ScrollWheelPicker.HORIZENTAL) {
48 | return (int) (((count + 1) * width + (count - 1) * space) / Math.PI);
49 | } else {
50 | return (int) (((count + 1) * height + (count - 1) * space) / Math.PI);
51 | }
52 | }
53 |
54 | @Override
55 | public int getWheelWidth(int radius, int width) {
56 | return mOrientation == ScrollWheelPicker.VETTAICL ? width : 2 * radius;
57 | }
58 |
59 | @Override
60 | public int getWheelHeight(int radius, int height) {
61 | return mOrientation == ScrollWheelPicker.VETTAICL ? 2 * radius : height;
62 | }
63 |
64 | @Override
65 | public int getDisplay(int count, int space, int width, int height) {
66 | if (mOrientation == ScrollWheelPicker.HORIZENTAL) {
67 | return (count / 2 + 1) * (width + space);
68 | } else {
69 | return (count / 2 + 1) * (height + space);
70 | }
71 | }
72 |
73 | @Override
74 | public void rotateCamera(Camera camera, float degree) {
75 | camera.rotateX(-degree);
76 | }
77 |
78 | @Override
79 | public void matrixToCenter(Matrix matrix, float space, float x, float y) {
80 | matrix.preTranslate(-x, -(y + space));
81 | matrix.postTranslate(x, y + space);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_picker_new.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
29 |
30 |
36 |
37 |
43 |
44 |
50 |
51 |
57 |
58 |
64 |
65 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/IWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.graphics.Camera;
4 | import android.graphics.Matrix;
5 | import android.widget.Scroller;
6 |
7 | /*
8 | * Copyright (C) 2017
9 | * 版权所有
10 | *
11 | * 功能描述:
12 | *
13 | * 作者:yijiebuyi
14 | * 创建时间:2017/11/26
15 | *
16 | * 修改人:
17 | * 修改描述:
18 | * 修改日期
19 | */
20 | public interface IWheelPicker {
21 | int getUnitDeltaTotal(Scroller scroller);
22 |
23 | /**
24 | * Start scrolling by providing a starting point and the distance to travel.
25 | * The scroll will use the default value of 250 milliseconds for the
26 | * duration.
27 | *
28 | * @param scroller
29 | * @param start
30 | * @param distance
31 | */
32 | void startScroll(Scroller scroller, int start, int distance);
33 |
34 | /**
35 | * Compute the radius of WheelPicker view
36 | * @param count The visible item count
37 | * @param space The space between two items
38 | * @param width The item width
39 | * @param height The item height
40 | * @return
41 | */
42 | int computeRadius(int count, int space, int width, int height);
43 |
44 | /**
45 | * Compute the WheelPicker content view width
46 | *
47 | * @param radius The radius of WheelPicker view
48 | * @param width The item width
49 | * @return
50 | */
51 | int getWheelWidth(int radius, int width);
52 |
53 | /**
54 | * Compute the WheelPicker content view height
55 | *
56 | * @param radius The radius of WheelPicker view
57 | * @param height The item height
58 | * @return
59 | */
60 | int getWheelHeight(int radius, int height);
61 |
62 | int getDisplay(int count, int space, int width, int height);
63 |
64 | /**
65 | * Applies a rotation transform around the X axis by camera.
66 | *
67 | * @param camera A camera instance can be used to compute 3D transformations and
68 | * generate a matrix that can be applied.
69 | * @param degree The angle of rotation around the X axis
70 | */
71 | void rotateCamera(Camera camera, float degree);
72 |
73 | /**
74 | *
75 | * @param matrix The Matrix holds a 3x3 for transforming coordinates.
76 | * @param space The space between two items
77 | * @param x
78 | * @param y
79 | */
80 | void matrixToCenter(Matrix matrix, float space, float x, float y);
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/bottom_sheet_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
26 |
27 |
38 |
39 |
50 |
51 |
52 |
56 |
57 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/SingleTextWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.view.animation.Interpolator;
7 |
8 | import com.wheelpicker.widget.IPickerView;
9 | import com.wheelpicker.widget.TextWheelPicker;
10 |
11 | /**
12 | * Copyright (C) 2017
13 | * 版权所有
14 | *
15 | * 功能描述:单wheel数据选择Picker
16 | *
17 | * 作者:yijiebuyi
18 | * 创建时间:2020/8/17
19 | *
20 | * 修改人:
21 | * 修改描述:
22 | * 修改日期
23 | */
24 |
25 | public class SingleTextWheelPicker extends TextWheelPicker implements IPickerView {
26 | public SingleTextWheelPicker(Context context) {
27 | super(context);
28 | }
29 |
30 | public SingleTextWheelPicker(Context context, int id) {
31 | super(context, id);
32 | }
33 |
34 | public SingleTextWheelPicker(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | }
37 |
38 | public SingleTextWheelPicker(Context context, AttributeSet attrs, int defStyleAttr) {
39 | super(context, attrs, defStyleAttr);
40 | }
41 |
42 |
43 | public void setTextSize(int textSize) {
44 | super.setTextSize(textSize);
45 | }
46 |
47 | public void setTextColor(int textColor) {
48 | super.setTextColor(textColor);
49 | }
50 |
51 | public void setLineColor(int lineColor) {
52 | super.setLineColor(lineColor);
53 | }
54 |
55 | public void setLineWidth(int width) {
56 | super.setLineStorkeWidth(width);
57 | }
58 |
59 | public void setItemSpace(int space) {
60 | super.setItemSpace(space);
61 | }
62 |
63 | public void setVisibleItemCount(int itemCount) {
64 | super.setVisibleItemCount(itemCount);
65 | }
66 |
67 | public void setItemSize(int itemWidth, int itemHeight) {
68 | super.setItemSize(itemWidth, itemHeight);
69 | }
70 |
71 | @Override
72 | public void setShadow(int gravity, float factor) {
73 | super.setShadowGravity(gravity);
74 | super.setShadowFactor(factor);
75 | }
76 |
77 | @Override
78 | public void setScrollAnimFactor(float factor) {
79 | super.setFlingAnimFactor(factor);
80 | }
81 |
82 | @Override
83 | public void setScrollMoveFactor(float factor) {
84 | super.setFingerMoveFactor(factor);
85 | }
86 |
87 | @Override
88 | public void setScrollOverOffset(int offset) {
89 | super.setOverOffset(offset);
90 | }
91 |
92 | @Override
93 | public View asView() {
94 | return this;
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/WheelPickerData.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import android.text.TextUtils;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.Nullable;
7 |
8 | import com.wheelpicker.core.WheelPickerUtil;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | /**
14 | * Copyright (C) 2017
15 | * 版权所有
16 | *
17 | * 功能描述:
18 | * 作者:yijiebuyi
19 | * 创建时间:2018/4/20
20 | *
21 | * 修改人:
22 | * 修改描述:
23 | * 修改日期
24 | */
25 | public class WheelPickerData {
26 | /**
27 | * 是否可滚动
28 | */
29 | public boolean scrollable = true;
30 | /**
31 | * 是否是占位view
32 | */
33 | public boolean placeHoldView = false;
34 | /**
35 | * 当前默认选择的txt
36 | */
37 | public T currentText;
38 | /**
39 | * 当前默认选中text的index
40 | */
41 | public int currentIndex;
42 | /**
43 | * 当前wheelPicker数据
44 | */
45 | public List data;
46 |
47 | public int indexOf(T targetElement) {
48 | if (data == null || data.isEmpty()) {
49 | return -1;
50 | }
51 |
52 | String targetStr = WheelPickerUtil.formString(targetElement);
53 | for (int i = 0; i < data.size(); i++) {
54 | String str = WheelPickerUtil.formString(data.get(i));
55 | if (TextUtils.equals(targetStr, str)) {
56 | return i;
57 | }
58 | }
59 |
60 | return -1;
61 | }
62 |
63 | public String getStringVal(int index) {
64 | return WheelPickerUtil.formString(data.get(index));
65 | }
66 |
67 | public T get(int index) {
68 | return data.get(index);
69 | }
70 |
71 |
72 | /**
73 | * 默认都是可以支持滚动和不占位
74 | * @param initIndex
75 | * @param srcData
76 | * @return
77 | */
78 | public static List wrapper(@Nullable List initIndex, @NonNull List> srcData) {
79 | List wrappers = new ArrayList();
80 | int size = initIndex != null ? initIndex.size() : 0;
81 | for (int i = 0; i < srcData.size(); i++) {
82 | List> d = srcData.get(i);
83 | WheelPickerData wp = new WheelPickerData();
84 | wp.data = d;
85 | wp.currentIndex = i < size ? initIndex.get(i) : 0;
86 | wp.currentText = d.get(wp.currentIndex);
87 | wrappers.add(wp);
88 | }
89 |
90 | return wrappers;
91 | }
92 |
93 | /**
94 | * 设置scrollable不可滚动
95 | * @param index
96 | * @param wheelPickerData
97 | */
98 | public static void disScrollable(int index, List wheelPickerData) {
99 | if (wheelPickerData == null) {
100 | return;
101 | }
102 |
103 | if (index < 0 || index >= wheelPickerData.size()) {
104 | return;
105 | }
106 |
107 | wheelPickerData.get(index).scrollable = false;
108 | }
109 |
110 | /**
111 | * 设置占位
112 | * @param index
113 | * @param wheelPickerData
114 | */
115 | public static void placeHold(int index, List wheelPickerData) {
116 | if (wheelPickerData == null) {
117 | return;
118 | }
119 |
120 | if (index < 0 || index >= wheelPickerData.size()) {
121 | return;
122 | }
123 |
124 | wheelPickerData.get(index).placeHoldView = true;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens_font.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5.0sp
4 | 5.5sp
5 | 6.0sp
6 | 6.5sp
7 | 7.0sp
8 | 7.5sp
9 | 8.0sp
10 | 8.5sp
11 | 9.0sp
12 | 9.5sp
13 | 10.0sp
14 | 10.5sp
15 | 11.0sp
16 | 11.5sp
17 | 12.0sp
18 | 12.5sp
19 | 13.0sp
20 | 13.5sp
21 | 14.0sp
22 | 14.5sp
23 | 15.0sp
24 | 15.5sp
25 | 16.0sp
26 | 16.5sp
27 | 17.0sp
28 | 17.5sp
29 | 18.0sp
30 | 18.5sp
31 | 19.0sp
32 | 19.5sp
33 | 20.0sp
34 | 20.5sp
35 | 21.0sp
36 | 21.5sp
37 | 22.0sp
38 | 22.5sp
39 | 23.0sp
40 | 23.5sp
41 | 24.0sp
42 | 24.5sp
43 | 25.0sp
44 | 25.5sp
45 | 26.0sp
46 | 26.5sp
47 | 27.0sp
48 | 27.5sp
49 | 28.0sp
50 | 28.5sp
51 | 29.0sp
52 | 29.5sp
53 | 30.0sp
54 | 30.5sp
55 | 31.0sp
56 | 31.5sp
57 | 32.0sp
58 | 32.5sp
59 | 33.0sp
60 | 33.5sp
61 | 34.0sp
62 | 34.5sp
63 | 35.0sp
64 | 35.5sp
65 | 36.0sp
66 | 36.5sp
67 | 37.0sp
68 | 37.5sp
69 | 38.0sp
70 | 38.5sp
71 | 39.0sp
72 | 39.5sp
73 | 40.0sp
74 | 40.5sp
75 | 41.0sp
76 | 41.5sp
77 | 42.0sp
78 | 42.5sp
79 | 43.0sp
80 | 43.5sp
81 | 44.0sp
82 | 44.5sp
83 | 45.0sp
84 | 45.5sp
85 | 46.0sp
86 | 46.5sp
87 | 47.0sp
88 | 47.5sp
89 | 48.0sp
90 | 48.5sp
91 | 49.0sp
92 | 49.5sp
93 | 50.0sp
94 |
95 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/ScrollerCompat.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.view.animation.Interpolator;
7 | import android.widget.Scroller;
8 |
9 | /*
10 | * Copyright (C) 2017
11 | * 版权所有
12 | *
13 | * 功能描述:
14 | *
15 | * 作者:yijiebuyi
16 | * 创建时间:2017/11/26
17 | *
18 | * 修改人:
19 | * 修改描述:
20 | * 修改日期
21 | */
22 | class ScrollerCompat implements WheelScroller {
23 | private Scroller mScroller;
24 |
25 | ScrollerCompat(Context context) {
26 | mScroller = new Scroller(context);
27 | }
28 |
29 | ScrollerCompat(Context context, Interpolator interpolator) {
30 | mScroller = new Scroller(context, interpolator);
31 | }
32 |
33 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
34 | ScrollerCompat(Context context, Interpolator interpolator, boolean flywheel) {
35 | mScroller = new Scroller(context, interpolator, flywheel);
36 | }
37 |
38 | @Override
39 | public void abortAnimation() {
40 | mScroller.abortAnimation();
41 | }
42 |
43 | @Override
44 | public boolean computeScrollOffset() {
45 | return mScroller.computeScrollOffset();
46 | }
47 |
48 | @Override
49 | public void extendDuration(int extend) {
50 | mScroller.extendDuration(extend);
51 | }
52 |
53 | @Override
54 | public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) {
55 | mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
56 | }
57 |
58 | @Override
59 | public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY) {
60 | mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
61 | }
62 |
63 | @Override
64 | public void forceFinished(boolean finished) {
65 | mScroller.forceFinished(finished);
66 | }
67 |
68 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
69 | @Override
70 | public float getCurrVelocity() {
71 | return mScroller.getCurrVelocity();
72 | }
73 |
74 | @Override
75 | public int getCurrX() {
76 | return mScroller.getCurrX();
77 | }
78 |
79 | @Override
80 | public int getCurrY() {
81 | return mScroller.getCurrY();
82 | }
83 |
84 | @Override
85 | public int getDuration() {
86 | return mScroller.getDuration();
87 | }
88 |
89 | @Override
90 | public int getFinalX() {
91 | return mScroller.getFinalX();
92 | }
93 |
94 | @Override
95 | public int getFinalY() {
96 | return mScroller.getFinalY();
97 | }
98 |
99 | @TargetApi(Build.VERSION_CODES.CUPCAKE)
100 | @Override
101 | public int getStartX() {
102 | return mScroller.getStartX();
103 | }
104 |
105 | @TargetApi(Build.VERSION_CODES.CUPCAKE)
106 | @Override
107 | public int getStartY() {
108 | return mScroller.getStartY();
109 | }
110 |
111 | @Override
112 | public boolean isFinished() {
113 | return mScroller.isFinished();
114 | }
115 |
116 | @Override
117 | public void setFinalX(int newX) {
118 | mScroller.setFinalX(newX);
119 | }
120 |
121 | @Override
122 | public void setFinalY(int newY) {
123 | mScroller.setFinalY(newY);
124 | }
125 |
126 | @Override
127 | public boolean isOverScrolled() {
128 | throw new RuntimeException("ScrollerCompat not support this method.");
129 | }
130 |
131 | @Override
132 | public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
133 | throw new RuntimeException("ScrollerCompat not support this method.");
134 | }
135 |
136 | @Override
137 | public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
138 | throw new RuntimeException("ScrollerCompat not support this method.");
139 | }
140 |
141 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
142 | @Override
143 | public void setFriction(float friction) {
144 | mScroller.setFriction(friction);
145 | }
146 |
147 | @Override
148 | public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
149 | throw new RuntimeException("ScrollerCompat not support this method.");
150 | }
151 |
152 | @Override
153 | public void startScroll(int startX, int startY, int dx, int dy) {
154 | mScroller.startScroll(startX, startY, dx, dy);
155 | }
156 |
157 | @Override
158 | public void startScroll(int startX, int startY, int dx, int dy, int duration) {
159 | mScroller.startScroll(startX, startY, dx, dy, duration);
160 | }
161 |
162 | @Override
163 | public int timePassed() {
164 | return mScroller.timePassed();
165 | }
166 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/OverScrollerCompat.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.view.animation.Interpolator;
7 | import android.widget.OverScroller;
8 |
9 | /*
10 | * Copyright (C) 2017
11 | * 版权所有
12 | *
13 | * 功能描述:
14 | *
15 | * 作者:yijiebuyi
16 | * 创建时间:2017/11/26
17 | *
18 | * 修改人:
19 | * 修改描述:
20 | * 修改日期
21 | */
22 | @TargetApi(Build.VERSION_CODES.GINGERBREAD)
23 | class OverScrollerCompat implements WheelScroller {
24 | private OverScroller mOverScroller;
25 |
26 | OverScrollerCompat(Context context) {
27 | mOverScroller = new OverScroller(context);
28 | }
29 |
30 | OverScrollerCompat(Context context, Interpolator interpolator) {
31 | mOverScroller = new OverScroller(context, interpolator);
32 | }
33 |
34 | OverScrollerCompat(Context context, Interpolator interpolator, float bounceCoefficientX, float bounceCoefficientY) {
35 | mOverScroller = new OverScroller(context, interpolator, bounceCoefficientX, bounceCoefficientY);
36 | }
37 |
38 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
39 | OverScrollerCompat(Context context, Interpolator interpolator, float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) {
40 | mOverScroller = new OverScroller(context, interpolator, bounceCoefficientX, bounceCoefficientY, flywheel);
41 | }
42 |
43 | @Override
44 | public void abortAnimation() {
45 | mOverScroller.abortAnimation();
46 | }
47 |
48 | @Override
49 | public boolean computeScrollOffset() {
50 | return mOverScroller.computeScrollOffset();
51 | }
52 |
53 | @Override
54 | public void extendDuration(int extend) {
55 | throw new RuntimeException("OverScrollerCompat not support this method.");
56 | }
57 |
58 | @Override
59 | public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) {
60 | mOverScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
61 | }
62 |
63 | @Override
64 | public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY) {
65 | mOverScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY);
66 | }
67 |
68 | @Override
69 | public void forceFinished(boolean finished) {
70 | mOverScroller.forceFinished(finished);
71 | }
72 |
73 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
74 | @Override
75 | public float getCurrVelocity() {
76 | return mOverScroller.getCurrVelocity();
77 | }
78 |
79 | @Override
80 | public int getCurrX() {
81 | return mOverScroller.getCurrX();
82 | }
83 |
84 | @Override
85 | public int getCurrY() {
86 | return mOverScroller.getCurrY();
87 | }
88 |
89 | @Override
90 | public int getDuration() {
91 | throw new RuntimeException("OverScrollerCompat not support this method.");
92 | }
93 |
94 | @Override
95 | public int getFinalX() {
96 | return mOverScroller.getFinalX();
97 | }
98 |
99 | @Override
100 | public int getFinalY() {
101 | return mOverScroller.getFinalY();
102 | }
103 |
104 | @Override
105 | public int getStartX() {
106 | return mOverScroller.getStartX();
107 | }
108 |
109 | @Override
110 | public int getStartY() {
111 | return mOverScroller.getStartY();
112 | }
113 |
114 | @Override
115 | public boolean isFinished() {
116 | return mOverScroller.isFinished();
117 | }
118 |
119 | @Override
120 | public void setFinalX(int newX) {
121 | throw new RuntimeException("OverScrollerCompat not support this method.");
122 | }
123 |
124 | @Override
125 | public void setFinalY(int newY) {
126 | throw new RuntimeException("OverScrollerCompat not support this method.");
127 | }
128 |
129 | @Override
130 | public boolean isOverScrolled() {
131 | return mOverScroller.isOverScrolled();
132 | }
133 |
134 | @Override
135 | public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
136 | mOverScroller.notifyHorizontalEdgeReached(startX, finalX, overX);
137 | }
138 |
139 | @Override
140 | public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
141 | mOverScroller.notifyVerticalEdgeReached(startY, finalY, overY);
142 |
143 | }
144 |
145 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
146 | @Override
147 | public void setFriction(float friction) {
148 | mOverScroller.setFriction(friction);
149 | }
150 |
151 | @Override
152 | public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
153 | return mOverScroller.springBack(startX, startY, minX, maxX, minY, maxY);
154 | }
155 |
156 | @Override
157 | public void startScroll(int startX, int startY, int dx, int dy) {
158 | mOverScroller.startScroll(startX, startY, dx, dy);
159 | }
160 |
161 | @Override
162 | public void startScroll(int startX, int startY, int dx, int dy, int duration) {
163 | mOverScroller.startScroll(startX, startY, dx, dy, duration);
164 | }
165 |
166 | @Override
167 | public int timePassed() {
168 | throw new RuntimeException("OverScrollerCompat not support this method.");
169 | }
170 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/OSUtils.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.text.TextUtils;
4 |
5 | import java.lang.reflect.Method;
6 |
7 | /*
8 | * Copyright (C) 2017
9 | * 版权所有
10 | *
11 | * 功能描述:
12 | * 作者:yijiebuyi
13 | * 创建时间:2018/4/19
14 | *
15 | * 修改人:
16 | * 修改描述:
17 | * 修改日期
18 | */
19 | public class OSUtils {
20 | private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
21 | private static final String KEY_EMUI_VERSION_NAME = "ro.build.version.emui";
22 | private static final String KEY_DISPLAY = "ro.build.display.id";
23 |
24 | /**
25 | * 判断是否为miui
26 | * Is miui boolean.
27 | *
28 | * @return the boolean
29 | */
30 | public static boolean isMIUI() {
31 | String property = getSystemProperty(KEY_MIUI_VERSION_NAME, "");
32 | return !TextUtils.isEmpty(property);
33 | }
34 |
35 | /**
36 | * 判断miui版本是否大于等于6
37 | * Is miui 6 later boolean.
38 | *
39 | * @return the boolean
40 | */
41 | public static boolean isMIUI6Later() {
42 | String version = getMIUIVersion();
43 | int num;
44 | if ((!version.isEmpty())) {
45 | try {
46 | num = Integer.valueOf(version.substring(1));
47 | return num >= 6;
48 | } catch (NumberFormatException e) {
49 | return false;
50 | }
51 | } else
52 | return false;
53 | }
54 |
55 | /**
56 | * 获得miui的版本
57 | * Gets miui version.
58 | *
59 | * @return the miui version
60 | */
61 | public static String getMIUIVersion() {
62 | return isMIUI() ? getSystemProperty(KEY_MIUI_VERSION_NAME, "") : "";
63 | }
64 |
65 | /**
66 | * 判断是否为emui
67 | * Is emui boolean.
68 | *
69 | * @return the boolean
70 | */
71 | public static boolean isEMUI() {
72 | String property = getSystemProperty(KEY_EMUI_VERSION_NAME, "");
73 | return !TextUtils.isEmpty(property);
74 | }
75 |
76 | /**
77 | * 得到emui的版本
78 | * Gets emui version.
79 | *
80 | * @return the emui version
81 | */
82 | public static String getEMUIVersion() {
83 | return isEMUI() ? getSystemProperty(KEY_EMUI_VERSION_NAME, "") : "";
84 | }
85 |
86 | /**
87 | * 判断是否为emui3.1版本
88 | * Is emui 3 1 boolean.
89 | *
90 | * @return the boolean
91 | */
92 | public static boolean isEMUI3_1() {
93 | String property = getEMUIVersion();
94 | if ("EmotionUI 3".equals(property) || property.contains("EmotionUI_3.1")) {
95 | return true;
96 | }
97 | return false;
98 | }
99 |
100 | /**
101 | * 判断是否为emui3.0版本
102 | * Is emui 3 1 boolean.
103 | *
104 | * @return the boolean
105 | */
106 | public static boolean isEMUI3_0() {
107 | String property = getEMUIVersion();
108 | if (property.contains("EmotionUI_3.0")) {
109 | return true;
110 | }
111 | return false;
112 | }
113 |
114 | /**
115 | * 判断是否为flymeOS
116 | * Is flyme os boolean.
117 | *
118 | * @return the boolean
119 | */
120 | public static boolean isFlymeOS() {
121 | return getFlymeOSFlag().toLowerCase().contains("flyme");
122 | }
123 |
124 | /**
125 | * 判断flymeOS的版本是否大于等于4
126 | * Is flyme os 4 later boolean.
127 | *
128 | * @return the boolean
129 | */
130 | public static boolean isFlymeOS4Later() {
131 | String version = getFlymeOSVersion();
132 | int num;
133 | if (!version.isEmpty()) {
134 | try {
135 | if (version.toLowerCase().contains("os")) {
136 | num = Integer.valueOf(version.substring(9, 10));
137 | } else {
138 | num = Integer.valueOf(version.substring(6, 7));
139 | }
140 | return num >= 4;
141 | } catch (NumberFormatException e) {
142 | return false;
143 | }
144 | }
145 | return false;
146 | }
147 |
148 | /**
149 | * 判断flymeOS的版本是否等于5
150 | * Is flyme os 5 boolean.
151 | *
152 | * @return the boolean
153 | */
154 | public static boolean isFlymeOS5() {
155 | String version = getFlymeOSVersion();
156 | int num;
157 | if (!version.isEmpty()) {
158 | try {
159 | if (version.toLowerCase().contains("os")) {
160 | num = Integer.valueOf(version.substring(9, 10));
161 | } else {
162 | num = Integer.valueOf(version.substring(6, 7));
163 | }
164 | return num == 5;
165 | } catch (NumberFormatException e) {
166 | return false;
167 | }
168 | }
169 | return false;
170 | }
171 |
172 |
173 | /**
174 | * 得到flymeOS的版本
175 | * Gets flyme os version.
176 | *
177 | * @return the flyme os version
178 | */
179 | public static String getFlymeOSVersion() {
180 | return isFlymeOS() ? getSystemProperty(KEY_DISPLAY, "") : "";
181 | }
182 |
183 | private static String getFlymeOSFlag() {
184 | return getSystemProperty(KEY_DISPLAY, "");
185 | }
186 |
187 | private static String getSystemProperty(String key, String defaultValue) {
188 | try {
189 | Class> clz = Class.forName("android.os.SystemProperties");
190 | Method get = clz.getMethod("get", String.class, String.class);
191 | return (String) get.invoke(clz, key, defaultValue);
192 | } catch (Exception e) {
193 | e.printStackTrace();
194 | }
195 | return defaultValue;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/DataPickerUtils.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import androidx.annotation.Nullable;
7 |
8 | import com.wheelpicker.widget.IPickerView;
9 |
10 | /**
11 | * Copyright (C) 2016-2021,
12 | * 版权所有
13 | *
14 | * 功能描述:
15 | *
16 | * 作者:yijiebuyi
17 | * 创建时间:2021/11/18
18 | *
19 | * 修改人:
20 | * 修改描述:
21 | * 修改日期
22 | */
23 |
24 | public class DataPickerUtils {
25 |
26 | /**
27 | * 是否是月底最后一天
28 | * 根据当前年月日判断
29 | *
30 | * @return
31 | */
32 | public static boolean isMonthEnd(int year, int month, int day) {
33 | month = month + 1;
34 | switch (month) {
35 | case 2:
36 | if (isLeapYear(year)) {
37 | return day == 29;
38 | } else {
39 | return day == 28;
40 | }
41 | case 1:
42 | case 3:
43 | case 5:
44 | case 7:
45 | case 8:
46 | case 10:
47 | case 12:
48 | return day == 31;
49 | default:
50 | return day == 30;
51 | }
52 | }
53 |
54 | /**
55 | * 是否是闰年
56 | *
57 | * @param year
58 | * @return
59 | */
60 | public static boolean isLeapYear(int year) {
61 | if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
62 | return true;
63 | }
64 |
65 | return false;
66 | }
67 |
68 |
69 | /**
70 | * @param context
71 | * @param option
72 | * @param mode
73 | * @return
74 | */
75 | public static DateTimePicker buildDateTimeWheelPicker(Context context, PickOption option,
76 | @PickMode.Mode int mode) {
77 | DateTimePicker pickerView = null;
78 | switch (mode) {
79 | case PickMode.MODE_BIRTHDAY:
80 | pickerView = new DateTimePicker(context, DateTimePicker.MODE_BIRTHDAY);
81 | pickerView.setWheelPickerVisibility(DateTimePicker.TYPE_HH_MM_SS, View.GONE);
82 | break;
83 | case PickMode.MODE_FUTURE_DATE:
84 | pickerView = new DateTimePicker(context, DateTimePicker.MODE_PENDING);
85 | pickerView.setWheelPickerVisibility(option.getDateWitchVisible(), View.VISIBLE);
86 | break;
87 | case PickMode.MODE_DATE:
88 | pickerView = new DateTimePicker(context);
89 | pickerView.setWheelPickerVisibility(option.getDateWitchVisible(), View.VISIBLE);
90 | break;
91 | }
92 |
93 | DataPickerUtils.setPickViewStyle(pickerView, option);
94 | return pickerView;
95 | }
96 |
97 | /**
98 | * @param context
99 | * @param option
100 | * @return
101 | */
102 | public static DateTimePicker buildDateTimeWheelPicker(Context context, PickOption option,
103 | long from, long to,
104 | @PickMode.Mode int pickMode) {
105 |
106 | int mode = DateTimePicker.MODE_NORMAL;
107 | switch (pickMode) {
108 | case PickMode.MODE_BIRTHDAY:
109 | mode = DateTimePicker.MODE_BIRTHDAY;
110 | break;
111 | case PickMode.MODE_FUTURE_DATE:
112 | mode = DateTimePicker.MODE_PENDING;
113 | break;
114 | case PickMode.MODE_PERIOD_DATE:
115 | mode = DateTimePicker.MODE_PERIOD;
116 | break;
117 | case PickMode.MODE_DATE:
118 | mode = DateTimePicker.MODE_NORMAL;
119 | break;
120 | }
121 |
122 | DateTimePicker pickerView = new DateTimePicker(context, from, to, mode);
123 | pickerView.setWheelPickerVisibility(option.getDateWitchVisible(), View.VISIBLE);
124 | DataPickerUtils.setPickViewStyle(pickerView, option);
125 | return pickerView;
126 | }
127 |
128 |
129 | /**
130 | * 设置滚轮样式
131 | *
132 | * @param pickerView
133 | * @param option
134 | */
135 | public static void setPickViewStyle(IPickerView pickerView, PickOption option) {
136 | pickerView.asView().setBackgroundColor(option.getBackgroundColor());
137 | pickerView.asView().setPadding(0, option.getVerPadding(), 0, option.getVerPadding());
138 |
139 | //设置Item样式
140 | pickerView.setTextColor(option.getItemTextColor());
141 | pickerView.setVisibleItemCount(option.getVisibleItemCount());
142 | pickerView.setTextSize(option.getItemTextSize());
143 | pickerView.setItemSpace(option.getItemSpace());
144 | pickerView.setLineColor(option.getItemLineColor());
145 | pickerView.setLineWidth(option.getItemLineWidth());
146 |
147 | pickerView.setShadow(option.getShadowGravity(), option.getShadowFactor());
148 | pickerView.setScrollMoveFactor(option.getFingerMoveFactor());
149 | pickerView.setScrollAnimFactor(option.getFlingAnimFactor());
150 | pickerView.setScrollOverOffset(option.getOverScrollOffset());
151 | }
152 |
153 | /**
154 | * 获取底部弹出框
155 | *
156 | * @param context
157 | * @param pickerView
158 | * @return
159 | */
160 | public static BottomSheet buildBottomSheet(Context context, @Nullable PickOption option,
161 | IPickerView pickerView) {
162 | BottomSheet bottomSheet = new BottomSheet(context);
163 | if (option != null) {
164 | bottomSheet.setLeftBtnText(option.getLeftTitleText());
165 | bottomSheet.setRightBtnText(option.getRightTitleText());
166 | bottomSheet.setMiddleText(option.getMiddleTitleText());
167 | bottomSheet.setLeftBtnTextColor(option.getLeftTitleColor());
168 | bottomSheet.setRightBtnTextColor(option.getRightTitleColor());
169 | bottomSheet.setMiddleTextColor(option.getMiddleTitleColor());
170 | bottomSheet.setTitleBackground(option.getTitleBackground());
171 |
172 | bottomSheet.setTitleHeight(option.getTitleHeight());
173 | }
174 | bottomSheet.setContent(pickerView.asView());
175 | return bottomSheet;
176 | }
177 |
178 | public static PickOption checkOption(Context context, @Nullable PickOption option) {
179 | if (option != null) {
180 | return option;
181 | }
182 |
183 | return PickOption.getPickDefaultOptionBuilder(context).build();
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/AbstractTextWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 | import com.wheelpicker.R;
4 | import com.wheelpicker.core.ScrollWheelPicker;
5 |
6 | import android.content.Context;
7 | import android.content.res.TypedArray;
8 | import android.graphics.Canvas;
9 | import android.graphics.Paint;
10 | import android.graphics.Paint.FontMetrics;
11 | import android.graphics.Point;
12 | import android.graphics.Rect;
13 | import android.text.TextUtils;
14 | import android.util.AttributeSet;
15 |
16 | /*
17 | * Copyright (C) 2017
18 | * 版权所有
19 | *
20 | * 功能描述:
21 | *
22 | * 作者:yijiebuyi
23 | * 创建时间:2017/11/26
24 | *
25 | * 修改人:
26 | * 修改描述:
27 | * 修改日期
28 | */
29 | public abstract class AbstractTextWheelPicker extends ScrollWheelPicker {
30 | protected int mUnitDegree;
31 |
32 | protected int mTextColor;
33 | protected float mTextSize;
34 |
35 | protected Rect mItemBounds = new Rect();
36 | private Point mItemSize = new Point();
37 |
38 | //是否自动测量item宽高
39 | private boolean mAutoMeasureItemHeight = true;
40 | private boolean mAutoMeasureItemWidth = true;
41 |
42 | private boolean mIsDatePicker;
43 |
44 |
45 | public AbstractTextWheelPicker(Context context) {
46 | super(context);
47 | }
48 |
49 | public AbstractTextWheelPicker(Context context, AttributeSet attrs) {
50 | super(context, attrs);
51 | }
52 |
53 | public AbstractTextWheelPicker(Context context, AttributeSet attrs, int defStyleAttr) {
54 | super(context, attrs, defStyleAttr);
55 | }
56 |
57 | @Override
58 | protected void obtainAttrs(AttributeSet attrs) {
59 | super.obtainAttrs(attrs);
60 |
61 | if (attrs != null) {
62 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.WheelPicker);
63 | mTextColor = a.getInt(R.styleable.WheelPicker_wheel_text_color, -1);
64 | mTextSize = a.getDimensionPixelSize(R.styleable.WheelPicker_wheel_text_size, 50);
65 | a.recycle();
66 | }
67 | }
68 |
69 | @Override
70 | protected void instantiation() {
71 | super.instantiation();
72 | mPaint.setTextAlign(Paint.Align.CENTER);
73 | if (mTextColor > 0) {
74 | mPaint.setColor(mTextColor);
75 | }
76 |
77 | if (mTextSize > 0) {
78 | mPaint.setTextSize(mTextSize);
79 | }
80 | }
81 |
82 | @Override
83 | public void setAdapter(TextBaseAdapter adapter) {
84 | super.setAdapter(adapter);
85 | }
86 |
87 | @Override
88 | protected void initItemSize() {
89 | if (mAdapter == null || mAdapter.isEmpty()) {
90 | return;
91 | }
92 |
93 | if (!mAutoMeasureItemWidth && !mAutoMeasureItemHeight) {
94 | return;
95 | }
96 |
97 | //TODO to be optimized
98 | if (mOrientation == HORIZENTAL && mAutoMeasureItemWidth) {
99 | for (int i = 0; i < mAdapter.getCount(); i++) {
100 | CharSequence cs = mAdapter.getItemText(i);
101 | String text = cs == null ? "" : cs.toString();
102 | Point p = computeItemSize(text);
103 |
104 | if (mAutoMeasureItemWidth) {
105 | mItemMaxWidth = Math.max(mItemMaxWidth, p.x);
106 | }
107 | }
108 | }
109 |
110 | if (mOrientation == VETTAICL && mAutoMeasureItemHeight) {
111 | mPaint.setTextSize(mTextSize);
112 | FontMetrics ftms = mPaint.getFontMetrics();
113 | mItemMaxHeight = (int) Math.abs(ftms.bottom - ftms.top);
114 | }
115 | }
116 |
117 | public void setAutoMeasureSize(boolean autoWidth, boolean autoHeight) {
118 | mAutoMeasureItemWidth = autoWidth;
119 | mAutoMeasureItemHeight = autoHeight;
120 | }
121 |
122 | public void setItemWidth(int itemWidth) {
123 | mItemMaxWidth = itemWidth;
124 | }
125 |
126 | public void setItemHeight(int itemHeight) {
127 | mItemMaxHeight = itemHeight;
128 | }
129 |
130 | public void setIsDatePicker(boolean isDatePicker) {
131 | mIsDatePicker = isDatePicker;
132 | }
133 |
134 | public void setGravity() {
135 |
136 | }
137 |
138 | @Override
139 | protected void onSizeChanged(int w, int h, int oldW, int oldH) {
140 | super.onSizeChanged(w, h, oldW, oldH);
141 |
142 | // h = descent + (-ascent); h / 2 + z = -ascent;
143 | // 求得z = (ascent + descent) / 2; 即中线到绘制文字基线的距离
144 | mWheelCenterTextY = mWheelCenterY - (mPaint.ascent() + mPaint.descent()) / 2.0f;
145 |
146 | }
147 |
148 | public void setTextColor(int textColor) {
149 | mColor = textColor;
150 | mPaint.setColor(textColor);
151 | }
152 |
153 | public void setTextSize(float textSize) {
154 | mTextSize = textSize;
155 | mPaint.setTextSize(textSize);
156 | }
157 |
158 | public Paint getPaint() {
159 | return mPaint;
160 | }
161 |
162 | public void setLineColor(int lineColor) {
163 | mLineColor = lineColor;
164 | mLinePaint.setColor(lineColor);
165 | }
166 |
167 | public void setLineStorkeWidth(float width) {
168 | mLinePaint.setStrokeWidth(width);
169 | }
170 |
171 | public void setItemSize(int itemWidth, int itemHeight) {
172 | if (itemWidth > 0) {
173 | mItemMaxWidth = itemWidth;
174 | }
175 |
176 | if (itemHeight > 0) {
177 | mItemMaxHeight = itemHeight;
178 | }
179 | }
180 |
181 | public Point computeItemSize(String text) {
182 | if (mPaint == null || TextUtils.isEmpty(text)) {
183 | mItemSize.set(0, 0);
184 | return mItemSize;
185 | }
186 | mPaint.getTextBounds(text, 0, text.length(), mItemBounds);
187 | mItemSize.set(mItemBounds.width(), mItemBounds.height());
188 | return mItemSize;
189 | }
190 |
191 | protected float computeSpace(float degree, float radius) {
192 | return (float) Math.sin(Math.toRadians(degree)) * radius;
193 | }
194 |
195 | protected float computeDepth(float degree, float radius) {
196 | return (float) (radius - Math.cos(Math.toRadians(degree)) * radius);
197 | }
198 |
199 | protected void correctLocation(int itemIndex, float offsetItemX, float offsetItemY) {
200 | if (Math.abs(offsetItemY) < 0.01f) {
201 | return;
202 | }
203 |
204 | float distance;
205 | if (offsetItemY > 0) {
206 | if (offsetItemY > mUnitDegree / 2) {
207 | distance = mUnitDegree - offsetItemY;
208 | } else {
209 | distance = -offsetItemY;
210 | }
211 | } else {
212 | if (Math.abs(offsetItemY) > mUnitDegree / 2)
213 | distance = -mUnitDegree - offsetItemY;
214 | else
215 | distance = -offsetItemY;
216 | }
217 |
218 | startCorrectAnimation(0, distance);
219 | }
220 |
221 | protected abstract void draw(Canvas canvas, Paint paint, String data, float space, float x, float y);
222 | }
223 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/BottomSheet.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.text.TextUtils;
7 | import android.view.Gravity;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.view.Window;
11 | import android.view.WindowManager;
12 | import android.widget.Button;
13 | import android.widget.FrameLayout;
14 | import android.widget.LinearLayout;
15 | import android.widget.TextView;
16 |
17 | import androidx.annotation.ColorInt;
18 | import androidx.annotation.ColorRes;
19 |
20 | /**
21 | * Copyright (C) 2017
22 | * 版权所有
23 | *
24 | * 功能描述:
25 | *
26 | * 作者:yijiebuyi
27 | * 创建时间:2017/11/26
28 | *
29 | * 修改人:
30 | * 修改描述:
31 | * 修改日期
32 | */
33 | public class BottomSheet extends Dialog implements DialogInterface.OnCancelListener,
34 | DialogInterface.OnDismissListener, View.OnClickListener {
35 |
36 | private View mContentView;// dialog content view
37 | private String mTitleText;
38 | private Button mLeftBtn;
39 | private Button mRightBtn;
40 | private TextView mTitleTv;
41 | private View.OnClickListener mLeftBtnClickListener;
42 | private View.OnClickListener mRightBtnClickListener;
43 |
44 | private OnDialogCloseListener mOnDismissListener;
45 |
46 | private View mTitleLayout;
47 |
48 |
49 | public BottomSheet(Context context) {
50 | super(context, R.style.CommonDialog);
51 | init();
52 | }
53 |
54 | public BottomSheet(Context context, int themeResId) {
55 | super(context, R.style.CommonDialog);
56 | init();
57 | }
58 |
59 | protected BottomSheet(Context context, boolean cancelable, OnCancelListener cancelListener) {
60 | super(context, cancelable, cancelListener);
61 | init();
62 | }
63 |
64 | private void init() {
65 | setCanceledOnTouchOutside(true);
66 | Window dialogWindow = getWindow();
67 | setContentView(R.layout.bottom_sheet_dialog);
68 | dialogWindow.setWindowAnimations(R.style.BottomSheetAnim);
69 | WindowManager.LayoutParams params = dialogWindow.getAttributes();
70 | params.dimAmount = 0.5f;
71 | dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
72 | dialogWindow.setAttributes(params);
73 | dialogWindow.setLayout(
74 | android.view.ViewGroup.LayoutParams.MATCH_PARENT,
75 | android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
76 | dialogWindow.setGravity(Gravity.BOTTOM);
77 |
78 | mTitleLayout = dialogWindow.findViewById(R.id.title);
79 | mLeftBtn = (Button) dialogWindow.findViewById(R.id.left_btn);
80 | mRightBtn = (Button) dialogWindow.findViewById(R.id.right_btn);
81 | mTitleTv = (TextView) dialogWindow.findViewById(R.id.middle_txt);
82 |
83 | mLeftBtn.setOnClickListener(this);
84 | mRightBtn.setOnClickListener(this);
85 |
86 | setOnCancelListener(this);
87 | setOnDismissListener(this);
88 | }
89 |
90 | public void setTitleVisibility(int visibility) {
91 | if (mTitleLayout != null) {
92 | mTitleLayout.setVisibility(visibility);
93 | }
94 | }
95 |
96 | public void setContent(View content) {
97 | ((FrameLayout) getWindow().findViewById(R.id.content_dialog)).addView(content);
98 | }
99 |
100 | public void setContent(int contentId) {
101 | View content = getLayoutInflater().inflate(contentId, null);
102 | ((FrameLayout) getWindow().findViewById(R.id.content_dialog)).addView(content);
103 | }
104 |
105 | public void setContent(View content, FrameLayout.LayoutParams params) {
106 | if (params instanceof FrameLayout.LayoutParams) {
107 | ((FrameLayout) getWindow().findViewById(R.id.content_dialog)).addView(content, params);
108 | } else {
109 | ((FrameLayout) getWindow().findViewById(R.id.content_dialog)).addView(content);
110 | }
111 | }
112 |
113 | public void setTitleBackground(@ColorInt int color) {
114 | if (mTitleLayout != null) {
115 | mTitleLayout.setBackgroundColor(color);
116 | }
117 | }
118 |
119 | public void setTitleHeight(int height) {
120 | if (height <= 0) {
121 | //throw new IllegalArgumentException("height must > 0");
122 | return;
123 | }
124 |
125 | if (mTitleLayout != null) {
126 | ViewGroup.LayoutParams params = mTitleLayout.getLayoutParams();
127 |
128 | LinearLayout.LayoutParams titleParams = (LinearLayout.LayoutParams) params;
129 | titleParams.height = height;
130 | }
131 | }
132 |
133 | public void setMiddleText(String text) {
134 | mTitleTv.setText(text);
135 | }
136 |
137 | public void setMiddleTextColor(@ColorInt int color) {
138 | mTitleTv.setTextColor(color);
139 | }
140 |
141 | public void setLeftBtnVisibility(int visibility) {
142 | mLeftBtn.setVisibility(visibility);
143 | }
144 |
145 | public void setRightBtnVisibility(int visibility) {
146 | mRightBtn.setVisibility(visibility);
147 | }
148 |
149 | public void setLeftBtnText(String text) {
150 | mLeftBtn.setText(text);
151 | }
152 |
153 | public void setRightBtnText(String text) {
154 | if (TextUtils.isEmpty(text)) {
155 | return;
156 | }
157 |
158 | mRightBtn.setText(text);
159 | }
160 |
161 | public void setTitleTextSize(int leftTextSize, int middleTextSize, int rightTextSize) {
162 | if (leftTextSize > 0) {
163 | mLeftBtn.setTextScaleX(leftTextSize);
164 | }
165 |
166 | if (middleTextSize > 0) {
167 | mTitleTv.setTextSize(middleTextSize);
168 | }
169 |
170 | if (rightTextSize > 0) {
171 | mRightBtn.setTextSize(rightTextSize);
172 | }
173 | }
174 |
175 | public void setLeftBtnTextColor(@ColorInt int color) {
176 | mLeftBtn.setTextColor(color);
177 | }
178 |
179 | public void setRightBtnTextColor(@ColorInt int color) {
180 | mRightBtn.setTextColor(color);
181 | }
182 |
183 | @Override
184 | public void onClick(View v) {
185 | if (v.getId() == R.id.left_btn) {
186 | dismiss();
187 | if (mLeftBtnClickListener != null) {
188 | mLeftBtnClickListener.onClick(v);
189 | }
190 | } else if (v.getId() == R.id.right_btn) {
191 | dismiss();
192 | if (mRightBtnClickListener != null) {
193 | mRightBtnClickListener.onClick(v);
194 | }
195 | }
196 | }
197 |
198 | @Override
199 | public void onCancel(DialogInterface dialog) {
200 | if (mOnDismissListener != null) {
201 | mOnDismissListener.onCancel();
202 | }
203 | }
204 |
205 | @Override
206 | public void onDismiss(DialogInterface dialog) {
207 | if (mOnDismissListener != null) {
208 | mOnDismissListener.onDismiss();
209 | }
210 | }
211 |
212 | public void setLeftBtnClickListener(View.OnClickListener listener) {
213 | mLeftBtnClickListener = listener;
214 | }
215 |
216 | public void setRightBtnClickListener(View.OnClickListener listener) {
217 | mRightBtnClickListener = listener;
218 | }
219 |
220 | public void setOnDismissListener(OnDialogCloseListener listener) {
221 | mOnDismissListener = listener;
222 | }
223 |
224 | public interface OnDialogCloseListener {
225 | public void onCancel();
226 | public void onDismiss();
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/AdministrativeUtil.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import android.content.Context;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONObject;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.io.InputStreamReader;
12 | import java.util.ArrayList;
13 | import java.util.Iterator;
14 | import java.util.List;
15 |
16 | /**
17 | * Copyright (C) 2017
18 | * 版权所有
19 | *
20 | * 功能描述:
21 | *
22 | * 作者:yijiebuyi
23 | * 创建时间:2020/8/19
24 | *
25 | * 修改人:
26 | * 修改描述:
27 | * 修改日期
28 | */
29 |
30 | public class AdministrativeUtil {
31 | /*
32 | * 显示省市区
33 | */
34 | public final static int PROVINCE_CITY_AREA = 0;
35 | /*
36 | * 显示省市
37 | */
38 | public final static int PROVINCE_CITY= 1;
39 |
40 | public static AdministrativeMap loadCity(Context context) {
41 | AdministrativeMap map = null;
42 | try {
43 | /*String fileName = "pca_compress.json";
44 | InputStream is = context.getAssets().open(fileName);
45 | String json = convertStreamToString(is);
46 | map = Json2AdministrativeMap(json);*/
47 |
48 | String fileName = "province_compress.json";
49 | InputStream is = context.getAssets().open(fileName);
50 | String json = convertStreamToString(is);
51 | map = Json2Provinces(json);
52 | //Log.i("aaa", "province: " + map.provinces.size());
53 | } catch (IOException e) {
54 | e.printStackTrace();
55 | }
56 |
57 | return map;
58 | }
59 |
60 | public static List> getDefaultPickString(AdministrativeMap map) {
61 | if (map == null) {
62 | return null;
63 | }
64 |
65 | List> pickDataList = new ArrayList<>();
66 | pickDataList.add(map.provinces);
67 | pickDataList.add(map.provinces.get(0).city);
68 | pickDataList.add(map.provinces.get(0).city.get(0).areas);
69 |
70 | return pickDataList;
71 | }
72 |
73 | public static List> getPickData(AdministrativeMap map, List indexArr, int mode) {
74 | if (map == null) {
75 | return null;
76 | }
77 |
78 | List> pickDataList = new ArrayList<>();
79 |
80 | int cityIndex = 0;
81 | int areaIndex = 0;
82 | if (indexArr != null && !indexArr.isEmpty()) {
83 | cityIndex = indexArr.get(0);
84 | areaIndex = indexArr.get(1);
85 | }
86 |
87 | pickDataList.add(map.provinces);
88 | List province = map.provinces;
89 | pickDataList.add(province == null || province.isEmpty() ? new ArrayList<>() :
90 | map.provinces.get(cityIndex).city);
91 | List cities = map.provinces.get(cityIndex).city;
92 | if (mode == PROVINCE_CITY_AREA) {
93 | pickDataList.add(cities == null || cities.isEmpty() ? new ArrayList<>() :
94 | cities.get(areaIndex).areas);
95 | }
96 |
97 | return pickDataList;
98 | }
99 |
100 | public static List> getPickData(AdministrativeMap map, List indexArr) {
101 | return getPickData(map, indexArr, PROVINCE_CITY_AREA);
102 | }
103 |
104 | /**
105 | * input 流转换为字符串
106 | *
107 | * @param is
108 | * @return
109 | */
110 | private static String convertStreamToString(InputStream is) {
111 | StringBuilder sb = new StringBuilder();
112 | try {
113 | BufferedReader bf = new BufferedReader(new InputStreamReader(is));
114 | String line;
115 | while ((line = bf.readLine()) != null) {
116 | sb.append(line);
117 | }
118 | } catch (
119 | IOException e) {
120 | e.printStackTrace();
121 | }
122 | return sb.toString();
123 | }
124 |
125 | private static AdministrativeMap Json2AdministrativeMap(String data) {
126 | AdministrativeMap map = new AdministrativeMap();
127 | map.provinces = new ArrayList<>(34);
128 | map.year = 2017;
129 | try {
130 | JSONObject jsonObject = new JSONObject(data);
131 | Iterator itr = jsonObject.keys();
132 |
133 | while (itr.hasNext()) {
134 | String key = String.valueOf(itr.next());//得到Key
135 | JSONObject provinceObj = (JSONObject) jsonObject.get(key);
136 | //String value = jsonObject.getString(key);//得到值
137 |
138 | AdministrativeMap.Province province = new AdministrativeMap.Province();
139 | province.name = provinceObj.get("name").toString();
140 | province.code = key;
141 | province.city = new ArrayList<>();
142 |
143 | JSONObject cityChild = (JSONObject) provinceObj.get("child");//所有的市级
144 | Iterator itrCty = cityChild.keys();
145 |
146 | while (itrCty.hasNext()) {
147 | String cityCode = String.valueOf(itrCty.next());//得到Key
148 | JSONObject cityObj = (JSONObject) cityChild.get(cityCode);
149 |
150 | //System.err.print("市code= " + cityCode);//筛选出市级名称
151 | //System.err.println(" " + cityObject.get("name") + " " + key);//筛选出市级code+上级code
152 | AdministrativeMap.City city = new AdministrativeMap.City();
153 | city.code = cityCode;
154 | city.name = cityObj.getString("name");
155 | city.areas = new ArrayList<>();
156 | province.city.add(city);
157 |
158 | JSONObject areaChild = (JSONObject) cityObj.get("child");//所有的区级 (取区级的时候,有些市级的区没有Key,会报错取不出来,导致之后的都取不出来,先删掉json数据!)
159 | Iterator itrArea = areaChild.keys();
160 |
161 | while (itrArea.hasNext()) {
162 | String areaKey = String.valueOf(itrArea.next());
163 | AdministrativeMap.Area area = new AdministrativeMap.Area();
164 | area.code = areaKey;
165 | area.name = areaChild.getString(areaKey);
166 | city.areas.add(area);
167 | }
168 | }
169 |
170 | map.provinces.add(province);
171 | }
172 | } catch (Exception e) {
173 | e.printStackTrace();
174 | }
175 |
176 | return map;
177 |
178 | }
179 |
180 | private static AdministrativeMap Json2Provinces(String data) {
181 | AdministrativeMap map = new AdministrativeMap();
182 | map.year = 2018;
183 | try {
184 | JSONArray provinceArr = new JSONArray(data);
185 | int provinceSize = provinceArr.length();
186 | map.provinces = new ArrayList<>(provinceSize);
187 | for (int i= 0; i < provinceSize; i++) {
188 | AdministrativeMap.Province province = new AdministrativeMap.Province();
189 | JSONObject provinceObj = provinceArr.getJSONObject(i);
190 | province.name = provinceObj.getString("name");
191 |
192 | JSONArray cityArr = provinceObj.getJSONArray("city");
193 | int citySize = cityArr.length();
194 | province.city = new ArrayList<>(citySize);
195 | for (int j= 0; j < citySize; j++) {
196 | AdministrativeMap.City city = new AdministrativeMap.City();
197 | JSONObject cityObj = cityArr.getJSONObject(j);
198 | city.name = cityObj.getString("name");
199 |
200 | JSONArray areaArr = cityObj.getJSONArray("area");
201 | int areaSize = areaArr.length();
202 | city.areas = new ArrayList<>(areaSize);
203 | for (int k = 0; k < areaSize; k++) {
204 | AdministrativeMap.Area area = new AdministrativeMap.Area();
205 | area.name = areaArr.getString(k);
206 |
207 | //add area to list
208 | city.areas.add(area);
209 | }
210 | //add city to list
211 | province.city.add(city);
212 | }
213 | //add province to list
214 | map.provinces.add(province);
215 | }
216 | } catch (Exception e) {
217 | e.printStackTrace();
218 | }
219 |
220 | return map;
221 |
222 | }
223 |
224 | }
225 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/DataPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 |
9 | import com.wheelpicker.core.WheelPickerUtil;
10 | import com.wheelpicker.widget.IPickerView;
11 | import com.wheelpicker.widget.TextWheelPickerAdapter;
12 |
13 | import java.util.Calendar;
14 | import java.util.Date;
15 | import java.util.List;
16 |
17 | /**
18 | * Copyright (C) 2017
19 | * 版权所有
20 | *
21 | * 功能描述:数据选择器(包括日期,时间,未来时间,数据等)
22 | *
23 | * 1. 日期时间选择:生日日期,未来日期,过去日期,时间段日期
24 | * 2. 数据选择:单行数据,多行数据,多行级联数据
25 | * 作者:yijiebuyi
26 | * 创建时间:2020/8/17
27 | *
28 | * 修改人:
29 | * 修改描述:
30 | * 修改日期
31 | */
32 | public class DataPicker {
33 |
34 | //==============================================================================================
35 | //==============================================================================================
36 | //=========================================date picker==========================================
37 | //==============================================================================================
38 | //==============================================================================================
39 |
40 | /**
41 | * 获取日期
42 | *
43 | * @param context
44 | * @param initDate 初始化时选择的日期
45 | * @param mode 获取哪一种数据
46 | * @param option
47 | * @param listener
48 | */
49 | public static void pickDate(Context context, @Nullable Date initDate, int mode,
50 | @Nullable PickOption option,
51 | final OnDatePickListener listener) {
52 | option = DataPickerUtils.checkOption(context, option);
53 | final Calendar calendar = Calendar.getInstance();
54 | calendar.setTime(initDate != null ? initDate : new Date());
55 |
56 | final DateTimePicker picker = DataPickerUtils.buildDateTimeWheelPicker(context, option, mode);
57 | picker.setDefaultSelectedDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
58 | calendar.get(Calendar.DATE));
59 | if (mode != PickMode.MODE_BIRTHDAY) {
60 | picker.setDefaultSelectedTime(calendar.get(Calendar.HOUR_OF_DAY),
61 | calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
62 | }
63 |
64 | showPicker(context, option, picker, listener);
65 | }
66 |
67 | /**
68 | * 获取日期
69 | *
70 | * @param context
71 | * @param initDate
72 | * @param mode
73 | * @param from 开始日期
74 | * @param to 结束日期
75 | * @param option
76 | * @param listener
77 | */
78 | public static void pickDate(Context context, @Nullable Date initDate, int mode,
79 | long from, long to,
80 | @Nullable PickOption option,
81 | final OnDatePickListener listener) {
82 | option = DataPickerUtils.checkOption(context, option);
83 | final Calendar calendar = Calendar.getInstance();
84 | calendar.setTime(initDate != null ? initDate : new Date());
85 |
86 | final DateTimePicker picker = DataPickerUtils.buildDateTimeWheelPicker(context, option,
87 | from, to, mode);
88 | picker.setDefaultSelectedDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
89 | calendar.get(Calendar.DATE));
90 | picker.setDefaultSelectedTime(calendar.get(Calendar.HOUR_OF_DAY),
91 | calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
92 |
93 | showPicker(context, option, picker, listener);
94 | }
95 |
96 | /**
97 | * 获取未来日期:显示(天、小时、分钟)的组合
98 | *
99 | * @param context
100 | * @param initDate
101 | * @param listener
102 | */
103 | public static void pickFutureDate(Context context, @Nullable Date initDate,
104 | @Nullable PickOption option,
105 | final OnDatePickListener listener) {
106 | final Calendar calendar = Calendar.getInstance();
107 | calendar.setTime(initDate != null ? initDate : new Date());
108 |
109 | option = DataPickerUtils.checkOption(context, option);
110 | final FutureTimePicker picker = new FutureTimePicker(context);
111 | //必须设置Duration Days
112 | picker.setFutureDuration(option.getDurationDays());
113 | DataPickerUtils.setPickViewStyle(picker, option);
114 | picker.setPickedTime(calendar.getTimeInMillis());
115 |
116 | showPicker(context, option, picker, listener);
117 | }
118 |
119 |
120 | /**
121 | * 显示 Time Picker
122 | *
123 | * @param context
124 | * @param option
125 | * @param picker
126 | * @param listener
127 | */
128 | private static void showPicker(Context context, @NonNull PickOption option,
129 | final IPickerView picker,
130 | final OnDatePickListener listener) {
131 | BottomSheet bottomSheet = DataPickerUtils.buildBottomSheet(context, option, picker);
132 | bottomSheet.show();
133 | bottomSheet.setRightBtnClickListener(new View.OnClickListener() {
134 | @Override
135 | public void onClick(View v) {
136 | if (listener != null) {
137 | listener.onDatePicked((IDateTimePicker) picker);
138 | }
139 | }
140 | });
141 | }
142 |
143 |
144 | //==============================================================================================
145 | //==============================================================================================
146 | //=========================================data picker==========================================
147 | //==============================================================================================
148 | //==============================================================================================
149 |
150 | /**
151 | * 获取单行数据
152 | *
153 | * @param context
154 | * @param initData
155 | * @param srcData
156 | * @param listener
157 | * @param
158 | */
159 | public static void pickData(Context context, @Nullable T initData, @NonNull final List srcData,
160 | @Nullable PickOption option, final OnDataPickListener listener) {
161 | option = DataPickerUtils.checkOption(context, option);
162 | final SingleTextWheelPicker picker = new SingleTextWheelPicker(context);
163 | DataPickerUtils.setPickViewStyle(picker, option);
164 |
165 | TextWheelPickerAdapter adapter = new TextWheelPickerAdapter(srcData);
166 | picker.setAdapter(adapter);
167 | int index = WheelPickerUtil.indexOf(initData, srcData);
168 | picker.setCurrentItem(index < 0 ? 0 : index);
169 |
170 | BottomSheet bottomSheet = DataPickerUtils.buildBottomSheet(context, option, picker);
171 | bottomSheet.show();
172 | bottomSheet.setRightBtnClickListener(new View.OnClickListener() {
173 | @Override
174 | public void onClick(View v) {
175 | if (listener != null) {
176 | int index = picker.getPickedIndex();
177 | listener.onDataPicked(index, picker.getPickedData(), srcData.get(index));
178 | }
179 | }
180 | });
181 | }
182 |
183 | /**
184 | * 多行数据选择
185 | */
186 | public static void pickData(Context context, @Nullable List initIndex,
187 | @NonNull List> srcData, @Nullable PickOption option,
188 | final OnMultiDataPickListener listener) {
189 | pickData(context, initIndex, srcData, option, false, listener, null);
190 | }
191 |
192 | /**
193 | * 多行数据选择
194 | */
195 | public static void pickData(Context context, @Nullable List initIndex,
196 | @NonNull List> srcData, @Nullable PickOption option,
197 | final OnMultiDataPickListener listener,
198 | final OnCascadeWheelListener cascadeListener) {
199 | pickData(context, initIndex, srcData, option, false, listener, cascadeListener);
200 | }
201 |
202 | /**
203 | * 多行数据选择
204 | *
205 | * @param context
206 | * @param initIndex
207 | * @param srcData
208 | * @param listener
209 | * @param
210 | */
211 | public static void pickData(Context context, @Nullable List initIndex,
212 | @NonNull List> srcData, @Nullable PickOption option,
213 | boolean wrapper, final OnMultiDataPickListener listener,
214 | final OnCascadeWheelListener cascadeListener) {
215 | option = DataPickerUtils.checkOption(context, option);
216 | //List pickerData = WheelPickerData.wrapper(initData, srcData);
217 | //WheelPickerData.disScrollable(0, pickerData);
218 | //WheelPickerData.placeHold(1, pickerData);
219 | final MultipleTextWheelPicker picker = wrapper ?
220 | new MultipleTextWheelPicker(context, WheelPickerData.wrapper(initIndex, srcData)) :
221 | new MultipleTextWheelPicker(context, initIndex, srcData);
222 |
223 | picker.setOnCascadeWheelListener(cascadeListener);
224 | DataPickerUtils.setPickViewStyle(picker, option);
225 |
226 | BottomSheet bottomSheet = DataPickerUtils.buildBottomSheet(context, option, picker);
227 | bottomSheet.show();
228 | bottomSheet.setRightBtnClickListener(new View.OnClickListener() {
229 | @Override
230 | public void onClick(View v) {
231 | if (listener != null) {
232 | List pickedVal = picker.getPickedVal();
233 | List pickedIndex = picker.getPickedIndex();
234 | List pickedData = picker.getPickedData();
235 | listener.onDataPicked(pickedIndex, pickedVal, pickedData);
236 | }
237 | }
238 | });
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/widget/TextWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.widget;
2 |
3 |
4 | import android.content.Context;
5 | import android.graphics.Camera;
6 | import android.graphics.Canvas;
7 | import android.graphics.Matrix;
8 | import android.graphics.Paint;
9 | import android.graphics.RectF;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 |
13 | import com.wheelpicker.R;
14 | import com.wheelpicker.core.OnWheelPickedListener;
15 |
16 | /*
17 | * Copyright (C) 2017
18 | * 版权所有
19 | *
20 | * 功能描述:The wheel picker for text
21 | *
22 | * 作者:yijiebuyi
23 | * 创建时间:2017/11/26
24 | *
25 | * 修改人:
26 | * 修改描述:
27 | * 修改日期
28 | */
29 | public class TextWheelPicker extends AbstractTextWheelPicker {
30 | private final static String TAG = "TextWheelPicker";
31 | private final static float DEPTH_FACTOR = 0.6F; //0 ~ 1.0F
32 | private final Camera mCamera = new Camera();
33 | private final Matrix mRotateMatrix = new Matrix();
34 | private final Matrix mDepthMatrix = new Matrix();
35 | private final RectF mCurrentItemRect = new RectF();
36 |
37 | private int mRadius;
38 |
39 | private int mOldOffsetItemIndex = 0;
40 | private int mOffsetItemIndex = 0;
41 | private float mOffsetItemDelta = 0;
42 |
43 | private float mShadowOffset = 0;
44 | private float mLineOffset = 0;
45 |
46 | private float mRelRadius;
47 |
48 | private String mPickedData;
49 | private int mPickedIndex;
50 |
51 | private OnWheelPickedListener mOnWheelPickedListener;
52 |
53 | public TextWheelPicker(Context context) {
54 | super(context);
55 | }
56 |
57 | public TextWheelPicker(Context context, int id) {
58 | super(context);
59 | setId(id);
60 | }
61 |
62 | public TextWheelPicker(Context context, AttributeSet attrs) {
63 | super(context, attrs);
64 | }
65 |
66 | public TextWheelPicker(Context context, AttributeSet attrs, int defStyleAttr) {
67 | super(context, attrs, defStyleAttr);
68 | }
69 |
70 | @Override
71 | protected void computeWheelSize() {
72 | mRadius = mWheelPickerImpl.computeRadius(mVisibleItemCount, mItemSpace, mItemMaxWidth, mItemMaxHeight);
73 | mUnitDegree = (int) (180 * 1.0F / mVisibleItemCount);
74 |
75 | mWheelContentWidth = mWheelPickerImpl.getWheelWidth(mRadius, mItemMaxWidth);
76 | mWheelContentHeight = mWheelPickerImpl.getWheelHeight(mRadius, mItemMaxHeight);
77 |
78 | mLineOffset = mItemMaxHeight / 2 + mItemSpace * 0.8f;
79 |
80 | mShadowOffset = SHADOW_MAX * mShadowFactor;
81 | mRelRadius = mRadius * DEPTH_FACTOR;
82 |
83 | if (mAdapter != null) {
84 | mOldOffsetItemIndex = 0;
85 |
86 | if (mCurrItemIndex < 0) {
87 | mCurrItemIndex = 0;
88 | }
89 |
90 | if (mCurrItemIndex >= mAdapter.getCount()) {
91 | mCurrItemIndex = mAdapter.getCount() - 1;
92 | }
93 |
94 | setScrollRange(-(mAdapter.getCount() - 1 - mCurrItemIndex) * mUnitDegree, mCurrItemIndex * mUnitDegree);
95 | }
96 | }
97 |
98 | @Override
99 | protected void drawBackground(Canvas canvas) {
100 | if (mAdapter != null && !mAdapter.isEmpty()) {
101 | canvas.drawLine(0, mWheelCenterY - mLineOffset, mViewWidth,
102 | mWheelCenterY - mLineOffset, mLinePaint);
103 | canvas.drawLine(0, mWheelCenterY + mLineOffset, mViewWidth,
104 | mWheelCenterY + mLineOffset, mLinePaint);
105 |
106 | mCurrentItemRect.set(0, mWheelCenterY - mLineOffset,
107 | mViewWidth, mWheelCenterY + mLineOffset);
108 | }
109 | }
110 |
111 | @Override
112 | protected void drawItems(Canvas canvas) {
113 | if (mAdapter == null || mAdapter.isEmpty()) {
114 | return;
115 | }
116 |
117 | for (int i = mItemStartIndex; i <= mItemEndIndex; i++) {
118 | float rotateDegree = mUnitDegree * (i - mCurrItemIndex) + mOffsetItemDelta;
119 | if (rotateDegree > 90 || rotateDegree < -90) {
120 | continue;
121 | }
122 |
123 | //为了避免角度太小计算产生误差,所以进行一个校正
124 | if (Math.abs(rotateDegree) < 0.1f) {
125 | if (rotateDegree < 0) {
126 | rotateDegree = -0.1f;
127 | } else {
128 | rotateDegree = 0.1f;
129 | }
130 | }
131 |
132 | float space = computeSpace(rotateDegree, mRadius);
133 | float relDegree = Math.abs(rotateDegree) / 90;
134 | canvas.save();
135 | mCamera.save();
136 | mRotateMatrix.reset();
137 |
138 | //apply gravity
139 | if (mShadowGravity == SHADOW_RIGHT) {
140 | mCamera.translate(-mShadowOffset, 0, 0);
141 | } else if (mShadowGravity == SHADOW_LEFT) {
142 | mCamera.translate(mShadowOffset, 0, 0);
143 | }
144 | //rotate
145 | mWheelPickerImpl.rotateCamera(mCamera, rotateDegree);
146 | mCamera.getMatrix(mRotateMatrix);
147 | mCamera.restore();
148 | mWheelPickerImpl.matrixToCenter(mRotateMatrix, space, mWheelCenterX, mWheelCenterY);
149 | //apply gravity
150 | if (mShadowGravity == SHADOW_RIGHT) {
151 | mRotateMatrix.postTranslate(mShadowOffset, 0);
152 | } else if (mShadowGravity == SHADOW_LEFT) {
153 | mRotateMatrix.postTranslate(-mShadowOffset, 0);
154 | }
155 |
156 | float depth = computeDepth(rotateDegree, mRelRadius);
157 | mCamera.save();
158 | mDepthMatrix.reset();
159 | mCamera.translate(0, 0, depth);
160 | mCamera.getMatrix(mDepthMatrix);
161 | mCamera.restore();
162 | mWheelPickerImpl.matrixToCenter(mDepthMatrix, space, mWheelCenterX, mWheelCenterY);
163 |
164 | mRotateMatrix.postConcat(mDepthMatrix);
165 | canvas.concat(mRotateMatrix);
166 |
167 | /*if (i == getHighLightItem(mCurrItemIndex)) {
168 | mPaint.setAlpha(255);
169 | } else {
170 | mPaint.setAlpha(128 - (int) (128 * relDegree));
171 | }*/
172 | //先绘制文本渐变,离当前item越远,alpha值越小
173 | mPaint.setAlpha(128 - (int) (128 * relDegree));
174 | draw(canvas, mPaint, mAdapter.getItemText(i), space, mWheelCenterX, mWheelCenterTextY);
175 |
176 | //设置当前选中item为非透明
177 | mPaint.setAlpha(255);
178 | canvas.clipRect(mCurrentItemRect);
179 | draw(canvas, mPaint, mAdapter.getItemText(i), space, mWheelCenterX, mWheelCenterTextY);
180 |
181 | canvas.restore();
182 | }
183 |
184 | }
185 |
186 | @Override
187 | protected void drawForeground(Canvas canvas) {
188 |
189 | }
190 |
191 | public void setOnWheelPickedListener(OnWheelPickedListener listener) {
192 | mOnWheelPickedListener = listener;
193 | }
194 |
195 | @Override
196 | protected void onWheelSelected(boolean touch, int index) {
197 | if (mAdapter != null && index > -1 && index < mAdapter.getCount()) {
198 | setPickedItemIndex(index);
199 | mPickedData = mAdapter.getItemText(index);
200 | mPickedIndex = index;
201 | if (mOnWheelPickedListener != null) {
202 | mOnWheelPickedListener.onWheelSelected(this, index, mPickedData, touch);
203 | }
204 | } else {
205 | Log.i(TAG, "error index:" + index);
206 | }
207 | }
208 |
209 | @Override
210 | public void onScrolling(float offsetX, float offsetY, boolean isFinshed) {
211 | mOffsetItemIndex = (int) (offsetY / mUnitDegree);
212 | mOffsetItemDelta = offsetY % mUnitDegree;
213 | if (mOffsetItemIndex != mOldOffsetItemIndex) {
214 | mCurrItemIndex = mCurrItemIndex - (mOffsetItemIndex - mOldOffsetItemIndex);
215 | }
216 |
217 | mOldOffsetItemIndex = mOffsetItemIndex;
218 | updateItemIndexRange();
219 | postInvalidate();
220 |
221 | if (isFinshed) {
222 | correctLocation(mOffsetItemIndex, 0, mOffsetItemDelta);
223 | if (Math.abs(mOffsetItemDelta) < 0.01f) {
224 | onWheelSelected(true, mCurrItemIndex);
225 | }
226 | }
227 | }
228 |
229 | @Override
230 | protected void draw(Canvas canvas, Paint paint, String data, float space, float x, float y) {
231 | fitTextSize(data, paint);
232 | canvas.drawText(/*getDrawText(data)*/data, x, y + space, paint);
233 | }
234 |
235 | /**
236 | * 调整text的大小,让所有文字都可以全部显示到view中
237 | * @param text
238 | * @param paint
239 | */
240 | private void fitTextSize(String text, Paint paint) {
241 | float fitTextSize = mTextSize;
242 | if (mTextSize == 0) {
243 | return;
244 | }
245 |
246 | paint.setTextSize(mTextSize);
247 | float currWidth = paint.measureText(text);
248 | while (currWidth > mViewWidth) {
249 | fitTextSize = fitTextSize - 4;
250 | paint.setTextSize(fitTextSize);
251 | currWidth = paint.measureText(text);
252 | }
253 | }
254 |
255 | @Override
256 | public int getCurrentItem() {
257 | int currentItem = super.getCurrentItem();
258 | if (currentItem < 0) {
259 | return 0;
260 | }
261 |
262 | if (mAdapter == null) {
263 | return currentItem;
264 | }
265 |
266 | if (currentItem >= mAdapter.getCount()) {
267 | currentItem = mAdapter.getCount() - 1;
268 | }
269 |
270 | return currentItem;
271 | }
272 |
273 | @Override
274 | public void setCurrentItem(int index) {
275 | mOldOffsetItemIndex = 0;
276 | mOffsetItemIndex = 0;
277 | super.setCurrentItem(index);
278 | }
279 |
280 | /**
281 | * 获取高亮显示的item索引
282 | * 已弃用,转至
283 | * @see #fitTextSize(String, Paint)
284 | * @param item
285 | * @return
286 | */
287 | @Deprecated
288 | private int getHighLightItem(int item) {
289 | if (mOffsetItemDelta > 0) {
290 | if (mOffsetItemDelta > mUnitDegree / 2) {
291 | return item - 1;
292 | } else {
293 | return item;
294 | }
295 | } else {
296 | if (Math.abs(mOffsetItemDelta) > mUnitDegree / 2)
297 | return item + 1;
298 | else
299 | return item;
300 | }
301 | }
302 |
303 | /**
304 | * 根据view的宽度截取字符串
305 | * @param data
306 | * @return
307 | */
308 | private String getDrawText(String data) {
309 | if (data == null) {
310 | return data;
311 | }
312 |
313 | float itemWidth = mItemMaxWidth;
314 | int viewWidth = getMeasuredWidth();
315 | int len = data.length();
316 | while (itemWidth > viewWidth && len > 0) {
317 | mPaint.getTextBounds(data, 0, --len, mItemBounds);
318 | itemWidth = mItemBounds.width();
319 | }
320 |
321 | return data.substring(0, len);
322 | }
323 |
324 | /**
325 | * 获取选择的数据
326 | *
327 | * @return
328 | */
329 | public String getPickedData() {
330 | return mPickedData;
331 | }
332 |
333 | /**
334 | * 获取选择的索引
335 | *
336 | * @return
337 | */
338 | public int getPickedIndex() {
339 | return mPickedIndex;
340 | }
341 | }
342 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/MultipleTextWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.view.Gravity;
6 | import android.view.View;
7 | import android.view.animation.Interpolator;
8 | import android.widget.LinearLayout;
9 |
10 | import com.wheelpicker.core.AbstractWheelPicker;
11 | import com.wheelpicker.core.WheelPickerUtil;
12 | import com.wheelpicker.widget.IPickerView;
13 | import com.wheelpicker.core.OnWheelPickedListener;
14 | import com.wheelpicker.widget.TextWheelPicker;
15 | import com.wheelpicker.widget.TextWheelPickerAdapter;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | /**
21 | * Copyright (C) 2017
22 | * 版权所有
23 | *
24 | * 功能描述:多wheel数据选择Picker
25 | * 作者:yijiebuyi
26 | * 创建时间:2018/4/20
27 | *
28 | * 修改人:
29 | * 修改描述:
30 | * 修改日期
31 | */
32 | public class MultipleTextWheelPicker extends LinearLayout
33 | implements OnWheelPickedListener, IPickerView {
34 | /**
35 | * 正常
36 | */
37 | private static final int MODE_NORMAL = 0;
38 | /**
39 | * 级联
40 | */
41 | private static final int MODE_CASCADE = 1;
42 |
43 | protected List mSrcDataList;
44 | protected List mInitIndex;
45 | protected List mWheelPickers;
46 | protected List mTextWheelPickerAdapters;
47 |
48 | protected List mPickedVal;
49 | protected List mPickedIndex;
50 | protected List mPickedData;
51 | /**
52 | * 级联数据监听器,当设置监听器时,表示数据级联;否则数据非级联
53 | */
54 | private OnCascadeWheelListener> mOnCascadeWheelListener;
55 | private boolean mTouch = false;
56 |
57 | public MultipleTextWheelPicker(Context context) {
58 | this(context, null);
59 | }
60 |
61 | public MultipleTextWheelPicker(Context context, List data) {
62 | super(context);
63 | init(data);
64 | }
65 |
66 | public MultipleTextWheelPicker(Context context, List index, List data) {
67 | super(context);
68 | mInitIndex = index;
69 | init(data);
70 | }
71 |
72 | public void setOnCascadeWheelListener (OnCascadeWheelListener listener) {
73 | mOnCascadeWheelListener = listener;
74 | }
75 |
76 | private void init(List data) {
77 | mSrcDataList = data;
78 |
79 | //set style
80 | setGravity(Gravity.CENTER);
81 | setOrientation(HORIZONTAL);
82 |
83 | //init data
84 | if (data != null && !data.isEmpty()) {
85 | int size = data.size();
86 |
87 | mWheelPickers = new ArrayList(size);
88 | mTextWheelPickerAdapters = new ArrayList(size);
89 |
90 | mPickedVal = new ArrayList(size);
91 | mPickedIndex = new ArrayList(size);
92 | mPickedData = new ArrayList<>(size);
93 |
94 | LayoutParams llParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
95 | llParams.weight = 1;
96 |
97 | Context context = getContext();
98 | for (int i = 0; i < size; i++) {
99 | T d = data.get(i);
100 | int id = i;
101 | List pickerDataList = null;
102 | if (isHolderView(d)) {
103 | //占位view
104 | TextWheelPicker holdView = new TextWheelPicker(context, id);
105 | holdView.setTouchable(false);
106 | holdView.setLineStorkeWidth(0);
107 |
108 | addView(holdView, llParams);
109 | } else if ((pickerDataList = asList(d)) != null /*&& !pickerDataList.isEmpty()*/) {
110 | TextWheelPicker twp = new TextWheelPicker(context, id);
111 | twp.setTouchable(scrollable(d));
112 | twp.setOnWheelPickedListener(this);
113 |
114 | addView(twp, llParams);
115 | mWheelPickers.add(twp);
116 |
117 | TextWheelPickerAdapter adapter = new TextWheelPickerAdapter();
118 | adapter.setData(pickerDataList);
119 | mTextWheelPickerAdapters.add(adapter);
120 |
121 | //set current
122 | int index = getIndex(i, d);
123 | twp.setCurrentItemWithoutReLayout(index);
124 |
125 | if (pickerDataList.isEmpty()) {
126 | mPickedVal.add(null);
127 | mPickedIndex.add(0);
128 | mPickedData.add(null);
129 | } else {
130 | mPickedVal.add(WheelPickerUtil.getStringVal(index, pickerDataList));
131 | mPickedIndex.add(index);
132 | mPickedData.add(pickerDataList.get(index));
133 | }
134 |
135 | twp.setAdapter(adapter);
136 | }
137 | }
138 | }
139 | }
140 |
141 | private int getIndex(int i, T data) {
142 | int index = 0;
143 | if (data instanceof WheelPickerData) {
144 | WheelPickerData wp = (WheelPickerData) data;
145 | index = Math.max(0, wp.currentIndex);
146 | } else if (mInitIndex != null && !mInitIndex.isEmpty() && i < mInitIndex.size()) {
147 | index = Math.max(0, mInitIndex.get(i));
148 | }
149 |
150 | return index;
151 | }
152 |
153 | private List asList(T data) {
154 | if (data instanceof List) {
155 | return (List) data;
156 | } else if (data instanceof WheelPickerData) {
157 | return ((WheelPickerData) data).data;
158 | }
159 | return null;
160 | }
161 |
162 | private boolean isHolderView(T data) {
163 | if (data instanceof WheelPickerData) {
164 | return ((WheelPickerData) data).placeHoldView;
165 | }
166 |
167 | return false;
168 | }
169 |
170 | private boolean scrollable(T data) {
171 | if (data instanceof WheelPickerData) {
172 | return ((WheelPickerData) data).scrollable;
173 | }
174 |
175 | return true;
176 | }
177 |
178 | public void notifyDataSetChanged() {
179 | if (mTextWheelPickerAdapters != null && !mTextWheelPickerAdapters.isEmpty()) {
180 | if (mOnCascadeWheelListener == null) {
181 | for (TextWheelPickerAdapter adapter : mTextWheelPickerAdapters) {
182 | adapter.notifyDataSetChanged();
183 | }
184 | } else {
185 | //当时级联的时候,只需要通知第一个数据变化
186 | mTextWheelPickerAdapters.get(0).notifyDataSetChanged();
187 | }
188 | }
189 | }
190 |
191 | @SuppressLint("ResourceType")
192 | @Override
193 | public void onWheelSelected(AbstractWheelPicker wheelPicker, int index, String data, boolean touch) {
194 | //默认不联动
195 | int pickerId = wheelPicker.getId();
196 | if (touch) {
197 | mTouch = true;
198 | }
199 |
200 | mPickedVal.set(pickerId, data);
201 | mPickedIndex.set(pickerId, index);
202 | List d = asList(mSrcDataList.get(pickerId));
203 | D pickedData = (d != null && !d.isEmpty() && d.size() > index) ? d.get(index) : null;
204 | mPickedData.set(pickerId, pickedData);
205 |
206 | /**
207 | * 第一次设置数据时(非手势触摸),不自动级联
208 | */
209 | if (mOnCascadeWheelListener != null && mTouch) {
210 | int size = mSrcDataList.size();
211 | if (pickerId < size - 1) {
212 | List cascadeData = mOnCascadeWheelListener.onCascade(pickerId, mPickedIndex);
213 | if (cascadeData != null && !cascadeData.isEmpty()) {
214 | mWheelPickers.get(pickerId + 1).setCurrentItem(0);
215 | mTextWheelPickerAdapters.get(pickerId + 1).setData(cascadeData);
216 | } else {
217 | for (int i = pickerId + 1; i < mTextWheelPickerAdapters.size(); i++) {
218 | mPickedVal.set(i, null);
219 | mPickedIndex.set(i, 0);
220 | mPickedData.set(i, null);
221 | mTextWheelPickerAdapters.get(i).setData(null);
222 | }
223 |
224 | for (int i = pickerId + 1; i < mWheelPickers.size(); i++) {
225 | mWheelPickers.get(i).setCurrentItem(0);
226 | }
227 | }
228 | }
229 | }
230 | }
231 |
232 | public void setTextSize(int textSize) {
233 | if (textSize < 0) {
234 | return;
235 | }
236 | if (mWheelPickers == null) {
237 | return;
238 | }
239 |
240 | for (TextWheelPicker picker : mWheelPickers) {
241 | picker.setTextSize(textSize);
242 | }
243 | }
244 |
245 | public void setTextColor(int textColor) {
246 | if (mWheelPickers == null) {
247 | return;
248 | }
249 |
250 | for (TextWheelPicker picker : mWheelPickers) {
251 | picker.setTextColor(textColor);
252 | }
253 | }
254 |
255 | public void setLineColor(int lineColor) {
256 | if (mWheelPickers == null) {
257 | return;
258 | }
259 |
260 | for (TextWheelPicker picker : mWheelPickers) {
261 | picker.setLineColor(lineColor);
262 | }
263 | }
264 |
265 | public void setLineWidth(int width) {
266 | if (mWheelPickers == null) {
267 | return;
268 | }
269 |
270 | for (TextWheelPicker picker : mWheelPickers) {
271 | picker.setLineStorkeWidth(width);
272 | }
273 | }
274 |
275 | public void setItemSpace(int space) {
276 | if (mWheelPickers == null) {
277 | return;
278 | }
279 |
280 | for (TextWheelPicker picker : mWheelPickers) {
281 | picker.setItemSpace(space);
282 | }
283 | }
284 |
285 | public void setVisibleItemCount(int itemCount) {
286 | if (mWheelPickers == null) {
287 | return;
288 | }
289 |
290 | for (TextWheelPicker picker : mWheelPickers) {
291 | picker.setVisibleItemCount(itemCount);
292 | }
293 | }
294 |
295 | @Override
296 | public void setItemSize(int itemWidth, int itemHeight) {
297 | if (mWheelPickers == null) {
298 | return;
299 | }
300 |
301 | for (TextWheelPicker picker : mWheelPickers) {
302 | picker.setItemSize(itemWidth, itemHeight);
303 | }
304 | }
305 |
306 | @Override
307 | public void setShadow(int gravity, float factor) {
308 | for (TextWheelPicker picker : mWheelPickers) {
309 | picker.setShadowGravity(gravity);
310 | picker.setShadowFactor(factor);
311 | }
312 | }
313 |
314 |
315 | @Override
316 | public void setScrollAnimFactor(float factor) {
317 | for (TextWheelPicker picker : mWheelPickers) {
318 | picker.setFlingAnimFactor(factor);
319 | }
320 | }
321 |
322 | @Override
323 | public void setScrollMoveFactor(float factor) {
324 | for (TextWheelPicker picker : mWheelPickers) {
325 | picker.setFingerMoveFactor(factor);
326 | }
327 | }
328 |
329 | @Override
330 | public void setScrollOverOffset(int offset) {
331 | for (TextWheelPicker picker : mWheelPickers) {
332 | picker.setOverOffset(offset);
333 | }
334 | }
335 |
336 | @Override
337 | public View asView() {
338 | return this;
339 | }
340 |
341 | public void setFakeBoldText(boolean fakeBoldText) {
342 | if (mWheelPickers == null) {
343 | return;
344 | }
345 |
346 | for (TextWheelPicker picker : mWheelPickers) {
347 | picker.getPaint().setFakeBoldText(fakeBoldText);
348 | }
349 | }
350 |
351 | public List getPickedVal() {
352 | return mPickedVal;
353 | }
354 |
355 | public List getPickedIndex() {
356 | return mPickedIndex;
357 | }
358 |
359 | public List getPickedData() {
360 | return mPickedData;
361 | }
362 |
363 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/ScrollWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 |
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.os.SystemClock;
7 | import android.util.AttributeSet;
8 | import android.view.MotionEvent;
9 | import android.view.ViewConfiguration;
10 | import android.view.animation.DecelerateInterpolator;
11 | import android.view.animation.Interpolator;
12 |
13 | import androidx.core.view.ViewCompat;
14 |
15 | import com.wheelpicker.R;
16 | import com.wheelpicker.anim.Animation;
17 |
18 | /*
19 | * Copyright (C) 2017
20 | * 版权所有
21 | *
22 | * 功能描述:
23 | *
24 | * 作者:yijiebuyi
25 | * 创建时间:2017/11/26
26 | *
27 | * 修改人:
28 | * 修改描述:
29 | * 修改日期
30 | */
31 | public abstract class ScrollWheelPicker extends AbstractWheelPicker {
32 | /**
33 | * Idle
34 | */
35 | public static final int SCROLL_STATE_IDLE = 0;
36 | /**
37 | * Down
38 | */
39 | public static final int SCROLL_STATE_DOWN = 1;
40 |
41 | /**
42 | * Dragging
43 | */
44 | public static final int SCROLL_STATE_DRAGGING = 2;
45 |
46 | /**
47 | * Scrolling
48 | */
49 | public static final int SCROLL_STATE_SCROLLING = 3;
50 |
51 | public static final int VETTAICL = 1 << 1;
52 | public static final int HORIZENTAL = 1 << 2;
53 |
54 | private static final int CORRECT_ANIMATION_DURATION = 250;
55 | /**
56 | * 手指滑动,滚轮跟随滚动因子
57 | */
58 | private static final float MOVE_FACTOR = 0.2F;
59 |
60 | private final Interpolator DEFAULT_FLING_ANIM_INTERPOLATOR = new DecelerateInterpolator(4);
61 | private final Interpolator DEFAULT_ROLLBACK_ANIM_INTERPOLATOR = new DecelerateInterpolator(4);
62 |
63 | protected static int mOrientation = VETTAICL;
64 | /**
65 | * 是否支持循环滚动
66 | */
67 | protected boolean mLoop = false;
68 |
69 | protected WheelPickerImpl mWheelPickerImpl;
70 |
71 | private CorrectAnimRunnable mCorrectAnimRunnable;
72 | private CorrectTransLateAnim mCorrectAnimController;
73 |
74 | private FlingRunnable mFlingRunnable;
75 |
76 | /**
77 | * overScroll偏移量
78 | */
79 | private int mOverScrollOffset;
80 | private int mMinScrollOffset;
81 | private int mMaxScrollOffset;
82 |
83 | private int mScrollState = SCROLL_STATE_IDLE;
84 |
85 | /**
86 | * 手指滑动,滚动跟随滚动因子
87 | */
88 | private float mFingerMoveFactor = MOVE_FACTOR * 1.0f;
89 | /**
90 | * fling滚动阻尼因子
91 | */
92 | private float mFlingAnimFactor = 0.8f;
93 | /**
94 | * 手指离开屏幕后,动画Anim的插值器
95 | */
96 | private Interpolator mFlingAnimInterpolator = DEFAULT_FLING_ANIM_INTERPOLATOR;
97 | /**
98 | * 回滚动画的插值器
99 | */
100 | private Interpolator mRollbackAnimInterpolator = DEFAULT_ROLLBACK_ANIM_INTERPOLATOR;
101 |
102 | public ScrollWheelPicker(Context context) {
103 | super(context);
104 | }
105 |
106 | public ScrollWheelPicker(Context context, AttributeSet attrs) {
107 | super(context, attrs);
108 | }
109 |
110 | public ScrollWheelPicker(Context context, AttributeSet attrs, int defStyleAttr) {
111 | super(context, attrs, defStyleAttr);
112 | }
113 |
114 | @Override
115 | protected void instantiation() {
116 | super.instantiation();
117 |
118 | mWheelPickerImpl = new WheelPickerImpl(mOrientation);
119 | init();
120 | }
121 |
122 | private void init() {
123 | mCorrectAnimController = new CorrectTransLateAnim();
124 | mFlingRunnable = new FlingRunnable(getContext());
125 | mCorrectAnimRunnable = new CorrectAnimRunnable();
126 |
127 | mOverScrollOffset = getResources().getDimensionPixelOffset(R.dimen.px24);
128 | }
129 |
130 | protected void setOrientation(int orientation) {
131 | mOrientation = orientation;
132 | }
133 |
134 | public void setScrollRange(int minOffset, int maxOffset) {
135 | mMinScrollOffset = minOffset;
136 | mMaxScrollOffset = maxOffset;
137 | }
138 |
139 | @Override
140 | protected void resetScroll() {
141 | if (mFlingRunnable != null) {
142 | mFlingRunnable.resetScroller(getContext());
143 | }
144 | }
145 |
146 | @Override
147 | protected void onTouchDown(MotionEvent event) {
148 | mFlingRunnable.stop();
149 | mCorrectAnimRunnable.stop();
150 | mScrollState = SCROLL_STATE_DOWN;
151 | }
152 |
153 | @Override
154 | protected void onTouchMove(MotionEvent event) {
155 | mScrollState = SCROLL_STATE_DRAGGING;
156 | mCurrentX = (mCurrentX + mDeltaX);
157 | mCurrentY = (mCurrentY + mDeltaY * mFingerMoveFactor);
158 |
159 | onScrolling(mCurrentX, mCurrentY, false);
160 | }
161 |
162 | @Override
163 | protected void onTouchUp(MotionEvent event) {
164 | mScrollState = SCROLL_STATE_SCROLLING;
165 | mFlingRunnable.fling();
166 | }
167 |
168 | @Override
169 | protected void onTouchCancel(MotionEvent event) {
170 | mFlingRunnable.stop();
171 | mScrollState = SCROLL_STATE_IDLE;
172 | }
173 |
174 | /**
175 | * Fling animation
176 | */
177 | private class FlingRunnable implements Runnable {
178 | protected WheelScroller mScroller;
179 | private float friction = ViewConfiguration.getScrollFriction();
180 |
181 | private FlingRunnable(Context context) {
182 | if (OSUtils.isEMUI()) {
183 | mScroller = new ScrollerCompat(context, mFlingAnimInterpolator);
184 | mScroller.setFriction(friction);
185 | } else {
186 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
187 | mScroller = new OverScrollerCompat(context, mFlingAnimInterpolator);
188 | mScroller.setFriction(friction);
189 | } else {
190 | mScroller = new ScrollerCompat(context, mFlingAnimInterpolator);
191 | mScroller.setFriction(friction);
192 | }
193 | }
194 | }
195 |
196 | public void run() {
197 | mScroller.computeScrollOffset();
198 | if (!mScroller.isFinished()) {
199 | ViewCompat.postOnAnimation(ScrollWheelPicker.this, this);
200 | } else {
201 | mCurrentX = mScroller.getFinalX();
202 | mCurrentY = mScroller.getFinalY();
203 | }
204 |
205 | mCurrentX = mScroller.getCurrX();
206 | mCurrentY = mScroller.getCurrY();
207 | onScrolling(mCurrentX, mCurrentY,
208 | mScrollState == SCROLL_STATE_SCROLLING && mScroller.isFinished());
209 | }
210 |
211 | public void stop() {
212 | if (!mScroller.isFinished())
213 | mScroller.abortAnimation();
214 | }
215 |
216 | public void fling() {
217 | int minScrollOffset = mLoop ? Integer.MIN_VALUE : mMinScrollOffset;
218 | int maxScrollOffset = mLoop ? Integer.MAX_VALUE : mMaxScrollOffset;
219 |
220 | //Log.i("aaa", " min=" + minScrollOffset + " max=" + maxScrollOffset);
221 | //minScrollOffset = 0;
222 | //maxScrollOffset = 200;
223 |
224 | if (mOrientation == HORIZENTAL) {
225 | mScroller.fling((int) (mCurrentX), 0,
226 | (int) (mTracker.getXVelocity() * mFlingAnimFactor), 0,
227 | minScrollOffset, maxScrollOffset,
228 | 0, 0,
229 | mOverScrollOffset, 0);
230 | } else {
231 | mScroller.fling(0, (int) (mCurrentY),
232 | 0, (int) (mTracker.getYVelocity() * mFlingAnimFactor),
233 | 0, 0,
234 | minScrollOffset, maxScrollOffset,
235 | 0, mOverScrollOffset);
236 | }
237 | //开启fling动画
238 | ViewCompat.postOnAnimation(ScrollWheelPicker.this, this);
239 | }
240 |
241 | public void resetScroller(Context context) {
242 | if (mCurrentX == 0 && mCurrentY == 0) {
243 | return;
244 | }
245 |
246 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
247 | int x = mScroller.getCurrX();
248 | int y = mScroller.getCurrY();
249 | mScroller.startScroll(x, y, -x, -y, 10); //scroll back in short time
250 | } else {
251 | mScroller.setFinalX(0);
252 | mScroller.setFinalY(0);
253 | }
254 |
255 | mCurrentX = 0;
256 | mCurrentY = 0;
257 | }
258 | }
259 |
260 | /**
261 | * start correction animation
262 | *
263 | * @param transLateX
264 | * @param transLateY
265 | */
266 | protected void startCorrectAnimation(float transLateX, float transLateY) {
267 | if (mCorrectAnimController == null) {
268 | mCorrectAnimController = new CorrectTransLateAnim();
269 | } else {
270 | mCorrectAnimController.forceStop();
271 | }
272 | if (mOrientation == HORIZENTAL) {
273 | if (transLateX == 0) {
274 | mScrollState = SCROLL_STATE_IDLE;
275 | return;
276 | }
277 | } else {
278 | if (transLateY == 0) {
279 | mScrollState = SCROLL_STATE_IDLE;
280 | return;
281 | }
282 | }
283 |
284 | mCorrectAnimController.setTransLate(transLateX, transLateY);
285 |
286 | ViewCompat.postOnAnimation(ScrollWheelPicker.this, mCorrectAnimRunnable);
287 | mCorrectAnimController.start();
288 | }
289 |
290 | /**
291 | * stop correction animation
292 | */
293 | protected void stopCorrectAnimation() {
294 | mCorrectAnimRunnable.stop();
295 | }
296 |
297 | /**
298 | * The animation runnable of correcting the location
299 | */
300 | private class CorrectAnimRunnable implements Runnable {
301 |
302 | public void run() {
303 | boolean running = mCorrectAnimController.calculate(SystemClock.uptimeMillis());
304 | if (running) {
305 | onScrolling(mCurrentX, mCurrentY, false);
306 | ViewCompat.postOnAnimation(ScrollWheelPicker.this, this);
307 | } else {
308 | mCurrentX = mCorrectAnimController.getFinalX();
309 | mCurrentY = mCorrectAnimController.getFinalY();
310 | onScrolling(mCurrentX, mCurrentY, true);
311 |
312 | mScrollState = SCROLL_STATE_IDLE;
313 | }
314 | }
315 |
316 | public void stop() {
317 | mCorrectAnimController.forceStop();
318 | }
319 | }
320 |
321 | /**
322 | * Translate animation
323 | */
324 | private class CorrectTransLateAnim extends Animation {
325 | private float mStartX;
326 | private float mStartY;
327 | private float mTransLateX;
328 | private float mTransLateY;
329 |
330 | public CorrectTransLateAnim() {
331 | setDuration(CORRECT_ANIMATION_DURATION);
332 | setInterpolator(new DecelerateInterpolator());
333 | }
334 |
335 | public void setTransLate(float transLateX, float transLateY) {
336 | mTransLateX = transLateX;
337 | mTransLateY = transLateY;
338 | mStartX = mCurrentX;
339 | mStartY = mCurrentY;
340 | }
341 |
342 | public float getFinalX() {
343 | return mStartX + mCurrentX;
344 | }
345 |
346 | public float getFinalY() {
347 | return mStartY + mTransLateY;
348 | }
349 |
350 | protected void onCalculate(float factor) {
351 | if (mOrientation == HORIZENTAL) {
352 | mCurrentX = (mStartX + (int) (factor * mTransLateX));
353 | } else {
354 | mCurrentY = (mStartY + factor * mTransLateY);
355 | }
356 | }
357 | }
358 |
359 | protected abstract void onScrolling(float offsetX, float offsetY, boolean isFinshed);
360 |
361 | public void setFingerMoveFactor(float fingerMoveFactor) {
362 | fingerMoveFactor = Animation.clamp(fingerMoveFactor, 0.001f, 1);
363 | fingerMoveFactor *= MOVE_FACTOR;
364 | mFingerMoveFactor = fingerMoveFactor;
365 | }
366 |
367 | public void setFlingAnimFactor(float flingAnimFactor) {
368 | flingAnimFactor = Animation.clamp(flingAnimFactor, 0.001f, 1);
369 | mFlingAnimFactor = flingAnimFactor;
370 | }
371 |
372 | public void setOverOffset(int overScrollOffset) {
373 | if (overScrollOffset < 0) {
374 | return;
375 | }
376 |
377 | mOverScrollOffset = overScrollOffset;
378 | }
379 | }
380 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 3DWheelPicker
2 | 3D效果 数据选择控件,源码地址:https://github.com/yijiebuyi/3DWheelPicker
3 | 类似效果的开源库也有几个,公司项目也用到类似于时间选择功能,但还达不到产品所要求的,效果并没有那么丝滑。所以自己才实现,下载Demo即可体验。
4 |
5 | # Demo下载
6 | APK下载链接1:http://d.firim.pro/3dwheelpicker
7 |
8 | #效果图
9 | 
10 |
11 |
12 | ### 功能
13 | - 时间选择(生日模式,时间段模式,未来时间模式)
14 | - 单数组的数据选择
15 | - 多数组的数据选择(支持多级联)
16 | - 省市区级联城市选择(城市数据可能不完整)
17 | - 可动态设置样式
18 |
19 | ## 使用
20 | - 在project的build.gradle添加如下代码
21 | ```gradle
22 | allprojects {
23 | repositories {
24 | ...
25 | maven { url "https://jitpack.io" }
26 | }
27 | }
28 | ```
29 |
30 | - 引用
31 | ```gradle
32 |
33 | dependencies {
34 | implementation 'com.github.yijiebuyi:3DWheelPicker:v1.2.0'
35 | }
36 |
37 | ```
38 |
39 | ### 基本用法:
40 | #### 使用 DataPicker (可参照demo的用法)
41 |
42 |
43 | #### 获取单行数据具体使用
44 | - 使用示例
45 | ```java
46 | PickOption option = new PickOption.Builder()
47 | .setVisibleItemCount(9) //设置pickerView有多少个可见的item,必须是单数(1,3,5,7....)
48 | .setItemSpace(context.getResources().getDimensionPixelOffset(R.dimen.px20)) //设置item的间距
49 | .setItemTextColor(context.getResources().getColor(R.color.font_black)) //设置item的文本颜色
50 | .setItemTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_36px)) //设置item的字体大小
51 | .setVerPadding(context.getResources().getDimensionPixelSize(R.dimen.px20)) //设置item的顶部,底部的padding
52 | .setShadowGravity(AbstractViewWheelPicker.SHADOW_RIGHT) //设置滚动的偏向
53 | .setShadowFactor(0.5f) //设置滚轮的偏向因子
54 | .setFingerMoveFactor(0.8f) //设置手指滑动的阻尼因子
55 | .setFlingAnimFactor(0.7f) //设置手指快速放开后,滚动动画的阻尼因子
56 | .setOverScrollOffset(context.getResources().getDimensionPixelSize(R.dimen.px36)) //设置滚轮滑动到底端顶端回滚动画的最大偏移
57 | .setBackgroundColor(Color.WHITE) //设置滚轮的背景颜色
58 | .setLeftTitleColor(0xFF1233DD) //设置底部弹出框左边文本的颜色
59 | .setRightTitleColor(0xFF1233DD) //设置底部弹出框右边文本的颜色
60 | .setMiddleTitleColor(0xFF333333) //设置底部弹出框中间文本的颜色
61 | .setTitleBackground(0XFFDDDDDD) //设置底部弹框title栏的背景颜色
62 | .setLeftTitleText("取消") //设置底部弹出框左边文本
63 | .setRightTitleText("确定") //设置底部弹出框右边文本
64 | .setMiddleTitleText("请选择数据") //设置底部弹出框中间
65 | .setTitleHeight(context.getResources().getDimensionPixelOffset(R.dimen.px80)) //设置底部弹框title高度
66 | .build();
67 | DataPicker.pickData(MainActivity.this, mInitData, getStudents(1), option, new OnDataPickListener() {
68 | @Override
69 | public void onDataPicked(int index, String val, Student data) {
70 | mInitData = data;
71 | Toast.makeText(MainActivity.this, val, Toast.LENGTH_SHORT).show();
72 | }
73 | });
74 | ```
75 | - 也可以使用默认的Builder,然后设置自己关注的属性
76 | ```java
77 | PickOption option = PickOption.getPickDefaultOptionBuilder(mContext)
78 | .setShadowFactor(0.5f) //设置滚轮的偏向因子
79 | .setFingerMoveFactor(0.8f) //设置手指滑动的阻尼因子
80 | .setFlingAnimFactor(0.7f) //设置手指快速放开后,滚动动画的阻尼因子
81 | .build();
82 | ```
83 |
84 | - 时间选择
85 | ```java
86 | /**
87 | * 获取日期
88 | *
89 | * @param context
90 | * @param initDate 初始化时选择的日期
91 | * @param mode 获取哪一种数据
92 | * @param option
93 | * @param listener
94 | */
95 | public static void pickDate(Context context, @Nullable Date initDate, int mode,
96 | @Nullable PickOption option,
97 | final OnDatePickListener listener)
98 |
99 | /**
100 | * 获取日期 (某个时间段范围)
101 | *
102 | * @param context
103 | * @param initDate
104 | * @param mode
105 | * @param from 开始日期
106 | * @param to 结束日期
107 | * @param option
108 | * @param listener
109 | */
110 | public static void pickDate(Context context, @Nullable Date initDate, int mode,
111 | long from, long to,
112 | @Nullable PickOption option,
113 | final OnDatePickListener listener) {
114 |
115 | ```
116 | - 数据选择:
117 | 为了保证picker控件显示的数据是期望的字符串,需要对数组中的类(String数组除外)实现PickString,或者重写toString
118 | ```java
119 | /**
120 | * 功能描述:当没有实现PickString,picker控件上显示是toString()的内容
121 | */
122 | class Student implements PickString {
123 | public String name;
124 | public int age;
125 |
126 | public Student(String n, int a) {
127 | name = n;
128 | age = a;
129 | }
130 |
131 | @NonNull
132 | @Override
133 | public String toString() {
134 | return age + "岁";
135 | }
136 |
137 | @Override
138 | public String pickDisplayName() {
139 | return name;
140 | }
141 | }
142 |
143 | /**
144 | * 获取单行数据
145 | * @param context
146 | * @param initData 初始化时,显示的数据
147 | * @param srcData 数据集合
148 | * @param listener dataPicker数据被选中监听器
149 | * @param
150 | */
151 | public static void pickData(Context context, @Nullable T initData, @NonNull final List srcData,
152 | @Nullable PickOption option, final OnDataPickListener listener
153 |
154 | /**
155 | * 多行数据选择(级联数据)
156 | * @param context
157 | * @param initIndex 每个Wheelpicker是初始化时,对应显示的数据index,如果没有特殊需求,传null
158 | * @param srcData 源数据,是一个二位数组,外层List代表是WheelPicker的集合,内层List代表具体的Wheelpicker对应的数据源
159 | * @param listener dataPicker数据被选中监听器
160 | * @param
161 | */
162 | public static void pickData(Context context, @Nullable List initIndex, @NonNull List> srcData,
163 | @Nullable PickOption option, final OnMultiDataPickListener listener)
164 |
165 | /**
166 | * 多行数据选择(级联数据)
167 | * @param context
168 | * @param initIndex 每个Wheelpicker是初始化时,对应显示的数据index,如果没有特殊需求,传null
169 | * @param srcData 源数据,是一个二位数组,外层List代表是WheelPicker的集合,内层List代表具体的Wheelpicker对应的数据源
170 | * @param listener dataPicker数据被选中监听器
171 | * @param cascadeListener 级联监听器,这里需要自己去实现级联的数据源,可参考demo中城市数据的使用方式
172 | * @param
173 | */
174 | public static void pickData(Context context, @Nullable List initIndex, @NonNull List> srcData,
175 | @Nullable PickOption option, boolean wrapper,
176 | final OnMultiDataPickListener listener, final OnCascadeWheelListener cascadeListener)
177 |
178 |
179 | ```
180 |
181 | #### 设置滚轮样式(见DataPicker中的使用方法)
182 | pickerview的样式详情见PickOption里面的属性,包括弹出框的顶部title样式,pickerview的的wheel样式,item样式
183 | ```java
184 | /**
185 | * 设置滚轮样式
186 | * @param pickerView
187 | * @param option
188 | */
189 | private static void setPickViewStyle(IPickerView pickerView, PickOption option) {
190 | //设置view样式
191 | pickerView.asView().setBackgroundColor(option.getBackgroundColor());
192 | pickerView.asView().setPadding(0, option.getVerPadding(), 0, option.getVerPadding());
193 |
194 | //设置Item样式
195 | pickerView.setTextColor(option.getItemTextColor()); //设置item的文本颜色
196 | pickerView.setVisibleItemCount(option.getVisibleItemCount()); //设置可见item的数量,必须是奇数: 如1,3,5,7,9...
197 | pickerView.setTextSize(option.getItemTextSize());//设置item的文本字体大小
198 | pickerView.setItemSpace(option.getItemSpace());//设置item的间距
199 | pickerView.setLineColor(option.getItemLineColor());//设置item的分割线的颜色
200 | pickerView.setLineWidth(option.getItemLineWidth());//设置item分割线的宽度
201 |
202 | //设置滚轮效果
203 | pickerView.setShadow(option.getShadowGravity(), option.getShadowFactor()); //设置滚轮偏向,偏向因子(偏向因子取值[0,1])
204 | pickerView.setScrollMoveFactor(option.getFingerMoveFactor()); //设置手指移动是item跟随滚动灵敏度(取值(0,1])
205 | pickerView.setScrollAnimFactor(option.getFlingAnimFactor()); //设置滚动动画阻尼因子(取值(0,1])
206 | pickerView.setScrollOverOffset(option.getOverScrollOffset()); //设置滚轮滑动到顶端或底端的最大回弹的偏移量
207 | }
208 | ```
209 |
210 | ```java
211 | /**
212 | * 获取底部弹出框
213 | * @param context
214 | * @param pickerView
215 | * @return
216 | */
217 | private static BottomSheet buildBottomSheet(Context context, @Nullable PickOption option, IPickerView pickerView) {
218 | BottomSheet bottomSheet = new BottomSheet(context);
219 | if (option != null) {
220 | bottomSheet.setLeftBtnText(option.getLeftTitleText());
221 | bottomSheet.setRightBtnText(option.getRightTitleText());
222 | bottomSheet.setMiddleText(option.getMiddleTitleText());
223 | bottomSheet.setLeftBtnTextColor(option.getLeftTitleColor());
224 | bottomSheet.setRightBtnTextColor(option.getRightTitleColor());
225 | bottomSheet.setMiddleTextColor(option.getMiddleTitleColor());
226 | bottomSheet.setTitleBackground(option.getTitleBackground());
227 |
228 | bottomSheet.setTitleHeight(option.getTitleHeight());
229 | }
230 | bottomSheet.setContent(pickerView.asView());
231 | return bottomSheet;
232 | }
233 | ```
234 |
235 |
236 | #### 级联数据使用案例 (城市选择)
237 | ```java
238 |
239 | //城市选择(级联操作)设置OnCascadeWheelListener即可满足级联
240 | findViewById(R.id.city_picker).setOnClickListener(new View.OnClickListener() {
241 | @Override
242 | public void onClick(View view) {
243 | pickCity(AdministrativeUtil.PROVINCE_CITY_AREA, mCascadeInitIndex);
244 | }
245 | });
246 |
247 | private void pickCity(int mode, final List initIndex) {
248 | if (mAdministrativeMap == null) {
249 | mAdministrativeMap = AdministrativeUtil.loadCity(MainActivity.this);
250 | }
251 |
252 | PickOption option = getPickDefaultOptionBuilder(mContext)
253 | .setMiddleTitleText("请选择城市")
254 | .setFlingAnimFactor(0.4f)
255 | .setVisibleItemCount(7)
256 | .setItemTextSize(mContext.getResources().getDimensionPixelSize(com.wheelpicker.R.dimen.font_24px))
257 | .setItemLineColor(0xFF558800)
258 | .build();
259 |
260 | DataPicker.pickData(mContext, initIndex,
261 | AdministrativeUtil.getPickData(mAdministrativeMap, initIndex, mode), option,
262 | new OnMultiDataPickListener() {
263 | @Override
264 | public void onDataPicked(List indexArr, List val, List data) {
265 | String s = indexArr.toString() + ":" + val.toString();
266 | Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
267 | initIndex.clear();
268 | initIndex.addAll(indexArr);
269 | }
270 | }, new OnCascadeWheelListener>() {
271 |
272 | @Override
273 | public List> onCascade(int wheelIndex, List itemIndex) {
274 | //级联数据
275 | if (wheelIndex == 0) {
276 | return mAdministrativeMap.provinces.get(itemIndex.get(0)).city;
277 | } else if (wheelIndex == 1) {
278 | return mAdministrativeMap.provinces.get(itemIndex.get(0)).city.get(itemIndex.get(1)).areas;
279 | }
280 |
281 | return null;
282 | }
283 | });
284 | }
285 |
286 | ```
287 |
288 | #### 也可以使用Textwheelpicker
289 |
290 | ```java
291 | 参照 DateWheelPicker,SingleTextWheelPicker
292 |
293 | ```
294 |
295 | #实现方式
296 | 继承View,重写 onDraw(Canvas canvas) 方法实现绘制逻辑
297 |
298 | 核心类:TextWheelPicker,使用它就可以实现你滚轮的效果,其drawItems(Canvas canvas)就是绘制滚轮效果的核心逻辑,使用系统的Camera类,Matrix矩阵变化,实现3d滚轮效果。
299 |
300 | ###绘制核心代码
301 | ```java
302 | ....
303 |
304 | float space = computeSpace(rotateDegree, mRadius);
305 | float relDegree = Math.abs(rotateDegree) / 90;
306 | canvas.save();
307 | mCamera.save();
308 | mRotateMatrix.reset();
309 |
310 | //旋转矩阵变换
311 | if (mShadowGravity == SHADOW_RIGHT) {
312 | mCamera.translate(-mShadowOffset, 0, 0);
313 | } else if (mShadowGravity == SHADOW_LEFT) {
314 | mCamera.translate(mShadowOffset, 0, 0);
315 | }
316 | //旋转
317 | mWheelPickerImpl.rotateCamera(mCamera, rotateDegree);
318 | mCamera.getMatrix(mRotateMatrix);
319 | mCamera.restore();
320 | mWheelPickerImpl.matrixToCenter(mRotateMatrix, space, mWheelCenterX, mWheelCenterY);
321 | if (mShadowGravity == SHADOW_RIGHT) {
322 | mRotateMatrix.postTranslate(mShadowOffset, 0);
323 | } else if (mShadowGravity == SHADOW_LEFT) {
324 | mRotateMatrix.postTranslate(-mShadowOffset, 0);
325 | }
326 | //偏移矩阵变换
327 | float depth = computeDepth(rotateDegree, mRelRadius);
328 | mCamera.save();
329 | mDepthMatrix.reset();
330 | mCamera.translate(0, 0, depth);
331 | mCamera.getMatrix(mDepthMatrix);
332 | mCamera.restore();
333 | mWheelPickerImpl.matrixToCenter(mDepthMatrix, space, mWheelCenterX, mWheelCenterY);
334 |
335 | mRotateMatrix.postConcat(mDepthMatrix);
336 | canvas.concat(mRotateMatrix);
337 |
338 | //绘制文本
339 | .......
340 | ```
341 |
342 | 相关关联类:ScrollWheelPicker ;AbstractWheelPicker
343 |
344 | #原理
345 | 
346 |
347 |
348 |
349 | ####参考文献
350 | 滚轮计算原理图:[https://blog.csdn.net/qq_22393017/article/details/59488906](https://blog.csdn.net/qq_22393017/article/details/59488906)
351 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wp/demo/OldPickerActivity.java:
--------------------------------------------------------------------------------
1 | package com.wp.demo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.Toast;
8 |
9 | import com.wheelpicker.AdministrativeMap;
10 | import com.wheelpicker.AdministrativeUtil;
11 | import com.wheelpicker.OldDataPicker;
12 | import com.wheelpicker.DateWheelPicker;
13 | import com.wheelpicker.IDateTimePicker;
14 | import com.wheelpicker.OnCascadeWheelListener;
15 | import com.wheelpicker.OnDataPickListener;
16 | import com.wheelpicker.OnDatePickListener;
17 | import com.wheelpicker.OnMultiDataPickListener;
18 | import com.wheelpicker.PickOption;
19 |
20 | import java.text.DateFormat;
21 | import java.text.SimpleDateFormat;
22 | import java.util.ArrayList;
23 | import java.util.Date;
24 | import java.util.List;
25 |
26 | public class OldPickerActivity extends Activity {
27 | public static final String TIME_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
28 | public static final String TIME_YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
29 | public static final String TIME_YYYY_MM_DD = "yyyy-MM-dd";
30 |
31 | private Date mInitBirthday = new Date();
32 | private Date mInitDate = new Date();
33 | private Date mInitFutureDateTime = new Date();
34 | private Date mInitFutureDate = new Date();
35 |
36 | private Student mInitData = null;
37 | private List mMultiInitIndex = null;
38 | private List mCascadeInitIndex = new ArrayList();
39 | private List mCascadeInitIndexNoArea = new ArrayList();
40 |
41 | private AdministrativeMap mAdministrativeMap;
42 | private Context mContext;
43 |
44 | @Override
45 | protected void onCreate(Bundle savedInstanceState) {
46 | super.onCreate(savedInstanceState);
47 |
48 | setContentView(R.layout.activity_picker_old);
49 | mContext = this;
50 |
51 | //选择生日
52 | findViewById(R.id.picker_birthday).setOnClickListener(new View.OnClickListener() {
53 | @Override
54 | public void onClick(View view) {
55 | PickOption option = getPickDefaultOptionBuilder(mContext)
56 | .setMiddleTitleText("请选择生日日期")
57 | .build();
58 | OldDataPicker.pickBirthday(mContext, mInitBirthday, option,
59 | new OnDatePickListener() {
60 | @Override
61 | public void onDatePicked(IDateTimePicker dateTimePicker) {
62 | mInitBirthday.setTime(dateTimePicker.getTime());
63 | Toast.makeText(mContext, formatDate(dateTimePicker.getTime(), TIME_YYYY_MM_DD), Toast.LENGTH_SHORT).show();
64 | }
65 | });
66 |
67 | }
68 | });
69 |
70 | //选择时间
71 | findViewById(R.id.picker_date).setOnClickListener(new View.OnClickListener() {
72 | @Override
73 | public void onClick(View view) {
74 | PickOption option = getPickDefaultOptionBuilder(mContext)
75 | .setMiddleTitleText("请选择时间")
76 | .setDateWitchVisible(DateWheelPicker.TYPE_ALL)
77 | .setAheadYears(100)
78 | .setAfterYears(100)
79 | .build();
80 | OldDataPicker.pickDate(mContext, mInitDate, option, new OnDatePickListener() {
81 | @Override
82 | public void onDatePicked(IDateTimePicker dateTimePicker) {
83 | mInitDate.setTime(dateTimePicker.getTime());
84 | Toast.makeText(mContext, formatDate(dateTimePicker.getTime(), TIME_YYYY_MM_DD_HH_MM_SS), Toast.LENGTH_SHORT).show();
85 | }
86 | });
87 |
88 |
89 | }
90 | });
91 |
92 | //选择未来时间(年月日时分秒)
93 | findViewById(R.id.picker_future_date).setOnClickListener(new View.OnClickListener() {
94 | @Override
95 | public void onClick(View view) {
96 | PickOption option = getPickDefaultOptionBuilder(mContext)
97 | .setMiddleTitleText("请选择时间")
98 | .setDateWitchVisible(DateWheelPicker.TYPE_ALL)
99 | .setAheadYears(0)
100 | .setAfterYears(100)
101 | .build();
102 | OldDataPicker.pickDate(mContext, mInitFutureDateTime, option, new OnDatePickListener() {
103 |
104 | @Override
105 | public void onDatePicked(IDateTimePicker picker) {
106 | mInitFutureDateTime.setTime(picker.getTime());
107 | Toast.makeText(mContext, formatDate(picker.getTime(), TIME_YYYY_MM_DD_HH_MM_SS), Toast.LENGTH_SHORT).show();
108 | }
109 | });
110 |
111 |
112 | }
113 | });
114 |
115 | //选择未来日期(天,小时,分钟组合)
116 | findViewById(R.id.picker_future).setOnClickListener(new View.OnClickListener() {
117 | @Override
118 | public void onClick(View view) {
119 | PickOption option = getPickDefaultOptionBuilder(mContext)
120 | .setMiddleTitleText("请选择日期")
121 | .setDurationDays(100)
122 | .build();
123 | OldDataPicker.pickFutureDate(OldPickerActivity.this, new Date(System.currentTimeMillis() + 30 * 60 * 1000),
124 | option, new OnDatePickListener() {
125 | @Override
126 | public void onDatePicked(IDateTimePicker picker) {
127 | Toast.makeText(mContext, formatDate(picker.getTime(), TIME_YYYY_MM_DD_HH_MM), Toast.LENGTH_SHORT).show();
128 | }
129 | });
130 | }
131 | });
132 |
133 | //选择单个文本
134 | findViewById(R.id.picker_txt).setOnClickListener(new View.OnClickListener() {
135 | @Override
136 | public void onClick(View view) {
137 | PickOption option = getPickDefaultOptionBuilder(mContext)
138 | .setMiddleTitleText("请选择")
139 | .setItemTextColor(0XFFFF0000)
140 | .setItemLineColor(0xFF00FF00)
141 | .setItemTextSize(mContext.getResources().getDimensionPixelSize(com.wheelpicker.R.dimen.font_22px))
142 | .setItemSpace(mContext.getResources().getDimensionPixelSize(com.wheelpicker.R.dimen.px36))
143 | .build();
144 | OldDataPicker.pickData(OldPickerActivity.this, mInitData, getStudents(1), option, new OnDataPickListener() {
145 | @Override
146 | public void onDataPicked(int index, String val, Student data) {
147 | mInitData = data;
148 | Toast.makeText(OldPickerActivity.this, val, Toast.LENGTH_SHORT).show();
149 | }
150 | });
151 | }
152 | });
153 |
154 | //多文本选择
155 | findViewById(R.id.multi_picker_txt).setOnClickListener(new View.OnClickListener() {
156 | @Override
157 | public void onClick(View view) {
158 |
159 | List> stu = new ArrayList<>();
160 | stu.add(getStudents(0));
161 | stu.add(getStudents(1));
162 | stu.add(getStudents(2));
163 |
164 | PickOption option = getPickDefaultOptionBuilder(mContext)
165 | .setMiddleTitleText("请选择")
166 | .setFlingAnimFactor(0.2f)
167 | .setVisibleItemCount(9)
168 | .setItemLineColor(0xFF0022FF)
169 | .build();
170 |
171 | OldDataPicker.pickData(mContext, mMultiInitIndex, stu, option, new OnMultiDataPickListener() {
172 |
173 | @Override
174 | public void onDataPicked(List indexArr, List val, List data) {
175 | mMultiInitIndex = indexArr;
176 | String s = indexArr.toString() + ":" + val.toString();
177 | Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();
178 | }
179 | });
180 | }
181 | });
182 |
183 | //城市选择(级联操作)设置OnCascadeWheelListener即可满足级联
184 | findViewById(R.id.city_picker).setOnClickListener(new View.OnClickListener() {
185 | @Override
186 | public void onClick(View view) {
187 | pickCity(AdministrativeUtil.PROVINCE_CITY_AREA, mCascadeInitIndex);
188 | }
189 | });
190 |
191 | findViewById(R.id.city_picker_no_area).setOnClickListener(new View.OnClickListener() {
192 | @Override
193 | public void onClick(View view) {
194 | pickCity(AdministrativeUtil.PROVINCE_CITY, mCascadeInitIndexNoArea);
195 | }
196 | });
197 | }
198 |
199 |
200 | private void pickCity(int mode, final List initIndex) {
201 | if (mAdministrativeMap == null) {
202 | mAdministrativeMap = AdministrativeUtil.loadCity(OldPickerActivity.this);
203 | }
204 |
205 | PickOption option = getPickDefaultOptionBuilder(mContext)
206 | .setMiddleTitleText("请选择城市")
207 | .setFlingAnimFactor(0.4f)
208 | .setVisibleItemCount(7)
209 | .setItemTextSize(mContext.getResources().getDimensionPixelSize(com.wheelpicker.R.dimen.font_24px))
210 | .setItemLineColor(0xFF558800)
211 | .build();
212 |
213 | OldDataPicker.pickData(mContext, initIndex,
214 | AdministrativeUtil.getPickData(mAdministrativeMap, initIndex, mode), option,
215 | new OnMultiDataPickListener() {
216 | @Override
217 | public void onDataPicked(List indexArr, List val, List data) {
218 | String s = indexArr.toString() + ":" + val.toString();
219 | Toast.makeText(OldPickerActivity.this, s, Toast.LENGTH_SHORT).show();
220 | initIndex.clear();
221 | initIndex.addAll(indexArr);
222 | }
223 | }, new OnCascadeWheelListener>() {
224 |
225 | @Override
226 | public List> onCascade(int wheelIndex, List itemIndex) {
227 | //级联数据
228 | if (wheelIndex == 0) {
229 | return mAdministrativeMap.provinces.get(itemIndex.get(0)).city;
230 | } else if (wheelIndex == 1) {
231 | return mAdministrativeMap.provinces.get(itemIndex.get(0)).city.get(itemIndex.get(1)).areas;
232 | }
233 |
234 | return null;
235 | }
236 | });
237 | }
238 |
239 | private List getTextList() {
240 | List data = new ArrayList<>();
241 | data.add("杨过");
242 | data.add("张无忌");
243 | data.add("郭靖");
244 | data.add("乔峰");
245 | data.add("令狐冲");
246 | data.add("赵敏");
247 | data.add("东方不败");
248 | data.add("小龙女");
249 | data.add("黄蓉");
250 | data.add("阿朱");
251 | data.add("王菇凉");
252 |
253 | data.add("杨过2");
254 | data.add("张无忌2");
255 | data.add("郭靖2");
256 | data.add("乔峰2");
257 | data.add("令狐冲2");
258 | data.add("赵敏2");
259 | data.add("东方不败2");
260 | data.add("小龙女2");
261 | data.add("黄蓉2");
262 | data.add("阿朱2");
263 | data.add("王菇凉2");
264 |
265 | data.add("杨过3");
266 | data.add("张无忌3");
267 | data.add("郭靖3");
268 | data.add("乔峰3");
269 | data.add("令狐冲3");
270 | data.add("赵敏3");
271 | data.add("东方不败3");
272 | data.add("小龙女3");
273 | data.add("黄蓉3");
274 | data.add("阿朱3");
275 | data.add("王菇凉3");
276 |
277 | return data;
278 | }
279 |
280 | private List getStudents(int c) {
281 | List data = new ArrayList();
282 | data.add(new Student("杨过" + c, 22));
283 | data.add(new Student("张无忌" + c, 24));
284 | data.add(new Student("小龙女" + c, 16));
285 | data.add(new Student("猪儿" + c, 18));
286 | data.add(new Student("周芷若" + c, 16));
287 | data.add(new Student("令狐冲" + c, 40));
288 | data.add(new Student("王姑娘" + c, 20));
289 |
290 | return data;
291 | }
292 |
293 | /**
294 | * 格式化时间
295 | *
296 | * @param date 需要被处理的日期,距离1970的long
297 | * @param format 最终返回的日期字符串的格式串
298 | * @return
299 | */
300 | public static String formatDate(long date, String format) {
301 | DateFormat sdf = new SimpleDateFormat(format);
302 | try {
303 | return sdf.format(date);
304 | } catch (Exception e) {
305 | e.printStackTrace();
306 | return "";
307 | }
308 | }
309 |
310 | private PickOption.Builder getPickDefaultOptionBuilder(Context context) {
311 | return PickOption.getPickDefaultOptionBuilder(context)
312 | .setLeftTitleColor(0xFF1233DD)
313 | .setRightTitleColor(0xFF1233DD)
314 | .setMiddleTitleColor(0xFF333333)
315 | .setTitleBackground(0XFFDDDDDD)
316 | .setLeftTitleText("取消")
317 | .setRightTitleText("确定");
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/library/src/main/java/com/wheelpicker/core/AbstractWheelPicker.java:
--------------------------------------------------------------------------------
1 | package com.wheelpicker.core;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.database.DataSetObserver;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.graphics.Paint.Style;
9 | import android.graphics.Rect;
10 | import android.util.AttributeSet;
11 | import android.view.MotionEvent;
12 | import android.view.VelocityTracker;
13 | import android.view.View;
14 |
15 | import com.wheelpicker.R;
16 |
17 |
18 | /*
19 | * Copyright (C) 2017
20 | * 版权所有
21 | *
22 | * 功能描述:
23 | *
24 | * 作者:yijiebuyi
25 | * 创建时间:2017/11/26
26 | *
27 | * 修改人:
28 | * 修改描述:
29 | * 修改日期
30 | */
31 | public abstract class AbstractWheelPicker extends View {
32 | private static final int VELOCITY_TRACKER_UNITS_DEFAULT = 600;
33 |
34 | private static final int DEFAULT_INDEX = 0;
35 | private static final int DEFAULT_COUNT = 5;
36 | private static final int DEFAULT_SPACE = 20;
37 | private static final int DEFAULT_COLOR = 0xFF000000;
38 | /**
39 | * 默认分割线颜色
40 | */
41 | private static final int DEFAULT_LINE_COLOR = 0XFFDDDDDD;
42 |
43 | protected VelocityTracker mTracker;
44 |
45 | public static final int SHADOW_LEFT = 0;
46 | public static final int SHADOW_MIDDLE = 1;
47 | public static final int SHADOW_RIGHT = 2;
48 | protected static final int SHADOW_MAX = 200;
49 |
50 | // 滚轮是偏右还是左
51 | protected int mShadowGravity = SHADOW_RIGHT;
52 | protected float mShadowFactor = 0.4f; //0 ~ 1.0f;
53 |
54 | protected Paint mPaint;
55 | protected Paint mLinePaint;
56 |
57 | protected int mWheelContentWidth;
58 | protected int mWheelContentHeight;
59 |
60 | protected int mViewWidth;
61 | protected int mViewHeight;
62 |
63 | protected Rect mBounds;
64 | protected float mWheelCenterX;
65 | protected float mWheelCenterY;
66 | protected float mWheelCenterTextY;
67 |
68 | /**
69 | * Default count of visible items
70 | */
71 | protected int mVisibleItemCount;
72 | protected int mItemSpace;
73 | /**
74 | * 当前变化的索引值,(当被初始化时,显示第几个item)
75 | */
76 | protected int mCurrItemIndex;
77 | /**
78 | * 滚动index时的索引值使用mPickedItemIndex表示
79 | */
80 | protected int mPickedItemIndex;
81 | protected int mColor;
82 | protected int mLineColor;
83 | protected int mLineStrokeWidth;
84 |
85 | protected int mItemStartIndex;
86 | protected int mItemEndIndex;
87 |
88 | protected int mItemMaxWidth;
89 | protected int mItemMaxHeight;
90 |
91 | protected boolean mIgnorePadding;
92 | protected T mAdapter;
93 | protected AdapterDataSetObserver mDataSetObserver;
94 |
95 | protected float mOldX;
96 | protected float mOldY;
97 | protected float mCurrentX;
98 | protected float mCurrentY;
99 | protected float mDeltaX;
100 | protected float mDeltaY;
101 |
102 | //是否可以响应滑动事件
103 | private boolean mTouchable = true;
104 |
105 | public AbstractWheelPicker(Context context) {
106 | super(context);
107 | init(null);
108 | }
109 |
110 | public AbstractWheelPicker(Context context, AttributeSet attrs) {
111 | super(context, attrs);
112 | init(attrs);
113 | }
114 |
115 | public AbstractWheelPicker(Context context, AttributeSet attrs, int defStyleAttr) {
116 | super(context, attrs, defStyleAttr);
117 | init(attrs);
118 | }
119 |
120 | private void init(AttributeSet attrs) {
121 | obtainAttrs(attrs);
122 | instantiation();
123 | //computeWheelSize();
124 | }
125 |
126 | protected void obtainAttrs(AttributeSet attrs) {
127 | if (attrs == null) {
128 | mColor = DEFAULT_COLOR;
129 | mLineColor = DEFAULT_LINE_COLOR;
130 | mCurrItemIndex = DEFAULT_INDEX;
131 | mVisibleItemCount = DEFAULT_COUNT;
132 | mItemSpace = DEFAULT_SPACE;
133 | mLineStrokeWidth = 1;
134 | } else {
135 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.WheelPicker);
136 |
137 | mCurrItemIndex = a.getInt(R.styleable.WheelPicker_wheel_item_index, DEFAULT_INDEX);
138 | mVisibleItemCount = a.getInt(R.styleable.WheelPicker_wheel_visible_item_count, DEFAULT_COUNT);
139 | mItemSpace = a.getDimensionPixelSize(R.styleable.WheelPicker_wheel_item_space, DEFAULT_SPACE);
140 | mColor = a.getColor(R.styleable.WheelPicker_wheel_text_color, DEFAULT_COLOR);
141 | mLineColor = a.getColor(R.styleable.WheelPicker_wheel_line_color, DEFAULT_LINE_COLOR);
142 | mLineStrokeWidth = a.getDimensionPixelSize(R.styleable.WheelPicker_wheel_line_width, 1);
143 | a.recycle();
144 | }
145 | }
146 |
147 | protected void instantiation() {
148 | mCurrentX = 0;
149 | mCurrentY = 0;
150 |
151 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
152 | mPaint.setColor(mColor);
153 | mPaint.setTextSize(70);
154 |
155 | mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
156 | mLinePaint.setColor(mLineColor);
157 | mLinePaint.setStyle(Style.FILL);
158 | mLinePaint.setStrokeWidth(mLineStrokeWidth);
159 |
160 | mBounds = new Rect();
161 | }
162 |
163 | @Override
164 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
165 | int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
166 | int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
167 |
168 | int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
169 | int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
170 |
171 | int resultWidth = mWheelContentWidth;
172 | int resultHeight = mWheelContentHeight;
173 |
174 | resultWidth += (getPaddingLeft() + getPaddingRight());
175 | resultHeight += (getPaddingTop() + getPaddingBottom());
176 | resultWidth = measureSize(modeWidth, sizeWidth, resultWidth);
177 | resultHeight = measureSize(modeHeight, sizeHeight, resultHeight);
178 |
179 | mViewWidth = resultWidth;
180 | mViewHeight = resultHeight;
181 |
182 | setMeasuredDimension(resultWidth, resultHeight);
183 | }
184 |
185 | protected int measureSize(int mode, int sizeExpect, int sizeActual) {
186 | int realSize;
187 | if (mode == MeasureSpec.EXACTLY) {
188 | realSize = sizeExpect;
189 | } else {
190 | realSize = sizeActual;
191 | if (mode == MeasureSpec.AT_MOST) {
192 | realSize = Math.min(realSize, sizeExpect);
193 | }
194 | }
195 | return realSize;
196 | }
197 |
198 | @Override
199 | protected void onSizeChanged(int w, int h, int oldW, int oldH) {
200 | mBounds.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom());
201 |
202 | mWheelCenterX = mBounds.centerX();
203 | mWheelCenterY = mBounds.centerY();
204 | }
205 |
206 | @Override
207 | protected void onDetachedFromWindow() {
208 | super.onDetachedFromWindow();
209 |
210 | if (mAdapter != null && mDataSetObserver != null) {
211 | mAdapter.unregisterDataSetObserver(mDataSetObserver);
212 | mDataSetObserver = null;
213 | }
214 | }
215 |
216 | @Override
217 | protected void onDraw(Canvas canvas) {
218 | drawBackground(canvas);
219 |
220 | canvas.save();
221 | canvas.clipRect(mBounds);
222 | drawItems(canvas);
223 | canvas.restore();
224 |
225 | drawForeground(canvas);
226 | }
227 |
228 | @Override
229 | public boolean onTouchEvent(MotionEvent event) {
230 | if (!mTouchable) {
231 | return true;
232 | }
233 |
234 | if (null == mTracker) {
235 | mTracker = VelocityTracker.obtain();
236 | }
237 | mTracker.addMovement(event);
238 |
239 | switch (event.getAction()) {
240 | case MotionEvent.ACTION_DOWN:
241 | mOldX = event.getX();
242 | mOldY = event.getY();
243 | onTouchDown(event);
244 | break;
245 | case MotionEvent.ACTION_MOVE:
246 | mDeltaX = event.getX() - mOldX;
247 | mDeltaY = event.getY() - mOldY;
248 | mOldX = event.getX();
249 | mOldY = event.getY();
250 |
251 | onTouchMove(event);
252 | break;
253 | case MotionEvent.ACTION_UP:
254 | mTracker.computeCurrentVelocity(VELOCITY_TRACKER_UNITS_DEFAULT);
255 | onTouchUp(event);
256 |
257 | mTracker.recycle();
258 | mTracker = null;
259 | break;
260 | case MotionEvent.ACTION_CANCEL:
261 | getParent().requestDisallowInterceptTouchEvent(false);
262 | mTracker.recycle();
263 | mTracker = null;
264 | onTouchCancel(event);
265 |
266 | break;
267 | }
268 | return true;
269 |
270 | }
271 |
272 | public void setCurrentItem(int index) {
273 | mCurrItemIndex = index;
274 | mPickedItemIndex = index;
275 | requestComputeLayout();
276 | }
277 |
278 | public void setCurrentItemWithoutReLayout(int index) {
279 | mCurrItemIndex = index;
280 | mPickedItemIndex = index;
281 | }
282 |
283 | public int getCurrentItem() {
284 | return mCurrItemIndex;
285 | }
286 |
287 | public void setPickedItemIndex(int index) {
288 | mPickedItemIndex = index;
289 | }
290 |
291 | public int getPickedItemIndex() {
292 | return mPickedItemIndex;
293 | }
294 |
295 | public void setItemSpace(int space) {
296 | mItemSpace = space;
297 | if (mAdapter != null && !mAdapter.isEmpty()) {
298 | requestComputeLayout();
299 | }
300 | }
301 |
302 | public void setVisibleItemCount(int count) {
303 | mVisibleItemCount = count;
304 | if (mAdapter != null && !mAdapter.isEmpty()) {
305 | requestComputeLayout();
306 | }
307 | }
308 |
309 | public void setShadowGravity(int gravity) {
310 | mShadowGravity = gravity;
311 | }
312 |
313 | public void setShadowFactor(float factor) {
314 | if (factor > 1.0) {
315 | factor = 1.0f;
316 | }
317 |
318 | if (factor < 0) {
319 | factor = 0;
320 | }
321 |
322 | mShadowFactor = factor;
323 | }
324 |
325 | public synchronized void setAdapter(T adapter) {
326 | if (adapter == null) {
327 | return;
328 | }
329 |
330 | if (mAdapter != null && mDataSetObserver != null) {
331 | mAdapter.unregisterDataSetObserver(mDataSetObserver);
332 | mDataSetObserver = null;
333 | }
334 |
335 | mAdapter = adapter;
336 | if (mCurrItemIndex > mAdapter.getCount() || mCurrItemIndex < 0) {
337 | mCurrItemIndex = 0;
338 | }
339 |
340 | if (mDataSetObserver == null) {
341 | mDataSetObserver = new AdapterDataSetObserver();
342 | mAdapter.registerDataSetObserver(mDataSetObserver);
343 | mDataSetObserver.onChanged();
344 | }
345 |
346 | requestComputeLayout();
347 | }
348 |
349 | public T getAdapter() {
350 | return mAdapter;
351 | }
352 |
353 | public void setTouchable(boolean touchable) {
354 | mTouchable = touchable;
355 | }
356 |
357 | protected void updateItemIndexRange() {
358 | if (mAdapter != null && !mAdapter.isEmpty()) {
359 | mItemStartIndex = Math.max(0, mCurrItemIndex - mVisibleItemCount / 2 - 1);
360 | mItemEndIndex = Math.min(mCurrItemIndex + mVisibleItemCount / 2 + 1, mAdapter.getCount() - 1);
361 | }
362 | }
363 |
364 | public void requestComputeLayout() {
365 | if ((mAdapter != null && mCurrItemIndex >= mAdapter.getCount())) {
366 | mCurrItemIndex = mAdapter.getCount() - 1;
367 | }
368 |
369 | resetScroll();
370 | initItemSize();
371 | computeWheelSize();
372 | updateItemIndexRange();
373 | postInvalidate();
374 | }
375 |
376 | /**
377 | * ==============abstract method==============
378 | */
379 | protected abstract void initItemSize();
380 |
381 | protected abstract void computeWheelSize();
382 |
383 | protected abstract void resetScroll();
384 |
385 | protected abstract void drawBackground(Canvas canvas);
386 |
387 | protected abstract void drawItems(Canvas canvas);
388 |
389 | protected abstract void drawForeground(Canvas canvas);
390 |
391 | protected abstract void onTouchDown(MotionEvent event);
392 |
393 | protected abstract void onTouchMove(MotionEvent event);
394 |
395 | protected abstract void onTouchUp(MotionEvent event);
396 |
397 | protected abstract void onTouchCancel(MotionEvent event);
398 |
399 | /**
400 | * ==============abstract method==============
401 | */
402 |
403 | protected void onWheelSelected(boolean touch, int index) {
404 |
405 | }
406 |
407 | class AdapterDataSetObserver extends DataSetObserver {
408 |
409 | @Override
410 | public void onChanged() {
411 | int count = 0;
412 | if (mAdapter != null) {
413 | count = mAdapter.getCount();
414 | }
415 | if (mCurrItemIndex > count - 1) {
416 | mCurrItemIndex = count - 1;
417 | }
418 | if (mCurrItemIndex < 0) {
419 | mCurrItemIndex = 0;
420 | }
421 | onWheelSelected(false, mCurrItemIndex);
422 | requestComputeLayout();
423 | }
424 |
425 | @Override
426 | public void onInvalidated() {
427 | invalidate();
428 | }
429 | }
430 |
431 | }
432 |
--------------------------------------------------------------------------------