├── .gradle └── 2.2 │ └── taskArtifacts │ ├── cache.properties │ ├── cache.properties.lock │ ├── fileHashes.bin │ ├── fileSnapshots.bin │ ├── outputFileStates.bin │ └── taskArtifacts.bin ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── libraries │ ├── appcompat_v7_23_1_0.xml │ ├── support_annotations_23_1_0.xml │ └── support_v4_23_1_0.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── workspace.xml ├── 1.png ├── Android-PickerView.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bigkoo │ │ └── pickerviewdemo │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bigkoo │ │ └── pickerviewdemo │ │ ├── MainActivity.java │ │ └── bean │ │ ├── PickerViewData.java │ │ ├── ProvinceBean.java │ │ └── TimeBean.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── build └── intermediates │ └── dex-cache │ └── cache.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── pickerview ├── .gitignore ├── build.gradle ├── pickerview.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bigkoo │ │ └── pickerview │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bigkoo │ │ └── pickerview │ │ ├── OptionsPickerView.java │ │ ├── TimePickerView.java │ │ ├── adapter │ │ ├── ArrayWheelAdapter.java │ │ ├── NumericWheelAdapter.java │ │ └── WheelAdapter.java │ │ ├── lib │ │ ├── InertiaTimerTask.java │ │ ├── LoopViewGestureListener.java │ │ ├── MessageHandler.java │ │ ├── OnItemSelectedRunnable.java │ │ ├── SmoothScrollTimerTask.java │ │ └── WheelView.java │ │ ├── listener │ │ ├── OnDismissListener.java │ │ └── OnItemSelectedListener.java │ │ ├── model │ │ └── IPickerViewData.java │ │ ├── utils │ │ └── PickerViewAnimateUtil.java │ │ └── view │ │ ├── BasePickerView.java │ │ ├── WheelOptions.java │ │ └── WheelTime.java │ └── res │ ├── anim │ ├── slide_in_bottom.xml │ └── slide_out_bottom.xml │ ├── drawable │ └── selector_pickerview_btn.xml │ ├── layout │ ├── include_pickerview_topbar.xml │ ├── layout_basepickerview.xml │ ├── pickerview_options.xml │ └── pickerview_time.xml │ └── values │ ├── attrs.xml │ ├── bools.xml │ ├── colors.xml │ ├── dimens.xml │ ├── integers.xml │ └── strings.xml ├── preview ├── pickerdemo.gif ├── pickerdemo1x.gif └── pickerdemo_zhangshangshenghuo.gif └── settings.gradle /.gradle/2.2/taskArtifacts/cache.properties: -------------------------------------------------------------------------------- 1 | #Mon Oct 24 17:44:51 CST 2016 2 | -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/cache.properties.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/.gradle/2.2/taskArtifacts/cache.properties.lock -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/.gradle/2.2/taskArtifacts/fileHashes.bin -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/fileSnapshots.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/.gradle/2.2/taskArtifacts/fileSnapshots.bin -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/outputFileStates.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/.gradle/2.2/taskArtifacts/outputFileStates.bin -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/taskArtifacts.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/.gradle/2.2/taskArtifacts/taskArtifacts.bin -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Android-PickerView -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/libraries/appcompat_v7_23_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_23_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_23_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/1.png -------------------------------------------------------------------------------- /Android-PickerView.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 仿滴滴时间选择控件 2 | 3 | ####国际案例,有图有真相: 4 | 5 | ![首页效果图](https://github.com/liangchengcheng/Android-PickerView/blob/master/1.png) 6 | 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.bigkoo.pickerviewdemo" 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile project(':pickerview') 25 | // compile 'com.bigkoo:pickerview:2.1.0' 26 | compile 'com.android.support:appcompat-v7:23.1.0' 27 | } 28 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Sai/Documents/software/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bigkoo/pickerviewdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerviewdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/bigkoo/pickerviewdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerviewdemo; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.ArrayList; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.List; 8 | 9 | import android.app.Activity; 10 | import android.os.Bundle; 11 | import android.view.KeyEvent; 12 | import android.view.View; 13 | import android.view.View.OnClickListener; 14 | import android.widget.TextView; 15 | 16 | import com.bigkoo.pickerview.OptionsPickerView; 17 | import com.bigkoo.pickerview.TimePickerView; 18 | import com.bigkoo.pickerview.model.IPickerViewData; 19 | import com.bigkoo.pickerviewdemo.bean.PickerViewData; 20 | import com.bigkoo.pickerviewdemo.bean.ProvinceBean; 21 | import com.bigkoo.pickerviewdemo.bean.TimeBean; 22 | 23 | 24 | public class MainActivity extends Activity { 25 | 26 | private ArrayList options1Items = new ArrayList<>(); 27 | private ArrayList> options2Items = new ArrayList<>(); 28 | private ArrayList>> options3Items = new ArrayList<>(); 29 | private TextView tvTime, tvOptions; 30 | TimePickerView pvTime; 31 | OptionsPickerView pvOptions; 32 | View vMasker; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_main); 38 | vMasker=findViewById(R.id.vMasker); 39 | tvTime=(TextView) findViewById(R.id.tvTime); 40 | tvOptions=(TextView) findViewById(R.id.tvOptions); 41 | //时间选择器 42 | pvTime = new TimePickerView(this, TimePickerView.Type.MONTH_DAY_HOUR_MIN); 43 | //控制时间范围 44 | // Calendar calendar = Calendar.getInstance(); 45 | // pvTime.setRange(calendar.get(Calendar.YEAR) - 20, calendar.get(Calendar.YEAR));//要在setTime 之前才有效果哦 46 | pvTime.setTime(new Date()); 47 | pvTime.setCyclic(false); 48 | pvTime.setCancelable(true); 49 | //时间选择后回调 50 | pvTime.setOnTimeSelectListener(new TimePickerView.OnTimeSelectListener() { 51 | 52 | @Override 53 | public void onTimeSelect(Date date) { 54 | tvTime.setText(getTime(date)); 55 | } 56 | }); 57 | //弹出时间选择器 58 | tvTime.setOnClickListener(new OnClickListener() { 59 | 60 | @Override 61 | public void onClick(View v) { 62 | pvTime.show(); 63 | } 64 | }); 65 | 66 | 67 | //选项选择器 68 | pvOptions = new OptionsPickerView(this); 69 | //选项1 70 | options1Items.add(new TimeBean("现在")); 71 | options1Items.add(new TimeBean("今天")); 72 | options1Items.add(new TimeBean("明天")); 73 | options1Items.add(new TimeBean("后天")); 74 | 75 | 76 | //选项 1 2 77 | ArrayList options2Items_01=new ArrayList<>(); 78 | options2Items_01.add("--"); 79 | 80 | //22 81 | ArrayList options2Items_02=getTodayHourData(); 82 | //32 83 | ArrayList options2Items_03=getHourData(); 84 | //32 85 | ArrayList options2Items_04=getHourData(); 86 | 87 | options2Items.add(options2Items_01); 88 | options2Items.add(options2Items_02); 89 | options2Items.add(options2Items_03); 90 | options2Items.add(options2Items_04); 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | //选项3 100 | ArrayList> options3Items_01 = new ArrayList<>(); 101 | ArrayList> options3Items_02 = new ArrayList<>(); 102 | ArrayList> options3Items_03 = new ArrayList<>(); 103 | ArrayList> options3Items_04 = new ArrayList<>(); 104 | 105 | 106 | ArrayList options3Items_01_01=new ArrayList<>(); 107 | options3Items_01_01.add(new PickerViewData("--")); 108 | options3Items_01.add(options3Items_01_01); 109 | options3Items_02 =getmD2(); 110 | options3Items_03 =getmD(); 111 | options3Items_04 =getmD(); 112 | 113 | 114 | 115 | options3Items.add(options3Items_01); 116 | options3Items.add(options3Items_02); 117 | options3Items.add(options3Items_03); 118 | options3Items.add(options3Items_04); 119 | 120 | 121 | //三级联动效果 122 | pvOptions.setPicker(options1Items, options2Items, options3Items, true); 123 | //设置选择的三级单位 124 | // pwOptions.setLabels("省", "市", "区"); 125 | pvOptions.setTitle(" "); 126 | pvOptions.setCyclic(false, false, false); 127 | //设置默认选中的三级项目 128 | //监听确定选择按钮 129 | pvOptions.setSelectOptions(0, 0, 0); 130 | pvOptions.setOnoptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() { 131 | 132 | @Override 133 | public void onOptionsSelect(int options1, int option2, int options3) { 134 | //返回的分别是三个级别的选中位置 135 | String tx = options1Items.get(options1).getPickerViewText() 136 | + options2Items.get(options1).get(option2) 137 | + options3Items.get(options1).get(option2).get(options3).getPickerViewText(); 138 | tvOptions.setText(tx); 139 | vMasker.setVisibility(View.GONE); 140 | } 141 | }); 142 | //点击弹出选项选择器 143 | tvOptions.setOnClickListener(new View.OnClickListener() { 144 | 145 | @Override 146 | public void onClick(View v) { 147 | pvOptions.show(); 148 | } 149 | }); 150 | } 151 | 152 | public static String getTime(Date date) { 153 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 154 | return format.format(date); 155 | } 156 | 157 | @Override 158 | public boolean onKeyDown(int keyCode, KeyEvent event) { 159 | if (keyCode == KeyEvent.KEYCODE_BACK) { 160 | if(pvOptions.isShowing()||pvTime.isShowing()){ 161 | pvOptions.dismiss(); 162 | pvTime.dismiss(); 163 | return true; 164 | } 165 | if(pvTime.isShowing()){ 166 | pvTime.dismiss(); 167 | return true; 168 | } 169 | } 170 | return super.onKeyDown(keyCode, event); 171 | } 172 | 173 | /** 174 | * 今天 点 175 | */ 176 | private ArrayList getTodayHourData(){ 177 | int max =currentHour(); 178 | if (max<23&¤tMin()>45){ 179 | max=max+1; 180 | } 181 | ArrayList lists=new ArrayList<>(); 182 | for (int i=max;i<24;i++){ 183 | lists.add(i+"点"); 184 | } 185 | return lists; 186 | } 187 | 188 | /** 189 | * 明天 后天 点 190 | */ 191 | private ArrayList getHourData(){ 192 | ArrayList lists=new ArrayList<>(); 193 | for (int i=0;i<24;i++){ 194 | lists.add(i+"点"); 195 | } 196 | return lists; 197 | } 198 | 199 | /** 200 | * 明天 后天 分 201 | */ 202 | private ArrayList getMinData(){ 203 | ArrayList dataArrayList=new ArrayList<>(); 204 | for (int i=0;i<6;i++){ 205 | dataArrayList.add(new PickerViewData((i*10)+"分")); 206 | } 207 | return dataArrayList; 208 | } 209 | /** 210 | * 明天 后天 211 | */ 212 | private ArrayList> getmD(){ 213 | ArrayList> d=new ArrayList<>(); 214 | for (int i=0;i<24;i++){ 215 | d.add(getMinData()); 216 | } 217 | return d; 218 | } 219 | 220 | /** 221 | * 明天 后天 2222 222 | */ 223 | private ArrayList> getmD2(){ 224 | //14 225 | int max =currentHour(); 226 | if (currentMin()>45){ 227 | max=max+1; 228 | } 229 | int value =24-max; 230 | ArrayList> d=new ArrayList<>(); 231 | for (int i=0;i getTodyMinData(){ 246 | 247 | int min = currentMin(); 248 | int current=0; 249 | if (min>35&&min<=45){ 250 | current =0; 251 | }else if (min>45&&min<=55){ 252 | current=1; 253 | } else if (min>55){ 254 | current=2; 255 | }else if (min<=5){ 256 | current=2; 257 | }else if (min>5&&min<=15){ 258 | current=3; 259 | }else if (min>15&&min<=25){ 260 | current=4; 261 | }else if (min>25&&min<=35){ 262 | current=5; 263 | } 264 | int max =currentHour(); 265 | if (max>23&& min>35){ 266 | current=5; 267 | } 268 | 269 | ArrayList dataArrayList=new ArrayList<>(); 270 | for (int i=current;i<6;i++){ 271 | dataArrayList.add(new PickerViewData((i*10)+"分")); 272 | } 273 | return dataArrayList; 274 | } 275 | 276 | private int currentMin(){ 277 | Calendar cal = Calendar.getInstance(); 278 | return cal.get(Calendar.MINUTE); 279 | } 280 | 281 | 282 | private int currentHour(){ 283 | Calendar cal = Calendar.getInstance(); 284 | return cal.get(Calendar.HOUR_OF_DAY); 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /app/src/main/java/com/bigkoo/pickerviewdemo/bean/PickerViewData.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerviewdemo.bean; 2 | 3 | import com.bigkoo.pickerview.model.IPickerViewData; 4 | 5 | /** 6 | * Created by Administrator on 2016/7/13. 7 | */ 8 | public class PickerViewData implements IPickerViewData { 9 | private String content; 10 | 11 | public PickerViewData(String content) { 12 | this.content = content; 13 | } 14 | 15 | public void setContent(String content) { 16 | this.content = content; 17 | } 18 | 19 | @Override 20 | public String getPickerViewText() { 21 | return content; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/bigkoo/pickerviewdemo/bean/ProvinceBean.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerviewdemo.bean; 2 | 3 | import com.bigkoo.pickerview.model.IPickerViewData; 4 | 5 | /** 6 | * Created by Sai on 15/11/22. 7 | */ 8 | public class ProvinceBean implements IPickerViewData { 9 | private long id; 10 | private String name; 11 | private String description; 12 | private String others; 13 | 14 | public ProvinceBean(long id,String name,String description,String others){ 15 | this.id = id; 16 | this.name = name; 17 | this.description = description; 18 | this.others = others; 19 | } 20 | 21 | public long getId() { 22 | return id; 23 | } 24 | 25 | public void setId(long id) { 26 | this.id = id; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public String getDescription() { 38 | return description; 39 | } 40 | 41 | public void setDescription(String description) { 42 | this.description = description; 43 | } 44 | 45 | public String getOthers() { 46 | return others; 47 | } 48 | 49 | public void setOthers(String others) { 50 | this.others = others; 51 | } 52 | 53 | //这个用来显示在PickerView上面的字符串,PickerView会通过IPickerViewData获取getPickerViewText方法显示出来。 54 | @Override 55 | public String getPickerViewText() { 56 | return name; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/bigkoo/pickerviewdemo/bean/TimeBean.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerviewdemo.bean; 2 | 3 | import com.bigkoo.pickerview.model.IPickerViewData; 4 | 5 | /** 6 | * Author: 梁铖城 7 | * Email: 1038127753@qq.com 8 | * Date: 2015年11月21日15:28:25 9 | * Description: 开始或者结束轮训器 10 | */ 11 | public class TimeBean implements IPickerViewData { 12 | private String time; 13 | 14 | public TimeBean(String time) { 15 | this.time = time; 16 | } 17 | 18 | public String getTime() { 19 | return time; 20 | } 21 | 22 | public void setTime(String time) { 23 | this.time = time; 24 | } 25 | 26 | @Override 27 | public String getPickerViewText() { 28 | return time; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 12 | 19 | 20 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PickerViewDemo 5 | Hello world! 6 | Settings 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.5.0' 9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 10 | classpath 'com.github.dcendents:android-maven-plugin:1.2' 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /build/intermediates/dex-cache/cache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 16 | 17 | 18 | 23 | 24 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangchengcheng/Android-PickerView/37e5eb80bc50a124d79fe5c3a1f1e16f44639475/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 22 15:54:07 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by Android Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # Location of the SDK. This is only used by Gradle. 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | #Mon Oct 24 17:43:32 CST 2016 11 | ndk.dir=/Users/lcc/Library/Android/sdk/ndk-bundle 12 | sdk.dir=/Users/lcc/Library/Android/sdk 13 | -------------------------------------------------------------------------------- /pickerview/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /pickerview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | 5 | version = "2.1.1" 6 | 7 | android { 8 | compileSdkVersion 23 9 | buildToolsVersion "23.0.2" 10 | 11 | defaultConfig { 12 | minSdkVersion 9 13 | targetSdkVersion 23 14 | versionCode 4 15 | versionName "2.1.1" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | } 31 | 32 | def siteUrl = 'https://github.com/saiwu-bigkoo/Android-PickerView' // #CONFIG# // project homepage 33 | def gitUrl = 'https://github.com/saiwu-bigkoo/Android-PickerView.git' // #CONFIG# // project git 34 | group = "com.bigkoo" 35 | 36 | install { 37 | repositories.mavenInstaller { 38 | // This generates POM.xml with proper parameters 39 | pom { 40 | project { 41 | packaging 'aar' 42 | name 'PickerView For Android' // #CONFIG# // project title 43 | url siteUrl 44 | // Set your license 45 | licenses { 46 | license { 47 | name 'The Apache Software License, Version 2.0' 48 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 49 | } 50 | } 51 | developers { 52 | developer { 53 | id 'sai' // #CONFIG# // your user id (you can write your nickname) 54 | name 'sai.wu' // #CONFIG# // your user name 55 | email 'sai.wu@bigkoo.com' // #CONFIG# // your email 56 | } 57 | } 58 | scm { 59 | connection gitUrl 60 | developerConnection gitUrl 61 | url siteUrl 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | task sourcesJar(type: Jar) { 69 | from android.sourceSets.main.java.srcDirs 70 | classifier = 'sources' 71 | } 72 | 73 | task javadoc(type: Javadoc) { 74 | source = android.sourceSets.main.java.srcDirs 75 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 76 | } 77 | 78 | task javadocJar(type: Jar, dependsOn: javadoc) { 79 | classifier = 'javadoc' 80 | from javadoc.destinationDir 81 | } 82 | 83 | artifacts { 84 | archives javadocJar 85 | archives sourcesJar 86 | } 87 | 88 | Properties properties = new Properties() 89 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 90 | bintray { 91 | user = properties.getProperty("bintray.user") 92 | key = properties.getProperty("bintray.apikey") 93 | configurations = ['archives'] 94 | pkg { 95 | repo = "maven" 96 | name = "PickerView" // #CONFIG# project name in jcenter 97 | websiteUrl = siteUrl 98 | vcsUrl = gitUrl 99 | licenses = ["Apache-2.0"] 100 | publish = true 101 | } 102 | } -------------------------------------------------------------------------------- /pickerview/pickerview.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /pickerview/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Sai/Documents/software/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /pickerview/src/androidTest/java/com/bigkoo/pickerview/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /pickerview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/OptionsPickerView.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.bigkoo.pickerview.view.BasePickerView; 9 | import com.bigkoo.pickerview.model.IPickerViewData; 10 | import com.bigkoo.pickerview.view.WheelOptions; 11 | 12 | import java.util.ArrayList; 13 | 14 | /** 15 | * 条件选择器 16 | * Created by Sai on 15/11/22. 17 | */ 18 | public class OptionsPickerView extends BasePickerView implements View.OnClickListener { 19 | WheelOptions wheelOptions; 20 | private View btnSubmit, btnCancel; 21 | private TextView tvTitle; 22 | private OnOptionsSelectListener optionsSelectListener; 23 | private static final String TAG_SUBMIT = "submit"; 24 | private static final String TAG_CANCEL = "cancel"; 25 | public OptionsPickerView(Context context) { 26 | super(context); 27 | LayoutInflater.from(context).inflate(R.layout.pickerview_options, contentContainer); 28 | // -----确定和取消按钮 29 | btnSubmit = findViewById(R.id.btnSubmit); 30 | btnSubmit.setTag(TAG_SUBMIT); 31 | btnCancel = findViewById(R.id.btnCancel); 32 | btnCancel.setTag(TAG_CANCEL); 33 | btnSubmit.setOnClickListener(this); 34 | btnCancel.setOnClickListener(this); 35 | //顶部标题 36 | tvTitle = (TextView) findViewById(R.id.tvTitle); 37 | // ----转轮 38 | final View optionspicker = findViewById(R.id.optionspicker); 39 | wheelOptions = new WheelOptions(optionspicker); 40 | } 41 | public void setPicker(ArrayList optionsItems) { 42 | wheelOptions.setPicker(optionsItems, null, null, false); 43 | } 44 | 45 | public void setPicker(ArrayList options1Items, 46 | ArrayList> options2Items, boolean linkage) { 47 | wheelOptions.setPicker(options1Items, options2Items, null, linkage); 48 | } 49 | 50 | public void setPicker(ArrayList options1Items, 51 | ArrayList> options2Items, 52 | ArrayList>> options3Items, 53 | boolean linkage) { 54 | wheelOptions.setPicker(options1Items, options2Items, options3Items, 55 | linkage); 56 | } 57 | /** 58 | * 设置选中的item位置 59 | * @param option1 位置 60 | */ 61 | public void setSelectOptions(int option1){ 62 | wheelOptions.setCurrentItems(option1, 0, 0); 63 | } 64 | /** 65 | * 设置选中的item位置 66 | * @param option1 位置 67 | * @param option2 位置 68 | */ 69 | public void setSelectOptions(int option1, int option2){ 70 | wheelOptions.setCurrentItems(option1, option2, 0); 71 | } 72 | /** 73 | * 设置选中的item位置 74 | * @param option1 位置 75 | * @param option2 位置 76 | * @param option3 位置 77 | */ 78 | public void setSelectOptions(int option1, int option2, int option3){ 79 | wheelOptions.setCurrentItems(option1, option2, option3); 80 | } 81 | /** 82 | * 设置选项的单位 83 | * @param label1 单位 84 | */ 85 | public void setLabels(String label1){ 86 | wheelOptions.setLabels(label1, null, null); 87 | } 88 | /** 89 | * 设置选项的单位 90 | * @param label1 单位 91 | * @param label2 单位 92 | */ 93 | public void setLabels(String label1,String label2){ 94 | wheelOptions.setLabels(label1, label2, null); 95 | } 96 | /** 97 | * 设置选项的单位 98 | * @param label1 单位 99 | * @param label2 单位 100 | * @param label3 单位 101 | */ 102 | public void setLabels(String label1,String label2,String label3){ 103 | wheelOptions.setLabels(label1, label2, label3); 104 | } 105 | /** 106 | * 设置是否循环滚动 107 | * @param cyclic 是否循环 108 | */ 109 | public void setCyclic(boolean cyclic){ 110 | wheelOptions.setCyclic(cyclic); 111 | } 112 | public void setCyclic(boolean cyclic1,boolean cyclic2,boolean cyclic3) { 113 | wheelOptions.setCyclic(cyclic1,cyclic2,cyclic3); 114 | } 115 | 116 | @Override 117 | public void onClick(View v) { 118 | String tag=(String) v.getTag(); 119 | if(tag.equals(TAG_CANCEL)) { 120 | dismiss(); 121 | } 122 | else { 123 | if(optionsSelectListener!=null) { 124 | int[] optionsCurrentItems=wheelOptions.getCurrentItems(); 125 | optionsSelectListener.onOptionsSelect(optionsCurrentItems[0], optionsCurrentItems[1], optionsCurrentItems[2]); 126 | } 127 | dismiss(); 128 | } 129 | } 130 | 131 | public interface OnOptionsSelectListener { 132 | void onOptionsSelect(int options1, int option2, int options3); 133 | } 134 | 135 | public void setOnoptionsSelectListener( 136 | OnOptionsSelectListener optionsSelectListener) { 137 | this.optionsSelectListener = optionsSelectListener; 138 | } 139 | 140 | public void setTitle(String title){ 141 | tvTitle.setText(title); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/TimePickerView.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.bigkoo.pickerview.view.BasePickerView; 9 | import com.bigkoo.pickerview.view.WheelTime; 10 | 11 | import java.text.ParseException; 12 | import java.util.Calendar; 13 | import java.util.Date; 14 | 15 | /** 16 | * 时间选择器 17 | * Created by Sai on 15/11/22. 18 | */ 19 | public class TimePickerView extends BasePickerView implements View.OnClickListener { 20 | public enum Type { 21 | ALL, YEAR_MONTH_DAY, HOURS_MINS, MONTH_DAY_HOUR_MIN , YEAR_MONTH 22 | }// 四种选择模式,年月日时分,年月日,时分,月日时分 23 | 24 | WheelTime wheelTime; 25 | private View btnSubmit, btnCancel; 26 | private TextView tvTitle; 27 | private static final String TAG_SUBMIT = "submit"; 28 | private static final String TAG_CANCEL = "cancel"; 29 | private OnTimeSelectListener timeSelectListener; 30 | 31 | public TimePickerView(Context context, Type type) { 32 | super(context); 33 | 34 | LayoutInflater.from(context).inflate(R.layout.pickerview_time, contentContainer); 35 | // -----确定和取消按钮 36 | btnSubmit = findViewById(R.id.btnSubmit); 37 | btnSubmit.setTag(TAG_SUBMIT); 38 | btnCancel = findViewById(R.id.btnCancel); 39 | btnCancel.setTag(TAG_CANCEL); 40 | btnSubmit.setOnClickListener(this); 41 | btnCancel.setOnClickListener(this); 42 | //顶部标题 43 | tvTitle = (TextView) findViewById(R.id.tvTitle); 44 | // ----时间转轮 45 | final View timepickerview = findViewById(R.id.timepicker); 46 | wheelTime = new WheelTime(timepickerview, type); 47 | 48 | //默认选中当前时间 49 | Calendar calendar = Calendar.getInstance(); 50 | calendar.setTimeInMillis(System.currentTimeMillis()); 51 | int year = calendar.get(Calendar.YEAR); 52 | int month = calendar.get(Calendar.MONTH); 53 | int day = calendar.get(Calendar.DAY_OF_MONTH); 54 | int hours = calendar.get(Calendar.HOUR_OF_DAY); 55 | int minute = calendar.get(Calendar.MINUTE); 56 | wheelTime.setPicker(year, month, day, hours, minute); 57 | 58 | } 59 | 60 | /** 61 | * 设置可以选择的时间范围 62 | * 要在setTime之前调用才有效果 63 | * @param startYear 开始年份 64 | * @param endYear 结束年份 65 | */ 66 | public void setRange(int startYear, int endYear) { 67 | wheelTime.setStartYear(startYear); 68 | wheelTime.setEndYear(endYear); 69 | } 70 | 71 | /** 72 | * 设置选中时间 73 | * @param date 时间 74 | */ 75 | public void setTime(Date date) { 76 | Calendar calendar = Calendar.getInstance(); 77 | if (date == null) 78 | calendar.setTimeInMillis(System.currentTimeMillis()); 79 | else 80 | calendar.setTime(date); 81 | int year = calendar.get(Calendar.YEAR); 82 | int month = calendar.get(Calendar.MONTH); 83 | int day = calendar.get(Calendar.DAY_OF_MONTH); 84 | int hours = calendar.get(Calendar.HOUR_OF_DAY); 85 | int minute = calendar.get(Calendar.MINUTE); 86 | wheelTime.setPicker(year, month, day, hours, minute); 87 | } 88 | 89 | // /** 90 | // * 指定选中的时间,显示选择器 91 | // * 92 | // * @param date 93 | // */ 94 | // public void show(Date date) { 95 | // Calendar calendar = Calendar.getInstance(); 96 | // if (date == null) 97 | // calendar.setTimeInMillis(System.currentTimeMillis()); 98 | // else 99 | // calendar.setTime(date); 100 | // int year = calendar.get(Calendar.YEAR); 101 | // int month = calendar.get(Calendar.MONTH); 102 | // int day = calendar.get(Calendar.DAY_OF_MONTH); 103 | // int hours = calendar.get(Calendar.HOUR_OF_DAY); 104 | // int minute = calendar.get(Calendar.MINUTE); 105 | // wheelTime.setPicker(year, month, day, hours, minute); 106 | // show(); 107 | // } 108 | 109 | /** 110 | * 设置是否循环滚动 111 | * @param cyclic 是否循环 112 | */ 113 | public void setCyclic(boolean cyclic) { 114 | wheelTime.setCyclic(cyclic); 115 | } 116 | 117 | @Override 118 | public void onClick(View v) { 119 | String tag = (String) v.getTag(); 120 | if (tag.equals(TAG_CANCEL)) { 121 | dismiss(); 122 | return; 123 | } else { 124 | if (timeSelectListener != null) { 125 | try { 126 | Date date = WheelTime.dateFormat.parse(wheelTime.getTime()); 127 | timeSelectListener.onTimeSelect(date); 128 | } catch (ParseException e) { 129 | e.printStackTrace(); 130 | } 131 | } 132 | dismiss(); 133 | return; 134 | } 135 | } 136 | 137 | public interface OnTimeSelectListener { 138 | void onTimeSelect(Date date); 139 | } 140 | 141 | public void setOnTimeSelectListener(OnTimeSelectListener timeSelectListener) { 142 | this.timeSelectListener = timeSelectListener; 143 | } 144 | 145 | public void setTitle(String title){ 146 | tvTitle.setText(title); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/adapter/ArrayWheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.adapter; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * The simple Array wheel adapter 7 | * @param the element type 8 | */ 9 | public class ArrayWheelAdapter implements WheelAdapter { 10 | 11 | /** The default items length */ 12 | public static final int DEFAULT_LENGTH = 4; 13 | 14 | // items 15 | private ArrayList items; 16 | // length 17 | private int length; 18 | 19 | /** 20 | * Constructor 21 | * @param items the items 22 | * @param length the max items length 23 | */ 24 | public ArrayWheelAdapter(ArrayList items, int length) { 25 | this.items = items; 26 | this.length = length; 27 | } 28 | 29 | /** 30 | * Contructor 31 | * @param items the items 32 | */ 33 | public ArrayWheelAdapter(ArrayList items) { 34 | this(items, DEFAULT_LENGTH); 35 | } 36 | 37 | @Override 38 | public Object getItem(int index) { 39 | if (index >= 0 && index < items.size()) { 40 | return items.get(index); 41 | } 42 | return ""; 43 | } 44 | 45 | @Override 46 | public int getItemsCount() { 47 | return items.size(); 48 | } 49 | 50 | @Override 51 | public int indexOf(Object o){ 52 | return items.indexOf(o); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/adapter/NumericWheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.adapter; 2 | 3 | 4 | /** 5 | * Numeric Wheel adapter. 6 | */ 7 | public class NumericWheelAdapter implements WheelAdapter { 8 | 9 | /** The default min value */ 10 | public static final int DEFAULT_MAX_VALUE = 9; 11 | 12 | /** The default max value */ 13 | private static final int DEFAULT_MIN_VALUE = 0; 14 | 15 | // Values 16 | private int minValue; 17 | private int maxValue; 18 | 19 | /** 20 | * Default constructor 21 | */ 22 | public NumericWheelAdapter() { 23 | this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE); 24 | } 25 | 26 | /** 27 | * Constructor 28 | * @param minValue the wheel min value 29 | * @param maxValue the wheel max value 30 | */ 31 | public NumericWheelAdapter(int minValue, int maxValue) { 32 | this.minValue = minValue; 33 | this.maxValue = maxValue; 34 | } 35 | 36 | @Override 37 | public Object getItem(int index) { 38 | if (index >= 0 && index < getItemsCount()) { 39 | int value = minValue + index; 40 | return value; 41 | } 42 | return 0; 43 | } 44 | 45 | @Override 46 | public int getItemsCount() { 47 | return maxValue - minValue + 1; 48 | } 49 | 50 | @Override 51 | public int indexOf(Object o){ 52 | return (int)o - minValue; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/adapter/WheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.adapter; 2 | 3 | public interface WheelAdapter { 4 | /** 5 | * Gets items count 6 | * @return the count of wheel items 7 | */ 8 | int getItemsCount(); 9 | 10 | /** 11 | * Gets a wheel item by index. 12 | * @param index the item index 13 | * @return the wheel item text or null 14 | */ 15 | T getItem(int index); 16 | 17 | /** 18 | * Gets maximum item length. It is used to determine the wheel width. 19 | * If -1 is returned there will be used the default wheel width. 20 | * @param o 21 | * @return the maximum item length or -1 22 | */ 23 | int indexOf(T o); 24 | } 25 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/lib/InertiaTimerTask.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.lib; 2 | 3 | import java.util.TimerTask; 4 | 5 | final class InertiaTimerTask extends TimerTask { 6 | 7 | float a; 8 | final float velocityY; 9 | final WheelView loopView; 10 | 11 | InertiaTimerTask(WheelView loopview, float velocityY) { 12 | super(); 13 | loopView = loopview; 14 | this.velocityY = velocityY; 15 | a = Integer.MAX_VALUE; 16 | } 17 | 18 | @Override 19 | public final void run() { 20 | if (a == Integer.MAX_VALUE) { 21 | if (Math.abs(velocityY) > 2000F) { 22 | if (velocityY > 0.0F) { 23 | a = 2000F; 24 | } else { 25 | a = -2000F; 26 | } 27 | } else { 28 | a = velocityY; 29 | } 30 | } 31 | if (Math.abs(a) >= 0.0F && Math.abs(a) <= 20F) { 32 | loopView.cancelFuture(); 33 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_SMOOTH_SCROLL); 34 | return; 35 | } 36 | int i = (int) ((a * 10F) / 1000F); 37 | loopView.totalScrollY = loopView.totalScrollY - i; 38 | if (!loopView.isLoop) { 39 | float itemHeight = loopView.itemHeight; 40 | float top = (-loopView.initPosition) * itemHeight; 41 | float bottom = (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight; 42 | if(loopView.totalScrollY - itemHeight*0.3 < top){ 43 | top = loopView.totalScrollY + i; 44 | } 45 | else if(loopView.totalScrollY + itemHeight*0.3 > bottom){ 46 | bottom = loopView.totalScrollY + i; 47 | } 48 | 49 | if (loopView.totalScrollY <= top){ 50 | a = 40F; 51 | loopView.totalScrollY = (int)top; 52 | } else if (loopView.totalScrollY >= bottom) { 53 | loopView.totalScrollY = (int)bottom; 54 | a = -40F; 55 | } 56 | } 57 | if (a < 0.0F) { 58 | a = a + 20F; 59 | } else { 60 | a = a - 20F; 61 | } 62 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/lib/LoopViewGestureListener.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.lib; 2 | 3 | import android.view.MotionEvent; 4 | 5 | final class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener { 6 | 7 | final WheelView loopView; 8 | 9 | LoopViewGestureListener(WheelView loopview) { 10 | loopView = loopview; 11 | } 12 | 13 | @Override 14 | public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 15 | loopView.scrollBy(velocityY); 16 | return true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/lib/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.lib; 2 | 3 | import android.os.Handler; 4 | import android.os.Message; 5 | 6 | final class MessageHandler extends Handler { 7 | public static final int WHAT_INVALIDATE_LOOP_VIEW = 1000; 8 | public static final int WHAT_SMOOTH_SCROLL = 2000; 9 | public static final int WHAT_ITEM_SELECTED = 3000; 10 | 11 | final WheelView loopview; 12 | 13 | MessageHandler(WheelView loopview) { 14 | this.loopview = loopview; 15 | } 16 | 17 | @Override 18 | public final void handleMessage(Message msg) { 19 | switch (msg.what) { 20 | case WHAT_INVALIDATE_LOOP_VIEW: 21 | loopview.invalidate(); 22 | break; 23 | 24 | case WHAT_SMOOTH_SCROLL: 25 | loopview.smoothScroll(WheelView.ACTION.FLING); 26 | break; 27 | 28 | case WHAT_ITEM_SELECTED: 29 | loopview.onItemSelected(); 30 | break; 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/lib/OnItemSelectedRunnable.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.lib; 2 | 3 | final class OnItemSelectedRunnable implements Runnable { 4 | final WheelView loopView; 5 | 6 | OnItemSelectedRunnable(WheelView loopview) { 7 | loopView = loopview; 8 | } 9 | 10 | @Override 11 | public final void run() { 12 | loopView.onItemSelectedListener.onItemSelected(loopView.getCurrentItem()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/lib/SmoothScrollTimerTask.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.lib; 2 | 3 | import java.util.TimerTask; 4 | 5 | final class SmoothScrollTimerTask extends TimerTask { 6 | 7 | int realTotalOffset; 8 | int realOffset; 9 | int offset; 10 | final WheelView loopView; 11 | 12 | SmoothScrollTimerTask(WheelView loopview, int offset) { 13 | this.loopView = loopview; 14 | this.offset = offset; 15 | realTotalOffset = Integer.MAX_VALUE; 16 | realOffset = 0; 17 | } 18 | 19 | @Override 20 | public final void run() { 21 | if (realTotalOffset == Integer.MAX_VALUE) { 22 | realTotalOffset = offset; 23 | } 24 | //把要滚动的范围细分成十小份,按是小份单位来重绘 25 | realOffset = (int) ((float) realTotalOffset * 0.1F); 26 | 27 | if (realOffset == 0) { 28 | if (realTotalOffset < 0) { 29 | realOffset = -1; 30 | } else { 31 | realOffset = 1; 32 | } 33 | } 34 | 35 | if (Math.abs(realTotalOffset) <= 1) { 36 | loopView.cancelFuture(); 37 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED); 38 | } else { 39 | loopView.totalScrollY = loopView.totalScrollY + realOffset; 40 | 41 | //这里如果不是循环模式,则点击空白位置需要回滚,不然就会出现选到-1 item的 情况 42 | if (!loopView.isLoop) { 43 | float itemHeight = loopView.itemHeight; 44 | float top = (float) (-loopView.initPosition) * itemHeight; 45 | float bottom = (float) (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight; 46 | if (loopView.totalScrollY <= top||loopView.totalScrollY >= bottom) { 47 | loopView.totalScrollY = loopView.totalScrollY - realOffset; 48 | loopView.cancelFuture(); 49 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED); 50 | return; 51 | } 52 | } 53 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW); 54 | realTotalOffset = realTotalOffset - realOffset; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/lib/WheelView.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.lib; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.graphics.Typeface; 9 | import android.os.Handler; 10 | import android.util.AttributeSet; 11 | import android.view.GestureDetector; 12 | import android.view.Gravity; 13 | import android.view.MotionEvent; 14 | import android.view.View; 15 | 16 | import com.bigkoo.pickerview.R; 17 | import com.bigkoo.pickerview.adapter.WheelAdapter; 18 | import com.bigkoo.pickerview.listener.OnItemSelectedListener; 19 | import com.bigkoo.pickerview.model.IPickerViewData; 20 | 21 | import java.lang.reflect.InvocationTargetException; 22 | import java.lang.reflect.Method; 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.ScheduledExecutorService; 25 | import java.util.concurrent.ScheduledFuture; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /** 29 | * 3d滚轮控件 30 | */ 31 | public class WheelView extends View { 32 | 33 | public enum ACTION { 34 | // 点击,滑翔(滑到尽头),拖拽事件 35 | CLICK, FLING, DAGGLE 36 | } 37 | 38 | Context context; 39 | 40 | Handler handler; 41 | private GestureDetector gestureDetector; 42 | OnItemSelectedListener onItemSelectedListener; 43 | 44 | // Timer mTimer; 45 | ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor(); 46 | private ScheduledFuture mFuture; 47 | 48 | Paint paintOuterText; 49 | Paint paintCenterText; 50 | Paint paintIndicator; 51 | 52 | WheelAdapter adapter; 53 | 54 | private String label;//附加单位 55 | int textSize;//选项的文字大小 56 | boolean customTextSize;//自定义文字大小,为true则用于使setTextSize函数无效,只能通过xml修改 57 | int maxTextWidth; 58 | int maxTextHeight; 59 | float itemHeight;//每行高度 60 | 61 | int textColorOut; 62 | int textColorCenter; 63 | int dividerColor; 64 | 65 | // 条目间距倍数 66 | static final float lineSpacingMultiplier = 1.8F; 67 | boolean isLoop; 68 | 69 | // 第一条线Y坐标值 70 | float firstLineY; 71 | //第二条线Y坐标 72 | float secondLineY; 73 | //中间Y坐标 74 | float centerY; 75 | 76 | //滚动总高度y值 77 | int totalScrollY; 78 | //初始化默认选中第几个 79 | int initPosition; 80 | //选中的Item是第几个 81 | private int selectedItem; 82 | int preCurrentIndex; 83 | //滚动偏移值,用于记录滚动了多少个item 84 | int change; 85 | 86 | // 显示几个条目 87 | int itemsVisible = 11; 88 | 89 | int measuredHeight; 90 | int measuredWidth; 91 | 92 | // 半圆周长 93 | int halfCircumference; 94 | // 半径 95 | int radius; 96 | 97 | private int mOffset = 0; 98 | private float previousY = 0; 99 | long startTime = 0; 100 | 101 | // 修改这个值可以改变滑行速度 102 | private static final int VELOCITYFLING = 5; 103 | int widthMeasureSpec; 104 | 105 | private int mGravity = Gravity.CENTER; 106 | private int drawCenterContentStart = 0;//中间选中文字开始绘制位置 107 | private int drawOutContentStart = 0;//非中间文字开始绘制位置 108 | private static final float SCALECONTENT = 0.8F;//非中间文字则用此控制高度,压扁形成3d错觉 109 | private static final float CENTERCONTENTOFFSET = 6;//中间文字文字居中需要此偏移值 110 | 111 | public WheelView(Context context) { 112 | this(context, null); 113 | } 114 | 115 | public WheelView(Context context, AttributeSet attrs) { 116 | super(context, attrs); 117 | textColorOut = getResources().getColor(R.color.pickerview_wheelview_textcolor_out); 118 | textColorCenter = getResources().getColor(R.color.pickerview_wheelview_textcolor_center); 119 | dividerColor = getResources().getColor(R.color.pickerview_wheelview_textcolor_divider); 120 | //配合customTextSize使用,customTextSize为true才会发挥效果 121 | textSize = getResources().getDimensionPixelSize(R.dimen.pickerview_textsize); 122 | customTextSize = getResources().getBoolean(R.bool.pickerview_customTextSize); 123 | if (attrs != null) { 124 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.pickerview, 0, 0); 125 | mGravity = a.getInt(R.styleable.pickerview_pickerview_gravity, Gravity.CENTER); 126 | textColorOut = a.getColor(R.styleable.pickerview_pickerview_textColorOut, textColorOut); 127 | textColorCenter = a.getColor(R.styleable.pickerview_pickerview_textColorCenter, textColorCenter); 128 | dividerColor = a.getColor(R.styleable.pickerview_pickerview_dividerColor, dividerColor); 129 | textSize = a.getDimensionPixelOffset(R.styleable.pickerview_pickerview_textSize, textSize); 130 | } 131 | initLoopView(context); 132 | } 133 | 134 | private void initLoopView(Context context) { 135 | this.context = context; 136 | handler = new MessageHandler(this); 137 | gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this)); 138 | gestureDetector.setIsLongpressEnabled(false); 139 | isLoop = true; 140 | totalScrollY = 0; 141 | initPosition = -1; 142 | initPaints(); 143 | 144 | } 145 | 146 | private void initPaints() { 147 | paintOuterText = new Paint(); 148 | paintOuterText.setColor(textColorOut); 149 | paintOuterText.setAntiAlias(true); 150 | paintOuterText.setTypeface(Typeface.MONOSPACE); 151 | paintOuterText.setTextSize(textSize); 152 | paintCenterText = new Paint(); 153 | paintCenterText.setColor(textColorCenter); 154 | paintCenterText.setAntiAlias(true); 155 | paintCenterText.setTextScaleX(1.1F); 156 | paintCenterText.setTypeface(Typeface.MONOSPACE); 157 | paintCenterText.setTextSize(textSize); 158 | 159 | paintIndicator = new Paint(); 160 | paintIndicator.setColor(dividerColor); 161 | paintIndicator.setAntiAlias(true); 162 | 163 | if (android.os.Build.VERSION.SDK_INT >= 11) { 164 | setLayerType(LAYER_TYPE_SOFTWARE, null); 165 | } 166 | } 167 | 168 | private void remeasure() { 169 | if (adapter == null) { 170 | return; 171 | } 172 | 173 | measureTextWidthHeight(); 174 | //最大Text的高度乘间距倍数得到 可见文字实际的总高度,半圆的周长 175 | halfCircumference = (int) (itemHeight * (itemsVisible - 1)); 176 | //整个圆的周长除以PI得到直径,这个直径用作控件的总高度 177 | measuredHeight = (int) ((halfCircumference * 2) / Math.PI); 178 | //求出半径 179 | radius = (int) (halfCircumference / Math.PI); 180 | //控件宽度,这里支持weight 181 | measuredWidth = MeasureSpec.getSize(widthMeasureSpec); 182 | //计算两条横线和控件中间点的Y位置 183 | firstLineY = (measuredHeight - itemHeight) / 2.0F; 184 | secondLineY = (measuredHeight + itemHeight) / 2.0F; 185 | centerY = (measuredHeight + maxTextHeight) / 2.0F - CENTERCONTENTOFFSET; 186 | //初始化显示的item的position,根据是否loop 187 | if (initPosition == -1) { 188 | if (isLoop) { 189 | initPosition = (adapter.getItemsCount() + 1) / 2; 190 | } else { 191 | initPosition = 0; 192 | } 193 | } 194 | 195 | preCurrentIndex = initPosition; 196 | } 197 | 198 | /** 199 | * 计算最大len的Text的宽高度 200 | */ 201 | private void measureTextWidthHeight() { 202 | Rect rect = new Rect(); 203 | for (int i = 0; i < adapter.getItemsCount(); i++) { 204 | String s1 = getContentText(adapter.getItem(i)); 205 | paintCenterText.getTextBounds(s1, 0, s1.length(), rect); 206 | int textWidth = rect.width(); 207 | if (textWidth > maxTextWidth) { 208 | maxTextWidth = textWidth; 209 | } 210 | // 星期 211 | paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); 212 | int textHeight = rect.height(); 213 | if (textHeight > maxTextHeight) { 214 | maxTextHeight = textHeight; 215 | } 216 | } 217 | itemHeight = lineSpacingMultiplier * maxTextHeight; 218 | } 219 | 220 | void smoothScroll(ACTION action) { 221 | cancelFuture(); 222 | if (action == ACTION.FLING || action == ACTION.DAGGLE) { 223 | mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight); 224 | if ((float) mOffset > itemHeight / 2.0F) { 225 | mOffset = (int) (itemHeight - (float) mOffset); 226 | } else { 227 | mOffset = -mOffset; 228 | } 229 | } 230 | //停止的时候,位置有偏移,不是全部都能正确停止到中间位置的,这里把文字位置挪回中间去 231 | mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS); 232 | } 233 | 234 | protected final void scrollBy(float velocityY) { 235 | cancelFuture(); 236 | 237 | mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, VELOCITYFLING, TimeUnit.MILLISECONDS); 238 | } 239 | 240 | public void cancelFuture() { 241 | if (mFuture != null && !mFuture.isCancelled()) { 242 | mFuture.cancel(true); 243 | mFuture = null; 244 | } 245 | } 246 | 247 | /** 248 | * 设置是否循环滚动 249 | * @param cyclic 是否循环 250 | */ 251 | public final void setCyclic(boolean cyclic) { 252 | isLoop = cyclic; 253 | } 254 | 255 | public final void setTextSize(float size) { 256 | if (size > 0.0F && !customTextSize) { 257 | textSize = (int) (context.getResources().getDisplayMetrics().density * size); 258 | paintOuterText.setTextSize(textSize); 259 | paintCenterText.setTextSize(textSize); 260 | } 261 | } 262 | 263 | public final void setCurrentItem(int currentItem) { 264 | this.initPosition = currentItem; 265 | totalScrollY = 0;//回归顶部,不然重设setCurrentItem的话位置会偏移的,就会显示出不对位置的数据 266 | invalidate(); 267 | } 268 | 269 | public final void setOnItemSelectedListener(OnItemSelectedListener OnItemSelectedListener) { 270 | this.onItemSelectedListener = OnItemSelectedListener; 271 | } 272 | 273 | public final void setAdapter(WheelAdapter adapter) { 274 | this.adapter = adapter; 275 | remeasure(); 276 | invalidate(); 277 | } 278 | 279 | public final WheelAdapter getAdapter() { 280 | return adapter; 281 | } 282 | 283 | public final int getCurrentItem() { 284 | return selectedItem; 285 | } 286 | 287 | protected final void onItemSelected() { 288 | if (onItemSelectedListener != null) { 289 | postDelayed(new OnItemSelectedRunnable(this), 200L); 290 | } 291 | } 292 | 293 | @Override 294 | protected void onDraw(Canvas canvas) { 295 | if (adapter == null) { 296 | return; 297 | } 298 | //可见的item数组 299 | Object visibles[] = new Object[itemsVisible]; 300 | //滚动的Y值高度除去每行Item的高度,得到滚动了多少个item,即change数 301 | change = (int) (totalScrollY / itemHeight); 302 | try { 303 | //滚动中实际的预选中的item(即经过了中间位置的item) = 滑动前的位置 + 滑动相对位置 304 | preCurrentIndex = initPosition + change % adapter.getItemsCount(); 305 | } catch (ArithmeticException e) { 306 | System.out.println("出错了!adapter.getItemsCount() == 0,联动数据不匹配"); 307 | } 308 | //不循环的情况 309 | if (!isLoop) { 310 | if (preCurrentIndex < 0) { 311 | preCurrentIndex = 0; 312 | } 313 | if (preCurrentIndex > adapter.getItemsCount() - 1) { 314 | preCurrentIndex = adapter.getItemsCount() - 1; 315 | } 316 | } else { 317 | //循环 318 | if (preCurrentIndex < 0) {//举个例子:如果总数是5,preCurrentIndex = -1,那么preCurrentIndex按循环来说,其实是0的上面,也就是4的位置 319 | preCurrentIndex = adapter.getItemsCount() + preCurrentIndex; 320 | } 321 | if (preCurrentIndex > adapter.getItemsCount() - 1) {//同理上面,自己脑补一下 322 | preCurrentIndex = preCurrentIndex - adapter.getItemsCount(); 323 | } 324 | } 325 | 326 | //跟滚动流畅度有关,总滑动距离与每个item高度取余,即并不是一格格的滚动, 327 | //每个item不一定滚到对应Rect里的,这个item对应格子的偏移值 328 | int itemHeightOffset = (int) (totalScrollY % itemHeight); 329 | // 设置数组中每个元素的值 330 | int counter = 0; 331 | while (counter < itemsVisible) { 332 | int index = preCurrentIndex - (itemsVisible / 2 - counter);//索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值 333 | 334 | //判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项 335 | if (isLoop) { 336 | index = getLoopMappingIndex(index); 337 | visibles[counter] = adapter.getItem(index); 338 | } else if (index < 0) { 339 | visibles[counter] = ""; 340 | } else if (index > adapter.getItemsCount() - 1) { 341 | visibles[counter] = ""; 342 | } else { 343 | visibles[counter] = adapter.getItem(index); 344 | } 345 | 346 | counter++; 347 | } 348 | 349 | //中间两条横线 350 | canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator); 351 | canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator); 352 | //单位的Label 353 | if (label != null) { 354 | int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText, label); 355 | //靠右并留出空隙 356 | canvas.drawText(label, drawRightContentStart - CENTERCONTENTOFFSET, centerY, paintCenterText); 357 | } 358 | counter = 0; 359 | while (counter < itemsVisible) { 360 | canvas.save(); 361 | // L(弧长)=α(弧度)* r(半径) (弧度制) 362 | // 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长) 363 | float itemHeight = maxTextHeight * lineSpacingMultiplier; 364 | double radian = ((itemHeight * counter - itemHeightOffset) * Math.PI) / halfCircumference; 365 | // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限 366 | float angle = (float) (90D - (radian / Math.PI) * 180D); 367 | // 九十度以上的不绘制 368 | if (angle >= 90F || angle <= -90F) { 369 | canvas.restore(); 370 | } else { 371 | String contentText = getContentText(visibles[counter]); 372 | //计算开始绘制的位置 373 | measuredCenterContentStart(contentText); 374 | measuredOutContentStart(contentText); 375 | float translateY = (float) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D); 376 | //根据Math.sin(radian)来更改canvas坐标系原点,然后缩放画布,使得文字高度进行缩放,形成弧形3d视觉差 377 | canvas.translate(0.0F, translateY); 378 | canvas.scale(1.0F, (float) Math.sin(radian)); 379 | if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) { 380 | // 条目经过第一条线 381 | canvas.save(); 382 | canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY); 383 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); 384 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); 385 | canvas.restore(); 386 | canvas.save(); 387 | canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight)); 388 | canvas.scale(1.0F, (float) Math.sin(radian) * 1F); 389 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); 390 | canvas.restore(); 391 | } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) { 392 | // 条目经过第二条线 393 | canvas.save(); 394 | canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY); 395 | canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F); 396 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); 397 | canvas.restore(); 398 | canvas.save(); 399 | canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight)); 400 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); 401 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); 402 | canvas.restore(); 403 | } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) { 404 | // 中间条目 405 | canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); 406 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); 407 | int preSelectedItem = adapter.indexOf(visibles[counter]); 408 | if (preSelectedItem != -1) { 409 | selectedItem = preSelectedItem; 410 | } 411 | } else { 412 | // 其他条目 413 | canvas.save(); 414 | canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); 415 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); 416 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); 417 | canvas.restore(); 418 | } 419 | canvas.restore(); 420 | } 421 | counter++; 422 | } 423 | } 424 | 425 | //递归计算出对应的index 426 | private int getLoopMappingIndex(int index) { 427 | if (index < 0) { 428 | index = index + adapter.getItemsCount(); 429 | index = getLoopMappingIndex(index); 430 | } else if (index > adapter.getItemsCount() - 1) { 431 | index = index - adapter.getItemsCount(); 432 | index = getLoopMappingIndex(index); 433 | } 434 | return index; 435 | } 436 | 437 | /** 438 | * 根据传进来的对象获取getPickerViewText()方法,来获取需要显示的值 439 | * @param item 数据源的item 440 | * @return 对应显示的字符串 441 | */ 442 | private String getContentText(Object item) { 443 | if (item == null) { 444 | return ""; 445 | } 446 | else if (item instanceof IPickerViewData) { 447 | return ((IPickerViewData) item).getPickerViewText(); 448 | } 449 | return item.toString(); 450 | } 451 | 452 | private void measuredCenterContentStart(String content) { 453 | Rect rect = new Rect(); 454 | paintCenterText.getTextBounds(content, 0, content.length(), rect); 455 | switch (mGravity) { 456 | case Gravity.CENTER: 457 | drawCenterContentStart = (int) ((measuredWidth - rect.width()) * 0.5); 458 | break; 459 | case Gravity.LEFT: 460 | drawCenterContentStart = 0; 461 | break; 462 | case Gravity.RIGHT: 463 | drawCenterContentStart = measuredWidth - rect.width(); 464 | break; 465 | } 466 | } 467 | 468 | private void measuredOutContentStart(String content) { 469 | Rect rect = new Rect(); 470 | paintOuterText.getTextBounds(content, 0, content.length(), rect); 471 | switch (mGravity) { 472 | case Gravity.CENTER: 473 | drawOutContentStart = (int) ((measuredWidth - rect.width()) * 0.5); 474 | break; 475 | case Gravity.LEFT: 476 | drawOutContentStart = 0; 477 | break; 478 | case Gravity.RIGHT: 479 | drawOutContentStart = measuredWidth - rect.width(); 480 | break; 481 | } 482 | } 483 | 484 | @Override 485 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 486 | this.widthMeasureSpec = widthMeasureSpec; 487 | remeasure(); 488 | setMeasuredDimension(measuredWidth, measuredHeight); 489 | } 490 | 491 | @Override 492 | public boolean onTouchEvent(MotionEvent event) { 493 | boolean eventConsumed = gestureDetector.onTouchEvent(event); 494 | switch (event.getAction()) { 495 | case MotionEvent.ACTION_DOWN: 496 | startTime = System.currentTimeMillis(); 497 | cancelFuture(); 498 | previousY = event.getRawY(); 499 | break; 500 | 501 | case MotionEvent.ACTION_MOVE: 502 | float dy = previousY - event.getRawY(); 503 | previousY = event.getRawY(); 504 | totalScrollY = (int) (totalScrollY + dy); 505 | 506 | // 边界处理。 507 | if (!isLoop) { 508 | float top = -initPosition * itemHeight; 509 | float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight; 510 | if (totalScrollY - itemHeight * 0.3 < top) { 511 | top = totalScrollY - dy; 512 | } else if (totalScrollY + itemHeight * 0.3 > bottom) { 513 | bottom = totalScrollY - dy; 514 | } 515 | 516 | if (totalScrollY < top) { 517 | totalScrollY = (int) top; 518 | } else if (totalScrollY > bottom) { 519 | totalScrollY = (int) bottom; 520 | } 521 | } 522 | break; 523 | 524 | case MotionEvent.ACTION_UP: 525 | default: 526 | if (!eventConsumed) { 527 | float y = event.getY(); 528 | double l = Math.acos((radius - y) / radius) * radius; 529 | int circlePosition = (int) ((l + itemHeight / 2) / itemHeight); 530 | 531 | float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight; 532 | mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset); 533 | 534 | if ((System.currentTimeMillis() - startTime) > 120) { 535 | // 处理拖拽事件 536 | smoothScroll(ACTION.DAGGLE); 537 | } else { 538 | // 处理条目点击事件 539 | smoothScroll(ACTION.CLICK); 540 | } 541 | } 542 | break; 543 | } 544 | invalidate(); 545 | 546 | return true; 547 | } 548 | 549 | /** 550 | * 获取Item个数 551 | * @return item个数 552 | */ 553 | public int getItemsCount() { 554 | return adapter != null ? adapter.getItemsCount() : 0; 555 | } 556 | 557 | /** 558 | * 附加在右边的单位字符串 559 | * @param label 单位 560 | */ 561 | public void setLabel(String label) { 562 | this.label = label; 563 | } 564 | 565 | public void setGravity(int gravity) { 566 | this.mGravity = gravity; 567 | } 568 | 569 | public int getTextWidth(Paint paint, String str) { 570 | int iRet = 0; 571 | if (str != null && str.length() > 0) { 572 | int len = str.length(); 573 | float[] widths = new float[len]; 574 | paint.getTextWidths(str, widths); 575 | for (int j = 0; j < len; j++) { 576 | iRet += (int) Math.ceil(widths[j]); 577 | } 578 | } 579 | return iRet; 580 | } 581 | } -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/listener/OnDismissListener.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.listener; 2 | 3 | /** 4 | * Created by Sai on 15/8/9. 5 | */ 6 | public interface OnDismissListener { 7 | public void onDismiss(Object o); 8 | } 9 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/listener/OnItemSelectedListener.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.listener; 2 | 3 | 4 | public interface OnItemSelectedListener { 5 | void onItemSelected(int index); 6 | } 7 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/model/IPickerViewData.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.model; 2 | 3 | /** 4 | * Created by Sai on 2016/7/13. 5 | */ 6 | public interface IPickerViewData { 7 | String getPickerViewText(); 8 | } 9 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/utils/PickerViewAnimateUtil.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.utils; 2 | 3 | import android.view.Gravity; 4 | 5 | import com.bigkoo.pickerview.R; 6 | 7 | /** 8 | * Created by Sai on 15/8/9. 9 | */ 10 | public class PickerViewAnimateUtil { 11 | private static final int INVALID = -1; 12 | /** 13 | * Get default animation resource when not defined by the user 14 | * 15 | * @param gravity the gravity of the dialog 16 | * @param isInAnimation determine if is in or out animation. true when is is 17 | * @return the id of the animation resource 18 | */ 19 | public static int getAnimationResource(int gravity, boolean isInAnimation) { 20 | switch (gravity) { 21 | case Gravity.BOTTOM: 22 | return isInAnimation ? R.anim.slide_in_bottom : R.anim.slide_out_bottom; 23 | } 24 | return INVALID; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/view/BasePickerView.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.view; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.view.Gravity; 6 | import android.view.LayoutInflater; 7 | import android.view.MotionEvent; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.view.animation.Animation; 11 | import android.view.animation.AnimationUtils; 12 | import android.widget.FrameLayout; 13 | 14 | import com.bigkoo.pickerview.utils.PickerViewAnimateUtil; 15 | import com.bigkoo.pickerview.R; 16 | import com.bigkoo.pickerview.listener.OnDismissListener; 17 | 18 | /** 19 | * Created by Sai on 15/11/22. 20 | * 精仿iOSPickerViewController控件 21 | */ 22 | public class BasePickerView { 23 | private final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( 24 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM 25 | ); 26 | 27 | private Context context; 28 | protected ViewGroup contentContainer; 29 | private ViewGroup decorView;//activity的根View 30 | private ViewGroup rootView;//附加View 的 根View 31 | 32 | private OnDismissListener onDismissListener; 33 | private boolean dismissing; 34 | 35 | private Animation outAnim; 36 | private Animation inAnim; 37 | private boolean isShowing; 38 | private int gravity = Gravity.BOTTOM; 39 | 40 | public BasePickerView(Context context){ 41 | this.context = context; 42 | 43 | initViews(); 44 | init(); 45 | initEvents(); 46 | } 47 | 48 | protected void initViews(){ 49 | LayoutInflater layoutInflater = LayoutInflater.from(context); 50 | decorView = (ViewGroup) ((Activity)context).getWindow().getDecorView().findViewById(android.R.id.content); 51 | rootView = (ViewGroup) layoutInflater.inflate(R.layout.layout_basepickerview, decorView, false); 52 | rootView.setLayoutParams(new FrameLayout.LayoutParams( 53 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT 54 | )); 55 | contentContainer = (ViewGroup) rootView.findViewById(R.id.content_container); 56 | contentContainer.setLayoutParams(params); 57 | } 58 | 59 | protected void init() { 60 | inAnim = getInAnimation(); 61 | outAnim = getOutAnimation(); 62 | } 63 | protected void initEvents() { 64 | } 65 | /** 66 | * show的时候调用 67 | * 68 | * @param view 这个View 69 | */ 70 | private void onAttached(View view) { 71 | decorView.addView(view); 72 | contentContainer.startAnimation(inAnim); 73 | } 74 | /** 75 | * 添加这个View到Activity的根视图 76 | */ 77 | public void show() { 78 | if (isShowing()) { 79 | return; 80 | } 81 | isShowing = true; 82 | onAttached(rootView); 83 | } 84 | /** 85 | * 检测该View是不是已经添加到根视图 86 | * @return 如果视图已经存在该View返回true 87 | */ 88 | public boolean isShowing() { 89 | return rootView.getParent() != null || isShowing; 90 | } 91 | 92 | public void dismiss() { 93 | if (dismissing) { 94 | return; 95 | } 96 | 97 | dismissing = true; 98 | 99 | //消失动画 100 | outAnim.setAnimationListener(new Animation.AnimationListener() { 101 | @Override 102 | public void onAnimationStart(Animation animation) { 103 | 104 | } 105 | 106 | @Override 107 | public void onAnimationEnd(Animation animation) { 108 | decorView.post(new Runnable() { 109 | @Override 110 | public void run() { 111 | dismissImmediately(); 112 | } 113 | }); 114 | } 115 | 116 | @Override 117 | public void onAnimationRepeat(Animation animation) { 118 | 119 | } 120 | }); 121 | contentContainer.startAnimation(outAnim); 122 | } 123 | 124 | public void dismissImmediately() { 125 | //从activity根视图移除 126 | decorView.removeView(rootView); 127 | isShowing = false; 128 | dismissing = false; 129 | if (onDismissListener != null) { 130 | onDismissListener.onDismiss(BasePickerView.this); 131 | } 132 | 133 | } 134 | public Animation getInAnimation() { 135 | int res = PickerViewAnimateUtil.getAnimationResource(this.gravity, true); 136 | return AnimationUtils.loadAnimation(context, res); 137 | } 138 | 139 | public Animation getOutAnimation() { 140 | int res = PickerViewAnimateUtil.getAnimationResource(this.gravity, false); 141 | return AnimationUtils.loadAnimation(context, res); 142 | } 143 | 144 | public BasePickerView setOnDismissListener(OnDismissListener onDismissListener) { 145 | this.onDismissListener = onDismissListener; 146 | return this; 147 | } 148 | 149 | public BasePickerView setCancelable(boolean isCancelable) { 150 | View view = rootView.findViewById(R.id.outmost_container); 151 | 152 | if (isCancelable) { 153 | view.setOnTouchListener(onCancelableTouchListener); 154 | } 155 | else{ 156 | view.setOnTouchListener(null); 157 | } 158 | return this; 159 | } 160 | /** 161 | * Called when the user touch on black overlay in order to dismiss the dialog 162 | */ 163 | private final View.OnTouchListener onCancelableTouchListener = new View.OnTouchListener() { 164 | @Override 165 | public boolean onTouch(View v, MotionEvent event) { 166 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 167 | dismiss(); 168 | } 169 | return false; 170 | } 171 | }; 172 | 173 | public View findViewById(int id){ 174 | return contentContainer.findViewById(id); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/view/WheelOptions.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.view; 2 | 3 | import java.util.ArrayList; 4 | import android.view.View; 5 | 6 | import com.bigkoo.pickerview.R; 7 | import com.bigkoo.pickerview.adapter.ArrayWheelAdapter; 8 | import com.bigkoo.pickerview.lib.WheelView; 9 | import com.bigkoo.pickerview.listener.OnItemSelectedListener; 10 | 11 | public class WheelOptions { 12 | private View view; 13 | private WheelView wv_option1; 14 | private WheelView wv_option2; 15 | private WheelView wv_option3; 16 | 17 | private ArrayList mOptions1Items; 18 | private ArrayList> mOptions2Items; 19 | private ArrayList>> mOptions3Items; 20 | 21 | private boolean linkage = false; 22 | private OnItemSelectedListener wheelListener_option1; 23 | private OnItemSelectedListener wheelListener_option2; 24 | 25 | public View getView() { 26 | return view; 27 | } 28 | 29 | public void setView(View view) { 30 | this.view = view; 31 | } 32 | 33 | public WheelOptions(View view) { 34 | super(); 35 | this.view = view; 36 | setView(view); 37 | } 38 | 39 | public void setPicker(ArrayList optionsItems) { 40 | setPicker(optionsItems, null, null, false); 41 | } 42 | 43 | public void setPicker(ArrayList options1Items, 44 | ArrayList> options2Items, boolean linkage) { 45 | setPicker(options1Items, options2Items, null, linkage); 46 | } 47 | 48 | public void setPicker(ArrayList options1Items, 49 | ArrayList> options2Items, 50 | ArrayList>> options3Items, 51 | boolean linkage) { 52 | this.linkage = linkage; 53 | this.mOptions1Items = options1Items; 54 | this.mOptions2Items = options2Items; 55 | this.mOptions3Items = options3Items; 56 | int len = ArrayWheelAdapter.DEFAULT_LENGTH; 57 | if (this.mOptions3Items == null) 58 | len = 8; 59 | if (this.mOptions2Items == null) 60 | len = 12; 61 | // 选项1 62 | wv_option1 = (WheelView) view.findViewById(R.id.options1); 63 | wv_option1.setAdapter(new ArrayWheelAdapter(mOptions1Items, len));// 设置显示数据 64 | wv_option1.setCurrentItem(0);// 初始化时显示的数据 65 | // 选项2 66 | wv_option2 = (WheelView) view.findViewById(R.id.options2); 67 | if (mOptions2Items != null) 68 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items.get(0)));// 设置显示数据 69 | wv_option2.setCurrentItem(wv_option1.getCurrentItem());// 初始化时显示的数据 70 | // 选项3 71 | wv_option3 = (WheelView) view.findViewById(R.id.options3); 72 | if (mOptions3Items != null) 73 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items.get(0).get(0)));// 设置显示数据 74 | wv_option3.setCurrentItem(wv_option3.getCurrentItem());// 初始化时显示的数据 75 | 76 | int textSize = 25; 77 | 78 | wv_option1.setTextSize(textSize); 79 | wv_option2.setTextSize(textSize); 80 | wv_option3.setTextSize(textSize); 81 | 82 | if (this.mOptions2Items == null) 83 | wv_option2.setVisibility(View.GONE); 84 | if (this.mOptions3Items == null) 85 | wv_option3.setVisibility(View.GONE); 86 | 87 | // 联动监听器 88 | wheelListener_option1 = new OnItemSelectedListener() { 89 | 90 | @Override 91 | public void onItemSelected(int index) { 92 | int opt2Select = 0; 93 | if (mOptions2Items != null) { 94 | opt2Select = wv_option2.getCurrentItem();//上一个opt2的选中位置 95 | //新opt2的位置,判断如果旧位置没有超过数据范围,则沿用旧位置,否则选中最后一项 96 | opt2Select = opt2Select >= mOptions2Items.get(index).size() - 1 ? mOptions2Items.get(index).size() - 1 : opt2Select; 97 | 98 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items.get(index))); 99 | wv_option2.setCurrentItem(opt2Select); 100 | } 101 | if (mOptions3Items != null) { 102 | wheelListener_option2.onItemSelected(opt2Select); 103 | } 104 | } 105 | }; 106 | wheelListener_option2 = new OnItemSelectedListener() { 107 | 108 | @Override 109 | public void onItemSelected(int index) { 110 | if (mOptions3Items != null) { 111 | int opt1Select = wv_option1.getCurrentItem(); 112 | opt1Select = opt1Select >= mOptions3Items.size() - 1 ? mOptions3Items.size() - 1 : opt1Select; 113 | index = index >= mOptions2Items.get(opt1Select).size() - 1 ? mOptions2Items.get(opt1Select).size() - 1 : index; 114 | int opt3 = wv_option3.getCurrentItem();//上一个opt3的选中位置 115 | //新opt3的位置,判断如果旧位置没有超过数据范围,则沿用旧位置,否则选中最后一项 116 | opt3 = opt3 >= mOptions3Items.get(opt1Select).get(index).size() - 1 ? mOptions3Items.get(opt1Select).get(index).size() - 1 : opt3; 117 | 118 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items.get(wv_option1.getCurrentItem()).get(index))); 119 | wv_option3.setCurrentItem(opt3); 120 | 121 | } 122 | } 123 | }; 124 | 125 | // // 添加联动监听 126 | if (options2Items != null && linkage) 127 | wv_option1.setOnItemSelectedListener(wheelListener_option1); 128 | if (options3Items != null && linkage) 129 | wv_option2.setOnItemSelectedListener(wheelListener_option2); 130 | } 131 | 132 | /** 133 | * 设置选项的单位 134 | * @param label1 单位 135 | * @param label2 单位 136 | * @param label3 单位 137 | */ 138 | public void setLabels(String label1, String label2, String label3) { 139 | if (label1 != null) 140 | wv_option1.setLabel(label1); 141 | if (label2 != null) 142 | wv_option2.setLabel(label2); 143 | if (label3 != null) 144 | wv_option3.setLabel(label3); 145 | } 146 | 147 | /** 148 | * 设置是否循环滚动 149 | * @param cyclic 是否循环 150 | */ 151 | public void setCyclic(boolean cyclic) { 152 | wv_option1.setCyclic(cyclic); 153 | wv_option2.setCyclic(cyclic); 154 | wv_option3.setCyclic(cyclic); 155 | } 156 | 157 | /** 158 | * 分别设置第一二三级是否循环滚动 159 | * @param cyclic1,cyclic2,cyclic3 是否循环 160 | */ 161 | public void setCyclic(boolean cyclic1,boolean cyclic2,boolean cyclic3) { 162 | wv_option1.setCyclic(cyclic1); 163 | wv_option2.setCyclic(cyclic2); 164 | wv_option3.setCyclic(cyclic3); 165 | } 166 | /** 167 | * 设置第二级是否循环滚动 168 | * @param cyclic 是否循环 169 | */ 170 | public void setOption2Cyclic(boolean cyclic) { 171 | wv_option2.setCyclic(cyclic); 172 | } 173 | 174 | /** 175 | * 设置第三级是否循环滚动 176 | * @param cyclic 是否循环 177 | */ 178 | public void setOption3Cyclic(boolean cyclic) { 179 | wv_option3.setCyclic(cyclic); 180 | } 181 | 182 | /** 183 | * 返回当前选中的结果对应的位置数组 因为支持三级联动效果,分三个级别索引,0,1,2 184 | * @return 索引数组 185 | */ 186 | public int[] getCurrentItems() { 187 | int[] currentItems = new int[3]; 188 | currentItems[0] = wv_option1.getCurrentItem(); 189 | currentItems[1] = wv_option2.getCurrentItem(); 190 | currentItems[2] = wv_option3.getCurrentItem(); 191 | return currentItems; 192 | } 193 | 194 | public void setCurrentItems(int option1, int option2, int option3) { 195 | if(linkage){ 196 | itemSelected(option1, option2, option3); 197 | } 198 | wv_option1.setCurrentItem(option1); 199 | wv_option2.setCurrentItem(option2); 200 | wv_option3.setCurrentItem(option3); 201 | } 202 | 203 | private void itemSelected(int opt1Select, int opt2Select, int opt3Select) { 204 | if (mOptions2Items != null) { 205 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items 206 | .get(opt1Select))); 207 | wv_option2.setCurrentItem(opt2Select); 208 | } 209 | if (mOptions3Items != null) { 210 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items 211 | .get(opt1Select).get( 212 | opt2Select))); 213 | wv_option3.setCurrentItem(opt3Select); 214 | } 215 | } 216 | 217 | 218 | } 219 | -------------------------------------------------------------------------------- /pickerview/src/main/java/com/bigkoo/pickerview/view/WheelTime.java: -------------------------------------------------------------------------------- 1 | package com.bigkoo.pickerview.view; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import com.bigkoo.pickerview.R; 9 | import com.bigkoo.pickerview.TimePickerView.Type; 10 | import com.bigkoo.pickerview.adapter.NumericWheelAdapter; 11 | import com.bigkoo.pickerview.lib.WheelView; 12 | import com.bigkoo.pickerview.listener.OnItemSelectedListener; 13 | 14 | import android.content.Context; 15 | import android.view.View; 16 | 17 | 18 | public class WheelTime { 19 | public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 20 | private View view; 21 | private WheelView wv_year; 22 | private WheelView wv_month; 23 | private WheelView wv_day; 24 | private WheelView wv_hours; 25 | private WheelView wv_mins; 26 | 27 | private Type type; 28 | public static final int DEFULT_START_YEAR = 1990; 29 | public static final int DEFULT_END_YEAR = 2100; 30 | private int startYear = DEFULT_START_YEAR; 31 | private int endYear = DEFULT_END_YEAR; 32 | 33 | 34 | 35 | public WheelTime(View view) { 36 | super(); 37 | this.view = view; 38 | type = Type.ALL; 39 | setView(view); 40 | } 41 | public WheelTime(View view,Type type) { 42 | super(); 43 | this.view = view; 44 | this.type = type; 45 | setView(view); 46 | } 47 | public void setPicker(int year ,int month,int day){ 48 | this.setPicker(year, month, day, 0, 0); 49 | } 50 | 51 | public void setPicker(int year ,int month ,int day,int h,int m) { 52 | // 添加大小月月份并将其转换为list,方便之后的判断 53 | String[] months_big = { "1", "3", "5", "7", "8", "10", "12" }; 54 | String[] months_little = { "4", "6", "9", "11" }; 55 | 56 | final List list_big = Arrays.asList(months_big); 57 | final List list_little = Arrays.asList(months_little); 58 | 59 | Context context = view.getContext(); 60 | // 年 61 | wv_year = (WheelView) view.findViewById(R.id.year); 62 | wv_year.setAdapter(new NumericWheelAdapter(startYear, endYear));// 设置"年"的显示数据 63 | wv_year.setLabel(context.getString(R.string.pickerview_year));// 添加文字 64 | wv_year.setCurrentItem(year - startYear);// 初始化时显示的数据 65 | 66 | // 月 67 | wv_month = (WheelView) view.findViewById(R.id.month); 68 | wv_month.setAdapter(new NumericWheelAdapter(1, 12)); 69 | wv_month.setLabel(context.getString(R.string.pickerview_month)); 70 | wv_month.setCurrentItem(month); 71 | 72 | // 日 73 | wv_day = (WheelView) view.findViewById(R.id.day); 74 | // 判断大小月及是否闰年,用来确定"日"的数据 75 | if (list_big.contains(String.valueOf(month + 1))) { 76 | wv_day.setAdapter(new NumericWheelAdapter(1, 31)); 77 | } else if (list_little.contains(String.valueOf(month + 1))) { 78 | wv_day.setAdapter(new NumericWheelAdapter(1, 30)); 79 | } else { 80 | // 闰年 81 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) 82 | wv_day.setAdapter(new NumericWheelAdapter(1, 29)); 83 | else 84 | wv_day.setAdapter(new NumericWheelAdapter(1, 28)); 85 | } 86 | wv_day.setLabel(context.getString(R.string.pickerview_day)); 87 | wv_day.setCurrentItem(day - 1); 88 | 89 | 90 | wv_hours = (WheelView)view.findViewById(R.id.hour); 91 | wv_hours.setAdapter(new NumericWheelAdapter(0, 23)); 92 | wv_hours.setLabel(context.getString(R.string.pickerview_hours));// 添加文字 93 | wv_hours.setCurrentItem(h); 94 | 95 | wv_mins = (WheelView)view.findViewById(R.id.min); 96 | wv_mins.setAdapter(new NumericWheelAdapter(0, 59)); 97 | wv_mins.setLabel(context.getString(R.string.pickerview_minutes));// 添加文字 98 | wv_mins.setCurrentItem(m); 99 | 100 | // 添加"年"监听 101 | OnItemSelectedListener wheelListener_year = new OnItemSelectedListener() { 102 | @Override 103 | public void onItemSelected(int index) { 104 | int year_num = index + startYear; 105 | // 判断大小月及是否闰年,用来确定"日"的数据 106 | int maxItem = 30; 107 | if (list_big 108 | .contains(String.valueOf(wv_month.getCurrentItem() + 1))) { 109 | wv_day.setAdapter(new NumericWheelAdapter(1, 31)); 110 | maxItem = 31; 111 | } else if (list_little.contains(String.valueOf(wv_month 112 | .getCurrentItem() + 1))) { 113 | wv_day.setAdapter(new NumericWheelAdapter(1, 30)); 114 | maxItem = 30; 115 | } else { 116 | if ((year_num % 4 == 0 && year_num % 100 != 0) 117 | || year_num % 400 == 0){ 118 | wv_day.setAdapter(new NumericWheelAdapter(1, 29)); 119 | maxItem = 29; 120 | } 121 | else{ 122 | wv_day.setAdapter(new NumericWheelAdapter(1, 28)); 123 | maxItem = 28; 124 | } 125 | } 126 | if (wv_day.getCurrentItem() > maxItem - 1){ 127 | wv_day.setCurrentItem(maxItem - 1); 128 | } 129 | } 130 | }; 131 | // 添加"月"监听 132 | OnItemSelectedListener wheelListener_month = new OnItemSelectedListener() { 133 | @Override 134 | public void onItemSelected(int index) { 135 | int month_num = index + 1; 136 | int maxItem = 30; 137 | // 判断大小月及是否闰年,用来确定"日"的数据 138 | if (list_big.contains(String.valueOf(month_num))) { 139 | wv_day.setAdapter(new NumericWheelAdapter(1, 31)); 140 | maxItem = 31; 141 | } else if (list_little.contains(String.valueOf(month_num))) { 142 | wv_day.setAdapter(new NumericWheelAdapter(1, 30)); 143 | maxItem = 30; 144 | } else { 145 | if (((wv_year.getCurrentItem() + startYear) % 4 == 0 && (wv_year 146 | .getCurrentItem() + startYear) % 100 != 0) 147 | || (wv_year.getCurrentItem() + startYear) % 400 == 0){ 148 | wv_day.setAdapter(new NumericWheelAdapter(1, 29)); 149 | maxItem = 29; 150 | } 151 | else{ 152 | wv_day.setAdapter(new NumericWheelAdapter(1, 28)); 153 | maxItem = 28; 154 | } 155 | } 156 | if (wv_day.getCurrentItem() > maxItem - 1){ 157 | wv_day.setCurrentItem(maxItem - 1); 158 | } 159 | 160 | } 161 | }; 162 | wv_year.setOnItemSelectedListener(wheelListener_year); 163 | wv_month.setOnItemSelectedListener(wheelListener_month); 164 | 165 | // 根据屏幕密度来指定选择器字体的大小(不同屏幕可能不同) 166 | int textSize = 6; 167 | switch(type){ 168 | case ALL: 169 | textSize = textSize * 3; 170 | break; 171 | case YEAR_MONTH_DAY: 172 | textSize = textSize * 4; 173 | wv_hours.setVisibility(View.GONE); 174 | wv_mins.setVisibility(View.GONE); 175 | break; 176 | case HOURS_MINS: 177 | textSize = textSize * 4; 178 | wv_year.setVisibility(View.GONE); 179 | wv_month.setVisibility(View.GONE); 180 | wv_day.setVisibility(View.GONE); 181 | break; 182 | case MONTH_DAY_HOUR_MIN: 183 | textSize = textSize * 3; 184 | wv_year.setVisibility(View.GONE); 185 | break; 186 | case YEAR_MONTH: 187 | textSize = textSize * 4; 188 | wv_day.setVisibility(View.GONE); 189 | wv_hours.setVisibility(View.GONE); 190 | wv_mins.setVisibility(View.GONE); 191 | } 192 | wv_day.setTextSize(textSize); 193 | wv_month.setTextSize(textSize); 194 | wv_year.setTextSize(textSize); 195 | wv_hours.setTextSize(textSize); 196 | wv_mins.setTextSize(textSize); 197 | 198 | } 199 | 200 | /** 201 | * 设置是否循环滚动 202 | * @param cyclic 203 | */ 204 | public void setCyclic(boolean cyclic){ 205 | wv_year.setCyclic(cyclic); 206 | wv_month.setCyclic(cyclic); 207 | wv_day.setCyclic(cyclic); 208 | wv_hours.setCyclic(cyclic); 209 | wv_mins.setCyclic(cyclic); 210 | } 211 | public String getTime() { 212 | StringBuffer sb = new StringBuffer(); 213 | sb.append((wv_year.getCurrentItem() + startYear)).append("-") 214 | .append((wv_month.getCurrentItem() + 1)).append("-") 215 | .append((wv_day.getCurrentItem() + 1)).append(" ") 216 | .append(wv_hours.getCurrentItem()).append(":") 217 | .append(wv_mins.getCurrentItem()); 218 | return sb.toString(); 219 | } 220 | 221 | public View getView() { 222 | return view; 223 | } 224 | 225 | public void setView(View view) { 226 | this.view = view; 227 | } 228 | 229 | public int getStartYear() { 230 | return startYear; 231 | } 232 | 233 | public void setStartYear(int startYear) { 234 | this.startYear = startYear; 235 | } 236 | 237 | public int getEndYear() { 238 | return endYear; 239 | } 240 | 241 | public void setEndYear(int endYear) { 242 | this.endYear = endYear; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /pickerview/src/main/res/anim/slide_in_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /pickerview/src/main/res/anim/slide_out_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /pickerview/src/main/res/drawable/selector_pickerview_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /pickerview/src/main/res/layout/include_pickerview_topbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |