当前位置: 移动技术网 > IT编程>移动开发>Android > Android自定义控件实现可多选课程日历CalendarView

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

2019年07月24日  | 移动技术网IT编程  | 我要评论

少堡主的爱财娘子,七个侍寝夜结局,网络美女图片

可多选课程日历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~!

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网