这里主要记录一下在编写日历apk过程中一些主要的点:先看下效果图

一、主要功能

  • 1、支持农历、节气、常用节假日
  • 2、使用数据库,设置计划

二、基本结构

我们要实现的日历控件采用GestureDetector构造器,使用OnGestureListener监听滑动。目前我们设定日历左右滑动为月份切换的操作,每一个月份显示通过GridView实现,里面的数据是通过构造CalendarView,将其绑定到GridView。每次左右滑动,都会刷新CalendarView。布局上,会在GridView上显示当前日期农历时间。

三、主要代码及解析

CalendarActivity.java


/**
 * 日历显示activity
 *
 * @author 孔乙己大叔
 */
public class CalendarActivity extends Activity implements OnGestureListener {


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        gestureDetector = new GestureDetector(this, this);
        flipper = (ViewFlipper) findViewById(R.id.flipper);
        flipper.removeAllViews();
        calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
        addGridView();
        gridView.setAdapter(calV);
        flipper.addView(gridView, 0);
        topText = (BorderText) findViewById(R.id.toptext);
        addTextToTopTextView(topText);
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        int gvFlag = 0;         //每次添加gridview到viewflipper中时给的标记
        if (e1.getX() - e2.getX() > 120) {
            //像左滑动
            addGridView();   //添加一个gridView
            jumpMonth++;     //下一个月
            calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
            gridView.setAdapter(calV);
            addTextToTopTextView(topText);
            gvFlag++;
            flipper.addView(gridView, gvFlag);
            this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));
            this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));
            this.flipper.showNext();
            flipper.removeViewAt(0);
            return true;
        } else if (e1.getX() - e2.getX() < -120) {
            //向右滑动
            addGridView();   //添加一个gridView
            jumpMonth--;     //上一个月

            calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
            gridView.setAdapter(calV);
            gvFlag++;
            addTextToTopTextView(topText);
            //flipper.addView(gridView);
            flipper.addView(gridView, gvFlag);
            this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_in));
            this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_out));
            this.flipper.showPrevious();
            flipper.removeViewAt(0);
            return true;
        }
        return false;
    }

    /**
     * 创建菜单
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, menu.FIRST, menu.FIRST, "今天");
        menu.add(0, menu.FIRST + 1, menu.FIRST + 1, "跳转");
        menu.add(0, menu.FIRST + 2, menu.FIRST + 2, "日程");
        menu.add(0, menu.FIRST + 3, menu.FIRST + 3, "日期转换");
        return super.onCreateOptionsMenu(menu);
    }

    /**
     * 选择菜单
     */
    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        switch (item.getItemId()) {
            case Menu.FIRST:
                //跳转到今天
                int xMonth = jumpMonth;
                int xYear = jumpYear;
                int gvFlag = 0;
                jumpMonth = 0;
                jumpYear = 0;
                addGridView();   //添加一个gridView
                year_c = Integer.parseInt(currentDate.split("-")[0]);
                month_c = Integer.parseInt(currentDate.split("-")[1]);
                day_c = Integer.parseInt(currentDate.split("-")[2]);
                calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
                gridView.setAdapter(calV);
                addTextToTopTextView(topText);
                gvFlag++;
                flipper.addView(gridView, gvFlag);
                if (xMonth == 0 && xYear == 0) {
                    //nothing to do
                } else if ((xYear == 0 && xMonth > 0) || xYear > 0) {
                    this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));
                    this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));
                    this.flipper.showNext();
                } else {
                    this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_in));
                    this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_out));
                    this.flipper.showPrevious();
                }
                flipper.removeViewAt(0);
                break;
            case Menu.FIRST + 1:

                new DatePickerDialog(this, new OnDateSetListener() {

                    @Override
                    public void onDateSet(DatePicker view, int year, int monthOfYear,
                                          int dayOfMonth) {
                        //1901-1-1 ----> 2049-12-31
                        if (year < 1901 || year > 2049) {
                            //不在查询范围内
                            new AlertDialog.Builder(CalendarActivity.this).setTitle("错误日期").setMessage("跳转日期范围(1901/1/1-2049/12/31)").setPositiveButton("确认", null).show();
                        } else {
                            int gvFlag = 0;
                            addGridView();   //添加一个gridView
                            calV = new CalendarView(CalendarActivity.this, CalendarActivity.this.getResources(), year, monthOfYear + 1, dayOfMonth);
                            gridView.setAdapter(calV);
                            addTextToTopTextView(topText);
                            gvFlag++;
                            flipper.addView(gridView, gvFlag);
                            if (year == year_c && monthOfYear + 1 == month_c) {
                                //nothing to do
                            }
                            if ((year == year_c && monthOfYear + 1 > month_c) || year > year_c) {
                                CalendarActivity.this.flipper.setInAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_left_in));
                                CalendarActivity.this.flipper.setOutAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_left_out));
                                CalendarActivity.this.flipper.showNext();
                            } else {
                                CalendarActivity.this.flipper.setInAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_right_in));
                                CalendarActivity.this.flipper.setOutAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_right_out));
                                CalendarActivity.this.flipper.showPrevious();
                            }
                            flipper.removeViewAt(0);
                            //跳转之后将跳转之后的日期设置为当期日期
                            year_c = year;
                            month_c = monthOfYear + 1;
                            day_c = dayOfMonth;
                            jumpMonth = 0;
                            jumpYear = 0;
                        }
                    }
                }, year_c, month_c - 1, day_c).show();
                break;
            case Menu.FIRST + 2:
                Intent intent = new Intent();
                intent.setClass(CalendarActivity.this, ScheduleAll.class);
                startActivity(intent);
                break;
            case Menu.FIRST + 3:
                Intent intent1 = new Intent();
                intent1.setClass(CalendarActivity.this, CalendarConvert.class);
                intent1.putExtra("date", new int[]{year_c, month_c, day_c});
                startActivity(intent1);
                break;
        }
        return super.onMenuItemSelected(featureId, item);
    }

    

    //添加头部的年份 闰哪月等信息
    public void addTextToTopTextView(TextView view) {
        StringBuffer textDate = new StringBuffer();
        view.setBackgroundResource(R.drawable.top_day);
        view.setBackground(getResources().getDrawable(R.drawable.top_day));
        textDate.append(calV.getShowYear()).append("年").append(calV.getShowMonth()).append("月").append("\t");
        if (!calV.getLeapMonth().equals("") && calV.getLeapMonth() != null) {
            textDate.append("闰").append(calV.getLeapMonth()).append("月").append("\t");
        }
        textDate.append(calV.getAnimalsYear()).append("年").append("(").append(calV.getCyclical()).append("年)");
        view.setText(textDate);
        view.setTextColor(Color.BLACK);
        view.setTypeface(Typeface.DEFAULT_BOLD);
    }

    //添加gridview
    private void addGridView() {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);

        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int Width = displayMetrics.widthPixels;
        int Height = displayMetrics.heightPixels;
        gridView = new GridView(this);
        gridView.setNumColumns(7);
        gridView.setColumnWidth(46);

        if (Width == 480 && Height == 800) {
            gridView.setColumnWidth(69);
        }
        gridView.setGravity(Gravity.CENTER_VERTICAL);
        gridView.setSelector(new ColorDrawable(Color.TRANSPARENT)); // 去除gridView边框
        gridView.setVerticalSpacing(1);
        gridView.setHorizontalSpacing(1);
        gridView.setBackgroundResource(R.drawable.gridview_bk);
        gridView.setOnTouchListener(new OnTouchListener() {
            //将gridview中的触摸事件回传给gestureDetector
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                return CalendarActivity.this.gestureDetector
                        .onTouchEvent(event);
            }
        });

        gridView.setOnItemClickListener(new OnItemClickListener() {
            //gridView中的每一个item的点击事件
            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int position,
                                    long arg3) {
                //点击任何一个item,得到这个item的日期(排除点击的是周日到周六(点击不响应))
                int startPosition = calV.getStartPositon();
                int endPosition = calV.getEndPosition();
                if (startPosition <= position && position <= endPosition) {
                    String scheduleDay = calV.getDateByClickItem(position).split("\\.")[0];  //这一天的阳历
                    String scheduleYear = calV.getShowYear();
                    String scheduleMonth = calV.getShowMonth();
                    String week = "";

                    //通过日期查询这一天是否被标记,如果标记了日程就查询出这天的所有日程信息
                    String[] scheduleIDs = dao.getScheduleByTagDate(Integer.parseInt(scheduleYear), Integer.parseInt(scheduleMonth), Integer.parseInt(scheduleDay));
                    if (scheduleIDs != null && scheduleIDs.length > 0) {
                        //跳转到显示这一天的所有日程信息界面
                        Intent intent = new Intent();
                        intent.setClass(CalendarActivity.this, ScheduleInfoView.class);
                        intent.putExtra("scheduleID", scheduleIDs);
                        startActivity(intent);
                    } else {
                        //直接跳转到需要添加日程的界面

                        //得到这一天是星期几
                        switch (position % 7) {
                            case 0:
                                week = "星期日";
                                break;
                            case 1:
                                week = "星期一";
                                break;
                            case 2:
                                week = "星期二";
                                break;
                            case 3:
                                week = "星期三";
                                break;
                            case 4:
                                week = "星期四";
                                break;
                            case 5:
                                week = "星期五";
                                break;
                            case 6:
                                week = "星期六";
                                break;
                        }

                        ArrayList<String> scheduleDate = new ArrayList<String>();
                        scheduleDate.add(scheduleYear);
                        scheduleDate.add(scheduleMonth);
                        scheduleDate.add(scheduleDay);
                        scheduleDate.add(week);
                        //scheduleDate.add(scheduleLunarDay);

                        Intent intent = new Intent();
                        intent.putStringArrayListExtra("scheduleDate", scheduleDate);
                        intent.setClass(CalendarActivity.this, ScheduleView.class);
                        startActivity(intent);
                    }
                }
            }
        });
        gridView.setLayoutParams(params);
    }
}

自定义view实现:



/**
 * 日历gridview中的每一个item显示的textview
 *
 * @author 孔乙己大叔
 */
public class CalendarView extends BaseAdapter {


    public CalendarView(Context context, Resources rs, int jumpMonth, int jumpYear, int year_c, int month_c, int day_c) {
        this();
        this.context = context;
        sc = new SpecialCalendar();
        lc = new LunarCalendar();
        this.res = rs;

        int stepYear = year_c + jumpYear;
        int stepMonth = month_c + jumpMonth;
        if (stepMonth > 0) {
            //往下一个月滑动
            if (stepMonth % 12 == 0) {
                stepYear = year_c + stepMonth / 12 - 1;
                stepMonth = 12;
            } else {
                stepYear = year_c + stepMonth / 12;
                stepMonth = stepMonth % 12;
            }
        } else {
            //往上一个月滑动
            stepYear = year_c - 1 + stepMonth / 12;
            stepMonth = stepMonth % 12 + 12;
            if (stepMonth % 12 == 0) {

            }
        }
        currentYear = String.valueOf(stepYear);;  //得到当前的年份
        currentMonth = String.valueOf(stepMonth);  //得到本月 (jumpMonth为滑动的次数,每滑动一次就增加一月或减一月)
        currentDay = String.valueOf(day_c);  //得到当前日期是哪天
        getCalendar(Integer.parseInt(currentYear), Integer.parseInt(currentMonth));
    }

    public CalendarView(Context context, Resources rs, int year, int month, int day) {
        this();
        this.context = context;
        sc = new SpecialCalendar();
        lc = new LunarCalendar();
        this.res = rs;
        currentYear = String.valueOf(year);
        ;  //得到跳转到的年份
        currentMonth = String.valueOf(month);  //得到跳转到的月份
        currentDay = String.valueOf(day);  //得到跳转到的天

        getCalendar(Integer.parseInt(currentYear), Integer.parseInt(currentMonth));

    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return dayNumber.length;
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.calendar, null);
        }
        TextView textView = (TextView) convertView.findViewById(R.id.tvtext);
        String d = dayNumber[position].split("\\.")[0];
        String dv = dayNumber[position].split("\\.")[1];
        //Typeface typeface = Typeface.createFromAsset(context.getAssets(), "fonts/Helvetica.ttf");
        //textView.setTypeface(typeface);
        SpannableString sp = new SpannableString(d + "\n" + dv);
        sp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, d.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        sp.setSpan(new RelativeSizeSpan(1.2f), 0, d.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        if (dv != null || dv != "") {
            sp.setSpan(new RelativeSizeSpan(0.75f), d.length() + 1, dayNumber[position].length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        //sp.setSpan(new ForegroundColorSpan(Color.MAGENTA), 14, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        textView.setText(sp);
        textView.setTextColor(Color.GRAY);
        if (position < 7) {
            //设置周
            textView.setTextColor(Color.BLACK);
            textView.setBackgroundResource(R.drawable.week_top);
        }

        if (position < daysOfMonth + dayOfWeek + 7 && position >= dayOfWeek + 7) {
            // 当前月信息显示
            textView.setTextColor(Color.BLACK);// 当月字体设黑
            drawable = res.getDrawable(R.drawable.item);
            //textView.setBackgroundDrawable(drawable);
            //textView.setBackgroundColor(Color.WHITE);

        }
        if (schDateTagFlag != null && schDateTagFlag.length > 0) {
            for (int i = 0; i < schDateTagFlag.length; i++) {
                if (schDateTagFlag[i] == position) {
                    //设置日程标记背景
                    textView.setBackgroundResource(R.drawable.mark);
                }
            }
        }
        if (currentFlag == position) {
            //设置当天的背景
            textView.setBackgroundResource(R.drawable.current_day_bgc);
            textView.setTextColor(Color.WHITE);
        }
        return convertView;
    }

    //得到某年的某月的天数且这月的第一天是星期几
    public void getCalendar(int year, int month) {
        isLeapyear = sc.isLeapYear(year);              //是否为闰年
        daysOfMonth = sc.getDaysOfMonth(isLeapyear, month);  //某月的总天数
        dayOfWeek = sc.getWeekdayOfMonth(year, month);      //某月第一天为星期几
        lastDaysOfMonth = sc.getDaysOfMonth(isLeapyear, month - 1);  //上一个月的总天数
        Log.d("DAY", isLeapyear + " ======  " + daysOfMonth + "  ============  " + dayOfWeek + "  =========   " + lastDaysOfMonth);
        getweek(year, month);
    }

    //将一个月中的每一天的值添加入数组dayNuber中
    private void getweek(int year, int month) {
        int j = 1;
        int flag = 0;
        String lunarDay = "";
        //得到当前月的所有日程日期(这些日期需要标记)
        dao = new ScheduleDAO(context);
        ArrayList<ScheduleDateTag> dateTagList = dao.getTagDate(year, month);
        if (dateTagList != null && dateTagList.size() > 0) {
            schDateTagFlag = new int[dateTagList.size()];
        }
        for (int i = 0; i < dayNumber.length; i++) {
            // 周一
            if (i < 7) {
                dayNumber[i] = week[i] + "." + " ";
            } else if (i < dayOfWeek + 7) {  //前一个月
                int temp = lastDaysOfMonth - dayOfWeek + 1 - 7;
                lunarDay = lc.getLunarDate(year, month - 1, temp + i, false);
                dayNumber[i] = (temp + i) + "." + lunarDay;
            } else if (i < daysOfMonth + dayOfWeek + 7) {   //本月
                String day = String.valueOf(i - dayOfWeek + 1 - 7);   //得到的日期
                lunarDay = lc.getLunarDate(year, month, i - dayOfWeek + 1 - 7, false);
                dayNumber[i] = i - dayOfWeek + 1 - 7 + "." + lunarDay;
                //对于当前月才去标记当前日期
                if (sys_year.equals(String.valueOf(year)) && sys_month.equals(String.valueOf(month)) && sys_day.equals(day)) {
                    //笔记当前日期
                    currentFlag = i;
                }
                //标记日程日期
                if (dateTagList != null && dateTagList.size() > 0) {
                    for (int m = 0; m < dateTagList.size(); m++) {
                        ScheduleDateTag dateTag = dateTagList.get(m);
                        int matchYear = dateTag.getYear();
                        int matchMonth = dateTag.getMonth();
                        int matchDay = dateTag.getDay();
                        if (matchYear == year && matchMonth == month && matchDay == Integer.parseInt(day)) {
                            schDateTagFlag[flag] = i;
                            flag++;
                        }
                    }
                }
                setShowYear(String.valueOf(year));
                setShowMonth(String.valueOf(month));
                setAnimalsYear(lc.animalsYear(year));
                setLeapMonth(lc.leapMonth == 0 ? "" : String.valueOf(lc.leapMonth));
                setCyclical(lc.cyclical(year));
            } else {   //下一个月
                lunarDay = lc.getLunarDate(year, month + 1, j, false);
                dayNumber[i] = j + "." + lunarDay;
                j++;
            }
        }
        String abc = "";
        for (int i = 0; i < dayNumber.length; i++) {
            abc = abc + dayNumber[i] + ":";
        }
        Log.d("DAYNUMBER", abc);
    }
  
}

判断是否为闰年,当月多少天数,当天是星期几代码实现:

// 判断是否为闰年
    public boolean isLeapYear(int year) {
        if (year % 100 == 0 && year % 400 == 0) {
            return true;
        } else if (year % 100 != 0 && year % 4 == 0) {
            return true;
        }
        return false;
    }

    //得到某月有多少天数
    public int getDaysOfMonth(boolean isLeapyear, int month) {
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                daysOfMonth = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                daysOfMonth = 30;
                break;
            case 2:
                if (isLeapyear) {
                    daysOfMonth = 29;
                } else {
                    daysOfMonth = 28;
                }
        }
        return daysOfMonth;
    }

    //指定某年中的某月的第一天是星期几
    public int getWeekdayOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, 1);
        dayOfWeek = cal.get(Calendar.DAY_OF_WEEK) - 1;
        return dayOfWeek;
    }

四、小结

这里我们只介绍了日历的基本实现原理,和一些关键的点,其实这种实现方式相对还是比较简单的,容易理解,当然难免有不足的地方,后边根据需要再逐步完善和扩展吧。数据库操作暂时没进行解析,后面会单独用demo说明。

源码下载地址:https://download.csdn.net/download/Ctrl_qun/12436629

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐