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

点击上方“程序员大咖”,选择“置顶公众号”

关键时刻,第一时间送达!


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


效果图:



实现思路
  • 头部是一个自定义组合控件;

  • 显示一周的日期部分用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+""+                             "周几:"+ date.weekName+""+                             "今日:"+ date.isToday+""+                             "时间戳:"+ date.million+"");                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;    }}...设置手势给gridviewgridView.setOnTouchListener(new View.OnTouchListener() {        
       @Override        public boolean onTouch(View view, MotionEvent event) {            
           return gestureDetector.onTouchEvent(event);        }});

最后就是点击PopupWindow的时候自定义回调方法把选中日期带过去即可。好了,其他的代码也不贴了,关键点就那么点,没啥太大难度,感觉主要还是考验大家的基本功吧。这么一个自定义日历控件就写好了,是不是很简单感觉,希望能够对大家有启发和帮助,可以灵活自定义出设计产品需要的各种控件。


最后附上项目地址:https://github.com/wenzhihao123/Android-CalendarView-master



  • 作者:wenzhihao123

  • http://www.apkbus.com/blog-947568-77170.html

  • 程序员大咖整理发布,转载请联系作者获得授权

【点击成为Java大神】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值