Android-自定义日历控件

本文介绍如何在Android中创建一个自定义日历控件,实现包括当前月份展示、上下月补充、今日高亮、日期点击切换及回调等功能。详细代码可供参考,方便扩展其他特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看效果:


我也不知道图片为什么上传的这么大;

基本实现的功能:

1.当前月份显示,上下月补充空余部分

2.默认今天阴影显示,点击日期阴影显示

3.点到上下月的日期,自动跳转到上下月,并阴影显示

4.上下月切换

5.提供点击日期改变的接口

其他功能可以自己添加,上代码:

package com.sunrui.mysport.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.sunrui.mysport.listener.OnDateChangeListener;
import com.sunrui.mysport.utils.DateUtil;


/**
 * Created by sunrui on 15/9/11.
 * 自定义的日历
 */
public class CalendarView extends View {

    /**
     * 宽
     */
    private float width;

    /**
     * 高
     */
    private float height;

    /**
     * 单位长度
     */
    private float cell;

    /**
     * 日期画笔
     */
    private Paint date_P;

    /**
     * 当前的日期
     */
    private String cur_Date;

    /**
     * 日期容器
     */
    private Rect date_Bounds;

    /**
     * 星期的容器
     */
    private Rect week_Bounds;

    /**
     * 月的容器
     */
    private Rect moth_Bounds;

    /**
     * 深色画笔
     */
    private Paint line_gray_p;

    /**
     * 星期的画笔
     */
    private Paint week_p;

    /**
     * 星期
     */
    private String week;

    /**
     * 当前月份的天数
     */
    private int curMonth_days;

    /**
     * 年月日
     */
    private int year, month, day;

    /**
     * 显示的天数
     */
    private int[] dates;

    /**
     * 天数的标志
     */
    private int[] marks;

    private OnDateChangeListener onDateChangeListener;


    public CalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public CalendarView(Context context) {
        super(context);
    }

    public CalendarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        date_P = new Paint();
        date_P.setColor(Color.BLACK);
        date_P.setAntiAlias(true);
        date_Bounds = new Rect();
        line_gray_p = new Paint();
        line_gray_p.setColor(Color.GRAY);
        line_gray_p.setAntiAlias(true);
        line_gray_p.setStrokeWidth(1);
        week_p = new Paint();
        week_p.setColor(Color.RED);
        week_p.setAntiAlias(true);
        week = "日一二三四五六";
        week_Bounds = new Rect();
        moth_Bounds = new Rect();
        dates = new int[42];
        marks = new int[42];
        cur_Date = getCurDate();
        setCurDay();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        width = 0;
        height = 0;
        int specSize = MeasureSpec.getSize(widthMeasureSpec);
        width = getPaddingLeft() + getPaddingRight() + specSize;
        cell = width / 7;
        height = width + 1 + cell;
        setMeasuredDimension((int) width, (int) height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        date_P.setTextSize(cell / 3);
        date_P.getTextBounds(cur_Date, 0, cur_Date.length(), date_Bounds);
        float left = (width - date_Bounds.width()) / 2;
        float bottom = cell / 2 + date_Bounds.height() / 2;
        canvas.drawText(cur_Date, left, bottom, date_P);
        setDate();

        String last = "上一月";
        String next = "下一月";
        line_gray_p.setTextSize(cell / 3);
        line_gray_p.getTextBounds(last, 0, last.length(), moth_Bounds);
        canvas.drawText(last, (left - moth_Bounds.width()) / 2, bottom, line_gray_p);
        canvas.drawText(next, left + date_Bounds.width() + (left - moth_Bounds.width()) / 2, bottom, line_gray_p);

        week_p.setTextSize(cell / 3);
        week_p.getTextBounds(week, 0, 1, week_Bounds);

        canvas.drawLine(0, 0, 0, height, line_gray_p);
        canvas.drawLine(width - 2, 0, width - 2, height, line_gray_p);
        for (int i = 0; i < 9; i++) {
            if (i < 7) {
                canvas.drawText(week.substring(i, i + 1), cell / 2 - week_Bounds.width() / 2 + cell * i, cell + cell / 2 + week_Bounds.height() / 2, week_p);
            }
            if (i > 0 && i < 7) {
                canvas.drawLine(cell * i, cell * 2, cell * i, height, line_gray_p);
            }
            canvas.drawLine(0, cell * i, width, cell * i + 1, line_gray_p);
        }

        for (int i = 0; i < 42; i++) {

            int step_x = i % 7;
            int step_y = i / 7;
            if (marks[i] == 2) {
                if (dates[i] == day) {
                    canvas.drawRect(cell * step_x, cell * (2 + step_y), cell * step_x + cell, cell * (3 + step_y), line_gray_p);
                }
                canvas.drawText(dates[i] + "", cell / 2 - week_Bounds.width() / 2 + cell * step_x, cell * (2 + step_y) + cell / 2 + week_Bounds.height() / 2, date_P);
            } else {
                canvas.drawText(dates[i] + "", cell / 2 - week_Bounds.width() / 2 + cell * step_x, cell * (2 + step_y) + cell / 2 + week_Bounds.height() / 2, line_gray_p);
            }
        }


    }

    /**
     * 判断点击的位置
     *
     * @return
     */
    private void getTouchEvent(float x, float y) {
        if (y <= cell) {
            float left = (width - date_Bounds.width()) / 2;
            if (x <= left) {
                //点击的是上一月区域
                toLastMonth();
            }
            float right = (width + date_Bounds.width()) / 2;
            if (x >= right) {
                //点击的下一个月的区域
                toNextMonth();
            }
        } else if (y > cell * 2) {
            int h = (int) (x / cell);
            int v = (int) (y / cell) - 2;
            int value = v * 7 + h;
            day = dates[value];
            if (marks[value] == 2) {
                //点击的是本月的日期
                postInvalidate();
            } else if (marks[value] == 1) {
                //点击的是上个月的日期
                toLastMonth();
            } else {
                //点击的下个月的日期
                toNextMonth();
            }
        }
        if (onDateChangeListener != null) {
            onDateChangeListener.onDateChange(year, month, day);
        }
    }

    /**
     * 跳转到下一个月
     */
    private void toNextMonth() {
        if (month == 12) {
            month = 1;
            year += 1;
        } else {
            month += 1;
        }
        cur_Date = year + "年" + month + "月";
        postInvalidate();
    }

    /**
     * 跳转到上一月
     */
    private void toLastMonth() {
        if (month == 1) {
            month = 12;
            year = year - 1;
        } else {
            month = month - 1;
        }
        cur_Date = year + "年" + month + "月";
        postInvalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_UP) {
            float x = event.getX();
            float y = event.getY();
            getTouchEvent(x, y);
        }
        return true;
    }

    private String getCurDate() {
        year = DateUtil.getCurrentYear();
        month = DateUtil.getCurrentMonth();
        day = DateUtil.getCurrentDate();
        return year + "年" + month + "月";
    }

    private void setDate() {
        curMonth_days = DateUtil.getDaysByYearMonth(year, month);
        int i = 0;
        try {
            i = DateUtil.dayForWeek(year, month, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        int last_month;
        int last_year;
        if (month == 1) {
            last_month = 12;
            last_year = year - 1;
        } else {
            last_month = month - 1;
            last_year = year;
        }
        int lastMonthDays = DateUtil.getDaysByYearMonth(last_year, last_month);
        for (int m = 0; m < 42; ) {
            if (i == 1) {
                for (int h = 1; h <= curMonth_days; h++) {
                    dates[m] = h;
                    marks[m] = 2;
                    m++;
                }
                for (int n = 1; n <= 42; n++) {
                    dates[m] = n;
                    marks[m] = 3;
                    m++;
                    if (m >= 42) {
                        break;
                    }
                }
            } else {
                for (int j = 1; j < i; j++) {
                    dates[m] = lastMonthDays + j - i + 1;
                    marks[m] = 1;
                    m++;
                }
                for (int k = 1; k <= curMonth_days; k++) {
                    dates[m] = k;
                    marks[m] = 2;
                    m++;
                }
                for (int n = 1; n <= 42; n++) {
                    dates[m] = n;
                    marks[m] = 3;
                    m++;
                    if (m >= 42) {
                        break;
                    }
                }
            }
        }

    }

    /**
     * 获取当前的日期
     */
    private void setCurDay() {
        day = DateUtil.getCurrentDate();
    }

    public void setOnDateChangeListener(OnDateChangeListener onDateChangeListener) {
        this.onDateChangeListener = onDateChangeListener;
    }

}

package com.sunrui.mysport.listener;

/**
 * Created by sunrui on 15/9/11.
 * 日期改变的监听
 */
public interface OnDateChangeListener {
    void onDateChange(int year, int month, int date);
}

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.sunrui.mysport.ui.SportCalendarActivity">

    <RelativeLayout
        android:id="@+id/top"
        android:layout_width="match_parent"
        android:layout_height="@dimen/_96px"
        android:background="@color/top_background">

        <ImageView
            android:id="@+id/go_home"
            android:layout_width="@dimen/_96px"
            android:layout_height="@dimen/_96px"
            android:padding="@dimen/_20px"
            android:scaleType="fitXY"
            android:src="@mipmap/home" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/sport_calender"
            android:textColor="@color/top_text"
            android:textSize="@dimen/_48px" />

    </RelativeLayout>

    <com.sunrui.mysport.widget.CalendarView
        android:id="@+id/calendar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/top"
        android:layout_margin="@dimen/_24px" />

    <TextView
        android:id="@+id/date"
        android:layout_width="match_parent"
        android:layout_height="@dimen/_36px"
        android:textColor="@color/bottom_text"
        android:layout_marginLeft="@dimen/_24px"
        android:layout_below="@+id/calendar"
        android:textSize="@dimen/_24px" />

</RelativeLayout>

package com.sunrui.mysport.ui;

import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.sunrui.mysport.R;
import com.sunrui.mysport.listener.OnDateChangeListener;
import com.sunrui.mysport.utils.DateUtil;
import com.sunrui.mysport.widget.CalendarView;

/**
 * 孙瑞 2015/9/11
 * 运动日历
 */
public class SportCalendarActivity extends Activity implements View.OnClickListener, OnDateChangeListener {

    /**
     * 返回主页面
     */
    private ImageView go_home;

    /**
     * 日历控件
     */
    private CalendarView calendar;

    /**
     * 时间
     */
    private TextView dateT;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sport_calendar);
        initView();
        bindingData();
    }

    private void bindingData() {
        dateT.setText(DateUtil.getDateStr());
    }

    private void initView(){
        go_home = (ImageView) findViewById(R.id.go_home);
        go_home.setOnClickListener(this);
        calendar = (CalendarView) findViewById(R.id.calendar);
        calendar.setOnDateChangeListener(this);
        dateT = (TextView) findViewById(R.id.date);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.go_home:
                finish();
                break;
            default:
                break;
        }
    }

    @Override
    public void onDateChange(int year, int month, int date) {
        Log.v("sss", "year=" + year + ",month=" + month + ",date=" + date);
        dateT.setText(year + "年" + month + "月" + date + "日");
    }
}

package com.sunrui.mysport.utils;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * Created by sunrui on 15/9/11.
 * 日期功能类
 */
public class DateUtil {

    /**
     * 字符串转时间
     *
     * @param str
     * @param format
     * @return
     */
    public static Date str2Date(String str, String format) {
        if (str == null || str.length() == 0) {
            return null;
        }
        if (format == null || format.length() == 0) {
            format = "yyyy-MM-dd";
        }
        Date date = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.US);
            date = sdf.parse(str);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 获取当前月份的天数
     *
     * @return
     */
    public static int getCurrentMonthDays() {
        Calendar a = Calendar.getInstance();
        a.set(Calendar.DATE, 1);
        a.roll(Calendar.DATE, -1);
        int maxDate = a.get(Calendar.DATE);
        return maxDate;
    }

    /**
     * 根据年月获取当月天数
     *
     * @param year  年份
     * @param month 月份
     * @return
     */
    public static int getDaysByYearMonth(int year, int month) {
        Calendar a = Calendar.getInstance();
        a.set(Calendar.YEAR, year);
        a.set(Calendar.MONTH, month - 1);
        a.set(Calendar.DATE, 1);
        a.roll(Calendar.DATE, -1);
        int maxDate = a.get(Calendar.DATE);
        return maxDate;
    }

    /**
     * 根据年月日获取星期
     *
     * @param year
     * @param month
     * @param date
     * @return
     * @throws Exception
     */
    public static int dayForWeek(int year, int month, int date) throws Exception {
        String time = year + "-" + month + "-" + date;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
        Calendar c = Calendar.getInstance();
        c.setTime(format.parse(time));
        int day = c.get(Calendar.DAY_OF_WEEK);
        return day;
    }

    /**
     * 返回当前年份
     * @return
     */
    public static int getCurrentYear() {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy");
        String time = format.format(date);
        return Integer.parseInt(time);
    }

    /**
     * 返回当前月份
     * @return
     */
    public static int getCurrentMonth() {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("MM");
        String time = format.format(date);
        return Integer.parseInt(time);
    }

    /**
     * 返回当前日期
     * @return
     */
    public static int getCurrentDate(){
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("dd");
        String time = format.format(date);
        return Integer.parseInt(time);
    }

    /**
     * 返回当前年月日
     * @return
     */
    public static String getDateStr(){
        return getCurrentYear() + "年" + getCurrentMonth() + "月" + getCurrentDate() + "日";
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值