├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── airsaid
│ │ │ └── android_pickerview_library
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── airsaid
│ │ │ └── android_pickerview_library
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── airsaid
│ │ └── android_pickerview_library
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── pickerview
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── integers.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── attrs.xml
│ │ │ ├── drawable
│ │ │ │ └── selector_pickerview_btn.xml
│ │ │ ├── anim
│ │ │ │ ├── slide_in_bottom.xml
│ │ │ │ └── slide_out_bottom.xml
│ │ │ └── layout
│ │ │ │ ├── layout_basepickerview.xml
│ │ │ │ ├── pickerview_options.xml
│ │ │ │ ├── pickerview_time.xml
│ │ │ │ └── include_pickerview_topbar.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── airsaid
│ │ │ │ └── pickerviewlibrary
│ │ │ │ ├── listener
│ │ │ │ ├── OnItemSelectedListener.java
│ │ │ │ ├── OnDismissListener.java
│ │ │ │ ├── OnCitySelectListener.java
│ │ │ │ └── OnSimpleCitySelectListener.java
│ │ │ │ ├── model
│ │ │ │ └── IPickerViewData.java
│ │ │ │ ├── widget
│ │ │ │ ├── wheelview
│ │ │ │ │ ├── OnItemSelectedRunnable.java
│ │ │ │ │ ├── LoopViewGestureListener.java
│ │ │ │ │ ├── MessageHandler.java
│ │ │ │ │ ├── SmoothScrollTimerTask.java
│ │ │ │ │ ├── InertiaTimerTask.java
│ │ │ │ │ └── WheelView.java
│ │ │ │ ├── BasePickerView.java
│ │ │ │ ├── WheelOptions.java
│ │ │ │ └── WheelTime.java
│ │ │ │ ├── adapter
│ │ │ │ ├── WheelAdapter.java
│ │ │ │ ├── ArrayWheelAdapter.java
│ │ │ │ └── NumericWheelAdapter.java
│ │ │ │ ├── utils
│ │ │ │ └── PickerViewAnimateUtil.java
│ │ │ │ ├── CityPickerView.java
│ │ │ │ ├── OptionsPickerView.java
│ │ │ │ └── TimePickerView.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── airsaid
│ │ │ └── pickerviewlibrary
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── airsaid
│ │ └── pickerviewlibrary
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gif
└── pickerview.gif
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/pickerview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':pickerview'
2 |
--------------------------------------------------------------------------------
/gif/pickerview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Airsaid/Android-PickerView-Library/HEAD/gif/pickerview.gif
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android-PickerView-Library
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Airsaid/Android-PickerView-Library/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Airsaid/Android-PickerView-Library/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Airsaid/Android-PickerView-Library/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Airsaid/Android-PickerView-Library/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Airsaid/Android-PickerView-Library/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Airsaid/Android-PickerView-Library/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/pickerview/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 300
4 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/listener/OnItemSelectedListener.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.listener;
2 |
3 |
4 | public interface OnItemSelectedListener {
5 | void onItemSelected(int index);
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/model/IPickerViewData.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.model;
2 |
3 | /**
4 | * Created by Sai on 2016/7/13.
5 | */
6 | public interface IPickerViewData {
7 | String getPickerViewText();
8 | }
9 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/listener/OnDismissListener.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.listener;
2 |
3 | /**
4 | * Created by Sai on 15/8/9.
5 | */
6 | public interface OnDismissListener {
7 | public void onDismiss(Object o);
8 | }
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Dec 02 13:57:31 CST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/pickerview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/drawable/selector_pickerview_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/listener/OnCitySelectListener.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.listener;
2 |
3 | /**
4 | * Created by zhouyou on 2017/1/10.
5 | * Class desc: 城市选择回调
6 | */
7 | public interface OnCitySelectListener {
8 | void onCitySelect(String str);
9 | void onCitySelect(String prov, String city, String area);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/anim/slide_in_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/anim/slide_out_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/pickerview/src/test/java/com/airsaid/pickerviewlibrary/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/airsaid/android_pickerview_library/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.android_pickerview_library;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/pickerview/src/androidTest/java/com/airsaid/pickerviewlibrary/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/pickerview/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 取消
3 | 确定
4 | 年
5 | 月
6 | 日
7 | 时
8 | 分
9 | 秒
10 |
11 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/airsaid/android_pickerview_library/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.android_pickerview_library;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/listener/OnSimpleCitySelectListener.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.listener;
2 |
3 | /**
4 | * Created by zhouyou on 2017/1/10.
5 | * Class desc:
6 | */
7 | public class OnSimpleCitySelectListener implements OnCitySelectListener {
8 |
9 | @Override
10 | public void onCitySelect(String str) {
11 |
12 | }
13 |
14 | @Override
15 | public void onCitySelect(String prov, String city, String area) {
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/wheelview/OnItemSelectedRunnable.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget.wheelview;
2 |
3 | final class OnItemSelectedRunnable implements Runnable {
4 | final WheelView loopView;
5 |
6 | OnItemSelectedRunnable(WheelView loopview) {
7 | loopView = loopview;
8 | }
9 |
10 | @Override
11 | public final void run() {
12 | loopView.onItemSelectedListener.onItemSelected(loopView.getCurrentItem());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/layout/layout_basepickerview.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #057dff
4 | #c2daf5
5 | #f5f5f5
6 | #000000
7 | #a8a8a8
8 | #2a2a2a
9 | #d5d5d5
10 | #60000000
11 |
12 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 44dp
4 |
5 | 10dp
6 |
7 | 30dp
8 |
9 | 16sp
10 | 18sp
11 |
12 | 20sp
13 |
14 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/wheelview/LoopViewGestureListener.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget.wheelview;
2 |
3 | import android.view.MotionEvent;
4 |
5 | final class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
6 |
7 | final WheelView loopView;
8 |
9 | LoopViewGestureListener(WheelView loopview) {
10 | loopView = loopview;
11 | }
12 |
13 | @Override
14 | public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
15 | loopView.scrollBy(velocityY);
16 | return true;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/adapter/WheelAdapter.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.adapter;
2 |
3 | public interface WheelAdapter {
4 | /**
5 | * Gets items count
6 | * @return the count of wheel items
7 | */
8 | int getItemsCount();
9 |
10 | /**
11 | * Gets a wheel item by index.
12 | * @param index the item index
13 | * @return the wheel item text or null
14 | */
15 | T getItem(int index);
16 |
17 | /**
18 | * Gets maximum item length. It is used to determine the wheel width.
19 | * If -1 is returned there will be used the default wheel width.
20 | * @param o
21 | * @return the maximum item length or -1
22 | */
23 | int indexOf(T o);
24 | }
25 |
--------------------------------------------------------------------------------
/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:\WorkSoft\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/pickerview/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:\WorkSoft\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "24.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.airsaid.android_pickerview_library"
9 | minSdkVersion 15
10 | targetSdkVersion 24
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:24.2.1'
26 | compile project(path: ':pickerview')
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/utils/PickerViewAnimateUtil.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.utils;
2 |
3 | import android.view.Gravity;
4 |
5 | import com.airsaid.pickerviewlibrary.R;
6 |
7 |
8 | /**
9 | * Created by Sai on 15/8/9.
10 | */
11 | public class PickerViewAnimateUtil {
12 | private static final int INVALID = -1;
13 | /**
14 | * Get default animation resource when not defined by the user
15 | *
16 | * @param gravity the gravity of the dialog
17 | * @param isInAnimation determine if is in or out animation. true when is is
18 | * @return the id of the animation resource
19 | */
20 | public static int getAnimationResource(int gravity, boolean isInAnimation) {
21 | switch (gravity) {
22 | case Gravity.BOTTOM:
23 | return isInAnimation ? R.anim.slide_in_bottom : R.anim.slide_out_bottom;
24 | }
25 | return INVALID;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/wheelview/MessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget.wheelview;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 |
6 | final class MessageHandler extends Handler {
7 | public static final int WHAT_INVALIDATE_LOOP_VIEW = 1000;
8 | public static final int WHAT_SMOOTH_SCROLL = 2000;
9 | public static final int WHAT_ITEM_SELECTED = 3000;
10 |
11 | final WheelView loopview;
12 |
13 | MessageHandler(WheelView loopview) {
14 | this.loopview = loopview;
15 | }
16 |
17 | @Override
18 | public final void handleMessage(Message msg) {
19 | switch (msg.what) {
20 | case WHAT_INVALIDATE_LOOP_VIEW:
21 | loopview.invalidate();
22 | break;
23 |
24 | case WHAT_SMOOTH_SCROLL:
25 | loopview.smoothScroll(WheelView.ACTION.FLING);
26 | break;
27 |
28 | case WHAT_ITEM_SELECTED:
29 | loopview.onItemSelected();
30 | break;
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
19 |
20 |
25 |
26 |
31 |
32 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/adapter/ArrayWheelAdapter.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.adapter;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * The simple Array wheel adapter
7 | * @param the element type
8 | */
9 | public class ArrayWheelAdapter implements WheelAdapter {
10 |
11 | /** The default items length */
12 | public static final int DEFAULT_LENGTH = 4;
13 |
14 | // items
15 | private ArrayList items;
16 | // length
17 | private int length;
18 |
19 | /**
20 | * Constructor
21 | * @param items the items
22 | * @param length the max items length
23 | */
24 | public ArrayWheelAdapter(ArrayList items, int length) {
25 | this.items = items;
26 | this.length = length;
27 | }
28 |
29 | /**
30 | * Contructor
31 | * @param items the items
32 | */
33 | public ArrayWheelAdapter(ArrayList items) {
34 | this(items, DEFAULT_LENGTH);
35 | }
36 |
37 | @Override
38 | public Object getItem(int index) {
39 | if (index >= 0 && index < items.size()) {
40 | return items.get(index);
41 | }
42 | return "";
43 | }
44 |
45 | @Override
46 | public int getItemsCount() {
47 | return items.size();
48 | }
49 |
50 | @Override
51 | public int indexOf(Object o){
52 | return items.indexOf(o);
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/adapter/NumericWheelAdapter.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.adapter;
2 |
3 |
4 | /**
5 | * Numeric Wheel adapter.
6 | */
7 | public class NumericWheelAdapter implements WheelAdapter {
8 |
9 | /** The default min value */
10 | public static final int DEFAULT_MAX_VALUE = 9;
11 |
12 | /** The default max value */
13 | private static final int DEFAULT_MIN_VALUE = 0;
14 |
15 | // Values
16 | private int minValue;
17 | private int maxValue;
18 |
19 | /**
20 | * Default constructor
21 | */
22 | public NumericWheelAdapter() {
23 | this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE);
24 | }
25 |
26 | /**
27 | * Constructor
28 | * @param minValue the wheel min value
29 | * @param maxValue the wheel max value
30 | */
31 | public NumericWheelAdapter(int minValue, int maxValue) {
32 | this.minValue = minValue;
33 | this.maxValue = maxValue;
34 | }
35 |
36 | @Override
37 | public Object getItem(int index) {
38 | if (index >= 0 && index < getItemsCount()) {
39 | int value = minValue + index;
40 | return value;
41 | }
42 | return 0;
43 | }
44 |
45 | @Override
46 | public int getItemsCount() {
47 | return maxValue - minValue + 1;
48 | }
49 |
50 | @Override
51 | public int indexOf(Object o){
52 | return (int)o - minValue;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/layout/pickerview_options.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
18 |
19 |
24 |
25 |
30 |
31 |
36 |
37 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/layout/pickerview_time.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
18 |
19 |
24 |
25 |
30 |
31 |
36 |
37 |
42 |
43 |
48 |
49 |
--------------------------------------------------------------------------------
/pickerview/src/main/res/layout/include_pickerview_topbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
30 |
31 |
43 |
44 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/wheelview/SmoothScrollTimerTask.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget.wheelview;
2 |
3 | import java.util.TimerTask;
4 |
5 | final class SmoothScrollTimerTask extends TimerTask {
6 |
7 | int realTotalOffset;
8 | int realOffset;
9 | int offset;
10 | final WheelView loopView;
11 |
12 | SmoothScrollTimerTask(WheelView loopview, int offset) {
13 | this.loopView = loopview;
14 | this.offset = offset;
15 | realTotalOffset = Integer.MAX_VALUE;
16 | realOffset = 0;
17 | }
18 |
19 | @Override
20 | public final void run() {
21 | if (realTotalOffset == Integer.MAX_VALUE) {
22 | realTotalOffset = offset;
23 | }
24 | //把要滚动的范围细分成十小份,按是小份单位来重绘
25 | realOffset = (int) ((float) realTotalOffset * 0.1F);
26 |
27 | if (realOffset == 0) {
28 | if (realTotalOffset < 0) {
29 | realOffset = -1;
30 | } else {
31 | realOffset = 1;
32 | }
33 | }
34 |
35 | if (Math.abs(realTotalOffset) <= 1) {
36 | loopView.cancelFuture();
37 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED);
38 | } else {
39 | loopView.totalScrollY = loopView.totalScrollY + realOffset;
40 |
41 | //这里如果不是循环模式,则点击空白位置需要回滚,不然就会出现选到-1 item的 情况
42 | if (!loopView.isLoop) {
43 | float itemHeight = loopView.itemHeight;
44 | float top = (float) (-loopView.initPosition) * itemHeight;
45 | float bottom = (float) (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight;
46 | if (loopView.totalScrollY <= top||loopView.totalScrollY >= bottom) {
47 | loopView.totalScrollY = loopView.totalScrollY - realOffset;
48 | loopView.cancelFuture();
49 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED);
50 | return;
51 | }
52 | }
53 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW);
54 | realTotalOffset = realTotalOffset - realOffset;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/wheelview/InertiaTimerTask.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget.wheelview;
2 |
3 | import java.util.TimerTask;
4 |
5 | final class InertiaTimerTask extends TimerTask {
6 |
7 | float a;
8 | final float velocityY;
9 | final WheelView loopView;
10 |
11 | InertiaTimerTask(WheelView loopview, float velocityY) {
12 | super();
13 | loopView = loopview;
14 | this.velocityY = velocityY;
15 | a = Integer.MAX_VALUE;
16 | }
17 |
18 | @Override
19 | public final void run() {
20 | if (a == Integer.MAX_VALUE) {
21 | if (Math.abs(velocityY) > 2000F) {
22 | if (velocityY > 0.0F) {
23 | a = 2000F;
24 | } else {
25 | a = -2000F;
26 | }
27 | } else {
28 | a = velocityY;
29 | }
30 | }
31 | if (Math.abs(a) >= 0.0F && Math.abs(a) <= 20F) {
32 | loopView.cancelFuture();
33 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_SMOOTH_SCROLL);
34 | return;
35 | }
36 | int i = (int) ((a * 10F) / 1000F);
37 | loopView.totalScrollY = loopView.totalScrollY - i;
38 | if (!loopView.isLoop) {
39 | float itemHeight = loopView.itemHeight;
40 | float top = (-loopView.initPosition) * itemHeight;
41 | float bottom = (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight;
42 | if(loopView.totalScrollY - itemHeight*0.3 < top){
43 | top = loopView.totalScrollY + i;
44 | }
45 | else if(loopView.totalScrollY + itemHeight*0.3 > bottom){
46 | bottom = loopView.totalScrollY + i;
47 | }
48 |
49 | if (loopView.totalScrollY <= top){
50 | a = 40F;
51 | loopView.totalScrollY = (int)top;
52 | } else if (loopView.totalScrollY >= bottom) {
53 | loopView.totalScrollY = (int)bottom;
54 | a = -40F;
55 | }
56 | }
57 | if (a < 0.0F) {
58 | a = a + 20F;
59 | } else {
60 | a = a - 20F;
61 | }
62 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW);
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/pickerview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'com.jfrog.bintray'
4 |
5 | version = "1.0.3"
6 |
7 | android {
8 | compileSdkVersion 24
9 | buildToolsVersion "25.0.1"
10 | resourcePrefix "PickerView"
11 | defaultConfig {
12 | minSdkVersion 14
13 | targetSdkVersion 24
14 | versionCode 1
15 | versionName "1.0"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | testCompile 'junit:junit:4.12'
28 | }
29 |
30 | def siteUrl = 'https://github.com/Airsaid/Android-PickerView-Library'
31 | def gitUrl = 'https://github.com/Airsaid/Android-PickerView-Library.git'
32 | group = "com.airsaid.library"
33 | install {
34 | repositories.mavenInstaller {
35 | // This generates POM.xml with proper parameters
36 | pom {
37 | project {
38 | packaging 'aar'
39 | // Add your description here
40 | name 'Simple picker view for Android'
41 | url siteUrl
42 | // Set your license
43 | licenses {
44 | license {
45 | name 'The Apache Software License, Version 2.0'
46 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
47 | }
48 | }
49 | developers {
50 | developer {
51 | id 'airsaid'
52 | name 'airsaid'
53 | email 'airsaid1024@gamil.com'
54 | }
55 | }
56 | scm {
57 | connection gitUrl
58 | developerConnection gitUrl
59 | url siteUrl
60 | }
61 | }
62 | }
63 | }
64 | }
65 | task sourcesJar(type: Jar) {
66 | from android.sourceSets.main.java.srcDirs
67 | classifier = 'sources'
68 | }
69 | task javadoc(type: Javadoc) {
70 | source = android.sourceSets.main.java.srcDirs
71 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
72 | }
73 | task javadocJar(type: Jar, dependsOn: javadoc) {
74 | classifier = 'javadoc'
75 | from javadoc.destinationDir
76 | }
77 | javadoc {
78 | options{
79 | encoding "UTF-8"
80 | charSet 'UTF-8'
81 | }
82 | }
83 | artifacts {
84 | archives javadocJar
85 | archives sourcesJar
86 | }
87 | Properties properties = new Properties()
88 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
89 | bintray {
90 | user = properties.getProperty("bintray.user")
91 | key = properties.getProperty("bintray.apikey")
92 | configurations = ['archives']
93 | pkg {
94 | repo = "maven"
95 | name = "pickerview"
96 | websiteUrl = siteUrl
97 | vcsUrl = gitUrl
98 | licenses = ["Apache-2.0"]
99 | publish = true
100 | }
101 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android-PickerView-Library
2 | ~~这是一个高仿 IOS PickerView 控件的库:https://github.com/Airsaid/Android-PickerView-Library。在 https://github.com/saiwu-bigkoo/Android-PickerView 代码基础上进行封装、优化。由于原库作者不再维护,所以新开了该库。后期如有时间,将会对整体代码重构,核心采用自定义 LayouManager 实现。~~
3 |
4 | 原库已经有人接手更新,并且代码已经重构。推荐大家使用,地址:https://github.com/saiwu-bigkoo/Android-PickerView
5 |
6 | **该库暂时停止维护,后续可能会进行重构(重构前建议大家不要使用)**
7 |
8 | # 预览
9 | 
10 |
11 | # 使用
12 | * 首先需要在 build.gradle 文件中添加依赖:
13 | ```
14 | dependencies {
15 | compile 'com.airsaid.library:pickerview:1.0.3'
16 | }
17 | ```
18 |
19 | 添加好依赖后,重新同步工程。可根据需求使用如下选择器:
20 |
21 | * 城市选择:
22 | ```
23 | CityPickerView mCityPickerView = new CityPickerView(this);
24 | // 设置点击外部是否消失
25 | // mCityPickerView.setCancelable(true);
26 | // 设置滚轮字体大小
27 | // mCityPickerView.setTextSize(18f);
28 | // 设置标题
29 | // mCityPickerView.setTitle("我是标题");
30 | // 设置取消文字
31 | // mCityPickerView.setCancelText("我是取消文字");
32 | // 设置取消文字颜色
33 | // mCityPickerView.setCancelTextColor(Color.GRAY);
34 | // 设置取消文字大小
35 | // mCityPickerView.setCancelTextSize(14f);
36 | // 设置确定文字
37 | // mCityPickerView.setSubmitText("我是确定文字");
38 | // 设置确定文字颜色
39 | // mCityPickerView.setSubmitTextColor(Color.BLACK);
40 | // 设置确定文字大小
41 | // mCityPickerView.setSubmitTextSize(14f);
42 | // 设置头部背景
43 | // mCityPickerView.setHeadBackgroundColor(Color.RED);
44 | mCityPickerView.setOnCitySelectListener(new OnSimpleCitySelectListener(){
45 | @Override
46 | public void onCitySelect(String prov, String city, String area) {
47 | // 省、市、区 分开获取
48 | Log.e(TAG, "省: " + prov + " 市: " + city + " 区: " + area);
49 | }
50 |
51 | @Override
52 | public void onCitySelect(String str) {
53 | // 一起获取
54 | Toast.makeText(MainActivity.this, "选择了:" + str, Toast.LENGTH_SHORT).show();
55 | }
56 | });
57 | mCityPickerView.show();
58 | ```
59 | * 时间选择:
60 | ```
61 | // TimePickerView 同样有上面设置样式的方法
62 | TimePickerView mTimePickerView = new TimePickerView(this, TimePickerView.Type.YEAR_MONTH_DAY);
63 | // 设置是否循环
64 | // mTimePickerView.setCyclic(true);
65 | // 设置滚轮文字大小
66 | // mTimePickerView.setTextSize(TimePickerView.TextSize.SMALL);
67 | // 设置时间可选范围(结合 setTime 方法使用,必须在)
68 | // Calendar calendar = Calendar.getInstance();
69 | // mTimePickerView.setRange(calendar.get(Calendar.YEAR) - 100, calendar.get(Calendar.YEAR));
70 | // 设置选中时间
71 | // mTimePickerView.setTime(new Date());
72 | mTimePickerView.setOnTimeSelectListener(new TimePickerView.OnTimeSelectListener() {
73 | @Override
74 | public void onTimeSelect(Date date) {
75 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
76 | Toast.makeText(MainActivity.this, format.format(date), Toast.LENGTH_SHORT).show();
77 | }
78 | });
79 | mTimePickerView.show();
80 | ```
81 | * 选项选择:
82 | ```
83 | OptionsPickerView mOptionsPickerView = new OptionsPickerView<>(this);
84 | final ArrayList list = new ArrayList<>();
85 | list.add("男");
86 | list.add("女");
87 | // 设置数据
88 | mOptionsPickerView.setPicker(list);
89 | // 设置选项单位
90 | // mOptionsPickerView.setLabels("性");
91 | mOptionsPickerView.setOnOptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() {
92 | @Override
93 | public void onOptionsSelect(int option1, int option2, int option3) {
94 | String sex = list.get(option1);
95 | Toast.makeText(MainActivity.this, sex, Toast.LENGTH_SHORT).show();
96 | }
97 | });
98 | mOptionsPickerView.show();
99 | ```
100 | # 更新日志
101 | * 1.0.2:修复部分城市文字乱码bug,修复设置字体过小后分割线无法对齐bug。
102 | * 1.0.1:修复内存泄露问题,修复设置setTime()方法后字体设置无效bug。
103 |
104 |
105 | # 联系我
106 | * 博 客:http://blog.csdn.net/airsaid
107 | * QQ群:5707887
108 |
109 |
110 | # 感谢
111 | * https://github.com/saiwu-bigkoo/Android-PickerView
112 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/CityPickerView.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary;
2 |
3 | import android.content.Context;
4 | import android.content.res.AssetManager;
5 |
6 | import com.airsaid.pickerviewlibrary.listener.OnCitySelectListener;
7 |
8 | import org.json.JSONArray;
9 | import org.json.JSONException;
10 | import org.json.JSONObject;
11 |
12 | import java.io.InputStream;
13 | import java.util.ArrayList;
14 |
15 | /**
16 | * Created by zhouyou on 2016/12/5.
17 | * Class desc:
18 | */
19 | public class CityPickerView extends OptionsPickerView {
20 |
21 | private final Context mContext;
22 |
23 | // 省数据集合
24 | private ArrayList mListProvince = new ArrayList<>();
25 | // 市数据集合
26 | private ArrayList> mListCity = new ArrayList<>();
27 | // 区数据集合
28 | private ArrayList>> mListArea = new ArrayList<>();
29 | private JSONObject mJsonObj;
30 |
31 | public CityPickerView(Context context) {
32 | super(context);
33 | mContext = context;
34 | // 初始化Json对象
35 | initJsonData();
36 | // 初始化Json数据
37 | initJsonDatas();
38 | initCitySelect();
39 | }
40 |
41 | private void initCitySelect() {
42 | setTitle("选择城市");
43 | setPicker(mListProvince, mListCity, mListArea, true);
44 | setCyclic(false, false, false);
45 | setSelectOptions(0, 0, 0);
46 | setOnOptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() {
47 | @Override
48 | public void onOptionsSelect(int option1, int option2, int option3) {
49 | if(mOnCitySelectListener != null){
50 | if(mListCity.size() > option1 && mListCity.get(option1).size() > option2){
51 | if(mListArea.size() > option1 && mListArea.get(option1).size() > option2
52 | && mListArea.get(option1).get(option2).size() > option3){
53 | String prov = mListProvince.get(option1);
54 | String city = mListCity.get(option1).get(option2);
55 | String area = mListArea.get(option1).get(option2).get(option3);
56 | mOnCitySelectListener.onCitySelect(prov.concat(city).concat(area));
57 | mOnCitySelectListener.onCitySelect(prov, city, area);
58 | }
59 | }
60 | }
61 | }
62 | });
63 | }
64 |
65 | /** 从assert文件夹中读取省市区的json文件,然后转化为json对象 */
66 | private void initJsonData() {
67 | AssetManager assets = mContext.getAssets();
68 | try {
69 | InputStream is = assets.open("city.json");
70 | byte[] buf = new byte[is.available()];
71 | is.read(buf);
72 | String json = new String(buf, "UTF-8");
73 | mJsonObj = new JSONObject(json);
74 | is.close();
75 | } catch (Exception e) {
76 | e.printStackTrace();
77 | }
78 | }
79 |
80 | /** 初始化Json数据,并释放Json对象 */
81 | private void initJsonDatas(){
82 | try {
83 | JSONArray jsonArray = mJsonObj.getJSONArray("citylist");
84 | for (int i = 0; i < jsonArray.length(); i++) {
85 | JSONObject jsonP = jsonArray.getJSONObject(i);// 获取每个省的Json对象
86 | String province = jsonP.getString("name");
87 |
88 | ArrayList options2Items_01 = new ArrayList<>();
89 | ArrayList> options3Items_01 = new ArrayList<>();
90 | JSONArray jsonCs = jsonP.getJSONArray("city");
91 | for (int j = 0; j < jsonCs.length(); j++) {
92 | JSONObject jsonC = jsonCs.getJSONObject(j);// 获取每个市的Json对象
93 | String city = jsonC.getString("name");
94 | options2Items_01.add(city);// 添加市数据
95 |
96 | ArrayList options3Items_01_01 = new ArrayList<>();
97 | JSONArray jsonAs = jsonC.getJSONArray("area");
98 | for (int k = 0; k < jsonAs.length(); k++) {
99 | options3Items_01_01.add(jsonAs.getString(k));// 添加区数据
100 | }
101 | options3Items_01.add(options3Items_01_01);
102 | }
103 | mListProvince.add(province);// 添加省数据
104 | mListCity.add(options2Items_01);
105 | mListArea.add(options3Items_01);
106 | }
107 | } catch (JSONException e) {
108 | e.printStackTrace();
109 | }
110 | mJsonObj = null;
111 | }
112 |
113 | public OnCitySelectListener mOnCitySelectListener;
114 |
115 | public void setOnCitySelectListener(OnCitySelectListener listener) {
116 | this.mOnCitySelectListener = listener;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/src/main/java/com/airsaid/android_pickerview_library/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.android_pickerview_library;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.util.Log;
6 | import android.view.KeyEvent;
7 | import android.view.View;
8 | import android.widget.Toast;
9 |
10 | import com.airsaid.pickerviewlibrary.CityPickerView;
11 | import com.airsaid.pickerviewlibrary.OptionsPickerView;
12 | import com.airsaid.pickerviewlibrary.TimePickerView;
13 | import com.airsaid.pickerviewlibrary.listener.OnSimpleCitySelectListener;
14 |
15 | import java.text.SimpleDateFormat;
16 | import java.util.ArrayList;
17 | import java.util.Date;
18 | import java.util.Locale;
19 |
20 | public class MainActivity extends AppCompatActivity {
21 |
22 | public static final String TAG = "MainActivity";
23 |
24 | private TimePickerView mTimePickerView;
25 | private CityPickerView mCityPickerView;
26 | private OptionsPickerView mOptionsPickerView;
27 |
28 | @Override
29 | protected void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | setContentView(R.layout.activity_main);
32 |
33 | mCityPickerView = new CityPickerView(this);
34 | mTimePickerView = new TimePickerView(this, TimePickerView.Type.YEAR_MONTH_DAY);
35 | mOptionsPickerView = new OptionsPickerView<>(this);
36 | }
37 |
38 | public void selectCity(View v){
39 | // 设置点击外部是否消失
40 | // mCityPickerView.setCancelable(true);
41 | // 设置滚轮字体大小
42 | // mCityPickerView.setTextSize(18f);
43 | // 设置标题
44 | // mCityPickerView.setTitle("我是标题");
45 | // 设置取消文字
46 | // mCityPickerView.setCancelText("我是取消文字");
47 | // 设置取消文字颜色
48 | // mCityPickerView.setCancelTextColor(Color.GRAY);
49 | // 设置取消文字大小
50 | // mCityPickerView.setCancelTextSize(14f);
51 | // 设置确定文字
52 | // mCityPickerView.setSubmitText("我是确定文字");
53 | // 设置确定文字颜色
54 | // mCityPickerView.setSubmitTextColor(Color.BLACK);
55 | // 设置确定文字大小
56 | // mCityPickerView.setSubmitTextSize(14f);
57 | // 设置头部背景
58 | // mCityPickerView.setHeadBackgroundColor(Color.RED);
59 | mCityPickerView.setOnCitySelectListener(new OnSimpleCitySelectListener(){
60 | @Override
61 | public void onCitySelect(String prov, String city, String area) {
62 | // 省、市、区 分开获取
63 | Log.e(TAG, "省: " + prov + " 市: " + city + " 区: " + area);
64 | }
65 |
66 | @Override
67 | public void onCitySelect(String str) {
68 | // 一起获取
69 | Toast.makeText(MainActivity.this, "选择了:" + str, Toast.LENGTH_SHORT).show();
70 | }
71 | });
72 | mCityPickerView.show();
73 | }
74 |
75 | public void selectTime(View v){
76 | // TimePickerView 同样有上面设置样式的方法
77 | // 设置是否循环
78 | // mTimePickerView.setCyclic(true);
79 | // 设置滚轮文字大小
80 | // mTimePickerView.setTextSize(TimePickerView.TextSize.SMALL);
81 | // 设置时间可选范围(结合 setTime 方法使用,必须在)
82 | // Calendar calendar = Calendar.getInstance();
83 | // mTimePickerView.setRange(calendar.get(Calendar.YEAR) - 100, calendar.get(Calendar.YEAR));
84 | // 设置选中时间
85 | // mTimePickerView.setTime(new Date());
86 | mTimePickerView.setOnTimeSelectListener(new TimePickerView.OnTimeSelectListener() {
87 | @Override
88 | public void onTimeSelect(Date date) {
89 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
90 | Toast.makeText(MainActivity.this, format.format(date), Toast.LENGTH_SHORT).show();
91 | }
92 | });
93 | mTimePickerView.show();
94 | }
95 |
96 | public void selectOption(View v){
97 | mOptionsPickerView = new OptionsPickerView<>(this);
98 | final ArrayList list = new ArrayList<>();
99 | list.add("男");
100 | list.add("女");
101 | // 设置数据
102 | mOptionsPickerView.setPicker(list);
103 | // 设置选项单位
104 | // mOptionsPickerView.setLabels("性");
105 | mOptionsPickerView.setOnOptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() {
106 | @Override
107 | public void onOptionsSelect(int option1, int option2, int option3) {
108 | String sex = list.get(option1);
109 | Toast.makeText(MainActivity.this, sex, Toast.LENGTH_SHORT).show();
110 | }
111 | });
112 | mOptionsPickerView.show();
113 | }
114 |
115 | @Override
116 | public boolean onKeyDown(int keyCode, KeyEvent event) {
117 | if (keyCode == KeyEvent.KEYCODE_BACK) {
118 | if(mTimePickerView.isShowing()){
119 | mTimePickerView.dismiss();
120 | return true;
121 | }
122 |
123 | if(mCityPickerView.isShowing()){
124 | mCityPickerView.dismiss();
125 | return true;
126 | }
127 |
128 | if(mOptionsPickerView.isShowing()){
129 | mOptionsPickerView.dismiss();
130 | return true;
131 | }
132 | }
133 | return super.onKeyDown(keyCode, event);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/BasePickerView.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.view.Gravity;
6 | import android.view.LayoutInflater;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.view.animation.Animation;
11 | import android.view.animation.AnimationUtils;
12 | import android.widget.FrameLayout;
13 |
14 | import com.airsaid.pickerviewlibrary.R;
15 | import com.airsaid.pickerviewlibrary.listener.OnDismissListener;
16 | import com.airsaid.pickerviewlibrary.utils.PickerViewAnimateUtil;
17 |
18 |
19 | /**
20 | * Created by Sai on 15/11/22.
21 | * 精仿iOSPickerViewController控件
22 | */
23 | public class BasePickerView {
24 | private final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
25 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM
26 | );
27 |
28 | private Context context;
29 | protected ViewGroup contentContainer;
30 | private ViewGroup decorView;//activity的根View
31 | private ViewGroup rootView;//附加View 的 根View
32 |
33 | private OnDismissListener onDismissListener;
34 | private boolean dismissing;
35 |
36 | private Animation outAnim;
37 | private Animation inAnim;
38 | private boolean isShowing;
39 | private int gravity = Gravity.BOTTOM;
40 | private View mOutMostView;
41 |
42 | public BasePickerView(Context context){
43 | this.context = context;
44 |
45 | initViews();
46 | init();
47 | initEvents();
48 | }
49 |
50 | protected void initViews(){
51 | LayoutInflater layoutInflater = LayoutInflater.from(context);
52 | decorView = (ViewGroup) ((Activity)context).getWindow().getDecorView().findViewById(android.R.id.content);
53 | rootView = (ViewGroup) layoutInflater.inflate(R.layout.layout_basepickerview, decorView, false);
54 | rootView.setLayoutParams(new FrameLayout.LayoutParams(
55 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
56 | ));
57 | contentContainer = (ViewGroup) rootView.findViewById(R.id.content_container);
58 | contentContainer.setLayoutParams(params);
59 | mOutMostView = rootView.findViewById(R.id.outmost_container);
60 | mOutMostView.setOnTouchListener(onCancelableTouchListener);
61 | }
62 |
63 | protected void init() {
64 | inAnim = getInAnimation();
65 | outAnim = getOutAnimation();
66 | }
67 | protected void initEvents() {
68 | }
69 | /**
70 | * show的时候调用
71 | *
72 | * @param view 这个View
73 | */
74 | private void onAttached(View view) {
75 | decorView.addView(view);
76 | contentContainer.startAnimation(inAnim);
77 | }
78 | /**
79 | * 添加这个View到Activity的根视图
80 | */
81 | public void show() {
82 | if (isShowing()) {
83 | return;
84 | }
85 | isShowing = true;
86 | onAttached(rootView);
87 | }
88 | /**
89 | * 检测该View是不是已经添加到根视图
90 | * @return 如果视图已经存在该View返回true
91 | */
92 | public boolean isShowing() {
93 | return rootView.getParent() != null || isShowing;
94 | }
95 |
96 | public void dismiss() {
97 | if (dismissing) {
98 | return;
99 | }
100 | dismissing = true;
101 | //消失动画
102 | outAnim.setAnimationListener(new Animation.AnimationListener() {
103 | @Override
104 | public void onAnimationStart(Animation animation) {
105 |
106 | }
107 |
108 | @Override
109 | public void onAnimationEnd(Animation animation) {
110 | decorView.post(new Runnable() {
111 | @Override
112 | public void run() {
113 | dismissImmediately();
114 | }
115 | });
116 | }
117 |
118 | @Override
119 | public void onAnimationRepeat(Animation animation) {
120 |
121 | }
122 | });
123 | contentContainer.startAnimation(outAnim);
124 | }
125 |
126 | public void dismissImmediately() {
127 | //从activity根视图移除
128 | decorView.removeView(rootView);
129 | isShowing = false;
130 | dismissing = false;
131 | if (onDismissListener != null) {
132 | onDismissListener.onDismiss(BasePickerView.this);
133 | }
134 |
135 | }
136 | public Animation getInAnimation() {
137 | int res = PickerViewAnimateUtil.getAnimationResource(this.gravity, true);
138 | return AnimationUtils.loadAnimation(context, res);
139 | }
140 |
141 | public Animation getOutAnimation() {
142 | int res = PickerViewAnimateUtil.getAnimationResource(this.gravity, false);
143 | return AnimationUtils.loadAnimation(context, res);
144 | }
145 |
146 | public BasePickerView setOnDismissListener(OnDismissListener onDismissListener) {
147 | this.onDismissListener = onDismissListener;
148 | return this;
149 | }
150 |
151 | public BasePickerView setCancelable(boolean isCancelable) {
152 | mOutMostView.setOnTouchListener(isCancelable ? onCancelableTouchListener : null);
153 | return this;
154 | }
155 |
156 | /**
157 | * Called when the user touch on black overlay in order to dismiss the dialog
158 | */
159 | private final View.OnTouchListener onCancelableTouchListener = new View.OnTouchListener() {
160 | @Override
161 | public boolean onTouch(View v, MotionEvent event) {
162 | if (event.getAction() == MotionEvent.ACTION_DOWN) {
163 | dismiss();
164 | }
165 | return false;
166 | }
167 | };
168 |
169 | public View findViewById(int id){
170 | return contentContainer.findViewById(id);
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/OptionsPickerView.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.widget.Button;
7 | import android.widget.TextView;
8 |
9 | import com.airsaid.pickerviewlibrary.widget.BasePickerView;
10 | import com.airsaid.pickerviewlibrary.widget.WheelOptions;
11 |
12 | import java.util.ArrayList;
13 |
14 | /**
15 | * 条件选择器
16 | * Created by Sai on 15/11/22.
17 | */
18 | public class OptionsPickerView extends BasePickerView implements View.OnClickListener {
19 |
20 | private Context mContext;
21 |
22 | private OnOptionsSelectListener mOptionsSelectListener;
23 | private WheelOptions mWheelOptions;
24 | private Button mBtnSubmit, mBtnCancel;
25 | private TextView mTxtTitle;
26 | private View mHeadView;
27 |
28 | public OptionsPickerView(Context context) {
29 | super(context);
30 | mContext = context;
31 | initView();
32 | }
33 |
34 | private void initView() {
35 | LayoutInflater.from(mContext).inflate(R.layout.pickerview_options, contentContainer);
36 | mWheelOptions = new WheelOptions<>(findViewById(R.id.optionspicker));
37 | mHeadView = findViewById(R.id.rlt_head_view);
38 | mTxtTitle = (TextView) findViewById(R.id.tvTitle);
39 | mBtnSubmit = (Button) findViewById(R.id.btnSubmit);
40 | mBtnCancel = (Button) findViewById(R.id.btnCancel);
41 | mBtnSubmit.setOnClickListener(this);
42 | mBtnCancel.setOnClickListener(this);
43 | }
44 |
45 | /**
46 | * 设置一级数据
47 | */
48 | public void setPicker(ArrayList optionsItems) {
49 | mWheelOptions.setPicker(optionsItems, null, null, false);
50 | }
51 |
52 | /**
53 | * 设置二级数据
54 | */
55 | public void setPicker(ArrayList options1Items, ArrayList> options2Items, boolean linkage) {
56 | mWheelOptions.setPicker(options1Items, options2Items, null, linkage);
57 | }
58 |
59 | /**
60 | * 设置三级数据
61 | */
62 | public void setPicker(ArrayList options1Items, ArrayList> options2Items
63 | , ArrayList>> options3Items, boolean linkage) {
64 | mWheelOptions.setPicker(options1Items, options2Items, options3Items, linkage);
65 | }
66 |
67 | /**
68 | * 设置选中的item位置
69 | * @param option1 位置
70 | */
71 | public void setSelectOptions(int option1){
72 | mWheelOptions.setCurrentItems(option1, 0, 0);
73 | }
74 |
75 | public void setSelectOptions(int option1, int option2){
76 | mWheelOptions.setCurrentItems(option1, option2, 0);
77 | }
78 |
79 | public void setSelectOptions(int option1, int option2, int option3){
80 | mWheelOptions.setCurrentItems(option1, option2, option3);
81 | }
82 |
83 | /**
84 | * 设置选项的单位
85 | * @param label1 单位
86 | */
87 | public void setLabels(String label1){
88 | mWheelOptions.setLabels(label1, null, null);
89 | }
90 |
91 | public void setLabels(String label1, String label2){
92 | mWheelOptions.setLabels(label1, label2, null);
93 | }
94 |
95 | public void setLabels(String label1, String label2, String label3){
96 | mWheelOptions.setLabels(label1, label2, label3);
97 | }
98 |
99 | /**
100 | * 设置是否循环滚动
101 | */
102 | public void setCyclic(boolean cyclic){
103 | mWheelOptions.setCyclic(cyclic);
104 | }
105 |
106 | public void setCyclic(boolean cyclic1, boolean cyclic2, boolean cyclic3) {
107 | mWheelOptions.setCyclic(cyclic1, cyclic2, cyclic3);
108 | }
109 |
110 | /**
111 | * 设置头部背景颜色
112 | */
113 | public void setHeadBackgroundColor(int color){
114 | mHeadView.setBackgroundColor(color);
115 | }
116 |
117 | /**
118 | * 设置标题
119 | */
120 | public void setTitle(String title){
121 | mTxtTitle.setText(title);
122 | }
123 |
124 | /**
125 | * 设置标题颜色
126 | */
127 | public void setTitleColor(int resId){
128 | mTxtTitle.setTextColor(resId);
129 | }
130 |
131 | /**
132 | * 设置标题大小
133 | */
134 | public void setTitleSize(float size){
135 | mTxtTitle.setTextSize(size);
136 | }
137 |
138 | /**
139 | * 设置取消文字
140 | */
141 | public void setCancelText(String text){
142 | mBtnCancel.setText(text);
143 | }
144 |
145 | /**
146 | * 设置取消文字颜色
147 | */
148 | public void setCancelTextColor(int resId){
149 | mBtnCancel.setTextColor(resId);
150 | }
151 |
152 | /**
153 | * 设置取消文字大小
154 | */
155 | public void setCancelTextSize(float size){
156 | mBtnCancel.setTextSize(size);
157 | }
158 |
159 | /**
160 | * 设置确认文字
161 | */
162 | public void setSubmitText(String text){
163 | mBtnSubmit.setText(text);
164 | }
165 |
166 | /**
167 | * 设置确认文字颜色
168 | */
169 | public void setSubmitTextColor(int resId){
170 | mBtnSubmit.setTextColor(resId);
171 | }
172 |
173 | /**
174 | * 设置确认文字大小
175 | */
176 | public void setSubmitTextSize(float size){
177 | mBtnSubmit.setTextSize(size);
178 | }
179 |
180 | /**
181 | * 设置滚动文字大小
182 | */
183 | public void setTextSize(float size){
184 | mWheelOptions.setTextSize(size);
185 | }
186 |
187 | @Override
188 | public void onClick(View v){
189 | int id = v.getId();
190 | if(id == R.id.btnSubmit){
191 | if(mOptionsSelectListener != null){
192 | int[] optionsCurrentItems = mWheelOptions.getCurrentItems();
193 | mOptionsSelectListener.onOptionsSelect(optionsCurrentItems[0], optionsCurrentItems[1], optionsCurrentItems[2]);
194 | }
195 | dismiss();
196 | }else if(id == R.id.btnCancel){
197 | dismiss();
198 | }
199 | }
200 |
201 | public interface OnOptionsSelectListener {
202 | void onOptionsSelect(int option1, int option2, int option3);
203 | }
204 |
205 | public void setOnOptionsSelectListener(
206 | OnOptionsSelectListener optionsSelectListener) {
207 | this.mOptionsSelectListener = optionsSelectListener;
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/TimePickerView.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.widget.Button;
7 | import android.widget.TextView;
8 | import com.airsaid.pickerviewlibrary.widget.BasePickerView;
9 | import com.airsaid.pickerviewlibrary.widget.WheelTime;
10 |
11 | import java.text.ParseException;
12 | import java.util.Calendar;
13 | import java.util.Date;
14 |
15 | /**
16 | * 时间选择器
17 | * Created by Sai on 15/11/22.
18 | */
19 | public class TimePickerView extends BasePickerView implements View.OnClickListener {
20 |
21 | public enum Type {
22 | ALL, YEAR_MONTH_DAY, HOURS_MINS, MONTH_DAY_HOUR_MIN, YEAR_MONTH
23 | }// 四种选择模式,年月日时分,年月日,时分,月日时分
24 |
25 | private Context mContext;
26 |
27 | private View mHeadView;
28 | private TextView mTxtTitle;
29 | private WheelTime mWheelTime;
30 | private Button mBtnSubmit, mBtnCancel;
31 | private OnTimeSelectListener mTimeSelectListener;
32 |
33 | public TimePickerView(Context context, Type type) {
34 | super(context);
35 | mContext = context;
36 | initView();
37 | initSelectTime(type);
38 | }
39 |
40 | /**
41 | * 初始化 View
42 | */
43 | private void initView() {
44 | LayoutInflater.from(mContext).inflate(R.layout.pickerview_time, contentContainer);
45 | mHeadView = findViewById(R.id.rlt_head_view);
46 | mTxtTitle = (TextView) findViewById(R.id.tvTitle);
47 | mBtnSubmit = (Button) findViewById(R.id.btnSubmit);
48 | mBtnCancel = (Button) findViewById(R.id.btnCancel);
49 | mBtnSubmit.setOnClickListener(this);
50 | mBtnCancel.setOnClickListener(this);
51 | }
52 |
53 | /**
54 | * 初始化默认选中当前时间
55 | */
56 | private void initSelectTime(Type type) {
57 | View timePickerView = findViewById(R.id.timepicker);
58 | mWheelTime = new WheelTime(timePickerView, type);
59 | Calendar calendar = Calendar.getInstance();
60 | calendar.setTimeInMillis(System.currentTimeMillis());
61 | int year = calendar.get(Calendar.YEAR);
62 | int month = calendar.get(Calendar.MONTH);
63 | int day = calendar.get(Calendar.DAY_OF_MONTH);
64 | int hours = calendar.get(Calendar.HOUR_OF_DAY);
65 | int minute = calendar.get(Calendar.MINUTE);
66 | Calendar rangeCalendar = Calendar.getInstance();
67 | setRange(rangeCalendar.get(Calendar.YEAR) - 100, rangeCalendar.get(Calendar.YEAR) + 100);
68 | mWheelTime.setPicker(year, month, day, hours, minute);
69 | }
70 |
71 | /**
72 | * 设置可以选择的时间范围
73 | * 要在setTime之前调用才有效果
74 | * @param startYear 开始年份
75 | * @param endYear 结束年份
76 | */
77 | public void setRange(int startYear, int endYear) {
78 | mWheelTime.setStartYear(startYear);
79 | mWheelTime.setEndYear(endYear);
80 | }
81 |
82 | /**
83 | * 设置选中时间
84 | * @param date 时间
85 | */
86 | public void setTime(Date date) {
87 | Calendar calendar = Calendar.getInstance();
88 | if (date == null)
89 | calendar.setTimeInMillis(System.currentTimeMillis());
90 | else
91 | calendar.setTime(date);
92 | int year = calendar.get(Calendar.YEAR);
93 | int month = calendar.get(Calendar.MONTH);
94 | int day = calendar.get(Calendar.DAY_OF_MONTH);
95 | int hours = calendar.get(Calendar.HOUR_OF_DAY);
96 | int minute = calendar.get(Calendar.MINUTE);
97 | mWheelTime.setPicker(year, month, day, hours, minute);
98 | }
99 |
100 | /**
101 | * 设置是否循环滚动
102 | * @param cyclic 是否循环
103 | */
104 | public void setCyclic(boolean cyclic) {
105 | mWheelTime.setCyclic(cyclic);
106 | }
107 |
108 | /**
109 | * 设置头部背景颜色
110 | */
111 | public void setHeadBackgroundColor(int color){
112 | mHeadView.setBackgroundColor(color);
113 | }
114 |
115 | /**
116 | * 设置标题
117 | */
118 | public void setTitle(String title){
119 | mTxtTitle.setText(title);
120 | }
121 |
122 | /**
123 | * 设置标题颜色
124 | */
125 | public void setTitleColor(int resId){
126 | mTxtTitle.setTextColor(resId);
127 | }
128 |
129 | /**
130 | * 设置标题大小
131 | */
132 | public void setTitleSize(float size){
133 | mTxtTitle.setTextSize(size);
134 | }
135 |
136 | /**
137 | * 设置取消文字
138 | */
139 | public void setCancelText(String text){
140 | mBtnCancel.setText(text);
141 | }
142 |
143 | /**
144 | * 设置取消文字颜色
145 | */
146 | public void setCancelTextColor(int resId){
147 | mBtnCancel.setTextColor(resId);
148 | }
149 |
150 | /**
151 | * 设置取消文字大小
152 | */
153 | public void setCancelTextSize(float size){
154 | mBtnCancel.setTextSize(size);
155 | }
156 |
157 | /**
158 | * 设置确认文字
159 | */
160 | public void setSubmitText(String text){
161 | mBtnSubmit.setText(text);
162 | }
163 |
164 | /**
165 | * 设置确认文字颜色
166 | */
167 | public void setSubmitTextColor(int resId){
168 | mBtnSubmit.setTextColor(resId);
169 | }
170 |
171 | /**
172 | * 设置确认文字大小
173 | */
174 | public void setSubmitTextSize(float size){
175 | mBtnSubmit.setTextSize(size);
176 | }
177 |
178 | /**
179 | * 设置滚动文字大小
180 | */
181 | public void setTextSize(float size){
182 | mWheelTime.setTextSize(size);
183 | }
184 |
185 | @Override
186 | public void onClick(View v) {
187 | int id = v.getId();
188 | if(id == R.id.btnSubmit){
189 | if(mTimeSelectListener != null){
190 | try {
191 | Date date = WheelTime.dateFormat.parse(mWheelTime.getTime());
192 | mTimeSelectListener.onTimeSelect(date);
193 | } catch (ParseException e) {
194 | e.printStackTrace();
195 | }
196 | }
197 | dismiss();
198 | }else if(id == R.id.btnCancel){
199 | dismiss();
200 | }
201 | }
202 |
203 | public class TextSize{
204 | public static final float BIG = 6f;
205 | public static final float DEFAULT = 5f;
206 | public static final float SMALL = 4f;
207 | }
208 |
209 | public interface OnTimeSelectListener {
210 | void onTimeSelect(Date date);
211 | }
212 |
213 | public void setOnTimeSelectListener(OnTimeSelectListener timeSelectListener) {
214 | this.mTimeSelectListener = timeSelectListener;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/WheelOptions.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget;
2 |
3 | import android.view.View;
4 |
5 | import com.airsaid.pickerviewlibrary.R;
6 | import com.airsaid.pickerviewlibrary.adapter.ArrayWheelAdapter;
7 | import com.airsaid.pickerviewlibrary.listener.OnItemSelectedListener;
8 | import com.airsaid.pickerviewlibrary.widget.wheelview.WheelView;
9 |
10 | import java.util.ArrayList;
11 |
12 | public class WheelOptions {
13 |
14 | private View view;
15 | private WheelView wv_option1;
16 | private WheelView wv_option2;
17 | private WheelView wv_option3;
18 | private ArrayList mOptions1Items;
19 | private ArrayList> mOptions2Items;
20 | private ArrayList>> mOptions3Items;
21 |
22 | private boolean linkage = false;
23 | private OnItemSelectedListener wheelListener_option1;
24 | private OnItemSelectedListener wheelListener_option2;
25 |
26 | public View getView() {
27 | return view;
28 | }
29 |
30 | public void setView(View view) {
31 | this.view = view;
32 | }
33 |
34 | public WheelOptions(View view) {
35 | super();
36 | this.view = view;
37 | setView(view);
38 | }
39 |
40 | public void setPicker(ArrayList optionsItems) {
41 | setPicker(optionsItems, null, null, false);
42 | }
43 |
44 | public void setPicker(ArrayList options1Items,
45 | ArrayList> options2Items, boolean linkage) {
46 | setPicker(options1Items, options2Items, null, linkage);
47 | }
48 |
49 | public void setPicker(ArrayList options1Items,
50 | ArrayList> options2Items,
51 | ArrayList>> options3Items,
52 | boolean linkage) {
53 | this.linkage = linkage;
54 | this.mOptions1Items = options1Items;
55 | this.mOptions2Items = options2Items;
56 | this.mOptions3Items = options3Items;
57 | int len = ArrayWheelAdapter.DEFAULT_LENGTH;
58 | if (this.mOptions3Items == null)
59 | len = 8;
60 | if (this.mOptions2Items == null)
61 | len = 12;
62 | // 选项1
63 | wv_option1 = (WheelView) view.findViewById(R.id.options1);
64 | wv_option1.setAdapter(new ArrayWheelAdapter(mOptions1Items, len));// 设置显示数据
65 | wv_option1.setCurrentItem(0);// 初始化时显示的数据
66 | // 选项2
67 | wv_option2 = (WheelView) view.findViewById(R.id.options2);
68 | if (mOptions2Items != null)
69 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items.get(0)));// 设置显示数据
70 | wv_option2.setCurrentItem(wv_option1.getCurrentItem());// 初始化时显示的数据
71 | // 选项3
72 | wv_option3 = (WheelView) view.findViewById(R.id.options3);
73 | if (mOptions3Items != null)
74 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items.get(0)
75 | .get(0)));// 设置显示数据
76 | wv_option3.setCurrentItem(wv_option3.getCurrentItem());// 初始化时显示的数据
77 | if (this.mOptions2Items == null)
78 | wv_option2.setVisibility(View.GONE);
79 | if (this.mOptions3Items == null)
80 | wv_option3.setVisibility(View.GONE);
81 |
82 | // 联动监听器
83 | wheelListener_option1 = new OnItemSelectedListener() {
84 |
85 | @Override
86 | public void onItemSelected(int index) {
87 | int opt2Select = 0;
88 | if (mOptions2Items != null) {
89 | opt2Select = wv_option2.getCurrentItem();//上一个opt2的选中位置
90 | //新opt2的位置,判断如果旧位置没有超过数据范围,则沿用旧位置,否则选中最后一项
91 | opt2Select = opt2Select >= mOptions2Items.get(index).size() - 1 ? mOptions2Items.get(index).size() - 1 : opt2Select;
92 |
93 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items
94 | .get(index)));
95 | wv_option2.setCurrentItem(opt2Select);
96 | }
97 | if (mOptions3Items != null) {
98 | wheelListener_option2.onItemSelected(opt2Select);
99 | }
100 | }
101 | };
102 | wheelListener_option2 = new OnItemSelectedListener() {
103 |
104 | @Override
105 | public void onItemSelected(int index) {
106 | if (mOptions3Items != null) {
107 | int opt1Select = wv_option1.getCurrentItem();
108 | opt1Select = opt1Select >= mOptions3Items.size() - 1 ? mOptions3Items.size() - 1 : opt1Select;
109 | index = index >= mOptions2Items.get(opt1Select).size() - 1 ? mOptions2Items.get(opt1Select).size() - 1 : index;
110 | int opt3 = wv_option3.getCurrentItem();//上一个opt3的选中位置
111 | //新opt3的位置,判断如果旧位置没有超过数据范围,则沿用旧位置,否则选中最后一项
112 | opt3 = opt3 >= mOptions3Items.get(opt1Select).get(index).size() - 1 ? mOptions3Items.get(opt1Select).get(index).size() - 1 : opt3;
113 |
114 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items
115 | .get(wv_option1.getCurrentItem()).get(
116 | index)));
117 | wv_option3.setCurrentItem(opt3);
118 |
119 | }
120 | }
121 | };
122 |
123 | // // 添加联动监听
124 | if (options2Items != null && linkage)
125 | wv_option1.setOnItemSelectedListener(wheelListener_option1);
126 | if (options3Items != null && linkage)
127 | wv_option2.setOnItemSelectedListener(wheelListener_option2);
128 | }
129 |
130 | /**
131 | * 设置选项的单位
132 | * @param label1 单位
133 | * @param label2 单位
134 | * @param label3 单位
135 | */
136 | public void setLabels(String label1, String label2, String label3) {
137 | if (label1 != null)
138 | wv_option1.setLabel(label1);
139 | if (label2 != null)
140 | wv_option2.setLabel(label2);
141 | if (label3 != null)
142 | wv_option3.setLabel(label3);
143 | }
144 |
145 | /**
146 | * 设置是否循环滚动
147 | * @param cyclic 是否循环
148 | */
149 | public void setCyclic(boolean cyclic) {
150 | wv_option1.setCyclic(cyclic);
151 | wv_option2.setCyclic(cyclic);
152 | wv_option3.setCyclic(cyclic);
153 | }
154 |
155 | /**
156 | * 分别设置第一二三级是否循环滚动
157 | * @param cyclic1,cyclic2,cyclic3 是否循环
158 | */
159 | public void setCyclic(boolean cyclic1,boolean cyclic2,boolean cyclic3) {
160 | wv_option1.setCyclic(cyclic1);
161 | wv_option2.setCyclic(cyclic2);
162 | wv_option3.setCyclic(cyclic3);
163 | }
164 | /**
165 | * 设置第二级是否循环滚动
166 | * @param cyclic 是否循环
167 | */
168 | public void setOption2Cyclic(boolean cyclic) {
169 | wv_option2.setCyclic(cyclic);
170 | }
171 |
172 | /**
173 | * 设置第三级是否循环滚动
174 | * @param cyclic 是否循环
175 | */
176 | public void setOption3Cyclic(boolean cyclic) {
177 | wv_option3.setCyclic(cyclic);
178 | }
179 |
180 | /**
181 | * 返回当前选中的结果对应的位置数组 因为支持三级联动效果,分三个级别索引,0,1,2
182 | * @return 索引数组
183 | */
184 | public int[] getCurrentItems() {
185 | int[] currentItems = new int[3];
186 | currentItems[0] = wv_option1.getCurrentItem();
187 | currentItems[1] = wv_option2.getCurrentItem();
188 | currentItems[2] = wv_option3.getCurrentItem();
189 | return currentItems;
190 | }
191 |
192 | public void setCurrentItems(int option1, int option2, int option3) {
193 | if(linkage){
194 | itemSelected(option1, option2, option3);
195 | }
196 | wv_option1.setCurrentItem(option1);
197 | wv_option2.setCurrentItem(option2);
198 | wv_option3.setCurrentItem(option3);
199 | }
200 |
201 | private void itemSelected(int opt1Select, int opt2Select, int opt3Select) {
202 | if (mOptions2Items != null) {
203 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items
204 | .get(opt1Select)));
205 | wv_option2.setCurrentItem(opt2Select);
206 | }
207 | if (mOptions3Items != null) {
208 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items
209 | .get(opt1Select).get(
210 | opt2Select)));
211 | wv_option3.setCurrentItem(opt3Select);
212 | }
213 | }
214 |
215 | /**
216 | * 设置滚动文字大小
217 | */
218 | public void setTextSize(float size){
219 | if(wv_option1 != null)
220 | wv_option1.setTextSize(size);
221 | if(wv_option2 != null)
222 | wv_option2.setTextSize(size);
223 | if(wv_option3 != null)
224 | wv_option3.setTextSize(size);
225 | }
226 |
227 | }
228 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/WheelTime.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.view.View;
6 |
7 | import com.airsaid.pickerviewlibrary.R;
8 | import com.airsaid.pickerviewlibrary.TimePickerView;
9 | import com.airsaid.pickerviewlibrary.TimePickerView.Type;
10 | import com.airsaid.pickerviewlibrary.adapter.NumericWheelAdapter;
11 | import com.airsaid.pickerviewlibrary.listener.OnItemSelectedListener;
12 | import com.airsaid.pickerviewlibrary.widget.wheelview.WheelView;
13 |
14 | import java.text.DateFormat;
15 | import java.text.SimpleDateFormat;
16 | import java.util.Arrays;
17 | import java.util.List;
18 |
19 |
20 | public class WheelTime {
21 | public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
22 | private View view;
23 | private WheelView wv_year;
24 | private WheelView wv_month;
25 | private WheelView wv_day;
26 | private WheelView wv_hours;
27 | private WheelView wv_mins;
28 |
29 | private Type type;
30 | public static final int DEFULT_START_YEAR = 1990;
31 | public static final int DEFULT_END_YEAR = 2100;
32 | private int startYear = DEFULT_START_YEAR;
33 | private int endYear = DEFULT_END_YEAR;
34 |
35 | private float mTextSize = TimePickerView.TextSize.DEFAULT;
36 |
37 | public WheelTime(View view) {
38 | super();
39 | this.view = view;
40 | type = Type.ALL;
41 | setView(view);
42 | }
43 | public WheelTime(View view, Type type) {
44 | super();
45 | this.view = view;
46 | this.type = type;
47 | setView(view);
48 | }
49 | public void setPicker(int year ,int month,int day){
50 | this.setPicker(year, month, day, 0, 0);
51 | }
52 |
53 | public void setPicker(int year ,int month ,int day,int h,int m) {
54 | // 添加大小月月份并将其转换为list,方便之后的判断
55 | String[] months_big = { "1", "3", "5", "7", "8", "10", "12" };
56 | String[] months_little = { "4", "6", "9", "11" };
57 |
58 | final List list_big = Arrays.asList(months_big);
59 | final List list_little = Arrays.asList(months_little);
60 |
61 | Context context = view.getContext();
62 | // 年
63 | wv_year = (WheelView) view.findViewById(R.id.year);
64 | wv_year.setAdapter(new NumericWheelAdapter(startYear, endYear));// 设置"年"的显示数据
65 | wv_year.setLabel(context.getString(R.string.pickerview_year));// 添加文字
66 | wv_year.setCurrentItem(year - startYear);// 初始化时显示的数据
67 |
68 | // 月
69 | wv_month = (WheelView) view.findViewById(R.id.month);
70 | wv_month.setAdapter(new NumericWheelAdapter(1, 12));
71 | wv_month.setLabel(context.getString(R.string.pickerview_month));
72 | wv_month.setCurrentItem(month);
73 |
74 | // 日
75 | wv_day = (WheelView) view.findViewById(R.id.day);
76 | // 判断大小月及是否闰年,用来确定"日"的数据
77 | if (list_big.contains(String.valueOf(month + 1))) {
78 | wv_day.setAdapter(new NumericWheelAdapter(1, 31));
79 | } else if (list_little.contains(String.valueOf(month + 1))) {
80 | wv_day.setAdapter(new NumericWheelAdapter(1, 30));
81 | } else {
82 | // 闰年
83 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
84 | wv_day.setAdapter(new NumericWheelAdapter(1, 29));
85 | else
86 | wv_day.setAdapter(new NumericWheelAdapter(1, 28));
87 | }
88 | wv_day.setLabel(context.getString(R.string.pickerview_day));
89 | wv_day.setCurrentItem(day - 1);
90 |
91 |
92 | wv_hours = (WheelView)view.findViewById(R.id.hour);
93 | wv_hours.setAdapter(new NumericWheelAdapter(0, 23));
94 | wv_hours.setLabel(context.getString(R.string.pickerview_hours));// 添加文字
95 | wv_hours.setCurrentItem(h);
96 |
97 | wv_mins = (WheelView)view.findViewById(R.id.min);
98 | wv_mins.setAdapter(new NumericWheelAdapter(0, 59));
99 | wv_mins.setLabel(context.getString(R.string.pickerview_minutes));// 添加文字
100 | wv_mins.setCurrentItem(m);
101 |
102 | // 添加"年"监听
103 | OnItemSelectedListener wheelListener_year = new OnItemSelectedListener() {
104 | @Override
105 | public void onItemSelected(int index) {
106 | int year_num = index + startYear;
107 | // 判断大小月及是否闰年,用来确定"日"的数据
108 | int maxItem = 30;
109 | if (list_big
110 | .contains(String.valueOf(wv_month.getCurrentItem() + 1))) {
111 | wv_day.setAdapter(new NumericWheelAdapter(1, 31));
112 | maxItem = 31;
113 | } else if (list_little.contains(String.valueOf(wv_month
114 | .getCurrentItem() + 1))) {
115 | wv_day.setAdapter(new NumericWheelAdapter(1, 30));
116 | maxItem = 30;
117 | } else {
118 | if ((year_num % 4 == 0 && year_num % 100 != 0)
119 | || year_num % 400 == 0){
120 | wv_day.setAdapter(new NumericWheelAdapter(1, 29));
121 | maxItem = 29;
122 | }
123 | else{
124 | wv_day.setAdapter(new NumericWheelAdapter(1, 28));
125 | maxItem = 28;
126 | }
127 | }
128 | if (wv_day.getCurrentItem() > maxItem - 1){
129 | wv_day.setCurrentItem(maxItem - 1);
130 | }
131 | }
132 | };
133 | // 添加"月"监听
134 | OnItemSelectedListener wheelListener_month = new OnItemSelectedListener() {
135 | @Override
136 | public void onItemSelected(int index) {
137 | int month_num = index + 1;
138 | int maxItem = 30;
139 | // 判断大小月及是否闰年,用来确定"日"的数据
140 | if (list_big.contains(String.valueOf(month_num))) {
141 | wv_day.setAdapter(new NumericWheelAdapter(1, 31));
142 | maxItem = 31;
143 | } else if (list_little.contains(String.valueOf(month_num))) {
144 | wv_day.setAdapter(new NumericWheelAdapter(1, 30));
145 | maxItem = 30;
146 | } else {
147 | if (((wv_year.getCurrentItem() + startYear) % 4 == 0 && (wv_year
148 | .getCurrentItem() + startYear) % 100 != 0)
149 | || (wv_year.getCurrentItem() + startYear) % 400 == 0){
150 | wv_day.setAdapter(new NumericWheelAdapter(1, 29));
151 | maxItem = 29;
152 | }
153 | else{
154 | wv_day.setAdapter(new NumericWheelAdapter(1, 28));
155 | maxItem = 28;
156 | }
157 | }
158 | if (wv_day.getCurrentItem() > maxItem - 1){
159 | wv_day.setCurrentItem(maxItem - 1);
160 | }
161 |
162 | }
163 | };
164 | wv_year.setOnItemSelectedListener(wheelListener_year);
165 | wv_month.setOnItemSelectedListener(wheelListener_month);
166 | setTextSize();
167 | }
168 |
169 | /**
170 | * 设置是否循环滚动
171 | * @param cyclic
172 | */
173 | public void setCyclic(boolean cyclic){
174 | wv_year.setCyclic(cyclic);
175 | wv_month.setCyclic(cyclic);
176 | wv_day.setCyclic(cyclic);
177 | wv_hours.setCyclic(cyclic);
178 | wv_mins.setCyclic(cyclic);
179 | }
180 | public String getTime() {
181 | StringBuffer sb = new StringBuffer();
182 | sb.append((wv_year.getCurrentItem() + startYear)).append("-")
183 | .append((wv_month.getCurrentItem() + 1)).append("-")
184 | .append((wv_day.getCurrentItem() + 1)).append(" ")
185 | .append(wv_hours.getCurrentItem()).append(":")
186 | .append(wv_mins.getCurrentItem());
187 | return sb.toString();
188 | }
189 |
190 | public View getView() {
191 | return view;
192 | }
193 |
194 | public void setView(View view) {
195 | this.view = view;
196 | }
197 |
198 | public int getStartYear() {
199 | return startYear;
200 | }
201 |
202 | public void setStartYear(int startYear) {
203 | this.startYear = startYear;
204 | }
205 |
206 | public int getEndYear() {
207 | return endYear;
208 | }
209 |
210 | public void setEndYear(int endYear) {
211 | this.endYear = endYear;
212 | }
213 |
214 | public void setTextSize(float size){
215 | this.mTextSize = size;
216 | setTextSize();
217 | }
218 |
219 | private void setTextSize(){
220 | // 根据屏幕密度来指定选择器字体的大小(不同屏幕可能不同)
221 | Log.e("test", "mTextSize: " + mTextSize);
222 |
223 | float textSize = mTextSize;
224 | switch(type){
225 | case ALL:
226 | textSize = textSize * 3;
227 | break;
228 | case YEAR_MONTH_DAY:
229 | textSize = textSize * 4;
230 | wv_hours.setVisibility(View.GONE);
231 | wv_mins.setVisibility(View.GONE);
232 | break;
233 | case HOURS_MINS:
234 | textSize = textSize * 4;
235 | wv_year.setVisibility(View.GONE);
236 | wv_month.setVisibility(View.GONE);
237 | wv_day.setVisibility(View.GONE);
238 | break;
239 | case MONTH_DAY_HOUR_MIN:
240 | textSize = textSize * 3;
241 | wv_year.setVisibility(View.GONE);
242 | break;
243 | case YEAR_MONTH:
244 | textSize = textSize * 4;
245 | wv_day.setVisibility(View.GONE);
246 | wv_hours.setVisibility(View.GONE);
247 | wv_mins.setVisibility(View.GONE);
248 | }
249 | wv_day.setTextSize(textSize);
250 | wv_month.setTextSize(textSize);
251 | wv_year.setTextSize(textSize);
252 | wv_hours.setTextSize(textSize);
253 | wv_mins.setTextSize(textSize);
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/pickerview/src/main/java/com/airsaid/pickerviewlibrary/widget/wheelview/WheelView.java:
--------------------------------------------------------------------------------
1 | package com.airsaid.pickerviewlibrary.widget.wheelview;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.graphics.Typeface;
9 | import android.os.Handler;
10 | import android.util.AttributeSet;
11 | import android.view.GestureDetector;
12 | import android.view.Gravity;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 |
16 | import com.airsaid.pickerviewlibrary.R;
17 | import com.airsaid.pickerviewlibrary.adapter.WheelAdapter;
18 | import com.airsaid.pickerviewlibrary.listener.OnItemSelectedListener;
19 | import com.airsaid.pickerviewlibrary.model.IPickerViewData;
20 |
21 | import java.util.concurrent.Executors;
22 | import java.util.concurrent.ScheduledExecutorService;
23 | import java.util.concurrent.ScheduledFuture;
24 | import java.util.concurrent.TimeUnit;
25 |
26 | /**
27 | * 3d滚轮控件
28 | */
29 | public class WheelView extends View {
30 |
31 | public enum ACTION {
32 | // 点击,滑翔(滑到尽头),拖拽事件
33 | CLICK, FLING, DAGGLE
34 | }
35 |
36 | Context context;
37 |
38 | Handler handler;
39 | private GestureDetector gestureDetector;
40 | OnItemSelectedListener onItemSelectedListener;
41 |
42 | // Timer mTimer;
43 | ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
44 | private ScheduledFuture> mFuture;
45 |
46 | Paint paintOuterText;
47 | Paint paintCenterText;
48 | Paint paintIndicator;
49 |
50 | WheelAdapter adapter;
51 |
52 | private String label;//附加单位
53 | int textSize;//选项的文字大小
54 | int maxTextWidth;
55 | int maxTextHeight;
56 | float itemHeight;//每行高度
57 |
58 | int textColorOut;
59 | int textColorCenter;
60 | int dividerColor;
61 |
62 | // 条目间距倍数
63 | static final float lineSpacingMultiplier = 1.4F;
64 | boolean isLoop;
65 |
66 | // 第一条线Y坐标值
67 | float firstLineY;
68 | //第二条线Y坐标
69 | float secondLineY;
70 | //中间Y坐标
71 | float centerY;
72 |
73 | //滚动总高度y值
74 | int totalScrollY;
75 | //初始化默认选中第几个
76 | int initPosition;
77 | //选中的Item是第几个
78 | private int selectedItem;
79 | int preCurrentIndex;
80 | //滚动偏移值,用于记录滚动了多少个item
81 | int change;
82 |
83 | // 显示几个条目
84 | int itemsVisible = 11;
85 |
86 | int measuredHeight;
87 | int measuredWidth;
88 |
89 | // 半圆周长
90 | int halfCircumference;
91 | // 半径
92 | int radius;
93 |
94 | private int mOffset = 0;
95 | private float previousY = 0;
96 | long startTime = 0;
97 |
98 | // 修改这个值可以改变滑行速度
99 | private static final int VELOCITYFLING = 5;
100 | int widthMeasureSpec;
101 |
102 | private int mGravity = Gravity.CENTER;
103 | private int drawCenterContentStart = 0;//中间选中文字开始绘制位置
104 | private int drawOutContentStart = 0;//非中间文字开始绘制位置
105 | private static final float SCALECONTENT = 0.8F;//非中间文字则用此控制高度,压扁形成3d错觉
106 | private static final float CENTERCONTENTOFFSET = 6;//中间文字文字居中需要此偏移值
107 |
108 | public WheelView(Context context) {
109 | this(context, null);
110 | }
111 |
112 | public WheelView(Context context, AttributeSet attrs) {
113 | super(context, attrs);
114 | textColorOut = getResources().getColor(R.color.pickerview_wheelview_textcolor_out);
115 | textColorCenter = getResources().getColor(R.color.pickerview_wheelview_textcolor_center);
116 | dividerColor = getResources().getColor(R.color.pickerview_wheelview_textcolor_divider);
117 | //配合customTextSize使用,customTextSize为true才会发挥效果
118 | textSize = getResources().getDimensionPixelSize(R.dimen.pickerview_textsize);
119 | if (attrs != null) {
120 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PickerView_Library, 0, 0);
121 | mGravity = a.getInt(R.styleable.PickerView_Library_pickerview_gravity, Gravity.CENTER);
122 | textColorOut = a.getColor(R.styleable.PickerView_Library_pickerview_textColorOut, textColorOut);
123 | textColorCenter = a.getColor(R.styleable.PickerView_Library_pickerview_textColorCenter, textColorCenter);
124 | dividerColor = a.getColor(R.styleable.PickerView_Library_pickerview_dividerColor, dividerColor);
125 | textSize = a.getDimensionPixelOffset(R.styleable.PickerView_Library_pickerview_textSize, textSize);
126 | a.recycle();
127 | }
128 | initLoopView(context);
129 | }
130 |
131 | private void initLoopView(Context context) {
132 | this.context = context;
133 | handler = new MessageHandler(this);
134 | gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
135 | gestureDetector.setIsLongpressEnabled(false);
136 |
137 | isLoop = false;
138 |
139 | totalScrollY = 0;
140 | initPosition = -1;
141 |
142 | initPaints();
143 | }
144 |
145 | private void initPaints() {
146 | paintOuterText = new Paint();
147 | paintOuterText.setColor(textColorOut);
148 | paintOuterText.setAntiAlias(true);
149 | paintOuterText.setTypeface(Typeface.MONOSPACE);
150 | paintOuterText.setTextSize(textSize);
151 |
152 | paintCenterText = new Paint();
153 | paintCenterText.setColor(textColorCenter);
154 | paintCenterText.setAntiAlias(true);
155 | paintCenterText.setTextScaleX(1.1F);
156 | paintCenterText.setTypeface(Typeface.MONOSPACE);
157 | paintCenterText.setTextSize(textSize);
158 |
159 | paintIndicator = new Paint();
160 | paintIndicator.setColor(dividerColor);
161 | paintIndicator.setAntiAlias(true);
162 |
163 | if (android.os.Build.VERSION.SDK_INT >= 11) {
164 | setLayerType(LAYER_TYPE_SOFTWARE, null);
165 | }
166 | }
167 |
168 | private void remeasure() {
169 | if (adapter == null) {
170 | return;
171 | }
172 |
173 | measureTextWidthHeight();
174 |
175 | //最大Text的高度乘间距倍数得到 可见文字实际的总高度,半圆的周长
176 | halfCircumference = (int) (itemHeight * (itemsVisible - 1));
177 | //整个圆的周长除以PI得到直径,这个直径用作控件的总高度
178 | measuredHeight = (int) ((halfCircumference * 2) / Math.PI);
179 | //求出半径
180 | radius = (int) (halfCircumference / Math.PI);
181 | //控件宽度,这里支持weight
182 | measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
183 | //计算两条横线和控件中间点的Y位置
184 | firstLineY = (measuredHeight - itemHeight) / 2.0F;
185 | secondLineY = (measuredHeight + itemHeight) / 2.0F;
186 | centerY = (measuredHeight + maxTextHeight) / 2.0F - CENTERCONTENTOFFSET;
187 | //初始化显示的item的position,根据是否loop
188 | if (initPosition == -1) {
189 | if (isLoop) {
190 | initPosition = (adapter.getItemsCount() + 1) / 2;
191 | } else {
192 | initPosition = 0;
193 | }
194 | }
195 |
196 | preCurrentIndex = initPosition;
197 | }
198 |
199 | /**
200 | * 计算最大len的Text的宽高度
201 | */
202 | private void measureTextWidthHeight() {
203 | Rect rect = new Rect();
204 | for (int i = 0; i < adapter.getItemsCount(); i++) {
205 | String s1 = getContentText(adapter.getItem(i));
206 | paintCenterText.getTextBounds(s1, 0, s1.length(), rect);
207 | int textWidth = rect.width();
208 | if (textWidth > maxTextWidth) {
209 | maxTextWidth = textWidth;
210 | }
211 | paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); // 星期
212 | int textHeight = rect.height();
213 | if (textHeight > maxTextHeight) {
214 | maxTextHeight = textHeight;
215 | }
216 | }
217 | itemHeight = lineSpacingMultiplier * maxTextHeight;
218 | }
219 |
220 | void smoothScroll(ACTION action) {
221 | cancelFuture();
222 | if (action == ACTION.FLING || action == ACTION.DAGGLE) {
223 | mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight);
224 | if ((float) mOffset > itemHeight / 2.0F) {
225 | mOffset = (int) (itemHeight - (float) mOffset);
226 | } else {
227 | mOffset = -mOffset;
228 | }
229 | }
230 | //停止的时候,位置有偏移,不是全部都能正确停止到中间位置的,这里把文字位置挪回中间去
231 | mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS);
232 | }
233 |
234 | protected final void scrollBy(float velocityY) {
235 | cancelFuture();
236 |
237 | mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, VELOCITYFLING, TimeUnit.MILLISECONDS);
238 | }
239 |
240 | public void cancelFuture() {
241 | if (mFuture != null && !mFuture.isCancelled()) {
242 | mFuture.cancel(true);
243 | mFuture = null;
244 | }
245 | }
246 |
247 | /**
248 | * 设置是否循环滚动
249 | * @param cyclic 是否循环
250 | */
251 | public final void setCyclic(boolean cyclic) {
252 | isLoop = cyclic;
253 | }
254 |
255 | public final void setTextSize(float size) {
256 | if (size > 0.0F) {
257 | textSize = (int) (context.getResources().getDisplayMetrics().density * size);
258 | paintOuterText.setTextSize(textSize);
259 | paintCenterText.setTextSize(textSize);
260 | }
261 | }
262 |
263 | public final void setCurrentItem(int currentItem) {
264 | this.initPosition = currentItem;
265 | totalScrollY = 0;//回归顶部,不然重设setCurrentItem的话位置会偏移的,就会显示出不对位置的数据
266 | invalidate();
267 | }
268 |
269 | public final void setOnItemSelectedListener(OnItemSelectedListener OnItemSelectedListener) {
270 | this.onItemSelectedListener = OnItemSelectedListener;
271 | }
272 |
273 | public final void setAdapter(WheelAdapter adapter) {
274 | this.adapter = adapter;
275 | remeasure();
276 | invalidate();
277 | }
278 |
279 | public final WheelAdapter getAdapter() {
280 | return adapter;
281 | }
282 |
283 | public final int getCurrentItem() {
284 | return selectedItem;
285 | }
286 |
287 | protected final void onItemSelected() {
288 | if (onItemSelectedListener != null) {
289 | postDelayed(new OnItemSelectedRunnable(this), 200L);
290 | }
291 | }
292 |
293 | @Override
294 | protected void onDraw(Canvas canvas) {
295 | if (adapter == null) {
296 | return;
297 | }
298 | //可见的item数组
299 | Object visibles[] = new Object[itemsVisible];
300 | //滚动的Y值高度除去每行Item的高度,得到滚动了多少个item,即change数
301 | change = (int) (totalScrollY / itemHeight);
302 | try {
303 | //滚动中实际的预选中的item(即经过了中间位置的item) = 滑动前的位置 + 滑动相对位置
304 | preCurrentIndex = initPosition + change % adapter.getItemsCount();
305 | } catch (ArithmeticException e) {
306 | System.out.println("出错了!adapter.getItemsCount() == 0,联动数据不匹配");
307 | }
308 | if (!isLoop) {//不循环的情况
309 | if (preCurrentIndex < 0) {
310 | preCurrentIndex = 0;
311 | }
312 | if (preCurrentIndex > adapter.getItemsCount() - 1) {
313 | preCurrentIndex = adapter.getItemsCount() - 1;
314 | }
315 | } else {//循环
316 | if (preCurrentIndex < 0) {//举个例子:如果总数是5,preCurrentIndex = -1,那么preCurrentIndex按循环来说,其实是0的上面,也就是4的位置
317 | preCurrentIndex = adapter.getItemsCount() + preCurrentIndex;
318 | }
319 | if (preCurrentIndex > adapter.getItemsCount() - 1) {//同理上面,自己脑补一下
320 | preCurrentIndex = preCurrentIndex - adapter.getItemsCount();
321 | }
322 | }
323 |
324 | //跟滚动流畅度有关,总滑动距离与每个item高度取余,即并不是一格格的滚动,每个item不一定滚到对应Rect里的,这个item对应格子的偏移值
325 | int itemHeightOffset = (int) (totalScrollY % itemHeight);
326 | // 设置数组中每个元素的值
327 | int counter = 0;
328 | while (counter < itemsVisible) {
329 | int index = preCurrentIndex - (itemsVisible / 2 - counter);//索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值
330 |
331 | //判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项
332 | if (isLoop) {
333 | index = getLoopMappingIndex(index);
334 | visibles[counter] = adapter.getItem(index);
335 | } else if (index < 0) {
336 | visibles[counter] = "";
337 | } else if (index > adapter.getItemsCount() - 1) {
338 | visibles[counter] = "";
339 | } else {
340 | visibles[counter] = adapter.getItem(index);
341 | }
342 |
343 | counter++;
344 |
345 | }
346 |
347 | //中间两条横线
348 | canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
349 | canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator);
350 | //单位的Label
351 | if (label != null) {
352 | int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText, label);
353 | //靠右并留出空隙
354 | canvas.drawText(label, drawRightContentStart - CENTERCONTENTOFFSET, centerY, paintCenterText);
355 | }
356 | counter = 0;
357 | while (counter < itemsVisible) {
358 | canvas.save();
359 | // L(弧长)=α(弧度)* r(半径) (弧度制)
360 | // 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长)
361 | float itemHeight = maxTextHeight * lineSpacingMultiplier;
362 | double radian = ((itemHeight * counter - itemHeightOffset) * Math.PI) / halfCircumference;
363 | // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限
364 | float angle = (float) (90D - (radian / Math.PI) * 180D);
365 | // 九十度以上的不绘制
366 | if (angle >= 90F || angle <= -90F) {
367 | canvas.restore();
368 | } else {
369 |
370 |
371 | String contentText = getContentText(visibles[counter]);
372 |
373 | //计算开始绘制的位置
374 | measuredCenterContentStart(contentText);
375 | measuredOutContentStart(contentText);
376 | float translateY = (float) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D);
377 | //根据Math.sin(radian)来更改canvas坐标系原点,然后缩放画布,使得文字高度进行缩放,形成弧形3d视觉差
378 | canvas.translate(0.0F, translateY);
379 | canvas.scale(1.0F, (float) Math.sin(radian));
380 | if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) {
381 | // 条目经过第一条线
382 | canvas.save();
383 | canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY);
384 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
385 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
386 | canvas.restore();
387 | canvas.save();
388 | canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight));
389 | canvas.scale(1.0F, (float) Math.sin(radian) * 1F);
390 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText);
391 | canvas.restore();
392 | } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) {
393 | // 条目经过第二条线
394 | canvas.save();
395 | canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY);
396 | canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F);
397 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText);
398 | canvas.restore();
399 | canvas.save();
400 | canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight));
401 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
402 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
403 | canvas.restore();
404 | } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) {
405 | // 中间条目
406 | canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
407 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText);
408 | int preSelectedItem = adapter.indexOf(visibles[counter]);
409 | if (preSelectedItem != -1) {
410 | selectedItem = preSelectedItem;
411 | }
412 | } else {
413 | // 其他条目
414 | canvas.save();
415 | canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
416 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
417 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
418 | canvas.restore();
419 | }
420 | canvas.restore();
421 | }
422 | counter++;
423 | }
424 | }
425 |
426 | //递归计算出对应的index
427 | private int getLoopMappingIndex(int index) {
428 | if (index < 0) {
429 | index = index + adapter.getItemsCount();
430 | index = getLoopMappingIndex(index);
431 | } else if (index > adapter.getItemsCount() - 1) {
432 | index = index - adapter.getItemsCount();
433 | index = getLoopMappingIndex(index);
434 | }
435 | return index;
436 | }
437 |
438 | /**
439 | * 根据传进来的对象获取getPickerViewText()方法,来获取需要显示的值
440 | * @param item 数据源的item
441 | * @return 对应显示的字符串
442 | */
443 | private String getContentText(Object item) {
444 | if (item == null) {
445 | return "";
446 | }
447 | else if (item instanceof IPickerViewData) {
448 | return ((IPickerViewData) item).getPickerViewText();
449 | }
450 | return item.toString();
451 | }
452 |
453 | private void measuredCenterContentStart(String content) {
454 | Rect rect = new Rect();
455 | paintCenterText.getTextBounds(content, 0, content.length(), rect);
456 | switch (mGravity) {
457 | case Gravity.CENTER:
458 | drawCenterContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
459 | break;
460 | case Gravity.LEFT:
461 | drawCenterContentStart = 0;
462 | break;
463 | case Gravity.RIGHT:
464 | drawCenterContentStart = measuredWidth - rect.width();
465 | break;
466 | }
467 | }
468 |
469 | private void measuredOutContentStart(String content) {
470 | Rect rect = new Rect();
471 | paintOuterText.getTextBounds(content, 0, content.length(), rect);
472 | switch (mGravity) {
473 | case Gravity.CENTER:
474 | drawOutContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
475 | break;
476 | case Gravity.LEFT:
477 | drawOutContentStart = 0;
478 | break;
479 | case Gravity.RIGHT:
480 | drawOutContentStart = measuredWidth - rect.width();
481 | break;
482 | }
483 | }
484 |
485 | @Override
486 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
487 | this.widthMeasureSpec = widthMeasureSpec;
488 | remeasure();
489 | setMeasuredDimension(measuredWidth, measuredHeight);
490 | }
491 |
492 | @Override
493 | public boolean onTouchEvent(MotionEvent event) {
494 | boolean eventConsumed = gestureDetector.onTouchEvent(event);
495 | switch (event.getAction()) {
496 | case MotionEvent.ACTION_DOWN:
497 | startTime = System.currentTimeMillis();
498 | cancelFuture();
499 | previousY = event.getRawY();
500 | break;
501 |
502 | case MotionEvent.ACTION_MOVE:
503 | float dy = previousY - event.getRawY();
504 | previousY = event.getRawY();
505 | totalScrollY = (int) (totalScrollY + dy);
506 |
507 | // 边界处理。
508 | if (!isLoop) {
509 | float top = -initPosition * itemHeight;
510 | float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight;
511 | if (totalScrollY - itemHeight * 0.3 < top) {
512 | top = totalScrollY - dy;
513 | } else if (totalScrollY + itemHeight * 0.3 > bottom) {
514 | bottom = totalScrollY - dy;
515 | }
516 |
517 | if (totalScrollY < top) {
518 | totalScrollY = (int) top;
519 | } else if (totalScrollY > bottom) {
520 | totalScrollY = (int) bottom;
521 | }
522 | }
523 | break;
524 |
525 | case MotionEvent.ACTION_UP:
526 | default:
527 | if (!eventConsumed) {
528 | float y = event.getY();
529 | double l = Math.acos((radius - y) / radius) * radius;
530 | int circlePosition = (int) ((l + itemHeight / 2) / itemHeight);
531 |
532 | float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight;
533 | mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset);
534 |
535 | if ((System.currentTimeMillis() - startTime) > 120) {
536 | // 处理拖拽事件
537 | smoothScroll(ACTION.DAGGLE);
538 | } else {
539 | // 处理条目点击事件
540 | smoothScroll(ACTION.CLICK);
541 | }
542 | }
543 | break;
544 | }
545 | invalidate();
546 |
547 | return true;
548 | }
549 |
550 | /**
551 | * 获取Item个数
552 | * @return item个数
553 | */
554 | public int getItemsCount() {
555 | return adapter != null ? adapter.getItemsCount() : 0;
556 | }
557 |
558 | /**
559 | * 附加在右边的单位字符串
560 | * @param label 单位
561 | */
562 | public void setLabel(String label) {
563 | this.label = label;
564 | }
565 |
566 | public void setGravity(int gravity) {
567 | this.mGravity = gravity;
568 | }
569 |
570 | public int getTextWidth(Paint paint, String str) {
571 | int iRet = 0;
572 | if (str != null && str.length() > 0) {
573 | int len = str.length();
574 | float[] widths = new float[len];
575 | paint.getTextWidths(str, widths);
576 | for (int j = 0; j < len; j++) {
577 | iRet += (int) Math.ceil(widths[j]);
578 | }
579 | }
580 | return iRet;
581 | }
582 | }
--------------------------------------------------------------------------------