commit 801b889b14547091137d1fdb83bed5a65433ab79 Author: linglongxin24 Date: Tue May 31 16:53:19 2016 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fa6bf0 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f05aaf --- /dev/null +++ b/README.md @@ -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 + - 外观大整改
+ - 支持反射获取getPickerViewText()来获取要展示数据,以前只能传String的对象,现在可以传任意对象只要有getPickerViewText()函数即可显示对应的字符串,如果没有getPickerViewText()函数则使用对象toString作为显示
+ - 加入setTitle
+ +>v2.0.1 + - 去掉popupWindow,改用View,类名也对应修改为TimePickerView和 OptionsPickerView
+ - 加入遮罩效果
+ +>v2.0.2 + - 修复不循环模式下点击空白item处出现数组越界问题
+ - 修复循环模式下只有一条数据的时候只显示三条而不是填充满高度问题
+ +>v2.0.3 + - 修复时间选择的时候部分数字选不到直接跳到下一个数字的问题
+ +>v2.0.4 + - 修复不循环模式下顶部超出范围问题
+ - wheel view文字颜色通过xml配置
+ +>v2.0.5 + - 修复不循环模式下底部超出范围问题
+ +>v2.0.6 + - 修复不循环模式下点击超出范围问题,修复后点击空白的地方,只能滚到最顶或最底,不会滚出数据范围。
+ +>v2.0.7 + - 修复设置初始化position ,第三级数据不对的BUG。
+ +>v2.0.8 + - 修复#41 未选中项有错乱数据问题。
+ - 加入pickerview_customTextSize 和 pickerview_textsize 到 xml 中 来控制自定义文字大小
+ +---------------------华丽丽的分割线-------------------------- + +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/) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..c708bb4 --- /dev/null +++ b/app/build.gradle @@ -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' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..71a43e0 --- /dev/null +++ b/app/proguard-rules.pro @@ -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 *; +#} diff --git a/app/src/androidTest/java/com/bigkoo/pickerviewdemo/ApplicationTest.java b/app/src/androidTest/java/com/bigkoo/pickerviewdemo/ApplicationTest.java new file mode 100644 index 0000000..d1aee99 --- /dev/null +++ b/app/src/androidTest/java/com/bigkoo/pickerviewdemo/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.bigkoo.pickerviewdemo; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cc810d6 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/bigkoo/pickerviewdemo/MainActivity.java b/app/src/main/java/com/bigkoo/pickerviewdemo/MainActivity.java new file mode 100644 index 0000000..bddd930 --- /dev/null +++ b/app/src/main/java/com/bigkoo/pickerviewdemo/MainActivity.java @@ -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 options1Items = new ArrayList(); + private ArrayList> options2Items = new ArrayList>(); + private ArrayList>> options3Items = new ArrayList>>(); + 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 options2Items_01=new ArrayList(); + options2Items_01.add("广州"); + options2Items_01.add("佛山"); + options2Items_01.add("东莞"); + options2Items_01.add("阳江"); + options2Items_01.add("珠海"); + ArrayList options2Items_02=new ArrayList(); + options2Items_02.add("长沙"); + options2Items_02.add("岳阳"); + ArrayList options2Items_03=new ArrayList(); + options2Items_03.add("桂林"); + options2Items.add(options2Items_01); + options2Items.add(options2Items_02); + options2Items.add(options2Items_03); + + //选项3 + ArrayList> options3Items_01 = new ArrayList>(); + ArrayList> options3Items_02 = new ArrayList>(); + ArrayList> options3Items_03 = new ArrayList>(); + ArrayList options3Items_01_01=new ArrayList(); + options3Items_01_01.add("白云"); + options3Items_01_01.add("天河"); + options3Items_01_01.add("海珠"); + options3Items_01_01.add("越秀"); + options3Items_01.add(options3Items_01_01); + ArrayList options3Items_01_02=new ArrayList(); + options3Items_01_02.add("南海"); + options3Items_01_02.add("高明"); + options3Items_01_02.add("顺德"); + options3Items_01_02.add("禅城"); + options3Items_01.add(options3Items_01_02); + ArrayList options3Items_01_03=new ArrayList(); + options3Items_01_03.add("其他"); + options3Items_01_03.add("常平"); + options3Items_01_03.add("虎门"); + options3Items_01.add(options3Items_01_03); + ArrayList options3Items_01_04=new ArrayList(); + options3Items_01_04.add("其他1"); + options3Items_01_04.add("其他2"); + options3Items_01_04.add("其他3"); + options3Items_01.add(options3Items_01_04); + ArrayList options3Items_01_05=new ArrayList(); + options3Items_01_05.add("其他1"); + options3Items_01_05.add("其他2"); + options3Items_01_05.add("其他3"); + options3Items_01.add(options3Items_01_05); + + ArrayList options3Items_02_01=new ArrayList(); + 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 options3Items_02_02=new ArrayList(); + 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 options3Items_03_01=new ArrayList(); + 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); + } + +} diff --git a/app/src/main/java/com/bigkoo/pickerviewdemo/bean/ProvinceBean.java b/app/src/main/java/com/bigkoo/pickerviewdemo/bean/ProvinceBean.java new file mode 100644 index 0000000..23da4f5 --- /dev/null +++ b/app/src/main/java/com/bigkoo/pickerviewdemo/bean/ProvinceBean.java @@ -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; + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..4df1894 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..92ad457 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..b1cb908 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,6 @@ + + + diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..37e0ae1 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + PickerViewDemo + Hello world! + Settings + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..766ab99 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..c1b18e7 --- /dev/null +++ b/build.gradle @@ -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() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1d3591c --- /dev/null +++ b/gradle.properties @@ -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 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8c0fb64 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..720f710 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -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 "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -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 diff --git a/pickerview/.gitignore b/pickerview/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/pickerview/.gitignore @@ -0,0 +1 @@ +/build diff --git a/pickerview/build.gradle b/pickerview/build.gradle new file mode 100644 index 0000000..6540bd8 --- /dev/null +++ b/pickerview/build.gradle @@ -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 +// } +//} \ No newline at end of file diff --git a/pickerview/proguard-rules.pro b/pickerview/proguard-rules.pro new file mode 100644 index 0000000..71a43e0 --- /dev/null +++ b/pickerview/proguard-rules.pro @@ -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 *; +#} diff --git a/pickerview/src/androidTest/java/com/bigkoo/pickerview/ApplicationTest.java b/pickerview/src/androidTest/java/com/bigkoo/pickerview/ApplicationTest.java new file mode 100644 index 0000000..542bc94 --- /dev/null +++ b/pickerview/src/androidTest/java/com/bigkoo/pickerview/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.bigkoo.pickerview; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/pickerview/src/main/AndroidManifest.xml b/pickerview/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9d55db2 --- /dev/null +++ b/pickerview/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/OptionsPickerView.java b/pickerview/src/main/java/com/bigkoo/pickerview/OptionsPickerView.java new file mode 100644 index 0000000..1c48e02 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/OptionsPickerView.java @@ -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 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 optionsItems) { + wheelOptions.setPicker(optionsItems, null, null, false); + } + + public void setPicker(ArrayList options1Items, + ArrayList> options2Items, boolean linkage) { + wheelOptions.setPicker(options1Items, options2Items, null, linkage); + } + + public void setPicker(ArrayList options1Items, + ArrayList> options2Items, + ArrayList>> 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); + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/TimePickerView.java b/pickerview/src/main/java/com/bigkoo/pickerview/TimePickerView.java new file mode 100644 index 0000000..0beef4f --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/TimePickerView.java @@ -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); + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/adapter/ArrayWheelAdapter.java b/pickerview/src/main/java/com/bigkoo/pickerview/adapter/ArrayWheelAdapter.java new file mode 100644 index 0000000..0c642b7 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/adapter/ArrayWheelAdapter.java @@ -0,0 +1,55 @@ +package com.bigkoo.pickerview.adapter; + +import java.util.ArrayList; + +/** + * The simple Array wheel adapter + * @param the element type + */ +public class ArrayWheelAdapter implements WheelAdapter { + + /** The default items length */ + public static final int DEFAULT_LENGTH = 4; + + // items + private ArrayList items; + // length + private int length; + + /** + * Constructor + * @param items the items + * @param length the max items length + */ + public ArrayWheelAdapter(ArrayList items, int length) { + this.items = items; + this.length = length; + } + + /** + * Contructor + * @param items the items + */ + public ArrayWheelAdapter(ArrayList 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); + } + +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/adapter/NumericWheelAdapter.java b/pickerview/src/main/java/com/bigkoo/pickerview/adapter/NumericWheelAdapter.java new file mode 100644 index 0000000..d56e1ed --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/adapter/NumericWheelAdapter.java @@ -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; + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/adapter/WheelAdapter.java b/pickerview/src/main/java/com/bigkoo/pickerview/adapter/WheelAdapter.java new file mode 100644 index 0000000..a403553 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/adapter/WheelAdapter.java @@ -0,0 +1,25 @@ +package com.bigkoo.pickerview.adapter; + +public interface WheelAdapter { + /** + * 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); +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/lib/InertiaTimerTask.java b/pickerview/src/main/java/com/bigkoo/pickerview/lib/InertiaTimerTask.java new file mode 100644 index 0000000..f54366f --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/lib/InertiaTimerTask.java @@ -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); + } + +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/lib/LoopViewGestureListener.java b/pickerview/src/main/java/com/bigkoo/pickerview/lib/LoopViewGestureListener.java new file mode 100644 index 0000000..eb50e44 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/lib/LoopViewGestureListener.java @@ -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; + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/lib/MessageHandler.java b/pickerview/src/main/java/com/bigkoo/pickerview/lib/MessageHandler.java new file mode 100644 index 0000000..ce53ea4 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/lib/MessageHandler.java @@ -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; + } + } + +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/lib/OnItemSelectedRunnable.java b/pickerview/src/main/java/com/bigkoo/pickerview/lib/OnItemSelectedRunnable.java new file mode 100644 index 0000000..678a1b0 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/lib/OnItemSelectedRunnable.java @@ -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()); + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/lib/SmoothScrollTimerTask.java b/pickerview/src/main/java/com/bigkoo/pickerview/lib/SmoothScrollTimerTask.java new file mode 100644 index 0000000..36c752a --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/lib/SmoothScrollTimerTask.java @@ -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; + } + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/lib/WheelView.java b/pickerview/src/main/java/com/bigkoo/pickerview/lib/WheelView.java new file mode 100644 index 0000000..cac403f --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/lib/WheelView.java @@ -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) {//举个例子:如果总数是5,preCurrentIndex = -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; + } +} \ No newline at end of file diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/listener/OnDismissListener.java b/pickerview/src/main/java/com/bigkoo/pickerview/listener/OnDismissListener.java new file mode 100644 index 0000000..a56a1c5 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/listener/OnDismissListener.java @@ -0,0 +1,8 @@ +package com.bigkoo.pickerview.listener; + +/** + * Created by Sai on 15/8/9. + */ +public interface OnDismissListener { + public void onDismiss(Object o); +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/listener/OnItemSelectedListener.java b/pickerview/src/main/java/com/bigkoo/pickerview/listener/OnItemSelectedListener.java new file mode 100644 index 0000000..65daa28 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/listener/OnItemSelectedListener.java @@ -0,0 +1,6 @@ +package com.bigkoo.pickerview.listener; + + +public interface OnItemSelectedListener { + void onItemSelected(int index); +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/utils/PickerViewAnimateUtil.java b/pickerview/src/main/java/com/bigkoo/pickerview/utils/PickerViewAnimateUtil.java new file mode 100644 index 0000000..36e2dab --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/utils/PickerViewAnimateUtil.java @@ -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; + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/view/BasePickerView.java b/pickerview/src/main/java/com/bigkoo/pickerview/view/BasePickerView.java new file mode 100644 index 0000000..4c3e49f --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/view/BasePickerView.java @@ -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); + } +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/view/WheelOptions.java b/pickerview/src/main/java/com/bigkoo/pickerview/view/WheelOptions.java new file mode 100644 index 0000000..e16b9bc --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/view/WheelOptions.java @@ -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 { + private View view; + private WheelView wv_option1; + private WheelView wv_option2; + private WheelView wv_option3; + + private ArrayList mOptions1Items; + private ArrayList> mOptions2Items; + private ArrayList>> 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 optionsItems) { + setPicker(optionsItems, null, null, false); + } + + public void setPicker(ArrayList options1Items, + ArrayList> options2Items, boolean linkage) { + setPicker(options1Items, options2Items, null, linkage); + } + + public void setPicker(ArrayList options1Items, + ArrayList> options2Items, + ArrayList>> 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); + } + + /** + * 返回当前选中的结果对应的位置数组 因为支持三级联动效果,分三个级别索引,0,1,2 + * + * @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); + } + } + + +} diff --git a/pickerview/src/main/java/com/bigkoo/pickerview/view/WheelTime.java b/pickerview/src/main/java/com/bigkoo/pickerview/view/WheelTime.java new file mode 100644 index 0000000..084c322 --- /dev/null +++ b/pickerview/src/main/java/com/bigkoo/pickerview/view/WheelTime.java @@ -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 list_big = Arrays.asList(months_big); + final List 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; + } +} diff --git a/pickerview/src/main/res/anim/slide_in_bottom.xml b/pickerview/src/main/res/anim/slide_in_bottom.xml new file mode 100644 index 0000000..212e266 --- /dev/null +++ b/pickerview/src/main/res/anim/slide_in_bottom.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/pickerview/src/main/res/anim/slide_out_bottom.xml b/pickerview/src/main/res/anim/slide_out_bottom.xml new file mode 100644 index 0000000..a2d0ce9 --- /dev/null +++ b/pickerview/src/main/res/anim/slide_out_bottom.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/pickerview/src/main/res/drawable/selector_pickerview_btn.xml b/pickerview/src/main/res/drawable/selector_pickerview_btn.xml new file mode 100644 index 0000000..9ebc219 --- /dev/null +++ b/pickerview/src/main/res/drawable/selector_pickerview_btn.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/pickerview/src/main/res/layout/include_pickerview_topbar.xml b/pickerview/src/main/res/layout/include_pickerview_topbar.xml new file mode 100644 index 0000000..138584b --- /dev/null +++ b/pickerview/src/main/res/layout/include_pickerview_topbar.xml @@ -0,0 +1,41 @@ + + + +