android自定義view之實現(xiàn)日歷界面實例

現(xiàn)在網(wǎng)上有很多自定義view實現(xiàn)日歷的demo,今天講一講如何自己實現(xiàn)這個自定義view。

目前成都創(chuàng)新互聯(lián)公司已為上千多家的企業(yè)提供了網(wǎng)站建設、域名、虛擬主機、網(wǎng)站托管、企業(yè)網(wǎng)站設計、洛寧網(wǎng)站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

看一下最終效果圖:

android自定義view之實現(xiàn)日歷界面實例

在這個自定義view中,我使用了各種奇技淫巧的方法來實現(xiàn)這個日歷,真是費盡心思。廢話少說,開始進坑。

界面分析

頭部是一個textview,顯示年份和月份,然后下邊一行是星期幾,這兩行可以固定住,不隨月份切換而進出屏幕。

再下邊就是我們自定義view 的主角,每個月的天數(shù)。目前規(guī)定是星期日為每星期第一天。上個月的天數(shù)填充滿第一行,下個月的前幾天填充完最后一行,顏色設置為灰色,本月日期中的周一至周五設置為紅色,周六周日設置為青色,特殊日期設置為綠色,并且在右上角填充特殊標識符,用四分之三的圓弧包裹(上個月和下個月的日期沒有)。

此處還有個小細節(jié),每月的總行數(shù)會不斷改變,但是view的總高度并未改變,所以視覺效果會不一樣。

構造方法

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

  public MyCalendar(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);

  }

主要是實現(xiàn)上面兩個構造方法,第一個是用來在java代碼中使用的,第二個是用來在xml布局文件中使用的。

暴露的接口

目前接口共有下面幾個,setDate(CustomDate customDate),setWeekendHighLight(boolean b),setSpecialDay(int[] ints)

其中第一個是必須要設置的,否則是不會顯示任何東西,第二個設置的是否周末高亮,第三個設置的是特殊顯示的日期,第四個是設置是否可以點擊前一個月或者后一個月的日期,默認為不設置,后期可以根據(jù)自己需求增加其他接口。

  /**
   * 暴露接口,設置日期
   *
   * @param customDate
   */
  public void setDate(CustomDate customDate) {
    Log.d(TAG, customDate.toString());
    this.date = customDate;
    firstDayOfWeek = date.getFirstDayOfWeek();
    Log.d(TAG, (date.getMonth() + 1) + "月1號是星期" + firstDayOfWeek);
    lastDayOfWeek = date.getLastDayOfWeek();
    lineCount = calculateLineNum() + 1;
    lastMonthTotalDays = date.getLastMonthDays();
  }

  /**
   * 暴露接口,設置是否周末高亮
   *
   * @param b
   */
  public void setWeekendHighLight(boolean b) {
    this.weekendHighlight = b;
  }

  public void setSpecialDay(int[] ints) {
    this.specialDays = ints;
  }

  /**
   * 暴露接口,設置是否可以點擊前一個月和后一個月的日期
   *
   * @param b
   */
  public void setCanClickNextOrPreMonth(boolean b) {
    this.canClickNextOrPreMonth = b;
  }

在這里說明一下計算顯示行數(shù)的方法,首先要注意我們獲取的星期數(shù)與實際的星期幾會有一個增加一天的問題,也就是當前是星期4,那么你獲取的int將會是5.

 /**
   * 獲得應該設置為多少行
   *
   * @return
   */
  private int calculateLineNum() {
    monthDaySum = date.getTotalDayOfMonth();
    return (firstDayOfWeek - 1 + monthDaySum) / 7;
  }

我們將第一天是星期幾減去一后加上這個月總共多少天,就可以獲得最后一天是在什么位置,然后除以七取商的整數(shù)部分,然后在進一法即可獲得應該顯示多少行。

onSizechanged方法

onSizechanged方法中已經(jīng)可以獲得顯示的尺寸了,此時我們需要做一些工作:

 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    this.viewWidth = w;
    this.viewHeight = h;
    Log.d(TAG, "onSizeChanged" + w + h);
    cutGrid();
    init();
    setCellDay();
  }

首先是將寬和高引入進來,方便后邊使用。

cutGrid()方法是將區(qū)域分割為行X列的格式。

init()方法初始化了一些畫筆。

setCellDay()方法將每月的天對應過到坐標上。

首先看一下cutGrid()方法:

 /**
   * 切分為每天
   */
  private void cutGrid() {
    cellWidth = (float) viewWidth / ROW_COUNT;
    cellHeight = (float) viewHeight / lineCount;
    this.radius = Math.min(cellWidth / 2, cellHeight / 2);
    for (int i = 0; i < lineCount; i++) {
      for (int j = 0; j < ROW_COUNT; j++) {
        points.add(new PointF(cellWidth * j + cellWidth / 2, cellHeight * i + cellHeight / 2));
      }
    }
  }

cellWidth是每天的寬度,其中ROW_COUNT是一個常量7,表示每周7天;cellHeight是每行的高度,linecount是一個變量,需要我們根據(jù)日期計算,后邊會說到;radius是我們繪制區(qū)域的半徑,這個值是我們?nèi)挾群透叨戎休^小的值的一半。然后我們將每個方格中心坐標點利用雙重循環(huán)放入一個List<Point> points中。

android自定義view之實現(xiàn)日歷界面實例

整個view被分割為如上的形狀。

下面來看一下init()方法:

private void init() {
    circlePaint = new Paint();
    circlePaint.setStyle(Paint.Style.STROKE);
    circlePaint.setAntiAlias(true);
    circlePaint.setColor(Color.BLUE);
    textPaint = new Paint();
    textPaint.setAntiAlias(true);
    textPaint.setColor(Color.BLACK);
    textPaint.setTextSize(radius / 2);
    selectPaint = new Paint();
    selectPaint.setColor(Color.YELLOW);
    selectPaint.setAlpha(10);
    selectPaint.setAntiAlias(true);
    selectPaint.setStyle(Paint.Style.FILL);
    selectTextPaint = new Paint();
    selectTextPaint.setColor(Color.WHITE);
    selectTextPaint.setAntiAlias(true);
    selectTextPaint.setTextSize(radius / 2);
    selectTextPaint.setStyle(Paint.Style.FILL);
  }

基本都是畫筆工具。

然后是setAllDays()方法:

 /**
   * 設置總共顯示多少天,每天的狀態(tài)
   */
  private void setCellDay() {
    cellDays = new CellDay[lineCount * ROW_COUNT];
    for (int i = 0, length = cellDays.length; i < length; i++) {
      cellDays[i] = new CellDay();
      cellDays[i].setPointX(points.get(i).x);
      cellDays[i].setPointY(points.get(i).y);
      if (firstDayOfWeek > 1 && i < firstDayOfWeek - 1) {
        cellDays[i].setDayState(DayState.LASTMONTH);
        cellDays[i].setDate(String.valueOf(lastMonthTotalDays - firstDayOfWeek + i + 2));
        cellDays[i].setCustomDate(new CustomDate(
            date.getYear(), date.getMonth() - 1, lastMonthTotalDays - firstDayOfWeek + i + 2));
      }
      if (i >= firstDayOfWeek - 1 && i < monthDaySum + firstDayOfWeek - 1) {

        cellDays[i].setDayState(CURRENTMONTH);
        cellDays[i].setDate(String.valueOf(i + 2 - firstDayOfWeek));
        cellDays[i].setCustomDate(new CustomDate(
            date.getYear(), date.getMonth(), i - firstDayOfWeek + 2));
        //設置周末高亮
        if (weekendHighlight) {
          if (i % 7 == 0 || i % 7 == 6) {
            cellDays[i].setDayState(WEEKEND);
          }
        }
      }
      if (i >= monthDaySum + firstDayOfWeek - 1) {
        cellDays[i].setDayState(NEXTMONTH);
        cellDays[i].setDate(String.valueOf(i - monthDaySum - firstDayOfWeek + 2));
        cellDays[i].setCustomDate(new CustomDate(
            date.getYear(), date.getMonth() + 1, i - monthDaySum - firstDayOfWeek + 2));
      }
      for (int j = 0, s = specialDays.length; j < s; j++) {
        if (specialDays[j] + firstDayOfWeek - 2 == i) {
          cellDays[i].setDayState(SPECIALDAY);
        }
      }
    }
  }

在這里我們用到了一個自定的類-CellDay。

CellDay有以下幾個字段

    private String date;
    private DayState dayState;
    private CustomDate customDate;
    private float pointX;
    private float pointY;
    private boolean isSelected;

1、String date表示當前的日期。

2、dayState是一個美劇類型,定義了天的狀態(tài)值。

  • LASTMONTH:上個月的日期
  • CURRENTMONTH:本月的日期
  • NEXTMONTH: 下個月的日期
  • CURRENTDAY: 今天的日期
  • WEEKEND:周末的日期
  • SPECIALDAY:用戶自定義的可以設置狀態(tài)的日期

其中可以設置多種狀態(tài),用法和SPECIALDAY基本一樣。

  1. cusomedate是我們自己定義的一個工具類,包含項目中需要用到的一系列方法。
  2. pointX是橫坐標。
  3. pointY是縱坐標。
  4. isSelceted表示有沒有被選中。

CustomDate工具

public class CustomDate {
  private Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
  private int year;
  private int month;
  private int day;
  private int dayOfWeek;

  public CustomDate() {
  }

  /**
   * 獲取當前的日期
   * @return
   */
  public CustomDate getCurrentDate() {
    this.year = calendar.get(Calendar.YEAR);
    this.month = calendar.get(Calendar.MONTH);
    this.day = calendar.get(Calendar.DAY_OF_MONTH);
    this.dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
    return new CustomDate(year, month, day);
  }

  public CustomDate(int year, int month, int day) {
    this.year = year;
    this.month = month;
    this.day = day;
    calendar.set(year, month, day);
    dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
  }

  /**
   * 獲取上個月的天數(shù)
   * @return
   */
  public int getLastMonthDays() {
    return this.getDaysOfMonth(this.year, this.month - 1);
  }

  /**
   * 獲取第一天是星期幾
   *
   * @return
   */
  public int getFirstDayOfWeek() {
    calendar.set(this.year, this.month, 1);
    return calendar.get(Calendar.DAY_OF_WEEK);
  }

  /**
   * 獲取最后一天是星期幾
   *
   * @return
   */
  public int getLastDayOfWeek() {
    calendar.set(this.year, this.month, getTotalDayOfMonth());
    return calendar.get(Calendar.DAY_OF_WEEK);
  }

  /**
   * 獲取這個月總共的天數(shù)
   * @return
   */
  public int getTotalDayOfMonth() {
    return this.getDaysOfMonth(year, month);
  }

  public int getTotalWeekOfMonth() {
    return calendar.getMaximum(Calendar.WEEK_OF_MONTH);
  }

  public int getYear() {
    return year;
  }

  public void setYear(int year) {
    this.year = year;
  }

  public int getMonth() {
    return month;
  }

  public void setMonth(int month) {
    this.month = month;
  }

  public int getDay() {
    return day;
  }

  public void setDay(int day) {
    this.day = day;
  }

  public int getDayOfWeek() {
    return dayOfWeek;
  }

  public void setDayOfWeek(int dayOfWeek) {
    this.dayOfWeek = dayOfWeek;
  }

  @Override
  public String toString() {
    return "CustomDate{" +
        "year=" + year +
        ", month=" + (getMonth() + 1) +
        ", day=" + day +
        ", dayOfWeek=" + dayOfWeek +
        '}';
  }

  /**
   * 獲取年中每月的天數(shù)
   * @param year
   * @param month
   * @return
   */
  private int getDaysOfMonth(int year, int month) {
    if (month > 11) {
      month = 0;
      year += 1;
    } else if (month < 0) {
      month = 11;
      year -= 1;
    }

    int[] arr = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int daysOfMonth = 0;
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
      arr[1] = 29;
    }
    daysOfMonth = arr[month];
    return daysOfMonth;
  }
}

注釋中對每個方法的說明已經(jīng)非常清晰了。

int getLastMonthDays()

獲取上個月的天數(shù)是用來計算上個月最后一天是星期幾,然后以此推導出上個月在本月中顯示的天數(shù)和對應的星期。

getFirstDayOfWeek()

獲取本月第一天是星期幾,然后排序本月的天數(shù)與對應的星期。

int getTotalDayOfMonth()

獲取本月總共多少天。配合第一天是星期幾用來計算總共分為幾行,也就是確定linenumber。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

新聞名稱:android自定義view之實現(xiàn)日歷界面實例
URL地址:http://www.muchs.cn/article38/ghgspp.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷、域名注冊、靜態(tài)網(wǎng)站品牌網(wǎng)站設計、云服務器、微信公眾號

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名