Initial commit

This commit is contained in:
linglongxin24
2016-05-31 16:53:19 +08:00
commit 801b889b14
64 changed files with 3043 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@@ -0,0 +1,33 @@
# built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Eclipse project files
.classpath
.project
# Android Studio
.idea/
.gradle
/*/local.properties
/*/out
build
/*/*/production
*.iml
*.iws
*.ipr
*~
*.swp

74
README.md Normal file
View File

@@ -0,0 +1,74 @@
PickerView (2.x系列)
==========
精仿iOS的PickerView控件有时间选择和选项选择并支持一二三级联动效果
——TimePickerView 时间选择器,支持年月日时分,年月日,年月,时分等格式
——OptionsPickerView 选项选择器,支持一,二,三级选项选择,并且可以设置是否联动
2.x是全新的3D效果比1.x版本更加贴近iOS的效果从外观细节上也得到了改善。api兼容1.x版本只需要把依赖的版本号升级即可几乎不用修改代码即可完成升级。
####使用gradle 依赖:
```java
compile 'com.bigkoo:pickerview:2.0.8'
```
## Demo 图片
![](https://github.com/saiwu-bigkoo/PickerView/blob/master/preview/pickerdemo.gif)
- [demo代码请看戳这里](https://github.com/saiwu-bigkoo/Android-PickerView/blob/master/app/src/main/java/com/bigkoo/pickerviewdemo/MainActivity.java)
>## 更新说明
>v2.0.0 不需修改任何代码就可以兼容1.x
- 外观大整改 <br />
- 支持反射获取getPickerViewText()来获取要展示数据以前只能传String的对象现在可以传任意对象只要有getPickerViewText()函数即可显示对应的字符串如果没有getPickerViewText()函数则使用对象toString作为显示 <br />
- 加入setTitle <br />
>v2.0.1
- 去掉popupWindow改用View类名也对应修改为TimePickerView和 OptionsPickerView <br />
- 加入遮罩效果 <br />
>v2.0.2
- 修复不循环模式下点击空白item处出现数组越界问题 <br />
- 修复循环模式下只有一条数据的时候只显示三条而不是填充满高度问题 <br />
>v2.0.3
- 修复时间选择的时候部分数字选不到直接跳到下一个数字的问题 <br />
>v2.0.4
- 修复不循环模式下顶部超出范围问题 <br />
- wheel view文字颜色通过xml配置 <br />
>v2.0.5
- 修复不循环模式下底部超出范围问题 <br />
>v2.0.6
- 修复不循环模式下点击超出范围问题,修复后点击空白的地方,只能滚到最顶或最底,不会滚出数据范围。 <br />
>v2.0.7
- 修复设置初始化position 第三级数据不对的BUG。 <br />
>v2.0.8
- 修复41 未选中项有错乱数据问题。<br />
- 加入pickerview_customTextSize 和 pickerview_textsize 到 xml 中 来控制自定义文字大小<br />
---------------------华丽丽的分割线--------------------------
PickerView1.x (我已经把1.0.3版本分到v1.x的分支去了停止维护1.x的分支)
==========
####使用gradle 依赖:
```java
compile 'com.bigkoo:pickerview:1.0.3'
```
## Demo 图片(招行信用卡的“掌上生活”里面条件选择器他们用的就是我这个库,大家可以当实际项目参考)
![](https://github.com/saiwu-bigkoo/PickerView/blob/master/preview/pickerdemo1x.gif)
![](https://github.com/saiwu-bigkoo/Android-PickerView/blob/master/preview/pickerdemo_zhangshangshenghuo.gif)
## Thanks
- WheelView
- [androidWheelView](https://github.com/weidongjian/androidWheelView/)

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

27
app/build.gradle Normal file
View File

@@ -0,0 +1,27 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "com.bigkoo.pickerviewdemo"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':pickerview')
// compile 'com.bigkoo:pickerview:2.0.8'
compile 'com.android.support:appcompat-v7:23.1.0'
}

17
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/Sai/Documents/software/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,13 @@
package com.bigkoo.pickerviewdemo;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bigkoo.pickerviewdemo" >
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,184 @@
package com.bigkoo.pickerviewdemo;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import com.bigkoo.pickerview.OptionsPickerView;
import com.bigkoo.pickerview.TimePickerView;
import com.bigkoo.pickerviewdemo.bean.ProvinceBean;
public class MainActivity extends Activity {
private ArrayList<ProvinceBean> options1Items = new ArrayList<ProvinceBean>();
private ArrayList<ArrayList<String>> options2Items = new ArrayList<ArrayList<String>>();
private ArrayList<ArrayList<ArrayList<String>>> options3Items = new ArrayList<ArrayList<ArrayList<String>>>();
private TextView tvTime, tvOptions;
TimePickerView pvTime;
OptionsPickerView pvOptions;
View vMasker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vMasker=findViewById(R.id.vMasker);
tvTime=(TextView) findViewById(R.id.tvTime);
tvOptions=(TextView) findViewById(R.id.tvOptions);
//时间选择器
pvTime = new TimePickerView(this, TimePickerView.Type.YEAR_MONTH_DAY);
//控制时间范围
// Calendar calendar = Calendar.getInstance();
// pvTime.setRange(calendar.get(Calendar.YEAR) - 20, calendar.get(Calendar.YEAR));
pvTime.setTime(new Date());
pvTime.setCyclic(false);
pvTime.setCancelable(true);
//时间选择后回调
pvTime.setOnTimeSelectListener(new TimePickerView.OnTimeSelectListener() {
@Override
public void onTimeSelect(Date date) {
tvTime.setText(getTime(date));
}
});
//弹出时间选择器
tvTime.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
pvOptions.setTitle("");
pvTime.show();
}
});
//选项选择器
pvOptions = new OptionsPickerView(this);
pvOptions.setTitle("");
//选项1
options1Items.add(new ProvinceBean(0,"广东","广东省,以岭南东道、广南东路得名","其他数据"));
options1Items.add(new ProvinceBean(1,"湖南","湖南省地处中国中部、长江中游,因大部分区域处于洞庭湖以南而得名湖南","芒果TV"));
options1Items.add(new ProvinceBean(3,"广西","嗯~~",""));
//选项2
ArrayList<String> options2Items_01=new ArrayList<String>();
options2Items_01.add("广州");
options2Items_01.add("佛山");
options2Items_01.add("东莞");
options2Items_01.add("阳江");
options2Items_01.add("珠海");
ArrayList<String> options2Items_02=new ArrayList<String>();
options2Items_02.add("长沙");
options2Items_02.add("岳阳");
ArrayList<String> options2Items_03=new ArrayList<String>();
options2Items_03.add("桂林");
options2Items.add(options2Items_01);
options2Items.add(options2Items_02);
options2Items.add(options2Items_03);
//选项3
ArrayList<ArrayList<String>> options3Items_01 = new ArrayList<ArrayList<String>>();
ArrayList<ArrayList<String>> options3Items_02 = new ArrayList<ArrayList<String>>();
ArrayList<ArrayList<String>> options3Items_03 = new ArrayList<ArrayList<String>>();
ArrayList<String> options3Items_01_01=new ArrayList<String>();
options3Items_01_01.add("白云");
options3Items_01_01.add("天河");
options3Items_01_01.add("海珠");
options3Items_01_01.add("越秀");
options3Items_01.add(options3Items_01_01);
ArrayList<String> options3Items_01_02=new ArrayList<String>();
options3Items_01_02.add("南海");
options3Items_01_02.add("高明");
options3Items_01_02.add("顺德");
options3Items_01_02.add("禅城");
options3Items_01.add(options3Items_01_02);
ArrayList<String> options3Items_01_03=new ArrayList<String>();
options3Items_01_03.add("其他");
options3Items_01_03.add("常平");
options3Items_01_03.add("虎门");
options3Items_01.add(options3Items_01_03);
ArrayList<String> options3Items_01_04=new ArrayList<String>();
options3Items_01_04.add("其他1");
options3Items_01_04.add("其他2");
options3Items_01_04.add("其他3");
options3Items_01.add(options3Items_01_04);
ArrayList<String> options3Items_01_05=new ArrayList<String>();
options3Items_01_05.add("其他1");
options3Items_01_05.add("其他2");
options3Items_01_05.add("其他3");
options3Items_01.add(options3Items_01_05);
ArrayList<String> options3Items_02_01=new ArrayList<String>();
options3Items_02_01.add("长沙长沙长沙长沙长沙长沙长沙长沙长沙1111111111");
options3Items_02_01.add("长沙2");
options3Items_02_01.add("长沙3");
options3Items_02_01.add("长沙4");
options3Items_02_01.add("长沙5");
options3Items_02_01.add("长沙6");
options3Items_02_01.add("长沙7");
options3Items_02_01.add("长沙8");
options3Items_02.add(options3Items_02_01);
ArrayList<String> options3Items_02_02=new ArrayList<String>();
options3Items_02_02.add("岳1");
options3Items_02_02.add("岳2");
options3Items_02_02.add("岳3");
options3Items_02_02.add("岳4");
options3Items_02_02.add("岳5");
options3Items_02_02.add("岳6");
options3Items_02_02.add("岳7");
options3Items_02_02.add("岳8");
options3Items_02_02.add("岳9");
options3Items_02.add(options3Items_02_02);
ArrayList<String> options3Items_03_01=new ArrayList<String>();
options3Items_03_01.add("好山水");
options3Items_03.add(options3Items_03_01);
options3Items.add(options3Items_01);
options3Items.add(options3Items_02);
options3Items.add(options3Items_03);
//三级联动效果
pvOptions.setPicker(options1Items, options2Items, options3Items, true);
//设置选择的三级单位
// pwOptions.setLabels("省", "市", "区");
pvOptions.setTitle("");
pvOptions.setCyclic(false, true, true);
//设置默认选中的三级项目
//监听确定选择按钮
pvOptions.setSelectOptions(1, 1, 1);
pvOptions.setOnoptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int option2, int options3) {
//返回的分别是三个级别的选中位置
String tx = options1Items.get(options1).getPickerViewText()
+ options2Items.get(options1).get(option2)
+ options3Items.get(options1).get(option2).get(options3);
tvOptions.setText(tx);
vMasker.setVisibility(View.GONE);
}
});
//点击弹出选项选择器
tvOptions.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pvOptions.show();
}
});
}
public static String getTime(Date date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
return format.format(date);
}
}

View File

@@ -0,0 +1,56 @@
package com.bigkoo.pickerviewdemo.bean;
/**
* Created by Sai on 15/11/22.
*/
public class ProvinceBean {
private long id;
private String name;
private String description;
private String others;
public ProvinceBean(long id,String name,String description,String others){
this.id = id;
this.name = name;
this.description = description;
this.others = others;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getOthers() {
return others;
}
public void setOthers(String others) {
this.others = others;
}
//这个用来显示在PickerView上面的字符串,PickerView会通过反射获取getPickerViewText方法显示出来。
public String getPickerViewText() {
//这里还可以判断文字超长截断再提供显示
return name;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_margin="50dp"
android:id="@+id/tvTime"
android:text="时间"
android:layout_width="match_parent"
android:layout_height="50dp"/>
<TextView
android:id="@+id/tvOptions"
android:layout_margin="50dp"
android:layout_below="@+id/tvTime"
android:text="条件"
android:layout_width="match_parent"
android:layout_height="50dp"/>
<View
android:id="@+id/vMasker"
android:visibility="gone"
android:background="#e0000000"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</RelativeLayout>

View File

@@ -0,0 +1,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
<item android:id="@+id/action_settings" android:title="@string/action_settings"
android:orderInCategory="100" app:showAsAction="never" />
</menu>

View File

@@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PickerViewDemo</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
</resources>

View File

@@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

20
build.gradle Normal file
View File

@@ -0,0 +1,20 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
// classpath 'com.github.dcendents:android-maven-plugin:1.2'
// // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}

18
gradle.properties Normal file
View File

@@ -0,0 +1,18 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Tue May 31 15:57:55 CST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip

164
gradlew vendored Normal file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
pickerview/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

99
pickerview/build.gradle Normal file
View File

@@ -0,0 +1,99 @@
apply plugin: 'com.android.library'
//apply plugin: 'com.github.dcendents.android-maven'
//apply plugin: 'com.jfrog.bintray'
version = "2.0.8"
android {
compileSdkVersion 21
buildToolsVersion "20.0.0"
defaultConfig {
minSdkVersion 9
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
def siteUrl = 'https://github.com/saiwu-bigkoo/Android-PickerView' // #CONFIG# // project homepage
def gitUrl = 'https://github.com/saiwu-bigkoo/Android-PickerView.git' // #CONFIG# // project git
group = "com.bigkoo"
//
//install {
// repositories.mavenInstaller {
// // This generates POM.xml with proper parameters
// pom {
// project {
// packaging 'aar'
// name 'PickerView For Android' // #CONFIG# // project title
// url siteUrl
// // Set your license
// licenses {
// license {
// name 'The Apache Software License, Version 2.0'
// url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
// }
// }
// developers {
// developer {
// id 'sai' // #CONFIG# // your user id (you can write your nickname)
// name 'sai.wu' // #CONFIG# // your user name
// email 'sai.wu@bigkoo.com' // #CONFIG# // your email
// }
// }
// scm {
// connection gitUrl
// developerConnection gitUrl
// url siteUrl
// }
// }
// }
// }
//}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
//bintray {
// user = properties.getProperty("bintray.user")
// key = properties.getProperty("bintray.apikey")
// configurations = ['archives']
// pkg {
// repo = "maven"
// name = "PickerView" // #CONFIG# project name in jcenter
// websiteUrl = siteUrl
// vcsUrl = gitUrl
// licenses = ["Apache-2.0"]
// publish = true
// }
//}

17
pickerview/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/Sai/Documents/software/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,13 @@
package com.bigkoo.pickerview;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bigkoo.pickerview">
</manifest>

View File

@@ -0,0 +1,148 @@
package com.bigkoo.pickerview;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.bigkoo.pickerview.view.BasePickerView;
import com.bigkoo.pickerview.view.WheelOptions;
import java.util.ArrayList;
/**
* Created by Sai on 15/11/22.
*/
public class OptionsPickerView<T> extends BasePickerView implements View.OnClickListener {
WheelOptions wheelOptions;
private View btnSubmit, btnCancel;
private TextView tvTitle;
private OnOptionsSelectListener optionsSelectListener;
private static final String TAG_SUBMIT = "submit";
private static final String TAG_CANCEL = "cancel";
public OptionsPickerView(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.pickerview_options, contentContainer);
// -----确定和取消按钮
btnSubmit = findViewById(R.id.btnSubmit);
btnSubmit.setTag(TAG_SUBMIT);
btnCancel = findViewById(R.id.btnCancel);
btnCancel.setTag(TAG_CANCEL);
btnSubmit.setOnClickListener(this);
btnCancel.setOnClickListener(this);
//顶部标题
tvTitle = (TextView) findViewById(R.id.tvTitle);
// ----转轮
final View optionspicker = findViewById(R.id.optionspicker);
wheelOptions = new WheelOptions(optionspicker);
}
public void setPicker(ArrayList<T> optionsItems) {
wheelOptions.setPicker(optionsItems, null, null, false);
}
public void setPicker(ArrayList<T> options1Items,
ArrayList<ArrayList<T>> options2Items, boolean linkage) {
wheelOptions.setPicker(options1Items, options2Items, null, linkage);
}
public void setPicker(ArrayList<T> options1Items,
ArrayList<ArrayList<T>> options2Items,
ArrayList<ArrayList<ArrayList<T>>> options3Items,
boolean linkage) {
wheelOptions.setPicker(options1Items, options2Items, options3Items,
linkage);
}
/**
* 设置选中的item位置
* @param option1
*/
public void setSelectOptions(int option1){
wheelOptions.setCurrentItems(option1, 0, 0);
}
/**
* 设置选中的item位置
* @param option1
* @param option2
*/
public void setSelectOptions(int option1, int option2){
wheelOptions.setCurrentItems(option1, option2, 0);
}
/**
* 设置选中的item位置
* @param option1
* @param option2
* @param option3
*/
public void setSelectOptions(int option1, int option2, int option3){
wheelOptions.setCurrentItems(option1, option2, option3);
}
/**
* 设置选项的单位
* @param label1
*/
public void setLabels(String label1){
wheelOptions.setLabels(label1, null, null);
}
/**
* 设置选项的单位
* @param label1
* @param label2
*/
public void setLabels(String label1,String label2){
wheelOptions.setLabels(label1, label2, null);
}
/**
* 设置选项的单位
* @param label1
* @param label2
* @param label3
*/
public void setLabels(String label1,String label2,String label3){
wheelOptions.setLabels(label1, label2, label3);
}
/**
* 设置是否循环滚动
* @param cyclic
*/
public void setCyclic(boolean cyclic){
wheelOptions.setCyclic(cyclic);
}
public void setCyclic(boolean cyclic1,boolean cyclic2,boolean cyclic3) {
wheelOptions.setCyclic(cyclic1,cyclic2,cyclic3);
}
@Override
public void onClick(View v)
{
String tag=(String) v.getTag();
if(tag.equals(TAG_CANCEL))
{
dismiss();
return;
}
else
{
if(optionsSelectListener!=null)
{
int[] optionsCurrentItems=wheelOptions.getCurrentItems();
optionsSelectListener.onOptionsSelect(optionsCurrentItems[0], optionsCurrentItems[1], optionsCurrentItems[2]);
}
dismiss();
return;
}
}
public interface OnOptionsSelectListener {
public void onOptionsSelect(int options1, int option2, int options3);
}
public void setOnoptionsSelectListener(
OnOptionsSelectListener optionsSelectListener) {
this.optionsSelectListener = optionsSelectListener;
}
public void setTitle(String title){
tvTitle.setText(title);
}
}

View File

@@ -0,0 +1,148 @@
package com.bigkoo.pickerview;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.bigkoo.pickerview.view.BasePickerView;
import com.bigkoo.pickerview.view.WheelTime;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
/**
* Created by Sai on 15/11/22.
*/
public class TimePickerView extends BasePickerView implements View.OnClickListener {
public enum Type {
ALL, YEAR_MONTH_DAY, HOURS_MINS, MONTH_DAY_HOUR_MIN , YEAR_MONTH
}// 四种选择模式,年月日时分,年月日,时分,月日时分
WheelTime wheelTime;
private View btnSubmit, btnCancel;
private TextView tvTitle;
private static final String TAG_SUBMIT = "submit";
private static final String TAG_CANCEL = "cancel";
private OnTimeSelectListener timeSelectListener;
public TimePickerView(Context context, Type type) {
super(context);
LayoutInflater.from(context).inflate(R.layout.pickerview_time, contentContainer);
// -----确定和取消按钮
btnSubmit = findViewById(R.id.btnSubmit);
btnSubmit.setTag(TAG_SUBMIT);
btnCancel = findViewById(R.id.btnCancel);
btnCancel.setTag(TAG_CANCEL);
btnSubmit.setOnClickListener(this);
btnCancel.setOnClickListener(this);
//顶部标题
tvTitle = (TextView) findViewById(R.id.tvTitle);
// ----时间转轮
final View timepickerview = findViewById(R.id.timepicker);
wheelTime = new WheelTime(timepickerview, type);
//默认选中当前时间
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
wheelTime.setPicker(year, month, day, hours, minute);
}
/**
* 设置可以选择的时间范围
*
* @param startYear
* @param endYear
*/
public void setRange(int startYear, int endYear) {
wheelTime.setStartYear(startYear);
wheelTime.setEndYear(endYear);
}
/**
* 设置选中时间
* @param date
*/
public void setTime(Date date) {
Calendar calendar = Calendar.getInstance();
if (date == null)
calendar.setTimeInMillis(System.currentTimeMillis());
else
calendar.setTime(date);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
wheelTime.setPicker(year, month, day, hours, minute);
}
// /**
// * 指定选中的时间,显示选择器
// *
// * @param date
// */
// public void show(Date date) {
// Calendar calendar = Calendar.getInstance();
// if (date == null)
// calendar.setTimeInMillis(System.currentTimeMillis());
// else
// calendar.setTime(date);
// int year = calendar.get(Calendar.YEAR);
// int month = calendar.get(Calendar.MONTH);
// int day = calendar.get(Calendar.DAY_OF_MONTH);
// int hours = calendar.get(Calendar.HOUR_OF_DAY);
// int minute = calendar.get(Calendar.MINUTE);
// wheelTime.setPicker(year, month, day, hours, minute);
// show();
// }
/**
* 设置是否循环滚动
*
* @param cyclic
*/
public void setCyclic(boolean cyclic) {
wheelTime.setCyclic(cyclic);
}
@Override
public void onClick(View v) {
String tag = (String) v.getTag();
if (tag.equals(TAG_CANCEL)) {
dismiss();
return;
} else {
if (timeSelectListener != null) {
try {
Date date = WheelTime.dateFormat.parse(wheelTime.getTime());
timeSelectListener.onTimeSelect(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
dismiss();
return;
}
}
public interface OnTimeSelectListener {
public void onTimeSelect(Date date);
}
public void setOnTimeSelectListener(OnTimeSelectListener timeSelectListener) {
this.timeSelectListener = timeSelectListener;
}
public void setTitle(String title){
tvTitle.setText(title);
}
}

View File

@@ -0,0 +1,55 @@
package com.bigkoo.pickerview.adapter;
import java.util.ArrayList;
/**
* The simple Array wheel adapter
* @param <T> the element type
*/
public class ArrayWheelAdapter<T> implements WheelAdapter {
/** The default items length */
public static final int DEFAULT_LENGTH = 4;
// items
private ArrayList<T> items;
// length
private int length;
/**
* Constructor
* @param items the items
* @param length the max items length
*/
public ArrayWheelAdapter(ArrayList<T> items, int length) {
this.items = items;
this.length = length;
}
/**
* Contructor
* @param items the items
*/
public ArrayWheelAdapter(ArrayList<T> items) {
this(items, DEFAULT_LENGTH);
}
@Override
public Object getItem(int index) {
if (index >= 0 && index < items.size()) {
return items.get(index);
}
return "";
}
@Override
public int getItemsCount() {
return items.size();
}
@Override
public int indexOf(Object o){
return items.indexOf(o);
}
}

View File

@@ -0,0 +1,54 @@
package com.bigkoo.pickerview.adapter;
/**
* Numeric Wheel adapter.
*/
public class NumericWheelAdapter implements WheelAdapter {
/** The default min value */
public static final int DEFAULT_MAX_VALUE = 9;
/** The default max value */
private static final int DEFAULT_MIN_VALUE = 0;
// Values
private int minValue;
private int maxValue;
/**
* Default constructor
*/
public NumericWheelAdapter() {
this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE);
}
/**
* Constructor
* @param minValue the wheel min value
* @param maxValue the wheel max value
*/
public NumericWheelAdapter(int minValue, int maxValue) {
this.minValue = minValue;
this.maxValue = maxValue;
}
@Override
public Object getItem(int index) {
if (index >= 0 && index < getItemsCount()) {
int value = minValue + index;
return value;
}
return 0;
}
@Override
public int getItemsCount() {
return maxValue - minValue + 1;
}
@Override
public int indexOf(Object o){
return (int)o - minValue;
}
}

View File

@@ -0,0 +1,25 @@
package com.bigkoo.pickerview.adapter;
public interface WheelAdapter<T> {
/**
* Gets items count
* @return the count of wheel items
*/
public int getItemsCount();
/**
* Gets a wheel item by index.
*
* @param index the item index
* @return the wheel item text or null
*/
public T getItem(int index);
/**
* Gets maximum item length. It is used to determine the wheel width.
* If -1 is returned there will be used the default wheel width.
*
* @return the maximum item length or -1
*/
public int indexOf(T o);
}

View File

@@ -0,0 +1,65 @@
package com.bigkoo.pickerview.lib;
import java.util.TimerTask;
final class InertiaTimerTask extends TimerTask {
float a;
final float velocityY;
final WheelView loopView;
InertiaTimerTask(WheelView loopview, float velocityY) {
super();
loopView = loopview;
this.velocityY = velocityY;
a = Integer.MAX_VALUE;
}
@Override
public final void run() {
if (a == Integer.MAX_VALUE) {
if (Math.abs(velocityY) > 2000F) {
if (velocityY > 0.0F) {
a = 2000F;
} else {
a = -2000F;
}
} else {
a = velocityY;
}
}
if (Math.abs(a) >= 0.0F && Math.abs(a) <= 20F) {
loopView.cancelFuture();
loopView.handler.sendEmptyMessage(MessageHandler.WHAT_SMOOTH_SCROLL);
return;
}
int i = (int) ((a * 10F) / 1000F);
loopView.totalScrollY = loopView.totalScrollY - i;
if (!loopView.isLoop) {
float itemHeight = loopView.itemHeight;
float top = (-loopView.initPosition) * itemHeight;
float bottom = (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight;
if(loopView.totalScrollY - itemHeight*0.3 < top){
top = loopView.totalScrollY + i;
}
else if(loopView.totalScrollY + itemHeight*0.3 > bottom){
bottom = loopView.totalScrollY + i;
}
if (loopView.totalScrollY <= top){
a = 40F;
loopView.totalScrollY = (int)top;
} else if (loopView.totalScrollY >= bottom) {
loopView.totalScrollY = (int)bottom;
a = -40F;
}
}
if (a < 0.0F) {
a = a + 20F;
} else {
a = a - 20F;
}
loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW);
}
}

View File

@@ -0,0 +1,18 @@
package com.bigkoo.pickerview.lib;
import android.view.MotionEvent;
final class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
final WheelView loopView;
LoopViewGestureListener(WheelView loopview) {
loopView = loopview;
}
@Override
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
loopView.scrollBy(velocityY);
return true;
}
}

View File

@@ -0,0 +1,34 @@
package com.bigkoo.pickerview.lib;
import android.os.Handler;
import android.os.Message;
final class MessageHandler extends Handler {
public static final int WHAT_INVALIDATE_LOOP_VIEW = 1000;
public static final int WHAT_SMOOTH_SCROLL = 2000;
public static final int WHAT_ITEM_SELECTED = 3000;
final WheelView loopview;
MessageHandler(WheelView loopview) {
this.loopview = loopview;
}
@Override
public final void handleMessage(Message msg) {
switch (msg.what) {
case WHAT_INVALIDATE_LOOP_VIEW:
loopview.invalidate();
break;
case WHAT_SMOOTH_SCROLL:
loopview.smoothScroll(WheelView.ACTION.FLING);
break;
case WHAT_ITEM_SELECTED:
loopview.onItemSelected();
break;
}
}
}

View File

@@ -0,0 +1,14 @@
package com.bigkoo.pickerview.lib;
final class OnItemSelectedRunnable implements Runnable {
final WheelView loopView;
OnItemSelectedRunnable(WheelView loopview) {
loopView = loopview;
}
@Override
public final void run() {
loopView.onItemSelectedListener.onItemSelected(loopView.getCurrentItem());
}
}

View File

@@ -0,0 +1,57 @@
package com.bigkoo.pickerview.lib;
import java.util.TimerTask;
final class SmoothScrollTimerTask extends TimerTask {
int realTotalOffset;
int realOffset;
int offset;
final WheelView loopView;
SmoothScrollTimerTask(WheelView loopview, int offset) {
this.loopView = loopview;
this.offset = offset;
realTotalOffset = Integer.MAX_VALUE;
realOffset = 0;
}
@Override
public final void run() {
if (realTotalOffset == Integer.MAX_VALUE) {
realTotalOffset = offset;
}
//把要滚动的范围细分成十小份,按是小份单位来重绘
realOffset = (int) ((float) realTotalOffset * 0.1F);
if (realOffset == 0) {
if (realTotalOffset < 0) {
realOffset = -1;
} else {
realOffset = 1;
}
}
if (Math.abs(realTotalOffset) <= 1) {
loopView.cancelFuture();
loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED);
} else {
loopView.totalScrollY = loopView.totalScrollY + realOffset;
//这里如果不是循环模式则点击空白位置需要回滚不然就会出现选到1 item的 情况
if (!loopView.isLoop) {
float itemHeight = loopView.itemHeight;
float top = (float) (-loopView.initPosition) * itemHeight;
float bottom = (float) (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight;
if (loopView.totalScrollY <= top||loopView.totalScrollY >= bottom) {
loopView.totalScrollY = loopView.totalScrollY - realOffset;
loopView.cancelFuture();
loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED);
return;
}
}
loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW);
realTotalOffset = realTotalOffset - realOffset;
}
}
}

View File

@@ -0,0 +1,588 @@
package com.bigkoo.pickerview.lib;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import com.bigkoo.pickerview.R;
import com.bigkoo.pickerview.adapter.WheelAdapter;
import com.bigkoo.pickerview.listener.OnItemSelectedListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* 3d滚轮控件
*/
public class WheelView extends View {
public enum ACTION {
// 点击,滑翔(滑到尽头),拖拽事件
CLICK, FLING, DAGGLE
}
Context context;
Handler handler;
private GestureDetector gestureDetector;
OnItemSelectedListener onItemSelectedListener;
// Timer mTimer;
ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> mFuture;
Paint paintOuterText;
Paint paintCenterText;
Paint paintIndicator;
WheelAdapter adapter;
private String label;//附加单位
int textSize=22;//选项的文字大小
boolean customTextSize;//自定义文字大小为true则用于使setTextSize函数无效只能通过xml修改
int maxTextWidth;
int maxTextHeight;
float itemHeight;//每行高度
int textColorOut;
int textColorCenter;
int dividerColor;
// 条目间距倍数
static final float lineSpacingMultiplier = 1.4F;
boolean isLoop;
// 第一条线Y坐标值
float firstLineY;
//第二条线Y坐标
float secondLineY;
//中间Y坐标
float centerY;
//滚动总高度y值
int totalScrollY;
//初始化默认选中第几个
int initPosition;
//选中的Item是第几个
private int selectedItem;
int preCurrentIndex;
//滚动偏移值,用于记录滚动了多少个item
int change;
// 显示几个条目
int itemsVisible = 11;
int measuredHeight;
int measuredWidth;
// 半圆周长
int halfCircumference;
// 半径
int radius;
private int mOffset = 0;
private float previousY = 0;
long startTime = 0;
// 修改这个值可以改变滑行速度
private static final int VELOCITYFLING = 5;
int widthMeasureSpec;
private int mGravity = Gravity.CENTER;
private int drawCenterContentStart = 0;//中间选中文字开始绘制位置
private int drawOutContentStart = 0;//非中间文字开始绘制位置
private static final float SCALECONTENT = 0.8F;//非中间文字则用此控制高度压扁形成3d错觉
private static final float CENTERCONTENTOFFSET = 6;//中间文字文字居中需要此偏移值
private static final String GETPICKERVIEWTEXT = "getPickerViewText";//反射的方法名
public WheelView(Context context) {
this(context, null);
}
public WheelView(Context context, AttributeSet attrs) {
super(context, attrs);
textColorOut = getResources().getColor(R.color.pickerview_wheelview_textcolor_out);
textColorCenter = getResources().getColor(R.color.pickerview_wheelview_textcolor_center);
dividerColor = getResources().getColor(R.color.pickerview_wheelview_textcolor_divider);
//配合customTextSize使用customTextSize为true才会发挥效果
textSize = getResources().getDimensionPixelSize(R.dimen.pickerview_textsize);
customTextSize = getResources().getBoolean(R.bool.pickerview_customTextSize);
if(attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.wheelview,0,0);
mGravity = a.getInt(R.styleable.wheelview_gravity, Gravity.CENTER);
textColorOut = a.getColor(R.styleable.wheelview_textColorOut, textColorOut);
textColorCenter = a.getColor(R.styleable.wheelview_textColorCenter,textColorCenter);
dividerColor = a.getColor(R.styleable.wheelview_dividerColor,dividerColor);
// textSize = a.getDimensionPixelOffset(R.styleable.wheelview_textSize,textSize);
}
initLoopView(context);
}
private void initLoopView(Context context) {
this.context = context;
handler = new MessageHandler(this);
gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
gestureDetector.setIsLongpressEnabled(false);
isLoop = true;
totalScrollY = 0;
initPosition = -1;
initPaints();
}
private void initPaints() {
paintOuterText = new Paint();
paintOuterText.setColor(textColorOut);
paintOuterText.setAntiAlias(true);
paintOuterText.setTypeface(Typeface.MONOSPACE);
paintOuterText.setTextSize(textSize);
paintCenterText = new Paint();
paintCenterText.setColor(textColorCenter);
paintCenterText.setAntiAlias(true);
paintCenterText.setTextScaleX(1.1F);
paintCenterText.setTypeface(Typeface.MONOSPACE);
paintCenterText.setTextSize(textSize);
paintIndicator = new Paint();
paintIndicator.setColor(dividerColor);
paintIndicator.setAntiAlias(true);
if (android.os.Build.VERSION.SDK_INT >= 11) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
}
private void remeasure() {
if (adapter == null) {
return;
}
measureTextWidthHeight();
//最大Text的高度乘间距倍数得到 可见文字实际的总高度,半圆的周长
halfCircumference = (int) (itemHeight * (itemsVisible - 1)) ;
//整个圆的周长除以PI得到直径这个直径用作控件的总高度
measuredHeight = (int) ((halfCircumference * 2) / Math.PI);
//求出半径
radius = (int) (halfCircumference / Math.PI);
//控件宽度这里支持weight
measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
//计算两条横线和控件中间点的Y位置
firstLineY = (measuredHeight - itemHeight) / 2.0F;
secondLineY = (measuredHeight + itemHeight) / 2.0F;
centerY = (measuredHeight + maxTextHeight) / 2.0F - CENTERCONTENTOFFSET;
//初始化显示的item的position根据是否loop
if (initPosition == -1) {
if (isLoop) {
initPosition = (adapter.getItemsCount() + 1) / 2;
} else {
initPosition = 0;
}
}
preCurrentIndex = initPosition;
}
/**
* 计算最大len的Text的宽高度
*/
private void measureTextWidthHeight() {
Rect rect = new Rect();
for (int i = 0; i < adapter.getItemsCount(); i++) {
String s1 = getContentText(adapter.getItem(i));
paintCenterText.getTextBounds(s1, 0, s1.length(), rect);
int textWidth = rect.width();
if (textWidth > maxTextWidth) {
maxTextWidth = textWidth;
}
paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); // 星期
int textHeight = rect.height();
if (textHeight > maxTextHeight) {
maxTextHeight = textHeight;
}
}
itemHeight = lineSpacingMultiplier * maxTextHeight;
}
void smoothScroll(ACTION action) {
cancelFuture();
if (action== ACTION.FLING||action== ACTION.DAGGLE) {
mOffset = (int) ((totalScrollY%itemHeight + itemHeight) % itemHeight);
if ((float) mOffset > itemHeight / 2.0F) {
mOffset = (int) (itemHeight - (float) mOffset);
} else {
mOffset = -mOffset;
}
}
//停止的时候,位置有偏移,不是全部都能正确停止到中间位置的,这里把文字位置挪回中间去
mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS);
}
protected final void scrollBy(float velocityY) {
cancelFuture();
mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, VELOCITYFLING, TimeUnit.MILLISECONDS);
}
public void cancelFuture() {
if (mFuture!=null&&!mFuture.isCancelled()) {
mFuture.cancel(true);
mFuture = null;
}
}
/**
* 设置是否循环滚动
* @param cyclic
*/
public final void setCyclic(boolean cyclic) {
isLoop = cyclic;
}
public final void setTextSize(float size) {
if (size > 0.0F&&!customTextSize) {
textSize = (int) (context.getResources().getDisplayMetrics().density * size);
paintOuterText.setTextSize(textSize);
paintCenterText.setTextSize(textSize);
}
}
public final void setCurrentItem(int currentItem) {
this.initPosition = currentItem;
totalScrollY = 0;//回归顶部不然重设setCurrentItem的话位置会偏移的就会显示出不对位置的数据
invalidate();
}
public final void setOnItemSelectedListener(OnItemSelectedListener OnItemSelectedListener) {
this.onItemSelectedListener = OnItemSelectedListener;
}
public final void setAdapter(WheelAdapter adapter) {
this.adapter = adapter;
remeasure();
invalidate();
}
public final WheelAdapter getAdapter(){
return adapter;
}
public final int getCurrentItem() {
return selectedItem;
}
protected final void onItemSelected() {
if (onItemSelectedListener != null) {
postDelayed(new OnItemSelectedRunnable(this), 200L);
}
}
@Override
protected void onDraw(Canvas canvas) {
if (adapter == null) {
return;
}
//可见的item数组
Object visibles[] = new Object[itemsVisible];
//滚动的Y值高度除去每行Item的高度得到滚动了多少个item即change数
change = (int) (totalScrollY / itemHeight);
try {
//滚动中实际的预选中的item(即经过了中间位置的item) 滑动前的位置 滑动相对位置
preCurrentIndex = initPosition + change % adapter.getItemsCount();
}catch (ArithmeticException e){
System.out.println("出错了adapter.getItemsCount() == 0联动数据不匹配");
}
if (!isLoop) {//不循环的情况
if (preCurrentIndex < 0) {
preCurrentIndex = 0;
}
if (preCurrentIndex > adapter.getItemsCount() - 1) {
preCurrentIndex = adapter.getItemsCount() - 1;
}
} else {//循环
if (preCurrentIndex < 0) {//举个例子如果总数是5preCurrentIndex 1那么preCurrentIndex按循环来说其实是0的上面也就是4的位置
preCurrentIndex = adapter.getItemsCount() + preCurrentIndex;
}
if (preCurrentIndex > adapter.getItemsCount() - 1) {//同理上面,自己脑补一下
preCurrentIndex = preCurrentIndex - adapter.getItemsCount();
}
}
//跟滚动流畅度有关总滑动距离与每个item高度取余即并不是一格格的滚动每个item不一定滚到对应Rect里的这个item对应格子的偏移值
int itemHeightOffset = (int) (totalScrollY % itemHeight);
// 设置数组中每个元素的值
int counter = 0;
while (counter < itemsVisible) {
int index = preCurrentIndex - (itemsVisible / 2 - counter);//索引值即当前在控件中间的item看作数据源的中间计算出相对源数据源的index值
//判断是否循环如果是循环数据源也使用相对循环的position获取对应的item值如果不是循环则超出数据源范围使用""空白字符串填充在界面上形成空白无数据的item项
if (isLoop) {
index = getLoopMappingIndex(index);
visibles[counter] = adapter.getItem(index);
} else if (index < 0) {
visibles[counter] = "";
} else if (index > adapter.getItemsCount() - 1) {
visibles[counter] = "";
} else {
visibles[counter] = adapter.getItem(index);
}
counter++;
}
//中间两条横线
canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator);
//单位的Label
if(label != null) {
int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText,label);
//靠右并留出空隙
canvas.drawText(label, drawRightContentStart - CENTERCONTENTOFFSET, centerY, paintCenterText);
}
counter = 0;
while (counter < itemsVisible) {
canvas.save();
// L(弧长)=α(弧度)* r(半径) (弧度制)
// 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长)
float itemHeight = maxTextHeight * lineSpacingMultiplier;
double radian = ((itemHeight * counter - itemHeightOffset) * Math.PI) / halfCircumference;
// 弧度转换成角度(把半圆以Y轴为轴心向右转90度使其处于第一象限及第四象限
float angle = (float) (90D - (radian / Math.PI) * 180D);
// 九十度以上的不绘制
if (angle >= 90F || angle <= -90F) {
canvas.restore();
} else {
String contentText = getContentText(visibles[counter]);
//计算开始绘制的位置
measuredCenterContentStart(contentText);
measuredOutContentStart(contentText);
float translateY = (float) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D);
//根据Math.sin(radian)来更改canvas坐标系原点然后缩放画布使得文字高度进行缩放形成弧形3d视觉差
canvas.translate(0.0F, translateY);
canvas.scale(1.0F, (float) Math.sin(radian));
if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) {
// 条目经过第一条线
canvas.save();
canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY);
canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
canvas.restore();
canvas.save();
canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight));
canvas.scale(1.0F, (float) Math.sin(radian) * 1F);
canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText);
canvas.restore();
} else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) {
// 条目经过第二条线
canvas.save();
canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY);
canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F);
canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText);
canvas.restore();
canvas.save();
canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight));
canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
canvas.restore();
} else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) {
// 中间条目
canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText);
int preSelectedItem = adapter.indexOf(visibles[counter]);
if(preSelectedItem != -1){
selectedItem = preSelectedItem;
}
} else {
// 其他条目
canvas.save();
canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
canvas.restore();
}
canvas.restore();
}
counter++;
}
}
//递归计算出对应的index
private int getLoopMappingIndex(int index){
if(index < 0){
index = index + adapter.getItemsCount();
index = getLoopMappingIndex(index);
}
else if (index > adapter.getItemsCount() - 1) {
index = index - adapter.getItemsCount();
index = getLoopMappingIndex(index);
}
return index;
}
/**
* 根据传进来的对象反射出getPickerViewText()方法,来获取需要显示的值
* @param item
* @return
*/
private String getContentText(Object item) {
String contentText = item.toString();
try {
Class<?> clz = item.getClass();
Method m = clz.getMethod(GETPICKERVIEWTEXT);
contentText = m.invoke(item, new Object[0]).toString();
} catch (NoSuchMethodException e) {
} catch (InvocationTargetException e) {
} catch (IllegalAccessException e) {
} catch (Exception e){
}
return contentText;
}
private void measuredCenterContentStart(String content) {
Rect rect = new Rect();
paintCenterText.getTextBounds(content, 0, content.length(), rect);
switch (mGravity){
case Gravity.CENTER:
drawCenterContentStart = (int)((measuredWidth - rect.width()) * 0.5);
break;
case Gravity.LEFT:
drawCenterContentStart = 0;
break;
case Gravity.RIGHT:
drawCenterContentStart = measuredWidth - rect.width();
break;
}
}
private void measuredOutContentStart(String content) {
Rect rect = new Rect();
paintOuterText.getTextBounds(content, 0, content.length(), rect);
switch (mGravity){
case Gravity.CENTER:
drawOutContentStart = (int)((measuredWidth - rect.width()) * 0.5);
break;
case Gravity.LEFT:
drawOutContentStart = 0;
break;
case Gravity.RIGHT:
drawOutContentStart = measuredWidth - rect.width();
break;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
this.widthMeasureSpec = widthMeasureSpec;
remeasure();
setMeasuredDimension(measuredWidth, measuredHeight);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean eventConsumed = gestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTime = System.currentTimeMillis();
cancelFuture();
previousY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float dy = previousY - event.getRawY();
previousY = event.getRawY();
totalScrollY = (int) (totalScrollY + dy);
// 边界处理。
if (!isLoop) {
float top = -initPosition * itemHeight;
float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight;
if(totalScrollY - itemHeight*0.3 < top){
top = totalScrollY - dy;
}
else if(totalScrollY + itemHeight*0.3 > bottom){
bottom = totalScrollY - dy;
}
if (totalScrollY < top) {
totalScrollY = (int) top;
} else if (totalScrollY > bottom) {
totalScrollY = (int) bottom;
}
}
break;
case MotionEvent.ACTION_UP:
default:
if (!eventConsumed) {
float y = event.getY();
double l = Math.acos((radius - y) / radius) * radius;
int circlePosition = (int) ((l + itemHeight / 2) / itemHeight);
float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight;
mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset);
if ((System.currentTimeMillis() - startTime) > 120) {
// 处理拖拽事件
smoothScroll(ACTION.DAGGLE);
} else {
// 处理条目点击事件
smoothScroll(ACTION.CLICK);
}
}
break;
}
invalidate();
return true;
}
/**
* 获取Item个数
* @return
*/
public int getItemsCount() {
return adapter != null ? adapter.getItemsCount() : 0;
}
/**
* 附加在右边的单位字符串
* @param label
*/
public void setLabel(String label){
this.label = label;
}
public void setGravity(int gravity) {
this.mGravity = gravity;
}
public int getTextWidth(Paint paint, String str) {
int iRet = 0;
if (str != null && str.length() > 0) {
int len = str.length();
float[] widths = new float[len];
paint.getTextWidths(str, widths);
for (int j = 0; j < len; j++) {
iRet += (int) Math.ceil(widths[j]);
}
}
return iRet;
}
}

View File

@@ -0,0 +1,8 @@
package com.bigkoo.pickerview.listener;
/**
* Created by Sai on 15/8/9.
*/
public interface OnDismissListener {
public void onDismiss(Object o);
}

View File

@@ -0,0 +1,6 @@
package com.bigkoo.pickerview.listener;
public interface OnItemSelectedListener {
void onItemSelected(int index);
}

View File

@@ -0,0 +1,26 @@
package com.bigkoo.pickerview.utils;
import android.view.Gravity;
import com.bigkoo.pickerview.R;
/**
* Created by Sai on 15/8/9.
*/
public class PickerViewAnimateUtil {
private static final int INVALID = -1;
/**
* Get default animation resource when not defined by the user
*
* @param gravity the gravity of the dialog
* @param isInAnimation determine if is in or out animation. true when is is
* @return the id of the animation resource
*/
public static int getAnimationResource(int gravity, boolean isInAnimation) {
switch (gravity) {
case Gravity.BOTTOM:
return isInAnimation ? R.anim.slide_in_bottom : R.anim.slide_out_bottom;
}
return INVALID;
}
}

View File

@@ -0,0 +1,168 @@
package com.bigkoo.pickerview.view;
import android.app.Activity;
import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import com.bigkoo.pickerview.utils.PickerViewAnimateUtil;
import com.bigkoo.pickerview.R;
import com.bigkoo.pickerview.listener.OnDismissListener;
/**
* Created by Sai on 15/11/22.
* 精仿iOSPickerViewController控件
*/
public class BasePickerView {
private final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM
);
private Context context;
protected ViewGroup contentContainer;
private ViewGroup decorView;//activity的根View
private ViewGroup rootView;//附加View 的 根View
private OnDismissListener onDismissListener;
private boolean isDismissing;
private Animation outAnim;
private Animation inAnim;
private int gravity = Gravity.BOTTOM;
public BasePickerView(Context context){
this.context = context;
initViews();
init();
initEvents();
}
protected void initViews(){
LayoutInflater layoutInflater = LayoutInflater.from(context);
decorView = (ViewGroup) ((Activity)context).getWindow().getDecorView().findViewById(android.R.id.content);
rootView = (ViewGroup) layoutInflater.inflate(R.layout.layout_basepickerview, decorView, false);
rootView.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
));
contentContainer = (ViewGroup) rootView.findViewById(R.id.content_container);
contentContainer.setLayoutParams(params);
}
protected void init() {
inAnim = getInAnimation();
outAnim = getOutAnimation();
}
protected void initEvents() {
}
/**
* show的时候调用
*
* @param view 这个View
*/
private void onAttached(View view) {
decorView.addView(view);
contentContainer.startAnimation(inAnim);
}
/**
* 添加这个View到Activity的根视图
*/
public void show() {
if (isShowing()) {
return;
}
onAttached(rootView);
}
/**
* 检测该View是不是已经添加到根视图
*
* @return 如果视图已经存在该View返回true
*/
public boolean isShowing() {
View view = decorView.findViewById(R.id.outmost_container);
return view != null;
}
public void dismiss() {
if (isDismissing) {
return;
}
//消失动画
outAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
decorView.post(new Runnable() {
@Override
public void run() {
//从activity根视图移除
decorView.removeView(rootView);
isDismissing = false;
if (onDismissListener != null) {
onDismissListener.onDismiss(BasePickerView.this);
}
}
});
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
contentContainer.startAnimation(outAnim);
isDismissing = true;
}
public Animation getInAnimation() {
int res = PickerViewAnimateUtil.getAnimationResource(this.gravity, true);
return AnimationUtils.loadAnimation(context, res);
}
public Animation getOutAnimation() {
int res = PickerViewAnimateUtil.getAnimationResource(this.gravity, false);
return AnimationUtils.loadAnimation(context, res);
}
public BasePickerView setOnDismissListener(OnDismissListener onDismissListener) {
this.onDismissListener = onDismissListener;
return this;
}
public BasePickerView setCancelable(boolean isCancelable) {
View view = rootView.findViewById(R.id.outmost_container);
if (isCancelable) {
view.setOnTouchListener(onCancelableTouchListener);
}
else{
view.setOnTouchListener(null);
}
return this;
}
/**
* Called when the user touch on black overlay in order to dismiss the dialog
*/
private final View.OnTouchListener onCancelableTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
dismiss();
}
return false;
}
};
public View findViewById(int id){
return contentContainer.findViewById(id);
}
}

View File

@@ -0,0 +1,226 @@
package com.bigkoo.pickerview.view;
import java.util.ArrayList;
import android.view.View;
import com.bigkoo.pickerview.R;
import com.bigkoo.pickerview.adapter.ArrayWheelAdapter;
import com.bigkoo.pickerview.lib.WheelView;
import com.bigkoo.pickerview.listener.OnItemSelectedListener;
public class WheelOptions<T> {
private View view;
private WheelView wv_option1;
private WheelView wv_option2;
private WheelView wv_option3;
private ArrayList<T> mOptions1Items;
private ArrayList<ArrayList<T>> mOptions2Items;
private ArrayList<ArrayList<ArrayList<T>>> mOptions3Items;
private boolean linkage = false;
private OnItemSelectedListener wheelListener_option1;
private OnItemSelectedListener wheelListener_option2;
public View getView() {
return view;
}
public void setView(View view) {
this.view = view;
}
public WheelOptions(View view) {
super();
this.view = view;
setView(view);
}
public void setPicker(ArrayList<T> optionsItems) {
setPicker(optionsItems, null, null, false);
}
public void setPicker(ArrayList<T> options1Items,
ArrayList<ArrayList<T>> options2Items, boolean linkage) {
setPicker(options1Items, options2Items, null, linkage);
}
public void setPicker(ArrayList<T> options1Items,
ArrayList<ArrayList<T>> options2Items,
ArrayList<ArrayList<ArrayList<T>>> options3Items,
boolean linkage) {
this.linkage = linkage;
this.mOptions1Items = options1Items;
this.mOptions2Items = options2Items;
this.mOptions3Items = options3Items;
int len = ArrayWheelAdapter.DEFAULT_LENGTH;
if (this.mOptions3Items == null)
len = 8;
if (this.mOptions2Items == null)
len = 12;
// 选项1
wv_option1 = (WheelView) view.findViewById(R.id.options1);
wv_option1.setAdapter(new ArrayWheelAdapter(mOptions1Items, len));// 设置显示数据
wv_option1.setCurrentItem(0);// 初始化时显示的数据
// 选项2
wv_option2 = (WheelView) view.findViewById(R.id.options2);
if (mOptions2Items != null)
wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items.get(0)));// 设置显示数据
wv_option2.setCurrentItem(wv_option1.getCurrentItem());// 初始化时显示的数据
// 选项3
wv_option3 = (WheelView) view.findViewById(R.id.options3);
if (mOptions3Items != null)
wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items.get(0)
.get(0)));// 设置显示数据
wv_option3.setCurrentItem(wv_option3.getCurrentItem());// 初始化时显示的数据
int textSize = 22;
wv_option1.setTextSize(textSize);
wv_option2.setTextSize(textSize);
wv_option3.setTextSize(textSize);
if (this.mOptions2Items == null)
wv_option2.setVisibility(View.GONE);
if (this.mOptions3Items == null)
wv_option3.setVisibility(View.GONE);
// 联动监听器
wheelListener_option1 = new OnItemSelectedListener() {
@Override
public void onItemSelected(int index) {
int opt2Select = 0;
if (mOptions2Items != null) {
opt2Select = wv_option2.getCurrentItem();//上一个opt2的选中位置
//新opt2的位置判断如果旧位置没有超过数据范围则沿用旧位置否则选中最后一项
opt2Select = opt2Select >= mOptions2Items.get(index).size() - 1 ? mOptions2Items.get(index).size() - 1 : opt2Select;
wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items
.get(index)));
wv_option2.setCurrentItem(opt2Select);
}
if (mOptions3Items != null) {
wheelListener_option2.onItemSelected(opt2Select);
}
}
};
wheelListener_option2 = new OnItemSelectedListener() {
@Override
public void onItemSelected(int index) {
if (mOptions3Items != null) {
int opt1Select = wv_option1.getCurrentItem();
opt1Select = opt1Select >= mOptions3Items.size() - 1 ? mOptions3Items.size() - 1 : opt1Select;
index = index >= mOptions2Items.get(opt1Select).size() - 1 ? mOptions2Items.get(opt1Select).size() - 1 : index;
int opt3 = wv_option3.getCurrentItem();//上一个opt3的选中位置
//新opt3的位置判断如果旧位置没有超过数据范围则沿用旧位置否则选中最后一项
opt3 = opt3 >= mOptions3Items.get(opt1Select).get(index).size() - 1 ? mOptions3Items.get(opt1Select).get(index).size() - 1 : opt3;
wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items
.get(wv_option1.getCurrentItem()).get(
index)));
wv_option3.setCurrentItem(opt3);
}
}
};
// // 添加联动监听
if (options2Items != null && linkage)
wv_option1.setOnItemSelectedListener(wheelListener_option1);
if (options3Items != null && linkage)
wv_option2.setOnItemSelectedListener(wheelListener_option2);
}
/**
* 设置选项的单位
*
* @param label1
* @param label2
* @param label3
*/
public void setLabels(String label1, String label2, String label3) {
if (label1 != null)
wv_option1.setLabel(label1);
if (label2 != null)
wv_option2.setLabel(label2);
if (label3 != null)
wv_option3.setLabel(label3);
}
/**
* 设置是否循环滚动
*
* @param cyclic
*/
public void setCyclic(boolean cyclic) {
wv_option1.setCyclic(cyclic);
wv_option2.setCyclic(cyclic);
wv_option3.setCyclic(cyclic);
}
/**
* 分别设置第一二三级是否循环滚动
*
* @param cyclic1,cyclic2,cyclic3
*/
public void setCyclic(boolean cyclic1,boolean cyclic2,boolean cyclic3) {
wv_option1.setCyclic(cyclic1);
wv_option2.setCyclic(cyclic2);
wv_option3.setCyclic(cyclic3);
}
/**
* 设置第二级是否循环滚动
*
* @param cyclic
*/
public void setOption2Cyclic(boolean cyclic) {
wv_option2.setCyclic(cyclic);
}
/**
* 设置第三级是否循环滚动
*
* @param cyclic
*/
public void setOption3Cyclic(boolean cyclic) {
wv_option3.setCyclic(cyclic);
}
/**
* 返回当前选中的结果对应的位置数组 因为支持三级联动效果分三个级别索引012
*
* @return
*/
public int[] getCurrentItems() {
int[] currentItems = new int[3];
currentItems[0] = wv_option1.getCurrentItem();
currentItems[1] = wv_option2.getCurrentItem();
currentItems[2] = wv_option3.getCurrentItem();
return currentItems;
}
public void setCurrentItems(int option1, int option2, int option3) {
if(linkage){
itemSelected(option1, option2, option3);
}
wv_option1.setCurrentItem(option1);
wv_option2.setCurrentItem(option2);
wv_option3.setCurrentItem(option3);
}
private void itemSelected(int opt1Select, int opt2Select, int opt3Select) {
if (mOptions2Items != null) {
wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items
.get(opt1Select)));
wv_option2.setCurrentItem(opt2Select);
}
if (mOptions3Items != null) {
wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items
.get(opt1Select).get(
opt2Select)));
wv_option3.setCurrentItem(opt3Select);
}
}
}

View File

@@ -0,0 +1,247 @@
package com.bigkoo.pickerview.view;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import com.bigkoo.pickerview.R;
import com.bigkoo.pickerview.TimePickerView.Type;
import com.bigkoo.pickerview.adapter.NumericWheelAdapter;
import com.bigkoo.pickerview.lib.WheelView;
import com.bigkoo.pickerview.listener.OnItemSelectedListener;
import android.content.Context;
import android.view.View;
public class WheelTime {
public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
private View view;
private WheelView wv_year;
private WheelView wv_month;
private WheelView wv_day;
private WheelView wv_hours;
private WheelView wv_mins;
private Type type;
public static final int DEFULT_START_YEAR = 1990;
public static final int DEFULT_END_YEAR = 2100;
private int startYear = DEFULT_START_YEAR;
private int endYear = DEFULT_END_YEAR;
public WheelTime(View view) {
super();
this.view = view;
type = Type.ALL;
setView(view);
}
public WheelTime(View view,Type type) {
super();
this.view = view;
this.type = type;
setView(view);
}
public void setPicker(int year ,int month,int day){
this.setPicker(year, month, day, 0, 0);
}
/**
* @Description: TODO 弹出日期时间选择器
*/
public void setPicker(int year ,int month ,int day,int h,int m) {
// 添加大小月月份并将其转换为list,方便之后的判断
String[] months_big = { "1", "3", "5", "7", "8", "10", "12" };
String[] months_little = { "4", "6", "9", "11" };
final List<String> list_big = Arrays.asList(months_big);
final List<String> list_little = Arrays.asList(months_little);
Context context = view.getContext();
// 年
wv_year = (WheelView) view.findViewById(R.id.year);
wv_year.setAdapter(new NumericWheelAdapter(startYear, endYear));// 设置"年"的显示数据
wv_year.setLabel(context.getString(R.string.pickerview_year));// 添加文字
wv_year.setCurrentItem(year - startYear);// 初始化时显示的数据
// 月
wv_month = (WheelView) view.findViewById(R.id.month);
wv_month.setAdapter(new NumericWheelAdapter(1, 12));
wv_month.setLabel(context.getString(R.string.pickerview_month));
wv_month.setCurrentItem(month);
// 日
wv_day = (WheelView) view.findViewById(R.id.day);
// 判断大小月及是否闰年,用来确定"日"的数据
if (list_big.contains(String.valueOf(month + 1))) {
wv_day.setAdapter(new NumericWheelAdapter(1, 31));
} else if (list_little.contains(String.valueOf(month + 1))) {
wv_day.setAdapter(new NumericWheelAdapter(1, 30));
} else {
// 闰年
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
wv_day.setAdapter(new NumericWheelAdapter(1, 29));
else
wv_day.setAdapter(new NumericWheelAdapter(1, 28));
}
wv_day.setLabel(context.getString(R.string.pickerview_day));
wv_day.setCurrentItem(day - 1);
wv_hours = (WheelView)view.findViewById(R.id.hour);
wv_hours.setAdapter(new NumericWheelAdapter(0, 23));
wv_hours.setLabel(context.getString(R.string.pickerview_hours));// 添加文字
wv_hours.setCurrentItem(h);
wv_mins = (WheelView)view.findViewById(R.id.min);
wv_mins.setAdapter(new NumericWheelAdapter(0, 59));
wv_mins.setLabel(context.getString(R.string.pickerview_minutes));// 添加文字
wv_mins.setCurrentItem(m);
// 添加"年"监听
OnItemSelectedListener wheelListener_year = new OnItemSelectedListener() {
@Override
public void onItemSelected(int index) {
int year_num = index + startYear;
// 判断大小月及是否闰年,用来确定"日"的数据
int maxItem = 30;
if (list_big
.contains(String.valueOf(wv_month.getCurrentItem() + 1))) {
wv_day.setAdapter(new NumericWheelAdapter(1, 31));
maxItem = 31;
} else if (list_little.contains(String.valueOf(wv_month
.getCurrentItem() + 1))) {
wv_day.setAdapter(new NumericWheelAdapter(1, 30));
maxItem = 30;
} else {
if ((year_num % 4 == 0 && year_num % 100 != 0)
|| year_num % 400 == 0){
wv_day.setAdapter(new NumericWheelAdapter(1, 29));
maxItem = 29;
}
else{
wv_day.setAdapter(new NumericWheelAdapter(1, 28));
maxItem = 28;
}
}
if (wv_day.getCurrentItem() > maxItem - 1){
wv_day.setCurrentItem(maxItem - 1);
}
}
};
// 添加"月"监听
OnItemSelectedListener wheelListener_month = new OnItemSelectedListener() {
@Override
public void onItemSelected(int index) {
int month_num = index + 1;
int maxItem = 30;
// 判断大小月及是否闰年,用来确定"日"的数据
if (list_big.contains(String.valueOf(month_num))) {
wv_day.setAdapter(new NumericWheelAdapter(1, 31));
maxItem = 31;
} else if (list_little.contains(String.valueOf(month_num))) {
wv_day.setAdapter(new NumericWheelAdapter(1, 30));
maxItem = 30;
} else {
if (((wv_year.getCurrentItem() + startYear) % 4 == 0 && (wv_year
.getCurrentItem() + startYear) % 100 != 0)
|| (wv_year.getCurrentItem() + startYear) % 400 == 0){
wv_day.setAdapter(new NumericWheelAdapter(1, 29));
maxItem = 29;
}
else{
wv_day.setAdapter(new NumericWheelAdapter(1, 28));
maxItem = 28;
}
}
if (wv_day.getCurrentItem() > maxItem - 1){
wv_day.setCurrentItem(maxItem - 1);
}
}
};
wv_year.setOnItemSelectedListener(wheelListener_year);
wv_month.setOnItemSelectedListener(wheelListener_month);
// 根据屏幕密度来指定选择器字体的大小(不同屏幕可能不同)
int textSize = 6;
switch(type){
case ALL:
textSize = textSize * 3;
break;
case YEAR_MONTH_DAY:
textSize = textSize * 4;
wv_hours.setVisibility(View.GONE);
wv_mins.setVisibility(View.GONE);
break;
case HOURS_MINS:
textSize = textSize * 4;
wv_year.setVisibility(View.GONE);
wv_month.setVisibility(View.GONE);
wv_day.setVisibility(View.GONE);
break;
case MONTH_DAY_HOUR_MIN:
textSize = textSize * 3;
wv_year.setVisibility(View.GONE);
break;
case YEAR_MONTH:
textSize = textSize * 4;
wv_day.setVisibility(View.GONE);
wv_hours.setVisibility(View.GONE);
wv_mins.setVisibility(View.GONE);
}
wv_day.setTextSize(textSize);
wv_month.setTextSize(textSize);
wv_year.setTextSize(textSize);
wv_hours.setTextSize(textSize);
wv_mins.setTextSize(textSize);
}
/**
* 设置是否循环滚动
* @param cyclic
*/
public void setCyclic(boolean cyclic){
wv_year.setCyclic(cyclic);
wv_month.setCyclic(cyclic);
wv_day.setCyclic(cyclic);
wv_hours.setCyclic(cyclic);
wv_mins.setCyclic(cyclic);
}
public String getTime() {
StringBuffer sb = new StringBuffer();
sb.append((wv_year.getCurrentItem() + startYear)).append("-")
.append((wv_month.getCurrentItem() + 1)).append("-")
.append((wv_day.getCurrentItem() + 1)).append(" ")
.append(wv_hours.getCurrentItem()).append(":")
.append(wv_mins.getCurrentItem());
return sb.toString();
}
public View getView() {
return view;
}
public void setView(View view) {
this.view = view;
}
public int getStartYear() {
return startYear;
}
public void setStartYear(int startYear) {
this.startYear = startYear;
}
public int getEndYear() {
return endYear;
}
public void setEndYear(int endYear) {
this.endYear = endYear;
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="@integer/animation_default_duration"
android:fromXDelta="0%"
android:toXDelta="0%"
android:fromYDelta="100%"
android:toYDelta="0%"/>
</set>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="@integer/animation_default_duration"
android:fromXDelta="0%"
android:toXDelta="0%"
android:fromYDelta="0%"
android:toYDelta="100%"/>
</set>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/pickerview_timebtn_pre" />
<item android:color="@color/pickerview_timebtn_nor" />
</selector>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/pickerview_bg_topbar">
<Button
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@android:color/transparent"
android:gravity="center_vertical|left"
android:paddingLeft="@dimen/pickerview_topbar_paddingleft"
android:paddingRight="@dimen/pickerview_topbar_paddingright"
android:textColor="#666666"
android:text="@string/pickerview_cancel"
android:textSize="@dimen/pickerview_topbar_btn_textsize" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="fill_parent"
android:gravity="center"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/btnSubmit"
android:layout_toRightOf="@+id/btnCancel"
android:textSize="@dimen/pickerview_topbar_title_textsize" />
<Button
android:id="@id/btnSubmit"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:background="@android:color/transparent"
android:gravity="center_vertical|left"
android:paddingLeft="@dimen/pickerview_topbar_paddingright"
android:textColor="@android:color/holo_red_light"
android:paddingRight="@dimen/pickerview_topbar_paddingleft"
android:text="@string/pickerview_submit"
android:textSize="@dimen/pickerview_topbar_btn_textsize" />
</RelativeLayout>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/outmost_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:background="@color/bgColor_overlay">
<FrameLayout
android:id="@+id/content_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</FrameLayout>
</FrameLayout>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
layout="@layout/include_pickerview_topbar"
android:layout_width="match_parent"
android:layout_height="@dimen/pickerview_topbar_height" />
<LinearLayout
android:id="@+id/optionspicker"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="horizontal">
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/options1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/options2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/options3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
layout="@layout/include_pickerview_topbar"
android:layout_width="match_parent"
android:layout_height="@dimen/pickerview_topbar_height"/>
<LinearLayout
android:id="@+id/timepicker"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="horizontal">
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/year"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/month"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.1" />
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/day"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.1" />
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/hour"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.1" />
<com.bigkoo.pickerview.lib.WheelView
android:id="@+id/min"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.1" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="wheelview">
<attr name="gravity">
<enum name="center" value="17"/>
<enum name="left" value="3"/>
<enum name="right" value="5"/>
</attr>
<attr name="textColorOut" format="color"/>
<attr name="textColorCenter" format="color"/>
<attr name="dividerColor" format="color"/>
</declare-styleable>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 此值为true pickerview_textsize才会有效果-->
<bool name="pickerview_customTextSize">false</bool>
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="pickerview_timebtn_nor">#057dff</color>
<color name="pickerview_timebtn_pre">#c2daf5</color>
<color name="pickerview_bg_topbar">#f5f5f5</color>
<color name="pickerview_topbar_title">#000000</color>
<color name="pickerview_wheelview_textcolor_out">#ABABAB</color>
<color name="pickerview_wheelview_textcolor_center">#666666</color>
<color name="pickerview_wheelview_textcolor_divider">#d5d5d5</color>
<!--覆盖颜色-->
<color name="bgColor_overlay">#60000000</color>
</resources>

View File

@@ -0,0 +1,13 @@
<resources>
<!-- 顶部按钮栏高度 -->
<dimen name="pickerview_topbar_height">44dp</dimen>
<!-- 顶部按钮paddingLeft -->
<dimen name="pickerview_topbar_paddingleft">10dp</dimen>
<!-- 顶部按钮paddingRight -->
<dimen name="pickerview_topbar_paddingright">30dp</dimen>
<!-- 顶部按钮文字大小 -->
<dimen name="pickerview_topbar_btn_textsize">20sp</dimen>
<dimen name="pickerview_topbar_title_textsize">20sp</dimen>
<!-- 选项文字大小 需要配合 bools.xml 的 pickerview_customTextSize 为 true 才有效果-->
<dimen name="pickerview_textsize">20sp</dimen>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="animation_default_duration">300</integer>
</resources>

View File

@@ -0,0 +1,11 @@
<resources>
<string name="pickerview_cancel">取消</string>
<string name="pickerview_submit">确定</string>
<string name="pickerview_year"></string>
<string name="pickerview_month"></string>
<string name="pickerview_day"></string>
<string name="pickerview_hours"></string>
<string name="pickerview_minutes"></string>
<string name="pickerview_seconds"></string>
</resources>

BIN
preview/pickerdemo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
preview/pickerdemo1x.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 KiB

1
settings.gradle Normal file
View File

@@ -0,0 +1 @@
include ':app', ':pickerview'