• 谈谈-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

  • 相关阅读:
    查找大文件 & 索引节点(inode)爆满 解决办法
    技术领导力 —— 左耳听风 专栏读后感
    左耳朵耗子关于技术变现一文读后感
    html--前端jquery初识
    html--JavaScript之DOM (文档对象模型)
    html--前端JavaScript基本内容
    html--前端javascript初识
    html--前端css常用属性
    html--前端css样式初识
    html--前端基本标签内容讲解
  • 原文地址:https://www.cnblogs.com/zly1022/p/7803160.html
Copyright © 2020-2023  润新知