美文网首页android 集结号自定义控件Android 自定义view
Android开发实现自定义日历、日期选择控件

Android开发实现自定义日历、日期选择控件

作者: wenzhihao123 | 来源:发表于2017-04-16 10:24 被阅读2445次

    最近项目需要日历效果,考虑用第三方的反而不太适合设计需求,修改复杂,与其这样不入自己重新写一个干净的控件。虽不是什么牛逼控件,但是也需要我们能按照设计自己写出来。在此记录一下实现思路。

    效果图:


    切换周 切换月份

    实现思路

    • 头部是一个自定义组合控件;
    • 显示一周的日期部分用GridView 更加方便更新;
    • 切换月的部分是一个自定义PopupWindow;
    • GridView选中效果;
    • GridView根据手势GestureDetector监听左右滑动;
    • 核心其实还是Calendar类,根据这个类我们可以获取制定日期一周的日期集合、可以获取制定日期一月的日期集合等等;
    • 根据阳历日期获取阴历日期

    使用

    // xml布局引用
    <com.wzh.calendar.view.DataView
            android:id="@+id/week"
            android:layout_width="match_parent"
            android:background="@color/color_ffffff"
            android:layout_height="wrap_content">
    </com.wzh.calendar.view.DataView>
    
    // 代码中,自定义回调监听选中的日期
    dataView = (DataView) findViewById(R.id.week);
    dataView.setOnSelectListener(new DataView.OnSelectListener() {
                @Override
                public void onSelected(DateEntity date) {
                    info.setText("日期:"+ date.date+"\n"+
                                 "周几:"+ date.weekName+"\n"+
                                 "今日:"+ date.isToday+"\n"+
                                 "时间戳:"+ date.million+"\n");
                    Log.e("wenzhiao--------------",date.toString());
                }
     });
    //需要传递此种格式的日期,不传默认是获取今日的日期
    dataView.getData("2017-04-19");
    

    实现整体逻辑

    回调的日期信息封装成一个实体类DateEntity:

    public class DateEntity {
        public long million ; //时间戳
        public String weekName ;  //周几
        public int weekNum ;  //一周中第几天,非中式
        public String date ; //日期
        public boolean isToday ;  //是否今天
        public String  day ;  //天
        public String luna ;  //阴历
    
        @Override
        public String toString() {
            return "DateEntity{" +
                    "million=" + million +
                    ", weekName='" + weekName + '\'' +
                    ", weekNum=" + weekNum +
                    ", date='" + date + '\'' +
                    ", isToday=" + isToday +
                    ", day='" + day + '\'' +
                    ", luna='" + luna + '\'' +
                    '}';
        }
    }
    

    封装的日期获取的工具类:

    package com.wzh.calendar.utils;
    
    import com.wzh.calendar.bean.DateEntity;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.Date;
    
    public class DataUtils {
        public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        public static int selectPosition =-1;
    
        public static int getSelectPosition() {
            return selectPosition;
        }
    
    
        /**
         *
         * 获取当前日期一周的日期
         * @param date
         * @return
         */
        public static ArrayList<DateEntity> getWeek(String date){
            ArrayList<DateEntity> result = new ArrayList<>();
            Calendar cal =Calendar.getInstance();
            try {
                cal.setTime(dateFormat.parse(date));
            } catch (ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); //获取本周一的日期
            for (int i = 0; i < 7; i++) {
                DateEntity entity = new DateEntity();
                entity.date = getValue(cal.get(cal.YEAR))+"-"+getValue(cal.get(cal.MONTH)+1)+"-"+getValue(cal.get(cal.DATE));
                entity.million = cal.getTimeInMillis() ;
                entity.day = getValue(cal.get(cal.DATE));
                entity.weekNum = cal.get(Calendar.DAY_OF_WEEK);
                entity.weekName = getWeekName(entity.weekNum);
                entity.isToday = isToday(entity.date);
                cal.add(Calendar.DATE, 1);
                result.add(entity);
            }
    
            return  result ;
    
        }
        /**
         * 获取当前日期一月的日期
         * @param date
         * @return
         */
        public static ArrayList<DateEntity> getMonth(String date){
            ArrayList<DateEntity> result = new ArrayList<>();
            Calendar cal =Calendar.getInstance();
            try {
                cal.setTime( new SimpleDateFormat("yyyy-MM").parse(date));
            } catch (ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
            for (int i = 1; i <=max; i++) {
                DateEntity entity = new DateEntity();
                entity.date = getValue(cal.get(cal.YEAR))+"-"+getValue(cal.get(cal.MONTH)+1)+"-"+getValue(cal.get(cal.DATE));
                entity.million = cal.getTimeInMillis() ;
                entity.weekNum = cal.get(Calendar.DAY_OF_WEEK);
                entity.day = getValue(cal.get(cal.DATE));
                entity.weekName = getWeekName(entity.weekNum);
                entity.isToday = isToday(entity.date);
                entity.luna = getLuna(entity.date);
                cal.add(Calendar.DATE, 1);
                result.add(entity);
            }
            //为了用空的值填补第一个之前的日期
            //先获取在本周内是周几
            int weekNum  = result.get(0).weekNum -1 ;
            for (int j = 0 ;j<weekNum;j++){
                DateEntity entity = new DateEntity();
                result.add(0,entity);
            }
            for (int i = 0; i <result.size(); i++) {
                 if (date.equals(result.get(i).date)){
                     selectPosition = i ;
                 }
            }
            return  result ;
    
        }
        /**
         * 根据美式周末到周一 返回
         * @param weekNum
         * @return
         */
        private static String getWeekName(int weekNum) {
            String name = "" ;
            switch (weekNum) {
                case 1:
                    name = "星期日";
                    break;
                case 2:
                    name = "星期一";
                    break;
                case 3:
                    name = "星期二";
                    break;
                case 4:
                    name = "星期三";
                    break;
                case 5:
                    name = "星期四";
                    break;
                case 6:
                    name = "星期五";
                    break;
                case 7:
                    name = "星期六";
                    break;
                default:
                    break;
            }
            return name;
        }
        /**
         * 是否是今天
         * @param sdate
         * @return
         */
        public static boolean isToday(String sdate){
            boolean b = false;
            Date time = null ;
            try {
                time = dateFormat.parse(sdate);
            } catch (ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Date today = new Date();
            if(time != null){
                String nowDate = dateFormater.get().format(today);
                String timeDate = dateFormater.get().format(time);
                if(nowDate.equals(timeDate)){
                    b = true;
                }
            }
            return b;
        }
        /**
         * 个位数补0操作
         * @param num
         * @return
         */
        public static String getValue(int num){
            return String.valueOf(num>9?num:("0"+num));
        }
    
    
        private final static ThreadLocal<SimpleDateFormat> dateFormater = new ThreadLocal<SimpleDateFormat>() {
            @Override
            protected SimpleDateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd");
            }
        };
    
        /**
         * 获取系统当前日期
         */
        public static String getCurrDate(String format) {
            SimpleDateFormat formatter = new SimpleDateFormat(format);
            Date curDate = new Date(System.currentTimeMillis());//获取当前时间
            String str = formatter.format(curDate);
            return str;
        }
        /**
         * 格式化日期
         */
        public static String formatDate(String date ,String format) {
            SimpleDateFormat formatter = new SimpleDateFormat(format);
            Date curDate = null;//获取当前时间
            try {
                curDate = formatter.parse(date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            String str = formatter.format(curDate);
            return str;
        }
    
        /**
         *  切换周的时候用
         * 获取前/后 几天的一个日期
         * @param currentData
         * @param dayNum
         * @return
         */
        public static String getSomeDays(String currentData,int dayNum){
            Calendar c = Calendar.getInstance();
            //过去七天
            try {
                c.setTime(DataUtils.dateFormat.parse(currentData));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            c.add(Calendar.DATE, dayNum);
            Date d = c.getTime();
            String day = DataUtils.dateFormat.format(d);
            return day ;
        }
        /**
         * 获取前/后 几个月的一个日期  切换月的时候用
         * @param currentData
         * @param monthNum
         * @return
         */
        public static String getSomeMonthDay(String currentData,int monthNum){
            Calendar c = Calendar.getInstance();
            try {
                c.setTime(new SimpleDateFormat("yyyy-MM").parse(currentData));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            c.set(Calendar.MONTH, c.get(Calendar.MONTH) +monthNum);
            Date day =  c.getTime();
            return   new SimpleDateFormat("yyyy-MM-dd").format(day);
        }
    
        /**
         * 获取阴历
         * @param date
         * @return
         */
        public static  String getLuna(String date){
            Calendar today = Calendar.getInstance();
            try {
                today.setTime(Lunar.chineseDateFormat.parse(date));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return new Lunar(today).toString() ;
        }
    }
    
    

    这里有个地方需要注意一下,因为我们一个月第一天是周几不确定,显示GridView的时候第一天的position也不确定,但是我们可以根据前面少了几天再添加上空对象即可:

    //为了用空的值填补第一个之前的日期
    //先获取在本周内是周几
    int weekNum  = result.get(0).weekNum -1 ;
    for (int j = 0 ;j<weekNum;j++){
           DateEntity entity = new DateEntity();
           result.add(0,entity);
    }
    
    

    还有一个获取阴历日期的工具类,比较复杂,所以直接从网上找了一个,这里就不贴了。

    剩下的就是去写布局、自定义PopupWindow了,这些应该是没什么难度吧。关于GridView选中,原理就是在Adapter里面设置一个选中方法:

    private int selectedPosition = -1;// 选中的位置
    public void setSelectedPosition(int position) {
            selectedPosition = position;
            notifyDataSetChanged();
    }
    ...
    在Adapter的getView(int position, View convertView, ViewGroup parent) 
    方法去判断 position是否和selectedPosition 是否相等,相等就表示选中了,可以修改背景、字体颜色等等
    ...
    当然在用到Adapter的地方也要调用setSelectedPosition方法
    
    具体怎么使用可以参考里面的代码。
    
    

    关于GrdiView左右滑动的判断(关键代码片段):

    private GestureDetector gestureDetector;
    //初始化
    gestureDetector = new GestureDetector(context,onGestureListener);
    /**
     * 手势监听是否是左右滑动,这里认为滑动距离超过100就算左右滑动
     */
    private GestureDetector.OnGestureListener onGestureListener =
            new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                                       float velocityY) {
                    float x = e2.getX() - e1.getX();
                    float y = e2.getY() - e1.getY();
    
                    if (x > 100) {
                        doResult(RIGHT);
                    } else if (x < -100) {
                        doResult(LEFT);
                    }
                    return true;
                }
            };
    public void doResult(int action) {
    
        switch (action) {
            case RIGHT:
                date = DataUtils.getSomeMonthDay(date,-1);
                adapter.setData(DataUtils.getMonth(date));
                adapter.setDateString(date);
                adapter.setSelectedPosition(DataUtils.getSelectPosition());
                currentDateTv.setText("当前月份:"+DataUtils.formatDate(date,"yyyy-MM"));
                Log.e("wenzihao","go right");
                break;
            case LEFT:
                date = DataUtils.getSomeMonthDay(date,+1);
                adapter.setData(DataUtils.getMonth(date));
                adapter.setDateString(date);
                adapter.setSelectedPosition(DataUtils.getSelectPosition());
                currentDateTv.setText("当前月份:"+DataUtils.formatDate(date,"yyyy-MM"));
                Log.e("wenzihao","go left");
                break;
        }
    }
    ...设置手势给gridview
    gridView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                return gestureDetector.onTouchEvent(event);
            }
    });
    
    

    最后就是点击PopupWindow的时候自定义回调方法把选中日期带过去即可。

    好了,其他的代码也不贴了,关键点就那么点,没啥太大难度,感觉主要还是考验大家的基本功吧。

    这么一个自定义日历控件就写好了,是不是很简单感觉,希望能够对大家有启发和帮助,可以灵活自定义出设计产品需要的各种控件。

    最后附上项目地址:github地址

    相关文章

      网友评论

        本文标题:Android开发实现自定义日历、日期选择控件

        本文链接:https://www.haomeiwen.com/subject/hnxgattx.html