谈谈-Android-PickerView系列之封装(三)

一、概述

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

,想要研究WheelView原理的话可以看这篇谈谈-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);
                }

            }
        };
        wv_year.setOnItemSelectedListener(wheelListener_year);
        wv_month.setOnItemSelectedListener(wheelListener_month);
        //根据type 来显示特定的模式
        switch (type) {
            case ALL:
               /* textSize = textSize * 3;*/
                break;
            case YEAR_MONTH_DAY:
               /* textSize = textSize * 4;*/
                wv_hours.setVisibility(View.GONE);
                wv_mins.setVisibility(View.GONE);
                wv_seconds.setVisibility(View.GONE);
                break;
            case HOURS_MINS:
                /*textSize = textSize * 4;*/
                wv_year.setVisibility(View.GONE);
                wv_month.setVisibility(View.GONE);
                wv_day.setVisibility(View.GONE);
                wv_seconds.setVisibility(View.GONE);
                break;
            case MONTH_DAY_HOUR_MIN:
               /* textSize = textSize * 3;*/
                wv_year.setVisibility(View.GONE);
                wv_seconds.setVisibility(View.GONE);
                break;
            case YEAR_MONTH:
               /* textSize = textSize * 4;*/
                wv_day.setVisibility(View.GONE);
                wv_hours.setVisibility(View.GONE);
                wv_mins.setVisibility(View.GONE);
                wv_seconds.setVisibility(View.GONE);
        }
        //设置文字的大小
        setContentTextSize();
    }

 

setContentTextSize()设置文字的大小,源码很简单了就调用wheelview提供的设置文字大小的方法就可以了,源码如下:

private void setContentTextSize() {
        //分别设置年,月,日,时,分,秒的文字大小
        wv_day.setTextSize(textSize);
        wv_month.setTextSize(textSize);
        wv_year.setTextSize(textSize);
        wv_hours.setTextSize(textSize);
        wv_mins.setTextSize(textSize);
        wv_seconds.setTextSize(textSize);
    }

 

再来看看显示TimePickerView的代码,也就是show方法,源码如下:

   public void show() {
        //首先判断是不是对话框模式
        if (isDialog()) {
        //如果是对话框模式就直接调用对话框的show方法即可
             if (mDialog != null) {
            mDialog.show();
        }
        } else {
          //如果不是对话框模式,先判断有没有显示
            if (isShowing()) {
                return;
            }
            isShowing = true;
            //将时间选择器的布局添加到activity的根view上
            onAttached(rootView);
            //请求重新绘制,将时间选择器显示出来
            rootView.requestFocus();
        }

    }

    /**
     * 检测该View是不是已经添加到根视图
     *
     * @return 如果视图已经存在该View返回true
     */
    public boolean isShowing() {
        if (isDialog()) {
            return false;
        } else {
            return rootView.getParent() != null || isShowing;
        }

    }

   /**
     * show的时候调用
     *
     * @param view 这个View
     */
    private void onAttached(View view) {
        decorView.addView(view);
        //开启显示的动画
        contentContainer.startAnimation(inAnim);
    }

 

 

显示TimePickerView的方法看完了,再看看关闭TimePickerView的方法,源码如下:

 public void dismiss() {
        if (isDialog()) {
        //如果是对话框模式,就直接调用对话框的dismiss方法
            dismissDialog();
        } else {
            //如果已经关闭了,就不继续执行下去了
            if (dismissing) {
                return;
            }

            dismissing = true;

            //消失动画
            outAnim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    decorView.post(new Runnable() {
                        @Override
                        //动画结束,就将时间控件的布局,从activity的根view中移除
                            dismissImmediately();
                        }
                    });
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            contentContainer.startAnimation(outAnim);
        }

    }

    public void dismissImmediately() {
        //从activity根视图移除
        decorView.removeView(rootView);
        isShowing = false;
        dismissing = false;
        if (onDismissListener != null) {
            onDismissListener.onDismiss(BasePickerView.this);
        }

    }

 

到此将如何显示TimePickerView的封装分析完了,再来看看TimePickerView中是如何处理取消和确定按钮的点击事件的,源码如下:

  @Override
    public void onClick(View v) {
        String tag = (String) v.getTag();
        if (tag.equals(TAG_CANCEL)) {
        //取消,就是直接关闭界面
            dismiss();
        } else {
            //确定
            if (timeSelectListener != null) {
                try {
                //获取选中的时间
                    Date date = WheelTime.dateFormat.parse(wheelTime.getTime());
                    //回调,将时间传递给使用者
                    timeSelectListener.onTimeSelect(date,v);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            dismiss();
        }
    }

三.总结

以上是我对Android-PickerView如何封装TimePickerView的分析,如果看的不是很懂的朋友,下载demo结合源代码进行理解,代码都有注释.Github项目地址:Android-PickerView

文章转载自:http://blog.csdn.net/qq_22393017/article/details/59480374

posted @ 2017-11-08 11:25  叶秋1022  阅读(1362)  评论(0编辑  收藏  举报
快递查询