欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义控件实现可多选课程日历CalendarView

程序员文章站 2023-12-01 10:50:16
可多选课程日历calendarview的效果图 开发环境 ide版本:androidstudio2.0 物理机版本:win7旗舰版(64位) 前言 最近的项...

可多选课程日历calendarview的效果图

Android自定义控件实现可多选课程日历CalendarView

开发环境

ide版本:androidstudio2.0
物理机版本:win7旗舰版(64位)

前言

最近的项目中用到了一个课程选择的日历view,于是在网上搜了搜自定义日历view,发现基本上都是单选的,不能够满足项目中的需求。于是自己重新造了个*,写了个可以被多选的自定义日历view。最后面会给出github地址。

代码实现

package widget;

import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.paint;
import android.util.attributeset;
import android.util.displaymetrics;
import android.view.motionevent;
import android.view.view;

import com.arisaid.calendarview.r;

import java.util.arraylist;
import java.util.calendar;
import java.util.list;

/**
 * created by zhouyou on 2016/7/25.
 * class desc:
 *
 * 自定义日历view,可多选
 */
public class calendarview extends view {

  // 列的数量
  private static final int num_columns  =  7;
  // 行的数量
  private static final int num_rows    =  6;

  /**
   * 可选日期数据
   */
  private list<string> moptionaldates;

  /**
   * 以选日期数据
   */
  private list<string> mselecteddates = new arraylist<>();

  // 背景颜色
  private int mbgcolor = color.parsecolor("#f7f7f7");
  // 天数默认颜色
  private int mdaynormalcolor = color.parsecolor("#0070f8");
  // 天数不可选颜色
  private int mdaynotoptcolor = color.parsecolor("#cbcbcb");
  // 天数选择后颜色
  private int mdaypressedcolor = color.white;
  // 天数字体大小
  private int mdaytextsize = 14;
  // 是否可以被点击状态
  private boolean mclickable = true;

  private displaymetrics mmetrics;
  private paint mpaint;
  private int mcuryear;
  private int mcurmonth;
  private int mcurdate;

  private int mselyear;
  private int mselmonth;
  private int mseldate;
  private int mcolumnsize;
  private int mrowsize;
  private int[][] mdays;

  // 当月一共有多少天
  private int mmonthdays;
  // 当月第一天位于周几
  private int mweeknumber;
  // 已选中背景bitmap
  private bitmap mbgoptbitmap;
  // 未选中背景bitmap
  private bitmap mbgnotoptbitmap;

  public calendarview(context context) {
    super(context);
    init();
  }

  public calendarview(context context, attributeset attrs) {
    super(context, attrs);
    init();
  }

  public calendarview(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    init();
  }

  private void init() {
    // 获取手机屏幕参数
    mmetrics = getresources().getdisplaymetrics();
    // 创建画笔
    mpaint = new paint();
    // 获取当前日期
    calendar calendar = calendar.getinstance();
    mcuryear  =  calendar.get(calendar.year);
    mcurmonth  =  calendar.get(calendar.month);
    mcurdate  =  calendar.get(calendar.date);
    setselytd(mcuryear, mcurmonth, mcurdate);

    // 获取背景bitmap
    mbgoptbitmap  = bitmapfactory.decoderesource(getresources(), r.mipmap.ic_bg_course_optional);
    mbgnotoptbitmap = bitmapfactory.decoderesource(getresources(), r.mipmap.ic_bg_course_not_optional);
  }

  @override
  public void invalidate() {
    // 避免程序过度绘制
    if(haswindowfocus()) super.invalidate();
  }

  @override
  protected void ondraw(canvas canvas) {
    initsize();

    // 绘制背景
    mpaint.setcolor(mbgcolor);
    canvas.drawrect(0, 0, canvas.getwidth(), canvas.getheight(), mpaint);

    mdays = new int[6][7];
    // 设置绘制字体大小
    mpaint.settextsize(mdaytextsize * mmetrics.scaleddensity);
    // 设置绘制字体颜色

    string daystr;
    // 获取当月一共有多少天
    mmonthdays = dateutils.getmonthdays(mselyear, mselmonth);
    // 获取当月第一天位于周几
    mweeknumber = dateutils.getfirstdayweek(mselyear, mselmonth);

    for(int day = 0; day < mmonthdays; day++){
      daystr = string.valueof(day + 1);
      int column = (day + mweeknumber - 1) % 7;
      int row   = (day + mweeknumber - 1) / 7;
      mdays[row][column] = day + 1;
      int startx = (int) (mcolumnsize * column + (mcolumnsize - mpaint.measuretext(daystr)) / 2);
      int starty = (int) (mrowsize * row + mrowsize / 2 - (mpaint.ascent() + mpaint.descent()) / 2);

      // 判断当前天数是否可选
      if(moptionaldates.contains(getseldata(mselyear, mselmonth, mdays[row][column]))){
        // 可选,继续判断是否是点击过的
        if(!mselecteddates.contains(getseldata(mselyear, mselmonth, mdays[row][column]))){
          // 没有点击过,绘制默认背景
          canvas.drawbitmap(mbgnotoptbitmap, startx - 22, starty - 55, mpaint);
          mpaint.setcolor(mdaynormalcolor);
        }else{
          // 点击过,绘制点击过的背景
          canvas.drawbitmap(mbgoptbitmap, startx - 22, starty - 55, mpaint);
          mpaint.setcolor(mdaypressedcolor);
        }
        // 绘制天数
        canvas.drawtext(daystr, startx, starty - 10, mpaint);
      }else{
        mpaint.setcolor(mdaynotoptcolor);
        canvas.drawtext(daystr, startx, starty, mpaint);
      }
    }
  }

  private int downx = 0,downy = 0;

  @override
  public boolean ontouchevent(motionevent event) {
    int eventcode = event.getaction();
    switch(eventcode){
      case motionevent.action_down:
        downx = (int) event.getx();
        downy = (int) event.gety();
        break;
      case motionevent.action_move:
        break;
      case motionevent.action_up:
        if(!mclickable) return true;

        int upx = (int) event.getx();
        int upy = (int) event.gety();
        if(math.abs(upx - downx) < 10 && math.abs(upy - downy) < 10){
          performclick();
          onclick((upx + downx) / 2, (upy + downy) / 2);
        }
        break;
    }
    return true;
  }

  /**
   * 点击事件
   */
  private void onclick(int x, int y){
    int row = y / mrowsize;
    int column = x / mcolumnsize;
    setselytd(mselyear, mselmonth, mdays[row][column]);

    // 判断是否点击过
    boolean isselected = mselecteddates.contains(getseldata(mselyear, mselmonth, mseldate));
    if(isselected){
      mselecteddates.remove(getseldata(mselyear, mselmonth, mseldate));
    }else{
      mselecteddates.add(getseldata(mselyear, mselmonth, mseldate));
    }

    invalidate();
    if(mlistener != null){
      // 执行回调
      mlistener.onclickdatelistener(mselyear, (mselmonth + 1), mseldate);
    }
  }

  /**
   * 初始化列宽和高
   */
  private void initsize() {
    // 初始化每列的大小
    mcolumnsize = getwidth() / num_columns;
    // 初始化每行的大小
    mrowsize = getheight() / num_rows;
  }

  /**
   * 设置可选择日期
   * @param dates 日期数据
   */
  public void setoptionaldate(list<string> dates){
    this.moptionaldates = dates;
  }

  /**
   * 设置年月日
   * @param year 年
   * @param month 月
   * @param date 日
   */
  public void setselytd(int year, int month, int date){
    this.mselyear  =  year;
    this.mselmonth =  month;
    this.mseldate  =  date;
  }

  /**
   * 设置上一个月日历
   */
  public void setlastmonth(){
    int year  =  mselyear;
    int month  =  mselmonth;
    int day   =  mseldate;
    // 如果是1月份,则变成12月份
    if(month == 0){
      year = mselyear-1;
      month = 11;
    }else if(dateutils.getmonthdays(year, month) == day){
      // 如果当前日期为该月最后一点,当向前推的时候,就需要改变选中的日期
      month = month-1;
      day = dateutils.getmonthdays(year, month);
    }else{
      month = month-1;
    }
    setselytd(year,month,day);
    invalidate();
  }

  /**
   * 设置下一个日历
   */
  public void setnextmonth(){
    int year  =  mselyear;
    int month  =  mselmonth;
    int day   =  mseldate;
    // 如果是12月份,则变成1月份
    if(month == 11){
      year = mselyear+1;
      month = 0;
    }else if(dateutils.getmonthdays(year, month) == day){
      // 如果当前日期为该月最后一点,当向前推的时候,就需要改变选中的日期
      month = month + 1;
      day = dateutils.getmonthdays(year, month);
    }else{
      month = month + 1;
    }
    setselytd(year,month,day);
    invalidate();
  }

  /**
   * 获取当前展示的年和月份
   * @return 格式:2016-06
   */
  public string getdate(){
    string data;
    if((mselmonth + 1) < 10){
      data = mselyear + "-0" + (mselmonth + 1);
    }else{
      data = mselyear + "-" + (mselmonth + 1);
    }
    return data;
  }

  /**
   * 获取当前展示的日期
   * @return 格式:20160606
   */
  private string getseldata(int year, int month, int date){
    string monty, day;
    month = (month + 1);

    // 判断月份是否有非0情况
    if((month) < 10) {
      monty = "0" + month;
    }else{
      monty = string.valueof(month);
    }

    // 判断天数是否有非0情况
    if((date) < 10){
      day = "0" + (date);
    }else{
      day = string.valueof(date);
    }
    return year + monty + day;
  }

  /**
   * 获取已选日期数据
   */
  public list<string> getselecteddates(){
    return mselecteddates;
  }

  /**
   * 设置已选日期数据
   */
  public void setselecteddates(list<string> dates){
    this.mselecteddates = dates;
  }

  /**
   * 设置日历是否可以点击
   */
  @override
  public void setclickable(boolean clickable) {
    this.mclickable = clickable;
  }

  private onclicklistener mlistener;

  public interface onclicklistener{
    void onclickdatelistener(int year, int month, int day);
  }

  /**
   * 设置点击回调
   */
  public void setonclickdate(onclicklistener listener){
    this.mlistener = listener;
  }

  @override
  protected void ondetachedfromwindow() {
    super.ondetachedfromwindow();
    recyclerbitmap(mbgoptbitmap);
    recyclerbitmap(mbgnotoptbitmap);
  }

  /**
   * 释放bitmap资源
   */
  private void recyclerbitmap(bitmap bitmap) {
    if(bitmap != null && !bitmap.isrecycled()){
      bitmap.recycle();
    }
  }
}

使用步骤

1、初始化自定义日历view:

calendarview mcalendarview = (calendarview) findviewbyid(r.id.calendarview);

2、初始化可以被选择的天数数据:

list<string> mdatas = new arraylist<>();
mdatas.add("20160801");
mdatas.add("20160802");
mdatas.add("20160803");
mdatas.add("20160816");
mdatas.add("20160817");
mdatas.add("20160826");
mdatas.add("20160910");
mdatas.add("20160911");
mdatas.add("20160912");

3、设置给自定义日历view:

// 设置可选日期
mcalendarview.setoptionaldate(mdatas);

设置点击监听

mcalendarview.setonclickdate(new calendarview.onclicklistener() {
  @override
  public void onclickdatelistener(int year, int month, int day) {
    toast.maketext(getapplication(), year + "年" + month + "月" + day + "天", toast.length_short).show();

    // 获取已选择日期
    list<string> dates = mcalendarview.getselecteddates();
    for (string date : dates) {
      log.e("test", "date: " + date);
    }

  }
});

如果只需要进行数据展示,而不需要点击,可以设置:

// 设置已选日期
mcalendarview.setselecteddates(mdatas);
// 设置不可以被点击
mcalendarview.setclickable(false);

源码下载:

github地址:https://github.com/airsaid/calendarview 欢迎star~!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。