items) {
+ this(items, DEFAULT_LENGTH);
+ }
+
+ @Override
+ public String getItem(int index) {
+ if (index >= 0 && index < items.size()) {
+ return items.get(index).toString();
+ }
+ return null;
+ }
+
+ @Override
+ public int getItemsCount() {
+ return items.size();
+ }
+
+ @Override
+ public int getMaximumLength() {
+ return length;
+ }
+
+}
diff --git a/TimeLib/src/pickerview/lib/DensityUtil.java b/TimeLib/src/pickerview/lib/DensityUtil.java
new file mode 100644
index 0000000..f1e2b7b
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/DensityUtil.java
@@ -0,0 +1,25 @@
+package pickerview.lib;
+
+import android.content.Context;
+
+public class DensityUtil {
+ private static float scale;
+
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ */
+ public static int dip2px(Context context, float dpValue) {
+ if (scale == 0)
+ scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+
+ /**
+ * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
+ */
+ public static int px2dip(Context context, float pxValue) {
+ if (scale == 0)
+ scale = context.getResources().getDisplayMetrics().density;
+ return (int) (pxValue / scale + 0.5f);
+ }
+}
diff --git a/TimeLib/src/pickerview/lib/NumericWheelAdapter.java b/TimeLib/src/pickerview/lib/NumericWheelAdapter.java
new file mode 100644
index 0000000..0598845
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/NumericWheelAdapter.java
@@ -0,0 +1,73 @@
+package pickerview.lib;
+
+
+/**
+ * Numeric Wheel adapter.
+ */
+public class NumericWheelAdapter implements WheelAdapter {
+
+ /** The default min value */
+ public static final int DEFAULT_MAX_VALUE = 9;
+
+ /** The default max value */
+ private static final int DEFAULT_MIN_VALUE = 0;
+
+ // Values
+ private int minValue;
+ private int maxValue;
+
+ // format
+ private String format;
+
+ /**
+ * Default constructor
+ */
+ public NumericWheelAdapter() {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE);
+ }
+
+ /**
+ * Constructor
+ * @param minValue the wheel min value
+ * @param maxValue the wheel max value
+ */
+ public NumericWheelAdapter(int minValue, int maxValue) {
+ this(minValue, maxValue, null);
+ }
+
+ /**
+ * Constructor
+ * @param minValue the wheel min value
+ * @param maxValue the wheel max value
+ * @param format the format string
+ */
+ public NumericWheelAdapter(int minValue, int maxValue, String format) {
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ this.format = format;
+ }
+
+ @Override
+ public String getItem(int index) {
+ if (index >= 0 && index < getItemsCount()) {
+ int value = minValue + index;
+ return format != null ? String.format(format, value) : Integer.toString(value);
+ }
+ return null;
+ }
+
+ @Override
+ public int getItemsCount() {
+ return maxValue - minValue + 1;
+ }
+
+ @Override
+ public int getMaximumLength() {
+ int max = Math.max(Math.abs(maxValue), Math.abs(minValue));
+ int maxLen = Integer.toString(max).length();
+ if (minValue < 0) {
+ maxLen++;
+ }
+ return maxLen;
+ }
+}
diff --git a/TimeLib/src/pickerview/lib/OnWheelChangedListener.java b/TimeLib/src/pickerview/lib/OnWheelChangedListener.java
new file mode 100644
index 0000000..a5178a4
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/OnWheelChangedListener.java
@@ -0,0 +1,17 @@
+package pickerview.lib;
+
+/**
+ * Wheel changed listener interface.
+ * The currentItemChanged() method is called whenever current wheel positions is changed:
+ *
New Wheel position is set
+ * Wheel view is scrolled
+ */
+public interface OnWheelChangedListener {
+ /**
+ * Callback method to be invoked when current item changed
+ * @param wheel the wheel view whose state has changed
+ * @param oldValue the old value of current item
+ * @param newValue the new value of current item
+ */
+ void onChanged(WheelView wheel, int oldValue, int newValue);
+}
diff --git a/TimeLib/src/pickerview/lib/OnWheelScrollListener.java b/TimeLib/src/pickerview/lib/OnWheelScrollListener.java
new file mode 100644
index 0000000..902ae34
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/OnWheelScrollListener.java
@@ -0,0 +1,18 @@
+package pickerview.lib;
+
+/**
+ * Wheel scrolled listener interface.
+ */
+public interface OnWheelScrollListener {
+ /**
+ * Callback method to be invoked when scrolling started.
+ * @param wheel the wheel view whose state has changed.
+ */
+ void onScrollingStarted(WheelView wheel);
+
+ /**
+ * Callback method to be invoked when scrolling ended.
+ * @param wheel the wheel view whose state has changed.
+ */
+ void onScrollingFinished(WheelView wheel);
+}
diff --git a/TimeLib/src/pickerview/lib/ScreenInfo.java b/TimeLib/src/pickerview/lib/ScreenInfo.java
new file mode 100644
index 0000000..934ec20
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/ScreenInfo.java
@@ -0,0 +1,76 @@
+package pickerview.lib;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.util.DisplayMetrics;
+
+/**
+ * 得到屏幕宽高密度等
+ */
+public class ScreenInfo {
+ private Activity activity;
+ /** 屏幕宽度(像素)*/
+ private int width;
+ /**屏幕高度(像素)*/
+ private int height;
+ /**屏幕密度(0.75 / 1.0 / 1.5)*/
+ private float density;
+ /**屏幕密度DPI(120 / 160 / 240)*/
+ private int densityDpi;
+ public Activity getActivity() {
+ return activity;
+ }
+
+ public void setActivity(Activity activity) {
+ this.activity = activity;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public float getDensity() {
+ return density;
+ }
+
+ public void setDensity(float density) {
+ this.density = density;
+ }
+
+ public int getDensityDpi() {
+ return densityDpi;
+ }
+
+ public void setDensityDpi(int densityDpi) {
+ this.densityDpi = densityDpi;
+ }
+
+ public ScreenInfo(Activity activity){
+ this.activity = activity;
+ ini();
+ }
+
+ @SuppressLint("NewApi")
+ private void ini(){
+ DisplayMetrics metric = new DisplayMetrics();
+ activity.getWindowManager().getDefaultDisplay().getMetrics(metric);
+ width = metric.widthPixels;
+ height = metric.heightPixels;
+ density = metric.density;
+ densityDpi = metric.densityDpi;
+ }
+
+
+}
diff --git a/TimeLib/src/pickerview/lib/WheelAdapter.java b/TimeLib/src/pickerview/lib/WheelAdapter.java
new file mode 100644
index 0000000..d825c74
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/WheelAdapter.java
@@ -0,0 +1,25 @@
+package pickerview.lib;
+
+public interface WheelAdapter {
+ /**
+ * Gets items count
+ * @return the count of wheel items
+ */
+ public int getItemsCount();
+
+ /**
+ * Gets a wheel item by index.
+ *
+ * @param index the item index
+ * @return the wheel item text or null
+ */
+ public String getItem(int index);
+
+ /**
+ * Gets maximum item length. It is used to determine the wheel width.
+ * If -1 is returned there will be used the default wheel width.
+ *
+ * @return the maximum item length or -1
+ */
+ public int getMaximumLength();
+}
diff --git a/TimeLib/src/pickerview/lib/WheelOptions.java b/TimeLib/src/pickerview/lib/WheelOptions.java
new file mode 100644
index 0000000..d753588
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/WheelOptions.java
@@ -0,0 +1,227 @@
+package pickerview.lib;
+
+import java.util.ArrayList;
+
+import android.text.TextUtils;
+import android.view.View;
+
+import com.bigkoo.pickerview.R;
+
+public class WheelOptions {
+ private View view;
+ private WheelView wv_option1;
+ private WheelView wv_option2;
+ private WheelView wv_option3;
+
+ private ArrayList mOptions1Items;
+ private ArrayList> mOptions2Items;
+ private ArrayList>> mOptions3Items;
+ public int screenheight;
+
+ public View getView() {
+ return view;
+ }
+
+ public void setView(View view) {
+ this.view = view;
+ }
+
+ public WheelOptions(View view) {
+ super();
+ this.view = view;
+ setView(view);
+ }
+
+ public void setPicker(ArrayList optionsItems) {
+ setPicker(optionsItems, null, null, false);
+ }
+
+ public void setPicker(ArrayList options1Items,
+ ArrayList> options2Items, boolean linkage) {
+ setPicker(options1Items, options2Items, null, linkage);
+ }
+
+ public void setPicker(ArrayList options1Items,
+ ArrayList> options2Items,
+ ArrayList>> options3Items,
+ boolean linkage) {
+ this.mOptions1Items = options1Items;
+ this.mOptions2Items = options2Items;
+ this.mOptions3Items = options3Items;
+ int len = ArrayWheelAdapter.DEFAULT_LENGTH;
+ if (mOptions3Items != null) {
+ len = 4;//三级联动选择
+ } else if (mOptions2Items != null) {
+ len = 10;//二级联动选择
+ } else {
+ len = 20;//单项选择
+ }
+// if (this.mOptions3Items == null)
+// len = 8;
+// if (this.mOptions2Items == null)
+// len = 12;
+ // 选项1
+ wv_option1 = (WheelView) view.findViewById(R.id.options1);
+ wv_option1.setAdapter(new OptionAdapter(mOptions1Items, len));// 设置显示数据
+ wv_option1.setCurrentItem(0);// 初始化时显示的数据
+ // 选项2
+ wv_option2 = (WheelView) view.findViewById(R.id.options2);
+ if (mOptions2Items != null){
+ wv_option2.setAdapter(new OptionAdapter(mOptions2Items.get(0), len));// 设置显示数据
+ }
+ wv_option2.setCurrentItem(wv_option1.getCurrentItem());// 初始化时显示的数据
+ // 选项3
+ wv_option3 = (WheelView) view.findViewById(R.id.options3);
+ if (mOptions3Items != null){
+ wv_option3.setAdapter(new OptionAdapter(mOptions3Items.get(0).get(0), len));// 设置显示数据
+ mOptions3Items.get(0).get(0).size();
+ }
+ wv_option3.setCurrentItem(wv_option3.getCurrentItem());// 初始化时显示的数据
+
+ // 根据屏幕密度来指定选择器字体的大小(不同屏幕可能不同)
+ int textSize = (int) ((screenheight / 100) * 3.0f);
+
+ wv_option1.TEXT_SIZE = textSize;
+ wv_option2.TEXT_SIZE = textSize;
+ wv_option3.TEXT_SIZE = textSize;
+
+ if (this.mOptions2Items == null)
+ wv_option2.setVisibility(View.GONE);
+ if (this.mOptions3Items == null)
+ wv_option3.setVisibility(View.GONE);
+ final int finalLen = len;
+ // 联动监听器
+ OnWheelChangedListener wheelListener_option1 = new OnWheelChangedListener() {
+
+ @Override
+ public void onChanged(WheelView wheel, int oldValue, int newValue) {
+ if (mOptions2Items != null) {
+ wv_option2.setAdapter(new OptionAdapter(mOptions2Items.get(wv_option1.getCurrentItem()), finalLen));
+ wv_option2.setCurrentItem(0);
+ }
+ if (mOptions3Items != null) {
+ wv_option3.setAdapter(new OptionAdapter(mOptions3Items
+ .get(wv_option1.getCurrentItem()).get(
+ wv_option2.getCurrentItem()), finalLen));
+ wv_option3.setCurrentItem(0);
+ }
+ }
+ };
+ OnWheelChangedListener wheelListener_option2 = new OnWheelChangedListener() {
+
+ @Override
+ public void onChanged(WheelView wheel, int oldValue, int newValue) {
+ if (mOptions3Items != null) {
+ wv_option3.setAdapter(new OptionAdapter(mOptions3Items
+ .get(wv_option1.getCurrentItem()).get(
+ wv_option2.getCurrentItem()), finalLen));
+ wv_option3.setCurrentItem(0);
+ }
+ }
+ };
+ // 添加联动监听
+ if (options2Items != null && linkage){
+ wv_option1.addChangingListener(wheelListener_option1);
+ }
+ if (options3Items != null && linkage){
+ wv_option2.addChangingListener(wheelListener_option2);
+ }
+ }
+
+ private class OptionAdapter extends ArrayWheelAdapter {
+
+ public OptionAdapter(ArrayList items, int length) {
+ super(items, length);
+ }
+
+ @Override
+ public String getItem(int index) {
+ String item = super.getItem(index);
+ // FIXME: 2015/10/23 如果名称较长,整个文字排版上就容易有问题,故截取显条目字数
+ if (TextUtils.isEmpty(item)) {
+ return item;
+ }
+ if (wv_option3.isShown()) {
+ item = cutString(item, 4, "...");
+ } else if (wv_option2.isShown()) {
+ item = cutString(item, 10, "...");
+ } else {
+ item = cutString(item, 20, "...");
+ }
+ return item;
+ }
+
+ private String cutString(String text, int length, String endWith) {
+ //GBK每个汉字长2,UTF-8每个汉字长度为3
+ final String CHINESE_ENCODE = "UTF-8";
+ final int CHINESE_LENGTH = 3;
+ int textLength = text.length();
+ int byteLength = 0;
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < textLength && byteLength < length * CHINESE_LENGTH; i++) {
+ String str_i = text.substring(i, i + 1);
+ if (str_i.getBytes().length == 1) {
+ byteLength++;//英文
+ } else {
+ byteLength += CHINESE_LENGTH;//中文
+ }
+ sb.append(str_i);
+ }
+ try {
+ if (byteLength < text.getBytes(CHINESE_ENCODE).length) {
+ sb.append(endWith);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return sb.toString();
+ }
+
+ }
+
+ /**
+ * 设置选项的单位
+ *
+ * @param label1
+ * @param label2
+ * @param label3
+ */
+ public void setLabels(String label1, String label2, String label3) {
+ if (label1 != null)
+ wv_option1.setLabel(label1);
+ if (label2 != null)
+ wv_option2.setLabel(label2);
+ if (label3 != null)
+ wv_option3.setLabel(label3);
+ }
+
+ /**
+ * 设置是否循环滚动
+ *
+ * @param cyclic
+ */
+ public void setCyclic(boolean cyclic) {
+ wv_option1.setCyclic(cyclic);
+ wv_option2.setCyclic(cyclic);
+ wv_option3.setCyclic(cyclic);
+ }
+
+ /**
+ * 返回当前选中的结果对应的位置数组 因为支持三级联动效果,分三个级别索引,0,1,2
+ *
+ * @return
+ */
+ public int[] getCurrentItems() {
+ int[] currentItems = new int[3];
+ currentItems[0] = wv_option1.getCurrentItem();
+ currentItems[1] = wv_option2.getCurrentItem();
+ currentItems[2] = wv_option3.getCurrentItem();
+ return currentItems;
+ }
+
+ public void setCurrentItems(int option1, int option2, int option3) {
+ wv_option1.setCurrentItem(option1);
+ wv_option2.setCurrentItem(option2);
+ wv_option3.setCurrentItem(option3);
+ }
+}
diff --git a/TimeLib/src/pickerview/lib/WheelTime.java b/TimeLib/src/pickerview/lib/WheelTime.java
new file mode 100644
index 0000000..e168aa4
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/WheelTime.java
@@ -0,0 +1,258 @@
+package pickerview.lib;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+
+import com.bigkoo.pickerview.R;
+
+import android.R.layout;
+import android.content.Context;
+import android.view.View;
+import pickerview.TimePopupWindow.Type;
+
+
+public class WheelTime {
+ public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ private View view;
+ private WheelView wv_year;
+ private WheelView wv_month;
+ private WheelView wv_day;
+ private WheelView wv_hours;
+ private WheelView wv_mins;
+ public int screenheight;
+
+ private Type type;
+// private static int START_YEAR = 1990, END_YEAR = 2100;
+
+ private static int START_YEAR = 1900, END_YEAR = Calendar.getInstance().get(Calendar.YEAR);
+
+
+ public View getView() {
+ return view;
+ }
+
+ public void setView(View view) {
+ this.view = view;
+ }
+
+ public static int getSTART_YEAR() {
+ return START_YEAR;
+ }
+
+ public static void setSTART_YEAR(int sTART_YEAR) {
+ START_YEAR = sTART_YEAR;
+ }
+
+ public static int getEND_YEAR() {
+ return END_YEAR;
+ }
+
+ public static void setEND_YEAR(int eND_YEAR) {
+ END_YEAR = eND_YEAR;
+ }
+
+ public WheelTime(View view) {
+ super();
+ this.view = view;
+ type = Type.ALL;
+ setView(view);
+ }
+ public WheelTime(View view,Type type) {
+ super();
+ this.view = view;
+ this.type = type;
+ setView(view);
+ }
+ public void setPicker(int year ,int month,int day){
+ this.setPicker(year, month, day, 0, 0);
+ }
+ //
+ public void setPicker(int year ){
+ this.setPicker(year, 0, 0);
+ }
+
+ /**
+ * @Description: TODO 弹出日期时间选择器
+ */
+ public void setPicker(int year ,int month ,int day,int h,int m) {
+ // 添加大小月月份并将其转换为list,方便之后的判断
+ String[] months_big = { "1", "3", "5", "7", "8", "10", "12" };
+ String[] months_little = { "4", "6", "9", "11" };
+
+ final List list_big = Arrays.asList(months_big);
+ final List list_little = Arrays.asList(months_little);
+
+ Context context = view.getContext();
+ // 年
+ wv_year = (WheelView) view.findViewById(R.id.year);
+ wv_year.setAdapter(new NumericWheelAdapter(START_YEAR, END_YEAR));// 设置"年"的显示数据
+ wv_year.setLabel(context.getString(R.string.pickerview_year));// 添加文字
+ wv_year.setCurrentItem(year - START_YEAR);// 初始化时显示的数据
+
+ // 月
+ wv_month = (WheelView) view.findViewById(R.id.month);
+ wv_month.setAdapter(new NumericWheelAdapter(1, 12));
+ wv_month.setLabel(context.getString(R.string.pickerview_month));
+ wv_month.setCurrentItem(month);
+
+ // 日
+ wv_day = (WheelView) view.findViewById(R.id.day);
+ // 判断大小月及是否闰年,用来确定"日"的数据
+ if (list_big.contains(String.valueOf(month + 1))) {
+ wv_day.setAdapter(new NumericWheelAdapter(1, 31));
+ } else if (list_little.contains(String.valueOf(month + 1))) {
+ wv_day.setAdapter(new NumericWheelAdapter(1, 30));
+ } else {
+ // 闰年
+ if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
+ wv_day.setAdapter(new NumericWheelAdapter(1, 29));
+ else
+ wv_day.setAdapter(new NumericWheelAdapter(1, 28));
+ }
+ wv_day.setLabel(context.getString(R.string.pickerview_day));
+ wv_day.setCurrentItem(day - 1);
+
+
+ wv_hours = (WheelView)view.findViewById(R.id.hour);
+ wv_hours.setAdapter(new NumericWheelAdapter(0, 23));
+ wv_hours.setLabel(context.getString(R.string.pickerview_hours));// 添加文字
+ wv_hours.setCurrentItem(h);
+
+ wv_mins = (WheelView)view.findViewById(R.id.min);
+ wv_mins.setAdapter(new NumericWheelAdapter(0, 59));
+ wv_mins.setLabel(context.getString(R.string.pickerview_minutes));// 添加文字
+ wv_mins.setCurrentItem(m);
+
+ // 添加"年"监听
+ OnWheelChangedListener wheelListener_year = new OnWheelChangedListener() {
+ @Override
+ public void onChanged(WheelView wheel, int oldValue, int newValue) {
+ int year_num = newValue + START_YEAR;
+ // 判断大小月及是否闰年,用来确定"日"的数据
+ int maxItem = 30;
+ if (list_big
+ .contains(String.valueOf(wv_month.getCurrentItem() + 1))) {
+ wv_day.setAdapter(new NumericWheelAdapter(1, 31));
+ maxItem = 31;
+ } else if (list_little.contains(String.valueOf(wv_month
+ .getCurrentItem() + 1))) {
+ wv_day.setAdapter(new NumericWheelAdapter(1, 30));
+ maxItem = 30;
+ } else {
+ if ((year_num % 4 == 0 && year_num % 100 != 0)
+ || year_num % 400 == 0){
+ wv_day.setAdapter(new NumericWheelAdapter(1, 29));
+ maxItem = 29;
+ }
+ else{
+ wv_day.setAdapter(new NumericWheelAdapter(1, 28));
+ maxItem = 28;
+ }
+ }
+ if (wv_day.getCurrentItem() > maxItem - 1){
+ wv_day.setCurrentItem(maxItem - 1);
+ }
+ }
+ };
+ // 添加"月"监听
+ OnWheelChangedListener wheelListener_month = new OnWheelChangedListener() {
+ @Override
+ public void onChanged(WheelView wheel, int oldValue, int newValue) {
+ int month_num = newValue + 1;
+ int maxItem = 30;
+ // 判断大小月及是否闰年,用来确定"日"的数据
+ if (list_big.contains(String.valueOf(month_num))) {
+ wv_day.setAdapter(new NumericWheelAdapter(1, 31));
+ maxItem = 31;
+ } else if (list_little.contains(String.valueOf(month_num))) {
+ wv_day.setAdapter(new NumericWheelAdapter(1, 30));
+ maxItem = 30;
+ } else {
+ if (((wv_year.getCurrentItem() + START_YEAR) % 4 == 0 && (wv_year
+ .getCurrentItem() + START_YEAR) % 100 != 0)
+ || (wv_year.getCurrentItem() + START_YEAR) % 400 == 0){
+ wv_day.setAdapter(new NumericWheelAdapter(1, 29));
+ maxItem = 29;
+ }
+ else{
+ wv_day.setAdapter(new NumericWheelAdapter(1, 28));
+ maxItem = 28;
+ }
+ }
+ if (wv_day.getCurrentItem() > maxItem - 1){
+ wv_day.setCurrentItem(maxItem - 1);
+ }
+
+ }
+ };
+ wv_year.addChangingListener(wheelListener_year);
+ wv_month.addChangingListener(wheelListener_month);
+
+ // 根据屏幕密度来指定选择器字体的大小(不同屏幕可能不同)
+ int textSize = 0;
+ switch(type){
+ case ALL:
+ textSize = (screenheight / 100) * 3;
+ break;
+ case YEAR_MONTH_DAY:
+ textSize = (screenheight / 100) * 4;
+ wv_hours.setVisibility(View.GONE);
+ wv_mins.setVisibility(View.GONE);
+ break;
+ case HOURS_MINS:
+ textSize = (screenheight / 100) * 4;
+ wv_year.setVisibility(View.GONE);
+ wv_month.setVisibility(View.GONE);
+ wv_day.setVisibility(View.GONE);
+ break;
+ case MONTH_DAY_HOUR_MIN:
+ textSize = (screenheight / 100) * 3;
+ wv_year.setVisibility(View.GONE);
+ break;
+ case YEAR_MONTH:
+ textSize = (screenheight / 100) * 4;
+ wv_day.setVisibility(View.GONE);
+ wv_hours.setVisibility(View.GONE);
+ wv_mins.setVisibility(View.GONE);
+ case YEAR:
+ textSize = (screenheight / 100) * 3;
+ wv_year.setLabel("");
+ wv_month.setVisibility(View.GONE);
+ wv_day.setVisibility(View.GONE);
+ wv_hours.setVisibility(View.GONE);
+ wv_mins.setVisibility(View.GONE);
+
+ }
+
+ wv_day.TEXT_SIZE = textSize;
+ wv_month.TEXT_SIZE = textSize;
+ wv_year.TEXT_SIZE = textSize;
+ wv_hours.TEXT_SIZE = textSize;
+ wv_mins.TEXT_SIZE = textSize;
+
+ }
+
+ /**
+ * 设置是否循环滚动
+ * @param cyclic
+ */
+ public void setCyclic(boolean cyclic){
+ wv_year.setCyclic(cyclic);
+ wv_month.setCyclic(cyclic);
+ wv_day.setCyclic(cyclic);
+ wv_hours.setCyclic(cyclic);
+ wv_mins.setCyclic(cyclic);
+ }
+ public String getTime() {
+ StringBuffer sb = new StringBuffer();
+ sb.append((wv_year.getCurrentItem() + START_YEAR)).append("-")
+ .append((wv_month.getCurrentItem() + 1)).append("-")
+ .append((wv_day.getCurrentItem() + 1)).append(" ")
+ .append(wv_hours.getCurrentItem()).append(":")
+ .append(wv_mins.getCurrentItem());
+ return sb.toString();
+ }
+}
diff --git a/TimeLib/src/pickerview/lib/WheelView.java b/TimeLib/src/pickerview/lib/WheelView.java
new file mode 100644
index 0000000..19cae43
--- /dev/null
+++ b/TimeLib/src/pickerview/lib/WheelView.java
@@ -0,0 +1,1024 @@
+/*
+ * Android Wheel Control.
+ * https://code.google.com/p/android-wheel/
+ *
+ * Copyright 2010 Yuri Kanivets
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pickerview.lib;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import com.bigkoo.pickerview.R;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.GradientDrawable.Orientation;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.util.FloatMath;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+
+/**
+ * Numeric wheel view.
+ *
+ * @author Yuri Kanivets
+ */
+public class WheelView extends View {
+ /** Scrolling duration */
+ private static final int SCROLLING_DURATION = 400;
+
+ /** Minimum delta for scrolling */
+ private static final int MIN_DELTA_FOR_SCROLLING = 1;
+
+ /** Current value & label text color */
+ private static final int VALUE_TEXT_COLOR = 0xFF666666;
+
+ /** Items text color */
+ private static final int ITEMS_TEXT_COLOR = 0xFF666666;
+
+ /** Top and bottom shadows colors */
+ private static final int[] SHADOWS_COLORS = new int[] { 0xFF111111,
+ 0x00AAAAAA, 0x00AAAAAA };
+
+ /** Additional items height (is added to standard text item height) */
+ private static final int ADDITIONAL_ITEM_HEIGHT = 15;
+
+ /** Text size */
+ public int TEXT_SIZE;
+
+ /** Top and bottom items offset (to hide that) */
+ private final int ITEM_OFFSET = TEXT_SIZE / 5;
+
+ /** Additional width for items layout */
+ private static final int ADDITIONAL_ITEMS_SPACE = 2;
+
+ /** Label offset , dip */
+ private static int LABEL_OFFSET_DIP = 20;
+ /** Label offset */
+ private static int LABEL_OFFSET = 30;
+
+ /** Left and right padding value */
+ private static final int PADDING = 10;
+
+ /** Default count of visible items */
+ private static final int DEF_VISIBLE_ITEMS = 7;
+
+ // Wheel Values
+ private WheelAdapter adapter = null;
+ private int currentItem = 0;
+
+ // Widths
+ private int itemsWidth = 0;
+ private int labelWidth = 0;
+
+ // Count of visible items
+ private int visibleItems = DEF_VISIBLE_ITEMS;
+
+ // Item height
+ private int itemHeight = 0;
+
+ // Text paints
+ private TextPaint itemsPaint;
+ private TextPaint valuePaint;
+
+ // Layouts
+ private StaticLayout itemsLayout;
+ private StaticLayout labelLayout;
+ private StaticLayout valueLayout;
+
+ // Label & background
+ private String label;
+ private Drawable centerDrawable;
+
+ // Shadows drawables
+ private GradientDrawable topShadow;
+ private GradientDrawable bottomShadow;
+
+ // Scrolling
+ private boolean isScrollingPerformed;
+ private int scrollingOffset;
+
+ // Scrolling animation
+ private GestureDetector gestureDetector;
+ private Scroller scroller;
+ private int lastScrollY;
+
+ // Cyclic
+ boolean isCyclic = false;
+
+ // Listeners
+ private List changingListeners = new LinkedList();
+ private List scrollingListeners = new LinkedList();
+
+ /**
+ * Constructor
+ */
+ public WheelView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initData(context);
+ }
+
+ /**
+ * Constructor
+ */
+ public WheelView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initData(context);
+ }
+
+ /**
+ * Constructor
+ */
+ public WheelView(Context context) {
+ super(context);
+ initData(context);
+ }
+
+ /**
+ * Initializes class data
+ *
+ * @param context
+ * the context
+ */
+ @SuppressLint("NewApi")
+ private void initData(Context context) {
+ gestureDetector = new GestureDetector(context, gestureListener);
+ gestureDetector.setIsLongpressEnabled(false);
+
+ LABEL_OFFSET = DensityUtil.dip2px(context, LABEL_OFFSET_DIP);
+
+ scroller = new Scroller(context);
+ }
+
+ /**
+ * Gets wheel adapter
+ *
+ * @return the adapter
+ */
+ public WheelAdapter getAdapter() {
+ return adapter;
+ }
+
+ /**
+ * Sets wheel adapter
+ *
+ * @param adapter
+ * the new wheel adapter
+ */
+ public void setAdapter(WheelAdapter adapter) {
+ this.adapter = adapter;
+ invalidateLayouts();
+ invalidate();
+ }
+
+ /**
+ * Set the the specified scrolling interpolator
+ *
+ * @param interpolator
+ * the interpolator
+ */
+ public void setInterpolator(Interpolator interpolator) {
+ scroller.forceFinished(true);
+ scroller = new Scroller(getContext(), interpolator);
+ }
+
+ /**
+ * Gets count of visible items
+ *
+ * @return the count of visible items
+ */
+ public int getVisibleItems() {
+ return visibleItems;
+ }
+
+ /**
+ * Sets count of visible items
+ *
+ * @param count
+ * the new count
+ */
+ public void setVisibleItems(int count) {
+ visibleItems = count;
+ invalidate();
+ }
+
+ /**
+ * Gets label
+ *
+ * @return the label
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * Sets label
+ *
+ * @param newLabel
+ * the label to set
+ */
+ public void setLabel(String newLabel) {
+ if (label == null || !label.equals(newLabel)) {
+ label = newLabel;
+ labelLayout = null;
+ invalidate();
+ }
+ }
+
+ /**
+ * Adds wheel changing listener
+ *
+ * @param listener
+ * the listener
+ */
+ public void addChangingListener(OnWheelChangedListener listener) {
+ changingListeners.add(listener);
+ }
+
+ /**
+ * Removes wheel changing listener
+ *
+ * @param listener
+ * the listener
+ */
+ public void removeChangingListener(OnWheelChangedListener listener) {
+ changingListeners.remove(listener);
+ }
+
+ /**
+ * Notifies changing listeners
+ *
+ * @param oldValue
+ * the old wheel value
+ * @param newValue
+ * the new wheel value
+ */
+ protected void notifyChangingListeners(int oldValue, int newValue) {
+ for (OnWheelChangedListener listener : changingListeners) {
+ listener.onChanged(this, oldValue, newValue);
+ }
+ }
+
+ /**
+ * Adds wheel scrolling listener
+ *
+ * @param listener
+ * the listener
+ */
+ public void addScrollingListener(OnWheelScrollListener listener) {
+ scrollingListeners.add(listener);
+ }
+
+ /**
+ * Removes wheel scrolling listener
+ *
+ * @param listener
+ * the listener
+ */
+ public void removeScrollingListener(OnWheelScrollListener listener) {
+ scrollingListeners.remove(listener);
+ }
+
+ /**
+ * Notifies listeners about starting scrolling
+ */
+ protected void notifyScrollingListenersAboutStart() {
+ for (OnWheelScrollListener listener : scrollingListeners) {
+ listener.onScrollingStarted(this);
+ }
+ }
+
+ /**
+ * Notifies listeners about ending scrolling
+ */
+ protected void notifyScrollingListenersAboutEnd() {
+ for (OnWheelScrollListener listener : scrollingListeners) {
+ listener.onScrollingFinished(this);
+ }
+ }
+
+ /**
+ * Gets current value
+ *
+ * @return the current value
+ */
+ public int getCurrentItem() {
+ return currentItem;
+ }
+
+ /**
+ * Sets the current item. Does nothing when index is wrong.
+ *
+ * @param index
+ * the item index
+ * @param animated
+ * the animation flag
+ */
+ public void setCurrentItem(int index, boolean animated) {
+ if (adapter == null || adapter.getItemsCount() == 0) {
+ return; // throw?
+ }
+ if (index < 0 || index >= adapter.getItemsCount()) {
+ if (isCyclic) {
+ while (index < 0) {
+ index += adapter.getItemsCount();
+ }
+ index %= adapter.getItemsCount();
+ } else {
+ return; // throw?
+ }
+ }
+ if (index != currentItem) {
+ if (animated) {
+ scroll(index - currentItem, SCROLLING_DURATION);
+ } else {
+ invalidateLayouts();
+
+ int old = currentItem;
+ currentItem = index;
+
+ notifyChangingListeners(old, currentItem);
+
+ invalidate();
+ }
+ }
+ }
+
+ /**
+ * Sets the current item w/o animation. Does nothing when index is wrong.
+ *
+ * @param index
+ * the item index
+ */
+ public void setCurrentItem(int index) {
+ setCurrentItem(index, false);
+ }
+
+ /**
+ * Tests if wheel is cyclic. That means before the 1st item there is shown
+ * the last one
+ *
+ * @return true if wheel is cyclic
+ */
+ public boolean isCyclic() {
+ return isCyclic;
+ }
+
+ /**
+ * Set wheel cyclic flag
+ * 设置是否循环滚动
+ * @param isCyclic
+ * the flag to set
+ */
+ public void setCyclic(boolean isCyclic) {
+ this.isCyclic = isCyclic;
+
+ invalidate();
+ invalidateLayouts();
+ }
+
+ /**
+ * Invalidates layouts
+ */
+ private void invalidateLayouts() {
+ itemsLayout = null;
+ valueLayout = null;
+ scrollingOffset = 0;
+ }
+
+ /**
+ * Initializes resources
+ */
+ private void initResourcesIfNecessary() {
+ if (itemsPaint == null) {
+ itemsPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+ // itemsPaint.density = getResources().getDisplayMetrics().density;
+ itemsPaint.setTextSize(TEXT_SIZE);
+ itemsPaint.setColor(0xFF666666);
+ }
+
+ if (valuePaint == null) {
+// valuePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG
+// | Paint.FAKE_BOLD_TEXT_FLAG | Paint.DITHER_FLAG);
+ // valuePaint.density = getResources().getDisplayMetrics().density;
+ valuePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+ valuePaint.setTextSize(TEXT_SIZE);
+// valuePaint.setShadowLayer(0.1f, 0, 0.1f, 0xFFC0C0C0);
+ }
+
+ if (centerDrawable == null) {
+ centerDrawable = getContext().getResources().getDrawable(
+ R.drawable.wheel_val);
+ }
+
+ if (topShadow == null) {
+ topShadow = new GradientDrawable(Orientation.TOP_BOTTOM,
+ SHADOWS_COLORS);
+ }
+
+ if (bottomShadow == null) {
+ bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP,
+ SHADOWS_COLORS);
+ }
+
+// setBackgroundResource(R.drawable.wheel_bg);
+ }
+
+ /**
+ * Calculates desired height for layout
+ *
+ * @param layout
+ * the source layout
+ * @return the desired layout height
+ */
+ private int getDesiredHeight(Layout layout) {
+ if (layout == null) {
+ return 0;
+ }
+
+ int desired = getItemHeight() * visibleItems - ITEM_OFFSET * 2
+ - ADDITIONAL_ITEM_HEIGHT;
+
+ // Check against our minimum height
+ desired = Math.max(desired, getSuggestedMinimumHeight());
+
+ return desired;
+ }
+
+ /**
+ * Returns text item by index
+ *
+ * @param index
+ * the item index
+ * @return the item or null
+ */
+ private String getTextItem(int index) {
+ if (adapter == null || adapter.getItemsCount() == 0) {
+ return null;
+ }
+ int count = adapter.getItemsCount();
+ if ((index < 0 || index >= count) && !isCyclic) {
+ return null;
+ } else {
+ while (index < 0) {
+ index = count + index;
+ }
+ }
+
+ index %= count;
+ return adapter.getItem(index);
+ }
+
+ /**
+ * Builds text depending on current value
+ *
+ * @param useCurrentValue
+ * @return the text
+ */
+ private String buildText(boolean useCurrentValue) {
+ StringBuilder itemsText = new StringBuilder();
+ int addItems = visibleItems / 2 + 1;
+
+ for (int i = currentItem - addItems; i <= currentItem + addItems; i++) {
+ if (useCurrentValue || i != currentItem) {
+ String text = getTextItem(i);
+ if (text != null) {
+ itemsText.append(text);
+ }
+ }
+ if (i < currentItem + addItems) {
+ itemsText.append("\n");
+ }
+ }
+
+ return itemsText.toString();
+ }
+
+ /**
+ * Returns the max item length that can be present
+ *
+ * @return the max length
+ */
+ private int getMaxTextLength() {
+ WheelAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return 0;
+ }
+
+ int adapterLength = adapter.getMaximumLength();
+ if (adapterLength > 0) {
+ return adapterLength;
+ }
+
+ String maxText = null;
+ int addItems = visibleItems / 2;
+ for (int i = Math.max(currentItem - addItems, 0); i < Math.min(
+ currentItem + visibleItems, adapter.getItemsCount()); i++) {
+ String text = adapter.getItem(i);
+ if (text != null
+ && (maxText == null || maxText.length() < text.length())) {
+ maxText = text;
+ }
+ }
+
+ return maxText != null ? maxText.length() : 0;
+ }
+
+ /**
+ * Returns height of wheel item
+ *
+ * @return the item height
+ */
+ private int getItemHeight() {
+ if (itemHeight != 0) {
+ return itemHeight;
+ } else if (itemsLayout != null && itemsLayout.getLineCount() > 2) {
+ itemHeight = itemsLayout.getLineTop(2) - itemsLayout.getLineTop(1);
+ return itemHeight;
+ }
+
+ return getHeight() / visibleItems;
+ }
+
+ /**
+ * Calculates control width and creates text layouts
+ *
+ * @param widthSize
+ * the input layout width
+ * @param mode
+ * the layout mode
+ * @return the calculated control width
+ */
+ private int calculateLayoutWidth(int widthSize, int mode) {
+ initResourcesIfNecessary();
+
+ int width = widthSize;
+
+ int maxLength = getMaxTextLength();
+ if (maxLength > 0) {
+ float textWidth = (float) Math.ceil(Layout.getDesiredWidth("0",
+ itemsPaint));
+ itemsWidth = (int) (maxLength * textWidth);
+ } else {
+ itemsWidth = 0;
+ }
+ itemsWidth += ADDITIONAL_ITEMS_SPACE; // make it some more
+
+ labelWidth = 0;
+ if (label != null && label.length() > 0) {
+ labelWidth = (int) Math.ceil(Layout.getDesiredWidth(label,
+ valuePaint));
+ }
+
+ boolean recalculate = false;
+ if (mode == MeasureSpec.EXACTLY) {
+ width = widthSize;
+ recalculate = true;
+ } else {
+ width = itemsWidth + labelWidth + 2 * PADDING;
+ if (labelWidth > 0) {
+ width += LABEL_OFFSET;
+ }
+
+ // Check against our minimum width
+ width = Math.max(width, getSuggestedMinimumWidth());
+
+ if (mode == MeasureSpec.AT_MOST && widthSize < width) {
+ width = widthSize;
+ recalculate = true;
+ }
+ }
+
+ if (recalculate) {
+ // recalculate width
+ int pureWidth = width - LABEL_OFFSET - 2 * PADDING;
+ if (pureWidth <= 0) {
+ itemsWidth = labelWidth = 0;
+ }
+ if (labelWidth > 0) {
+ double newWidthItems = (double) itemsWidth * pureWidth
+ / (itemsWidth + labelWidth);
+ itemsWidth = (int) newWidthItems;
+ labelWidth = pureWidth - itemsWidth;
+ } else {
+ itemsWidth = pureWidth + LABEL_OFFSET; // no label
+ }
+ }
+
+ if (itemsWidth > 0) {
+ createLayouts(itemsWidth, labelWidth);
+ }
+
+ return width;
+ }
+
+ /**
+ * Creates layouts
+ *
+ * @param widthItems
+ * width of items layout
+ * @param widthLabel
+ * width of label layout
+ */
+ private void createLayouts(int widthItems, int widthLabel) {
+ if (itemsLayout == null || itemsLayout.getWidth() > widthItems) {
+ itemsLayout = new StaticLayout(buildText(isScrollingPerformed), itemsPaint, widthItems,
+ widthLabel > 0 ? Layout.Alignment.ALIGN_CENTER
+ : Layout.Alignment.ALIGN_CENTER, 1, ADDITIONAL_ITEM_HEIGHT, false);
+ } else {
+ itemsLayout.increaseWidthTo(widthItems);
+ }
+
+ if (!isScrollingPerformed && (valueLayout == null || valueLayout.getWidth() > widthItems)) {
+ String text = getAdapter() != null ? getAdapter().getItem(currentItem) : null;
+ valueLayout = new StaticLayout(text != null ? text : "", valuePaint, widthItems,
+ widthLabel > 0 ? Layout.Alignment.ALIGN_CENTER
+ : Layout.Alignment.ALIGN_CENTER, 1, ADDITIONAL_ITEM_HEIGHT, false);
+ } else if (isScrollingPerformed) {
+ valueLayout = null;
+ } else {
+ valueLayout.increaseWidthTo(widthItems);
+ }
+
+ if (widthLabel > 0) {
+ if (labelLayout == null || labelLayout.getWidth() > widthLabel) {
+ labelLayout = new StaticLayout(label, valuePaint, widthLabel,
+ Layout.Alignment.ALIGN_NORMAL, 1, ADDITIONAL_ITEM_HEIGHT, false);
+ } else {
+ labelLayout.increaseWidthTo(widthLabel);
+ }
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ int width = calculateLayoutWidth(widthSize, widthMode);
+
+ int height;
+ if (heightMode == MeasureSpec.EXACTLY) {
+ height = heightSize;
+ } else {
+ height = getDesiredHeight(itemsLayout);
+
+ if (heightMode == MeasureSpec.AT_MOST) {
+ height = Math.min(height, heightSize);
+ }
+ }
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ drawCenterRect(canvas);
+
+ if (itemsLayout == null) {
+ if (itemsWidth == 0) {
+ calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
+ } else {
+ createLayouts(itemsWidth, labelWidth);
+ }
+ }
+
+ if (itemsWidth > 0) {
+ canvas.save();
+ // Skip padding space and hide a part of top and bottom items
+ canvas.translate(PADDING, -ITEM_OFFSET);
+ drawItems(canvas);
+ drawValue(canvas);
+ canvas.restore();
+ }
+// drawShadows(canvas);
+ }
+
+ /**
+ * Draws shadows on top and bottom of control
+ *
+ * @param canvas
+ * the canvas for drawing
+ */
+ private void drawShadows(Canvas canvas) {
+ topShadow.setBounds(0, 0, getWidth(), getHeight() / 2);
+ topShadow.draw(canvas);
+
+ bottomShadow.setBounds(0, getHeight() / 2,
+ getWidth(), getHeight());
+ bottomShadow.draw(canvas);
+ }
+
+ /**
+ * Draws value and label layout
+ * 选中的item
+ * @param canvas
+ * the canvas for drawing
+ */
+ private void drawValue(Canvas canvas) {
+ valuePaint.setColor(VALUE_TEXT_COLOR);
+ valuePaint.drawableState = getDrawableState();
+
+ Rect bounds = new Rect();
+ itemsLayout.getLineBounds(visibleItems / 2, bounds);
+
+// canvas.scale(1.2f, 1.2f);
+ // draw label
+ if (labelLayout != null) {
+ canvas.save();
+ canvas.translate(itemsLayout.getWidth() + LABEL_OFFSET, bounds.top);
+ labelLayout.draw(canvas);
+ canvas.restore();
+ }
+
+ // draw current value
+ if (valueLayout != null) {
+ canvas.save();
+ canvas.translate(0, bounds.top + scrollingOffset);
+ valueLayout.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+ /**
+ * Draws items
+ * 非选中的item
+ * @param canvas
+ * the canvas for drawing
+ */
+ private void drawItems(Canvas canvas) {
+ canvas.save();
+
+ int top = itemsLayout.getLineTop(1);
+ canvas.translate(0, -top + scrollingOffset);
+
+ itemsPaint.setColor(ITEMS_TEXT_COLOR);
+ itemsPaint.drawableState = getDrawableState();
+ itemsLayout.draw(canvas);
+
+ canvas.restore();
+ }
+
+ /**
+ * Draws rect for current value
+ *
+ * @param canvas
+ * the canvas for drawing
+ */
+ private void drawCenterRect(Canvas canvas) {
+ int center = getHeight() / 2;
+ int offset = getItemHeight() / 2;
+ centerDrawable.setBounds(0, center - offset, getWidth()+2, center
+ + offset);
+
+ centerDrawable.draw(canvas);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ WheelAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return true;
+ }
+
+ if (!gestureDetector.onTouchEvent(event)
+ && event.getAction() == MotionEvent.ACTION_UP) {
+ justify();
+ }
+ return true;
+ }
+
+ /**
+ * Scrolls the wheel
+ *
+ * @param delta
+ * the scrolling value
+ */
+ private void doScroll(int delta) {
+ scrollingOffset += delta;
+
+ int count = scrollingOffset / getItemHeight();
+ int pos = currentItem - count;
+ if (isCyclic && adapter.getItemsCount() > 0) {
+ // fix position by rotating
+ while (pos < 0) {
+ pos += adapter.getItemsCount();
+ }
+ pos %= adapter.getItemsCount();
+ } else if (isScrollingPerformed) {
+ //
+ if (pos < 0) {
+ count = currentItem;
+ pos = 0;
+ } else if (pos >= adapter.getItemsCount()) {
+ count = currentItem - adapter.getItemsCount() + 1;
+ pos = adapter.getItemsCount() - 1;
+ }
+ } else {
+ // fix position
+ pos = Math.max(pos, 0);
+ pos = Math.min(pos, adapter.getItemsCount() - 1);
+ }
+
+ int offset = scrollingOffset;
+ if (pos != currentItem) {
+ setCurrentItem(pos, false);
+ } else {
+ invalidate();
+ }
+
+ // update offset
+ scrollingOffset = offset - count * getItemHeight();
+ if (scrollingOffset > getHeight()) {
+ scrollingOffset = scrollingOffset % getHeight() + getHeight();
+ }
+ }
+
+ // gesture listener
+ private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ if (isScrollingPerformed) {
+ scroller.forceFinished(true);
+ clearMessages();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ startScrolling();
+ doScroll((int) -distanceY);
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ lastScrollY = currentItem * getItemHeight() + scrollingOffset;
+ int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount()
+ * getItemHeight();
+ int minY = isCyclic ? -maxY : 0;
+ scroller.fling(0, lastScrollY, 0, (int) -velocityY / 2, 0, 0, minY,
+ maxY);
+ setNextMessage(MESSAGE_SCROLL);
+ return true;
+ }
+ };
+
+ // Messages
+ private final int MESSAGE_SCROLL = 0;
+ private final int MESSAGE_JUSTIFY = 1;
+
+ /**
+ * Set next message to queue. Clears queue before.
+ *
+ * @param message
+ * the message to set
+ */
+ private void setNextMessage(int message) {
+ clearMessages();
+ animationHandler.sendEmptyMessage(message);
+ }
+
+ /**
+ * Clears messages from queue
+ */
+ private void clearMessages() {
+ animationHandler.removeMessages(MESSAGE_SCROLL);
+ animationHandler.removeMessages(MESSAGE_JUSTIFY);
+ }
+
+ // animation handler
+ private Handler animationHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ scroller.computeScrollOffset();
+ int currY = scroller.getCurrY();
+ int delta = lastScrollY - currY;
+ lastScrollY = currY;
+ if (delta != 0) {
+ doScroll(delta);
+ }
+
+ // scrolling is not finished when it comes to final Y
+ // so, finish it manually
+ if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) {
+ currY = scroller.getFinalY();
+ scroller.forceFinished(true);
+ }
+ if (!scroller.isFinished()) {
+ animationHandler.sendEmptyMessage(msg.what);
+ } else if (msg.what == MESSAGE_SCROLL) {
+ justify();
+ } else {
+ finishScrolling();
+ }
+ }
+ };
+
+ /**
+ * Justifies wheel
+ */
+ private void justify() {
+ if (adapter == null) {
+ return;
+ }
+
+ lastScrollY = 0;
+ int offset = scrollingOffset;
+ int itemHeight = getItemHeight();
+ boolean needToIncrease = offset > 0 ? currentItem < adapter
+ .getItemsCount() : currentItem > 0;
+ if ((isCyclic || needToIncrease)
+ && Math.abs((float) offset) > (float) itemHeight / 2) {
+ if (offset < 0)
+ offset += itemHeight + MIN_DELTA_FOR_SCROLLING;
+ else
+ offset -= itemHeight + MIN_DELTA_FOR_SCROLLING;
+ }
+ if (Math.abs(offset) > MIN_DELTA_FOR_SCROLLING) {
+ scroller.startScroll(0, 0, 0, offset, SCROLLING_DURATION);
+ setNextMessage(MESSAGE_JUSTIFY);
+ } else {
+ finishScrolling();
+ }
+ }
+
+ /**
+ * Starts scrolling
+ */
+ private void startScrolling() {
+ if (!isScrollingPerformed) {
+ isScrollingPerformed = true;
+ notifyScrollingListenersAboutStart();
+ }
+ }
+
+ /**
+ * Finishes scrolling
+ */
+ void finishScrolling() {
+ if (isScrollingPerformed) {
+ notifyScrollingListenersAboutEnd();
+ isScrollingPerformed = false;
+ }
+ invalidateLayouts();
+ invalidate();
+ }
+
+ /**
+ * Scroll the wheel
+ *
+ * @param itemsToScroll
+ * items to scroll
+ * @param time
+ * scrolling duration
+ */
+ public void scroll(int itemsToScroll, int time) {
+ scroller.forceFinished(true);
+
+ lastScrollY = scrollingOffset;
+ int offset = itemsToScroll * getItemHeight();
+
+ scroller.startScroll(0, lastScrollY, 0, offset - lastScrollY, time);
+ setNextMessage(MESSAGE_SCROLL);
+
+ startScrolling();
+ }
+}