一、概述

这是android-pickerview系列的第三篇文章,主要是讲解一下时间选择器TimePickerView是如何封装的,关于怎么使用这个控件的可以查看谈谈-Android-PickerView系列之介绍与使用(一)

,想要研究WheelView原理的话可以看这篇谈谈-Android-PickerView系列之源码解析(二)。

二、封装

android 好看的checkbox android-pickerview_设计模式

这是一张类的关系图

构造函数使用的Builder设计模式来封装的,通过builder来设置参数,最重要的就是initview()方法。源码如下:

public TimePickerView(Builder builder) {
        super(builder.context);
        this.timeSelectListener = builder.timeSelectListener; this.gravity = builder.gravity; this.type = builder.type; this.Str_Submit = builder.Str_Submit; this.Str_Cancel = builder.Str_Cancel; this.Str_Title = builder.Str_Title; this.Color_Submit = builder.Color_Submit; this.Color_Cancel = builder.Color_Cancel; this.Color_Title = builder.Color_Title; this.Color_Background_Wheel = builder.Color_Background_Wheel; this.Color_Background_Title = builder.Color_Background_Title; this.Size_Submit_Cancel = builder.Size_Submit_Cancel; this.Size_Title = builder.Size_Title; this.Size_Content = builder.Size_Content; this.startYear = builder.startYear; this.endYear = builder.endYear; this.date = builder.date; this.cyclic = builder.cyclic; this.cancelable = builder.cancelable; this.label_year = builder.label_year; this.label_month = builder.label_month; this.label_day = builder.label_day; this.label_hours = builder.label_hours; this.label_mins = builder.label_mins; this.label_seconds = builder.label_seconds; this.textColorCenter = builder.textColorCenter; this.textColorOut = builder.textColorOut; this.dividerColor = builder.dividerColor; this.lineSpacingMultiplier = builder.lineSpacingMultiplier; this.isDialog = builder.isDialog; initView(builder.context); }

initView()的源码如下:

void initView(Context context) {
        initViews();
        init();
        initEvents(); LayoutInflater.from(context).inflate(R.layout.pickerview_time, contentContainer); //顶部标题 tvTitle = (TextView) findViewById(R.id.tvTitle); //确定和取消按钮 btnSubmit = (Button) findViewById(R.id.btnSubmit); btnCancel = (Button) findViewById(R.id.btnCancel); btnSubmit.setTag(TAG_SUBMIT); btnCancel.setTag(TAG_CANCEL); btnSubmit.setOnClickListener(this); btnCancel.setOnClickListener(this); //设置文字 btnSubmit.setText(TextUtils.isEmpty(Str_Submit)?context.getResources().getString(R.string.pickerview_submit):Str_Submit); btnCancel.setText(TextUtils.isEmpty(Str_Cancel)?context.getResources().getString(R.string.pickerview_cancel):Str_Cancel); tvTitle.setText(TextUtils.isEmpty(Str_Title)?"":Str_Title);//默认为空 //设置文字颜色 btnSubmit.setTextColor(Color_Submit==0?pickerview_timebtn_nor:Color_Submit); btnCancel.setTextColor(Color_Cancel==0?pickerview_timebtn_nor:Color_Cancel); tvTitle.setTextColor(Color_Title==0?pickerview_topbar_title:Color_Title); //设置文字大小 btnSubmit.setTextSize(Size_Submit_Cancel); btnCancel.setTextSize(Size_Submit_Cancel); tvTitle.setTextSize(Size_Title); // 时间转轮 自定义控件,xml下面给出 LinearLayout timePickerView = (LinearLayout) findViewById(R.id.timepicker); RelativeLayout rv_top_bar = (RelativeLayout) findViewById(R.id.rv_topbar); //设置标题颜色 rv_top_bar.setBackgroundColor(Color_Background_Title==0?pickerview_bg_topbar:Color_Background_Title); //设置滚轮背景颜色 timePickerView.setBackgroundColor(Color_Background_Wheel==0?bgColor_default:Color_Background_Wheel); wheelTime = new WheelTime(timePickerView, type, gravity,Size_Content); if (startYear!=0&&endYear!=0&&startYear<=endYear){ //设置年的起始范围 setRange(); } //设置一开始的选中时间 setTime(); //设置单位 wheelTime.setLabels(label_year,label_month,label_day,label_hours,label_mins,label_seconds); //设置是否可以点击外部关闭界面 setOutSideCancelable(cancelable); //设置是否循环滚动 wheelTime.setCyclic(cyclic); //设置分隔线的颜色 wheelTime.setDividerColor(dividerColor); //设置行距的大小 wheelTime.setLineSpacingMultiplier(lineSpacingMultiplier); //设置文字颜色 wheelTime.setTextColorOut(textColorOut); wheelTime.setTextColorCenter(textColorCenter); }

 

 

R.id.timepicker 时间选择器的xml:

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

 

 

这个方法就真正负责加载View的方法,首先调用initViews(),intViews方法会根据builder中的isDialog参数来判断是要显示成对话框模式还是显示在屏幕的下边。源码如下:

protected void initViews() {
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        if (isDialog()) {
            //如果是对话框模式
            //这个是控件的布局,xml的源码在下面会贴出来
            dialogView = (ViewGroup) layoutInflater.inflate(R.layout.layout_basepickerview, null, false); //设置界面的背景为透明 dialogView.setBackgroundColor(Color.TRANSPARENT); //这个是真正要加载时间选取器的父布局 contentContainer = (ViewGroup) dialogView.findViewById(R.id.content_container); //设置对话框 左右间距屏幕30 this.params.leftMargin = 30; this.params.rightMargin = 30; contentContainer.setLayoutParams(this.params); //创建对话框,将dialogView设置成dialog要显示的view if (dialogView != null) { mDialog = new Dialog(context, R.style.custom_dialog2); mDialog.setCancelable(true);//不能点外面取消,也不 能点back取消 mDialog.setContentView(dialogView); } //给背景设置点击事件,这样当点击内容以外的地方会关闭界面 dialogView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { dismiss(); } }); } else { //如果只是要显示在屏幕的下方 //decorView是activity的根View decorView = (ViewGroup) ((Activity) context).getWindow().getDecorView().findViewById(android.R.id.content); //将控件添加到decorView中 rootView = (ViewGroup) layoutInflater.inflate(R.layout.layout_basepickerview, decorView, false); rootView.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT )); //这个是真正要加载时间选取器的父布局 contentContainer = (ViewGroup) rootView.findViewById(R.id.content_container); contentContainer.setLayoutParams(params); } }

 

 

R.layout.layout_basepickerview的xml文件源码如下:

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

 

 

然后调用init()方法加载动画。然后通过LayoutInflater.inflate()方法将时间选择器的布局加载到要显示的布局contentContainer中。最后通过wheelTime 类来初始化时间选择器.在调用wheeltime的setPicker来初始化滚轮的数据.
接下来看看WheelTime的构造方法:

public WheelTime(View view, Type type, int gravity, int textSize) {  super(); this.view = view;//这个view就是时间选择器的布局也就R.id.timepicker this.type = type;//表示要显示模式,例如:年月日时分秒,年月日 this.gravity = gravity; 滚轮文字的显示位置 默认居中 this.textSize = textSize;//这个表示滚轮文字的大小 }

 

 

构造方法其实就只是给对应的属性赋值,通过调用setPicker方法来给wheelview设置数据,下面就看下它的源码:

public void setPicker(int year, int month, int day, int h, int m, int s) {
        // 添加大小月月份并将其转换为list,方便之后的判断
        String[] months_big = {"1", "3", "5", "7", "8", "10", "12"}; String[] months_little = {"4", "6", "9", "11"}; final List<String> list_big = Arrays.asList(months_big); final List<String> list_little = Arrays.asList(months_little); Context context = view.getContext(); // 年 这view就是R.layout.timepicker布局 wv_year = (WheelView) view.findViewById(R.id.year); wv_year.setAdapter(new NumericWheelAdapter(startYear, endYear));// 设置"年"的显示数据 wv_year.setLabel(context.getString(R.string.pickerview_year));// 添加文字 wv_year.setCurrentItem(year - startYear);// 初始化时显示的数据 wv_year.setGravity(gravity); // 月 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_month.setGravity(gravity); // 日 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_day.setGravity(gravity); //时 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_hours.setGravity(gravity); //分 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); wv_mins.setGravity(gravity); //秒 wv_seconds = (WheelView) view.findViewById(R.id.second); wv_seconds.setAdapter(new NumericWheelAdapter(0, 59)); wv_seconds.setLabel(context.getString(R.string.pickerview_seconds));// 添加文字 wv_seconds.setCurrentItem(s); wv_seconds.setGravity(gravity); // 添加"年"监听 OnItemSelectedListener wheelListener_year = new OnItemSelectedListener() { @Override public void onItemSelected(int index) { int year_num = index + startYear; // 判断大小月及是否闰年,用来确定"日"的数据 int maxItem ; if (list_big .contains(String.valueOf(wv_month.getCurrentItem() + 1))) { wv_day.setAdapter(new NumericWheelAdapter(1, 31)); maxItem = 31; } else if (list_little.contains(String.valueOf(wv_month .getCurrentItem() + 1))) { wv_day.setAdapter(new NumericWheelAdapter(1, 30)); maxItem = 30; } else { if ((year_num % 4 == 0 && year_num % 100 != 0) || year_num % 400 == 0) { wv_day.setAdapter(new NumericWheelAdapter(1, 29)); maxItem = 29; } else { wv_day.setAdapter(new NumericWheelAdapter(1, 28)); maxItem = 28; } } if (wv_day.getCurrentItem() > maxItem - 1) { wv_day.setCurrentItem(maxItem - 1); } } }; // 添加"月"监听 OnItemSelectedListener wheelListener_month = new OnItemSelectedListener() { @Override public void onItemSelected(int index) { int month_num = index + 1; int maxItem = 30; // 判断大小月及是否闰年,用来确定"日"的数据 if (list_big.contains(String.valueOf(month_num))) { wv_day.setAdapter(new NumericWheelAdapter(1, 31)); maxItem = 31; } else if (list_little.contains(String.valueOf(month_num))) { wv_day.setAdapter(new NumericWheelAdapter(1, 30)); maxItem = 30; } else { if (((wv_year.getCurrentItem() + startYear) % 4 == 0 && (wv_year .getCurrentItem() + startYear) % 100 != 0) || (wv_year.getCurrentItem() + startYear) % 400 == 0) { wv_day.setAdapter(new NumericWheelAdapter(1, 29)); maxItem = 29; } else { wv_day.setAdapter(new NumericWheelAdapter(1, 28)); maxItem = 28; } } if (wv_day.getCurrentItem() > maxItem - 1) { wv_day.setCurrentItem(maxItem - 1)