Android 달력 컨트롤 구현 코드


먼저 동적 효과 도 몇 장 보 세 요!



프로젝트 주소:https://github.com/Othershe/CalendarView
달력 컨트롤 을 작성 하 는 과정 에서 중요 한 점 을 기록 합 니 다.
주요 기능
1.음력,절기,상용 공휴일 지원
2.날짜 범위 설정,기본 지원 하 는 최대 날짜 범위[1900.1~2049.12]
3.기본 선택 날짜 설정
4.단선,다 선
5.지 정 된 날짜 로 이동
6.사용자 정의 속성 을 통 해 날짜 모양 을 맞 추고 간단 한 날짜 item 레이아웃 설정
2.기본 구조
우리 가 실현 하고 자 하 는 달력 컨트롤 은 ViewPager 를 위주 로 하 는 프레임 워 크 를 사용 하고 CalendarView 는 ViewPager 를 계승 하여 좌우 슬라이딩 과 캐 시 기능 을 타고 납 니 다.현재 저 희 는 달력 의 좌우 슬라이딩 을 월 전환 으로 설정 합 니 다.매달 사용자 정의 ViewGroup 을 통 해 이 루어 집 니 다.즉,MonthView 입 니 다.월 의 날 짜 는 layot 레이아웃 을 통 해 분 석 된 View 입 니 다.월 에 따라 MonthView 는 6 x 7 또는 5 x 7 개의 날짜 View 를 포함 할 수 있 습 니 다.ViewPager 에 연결 하 는 데 이 터 는 PagerAdapter 를 통 해 이 루어 져 야 하기 때 문 입 니 다.그래서 PagerAdapter 를 계승 하여 CalendarPagerAdapter 를 확장 하여 MonthView 와 관련 된 초기 화 와 날짜 데이터 의 바 인 딩 을 완성 합 니 다.
3.각 MonthView 가 채 워 야 할 날짜 데 이 터 를 계산 합 니 다.
위의 캡 처 를 통 해 알 수 있 듯 이 각 MonthView 의 날짜 데 이 터 는 지난달 이후 0~6 일,현재 월 의 일수 와 다음 달 전 0~6 일 로 구성 되 어야 한다.먼저 현재 달 이 며칠 인지 계산 해 보 세 요.이것 은 간단 합 니 다.그리고 년 월 에 따라 현재 달의 첫날 이 무슨 요일 인지 계산 해 보 세 요.

public static int getFirstWeekOfMonth(int year, int month) {
    Calendar calendar = Calendar.getInstance();
    calendar.set(year, month, 1);
    return calendar.get(Calendar.DAY_OF_WEEK) - 1;
  }
0 은 일요일 을 대표 하고 1~6 은 월요일 부터 토요일 을 대표 합 니 다.위의 캡 처 를 예 로 들 면 2017 년 5 월 의 첫날 은 월요일 임 을 알 수 있 습 니 다week = getFirstWeekOfMonth(2017, 5-1),다음 과 같은 위조 코드 에 따라 포 함 된 지난달 날 짜 를 계산 할 수 있 습 니 다.

for (int i = 0; i < week; i++) {
      ld =       - week + 1 + i;
    }
포 함 된 다음 달 날 짜 는 현재 MonthView 에 표 시 된 줄 수 와 관련 이 있 습 니 다.현재 달의 일수+week 가 7 로 정 제 될 수 있다 면 다음 달 날 짜 를 포함 하지 않 아 도 됩 니 다.그렇지 않 으 면 포 함 된 다음 달 날 짜 를 계산 해 야 합 니 다.위조 코드 는 다음 과 같 습 니 다.

for (int i = 0; i < 7 *       -      - week; i++) {
      nd = i + 1;
    }
이렇게 필요 한 날짜 데 이 터 를 계산 하면 상세 한 알고리즘 은 소스 코드 를 참고 할 수 있다.
4.달력 의 총 페이지 수 를 계산 합 니 다.
총 페이지 수 는 달력 의 시작 년 월 에 받 아야 하 는데 사실은 ViewPager 의 총 페이지 수 를 확정 하 는 것 이 이해 하기 쉽다.다음 과 같은 방법 으로 계산 할 수 있다.

count = (dateEnd[0] - dateStart[0]) * 12 + dateEnd[1] - dateStart[1] + 1
그 중에서 dateStart,dateEnd 는 달력 시작 연월 과 끝 연월 을 포함 하 는 배열 입 니 다.이 count 도 Calendar Pager Adapter 가 필요 합 니 다.
5.position 으로 날 짜 를 계산한다.
PagerAdapter 에 instantiate Item()방법 이 있 습 니 다.

public Object instantiateItem(ViewGroup container, int position) {
    return instantiateItem((View) container, position);
  }
ViewPager 의 모든 페이지 를 만 들 기 위해 달력 의 모든 페이지 도 여기 서 만 들 었 습 니 다.즉,MonthView 입 니 다.여기 서 관건 적 인 점 은 positon 매개 변수 에 따라 달력 의 각 페이지 에 해당 하 는 년 월 을 계산 한 다음 에 년 월 을 통 해 현재 MonthView 에 필요 한 날짜 수 를 계산 하 는 것 입 니 다.어떻게 position 에 근거 하여 연월 을 추산 합 니까?

public static int[] positionToDate(int position, int startY, int startM) {
    int year = position / 12 + startY;
    int month = position % 12 + startM;

    if (month > 12) {
      month = month % 12;
      year = year + 1;
    }

    return new int[]{year, month};
  }

그 중에서 startY,startM 은 달력 의 사실 연월 을 대표 한다.해당 하 는 연월 이 있 으 면 두 번 째 점 에서 날짜 데 이 터 를 계산 한 다음 MothView 에 채 울 수 있 습 니 다.
6.MothView
앞에서 언급 했 듯 이 Monthview 는 View Group,즉 달력 의 각 페이지 를 계승 하여 날짜 데 이 터 를 받 은 후 Monthview 에서 데이터 구조 에 대응 하 는 날짜 View 를 바탕 으로 View 를 Monthview 에 추가 한 다음 에 onMeasure,onLayout 를 통 해 각 View 의 최종 크기 와 위 치 를 확인한다.여기에서 ViewPager 를 실행 하 는 기본 조건 이 만족 합 니 다.위 에서 언급 한 instantiate Item()방법 에서 MothView 초기 화 를 완성 합 니 다.

public Object instantiateItem(ViewGroup container, int position) {
    MonthView view = new MonthView(container.getContext());
    //  position     、 
    int[] date = CalendarUtil.positionToDate(position, dateStart[0], dateStart[1]);
    view.setDateList(CalendarUtil.getMonthDate(date[0], date[1]), SolarUtil.getMonthDays(date[0], date[1]));
    container.addView(view);

    return view;
  }

여기 에는 핵심 코드 만 남아 있 습 니 다.달력 이 달 을 바 꿀 때 position 에 따라 달 에 해당 하 는 날짜 데 이 터 를 계산 한 다음 MonthView 에 전송 하고 마지막 으로 MonthView 를 ViewPager 에 추가 합 니 다.
7.월 선택 날짜 전환
현재 설정 에 따 르 면 현재 달의 어느 날 짜 를 선택 한 후 달 을 바 꾸 면 새 달 에서 지난번 에 선택 한 날 짜 를 찾 아 선택 상태 로 표시 하고 찾 지 못 하면 새 달의 마지막 날 을 선택 합 니 다.사실 논 리 는 간단 하 다.새 달 에 해당 하 는 날 짜 를 찾 아 선택 하 는 것 이 관건 이다.먼저 선택 한 날 짜 를 기록 합 니 다.ViewPager 는 기본적으로 두 페이지 를 캐 시 합 니 다.게다가 현재 페이지 는 모두 세 페이지 입 니 다.CalendarPager Adapter 에서 position 에 따라 세 페이지 의 캐 시 를 저장 합 니 다.ViewPager 가 한 페이지 로 전환 하면 다음 과 같은 리 셋 을 수행 합 니 다.

addOnPageChangeListener(new SimpleOnPageChangeListener() {
      @Override
      public void onPageSelected(int position) {
      }
    });
onPageSelected(int position)방법 에서 position 을 통 해 캐 시 에서 해당 하 는 MonthView 를 가 져 오 는 것 도 전환 한 페이지 입 니 다.그러면 MonthView 에서 기 록 된 날짜 에 따라 해당 하 는 하위 날짜 View 를 찾 아 선택 상태 로 변경 할 수 있 습 니 다.
여덟,여러 가지 선택
이상 적 인 다 중 선택 기능 은 현재 달 에 여러 날 짜 를 선택 한 후에 다른 달 로 전환 한 다음 에 선택 한 날짜 가 있 는 달 로 돌아 가면 선택 한 날 짜 를 표시 할 수 있 습 니 다.ViewPager 는 기본 세 페이지 의 캐 시 가 있 기 때문에 현재 달 에서 지난달 이나 다음 달 로 전환 하 는 데 문제 가 없 지만 몇 개 월 전이 나 몇 개 월 후 로 전환 하면선택 한 날짜 가 있 는 달 로 돌아 가면 이전에 캐 시 된 페이지 가 삭제 되 어 재 구축 되 었 기 때문에 선택 한 달 도 볼 수 없습니다.우리 의 날짜 클릭 이 벤트 는 MonthView 에 있 습 니 다.선택 을 클릭 할 때마다 우 리 는 해당 년 월 에 선택 한 날 짜 를 기록 해 야 합 니 다.선택 을 취소 할 때 기록 에서 해당 날 짜 를 삭제 해 야 합 니 다.어떻게 저장 합 니까?CalendarView 클래스 에서 SparseArray 를 정의 합 니 다.

SparseArray<HashSet<Integer>> chooseDate = new SparseArray<>()
그 중에서 HashSet 은 년 월 에 선택 한 날 짜 를 지정 하 는 것 입 니 다.우리 의 규칙 에 따라 년 월 에 변 환 된 position 를 설정 하 는 것 이 유일 하 게 대응 하기 때문에 우 리 는 position 를 SparseArray 의 key 로 사용 하고 마지막 으로 CalendarView 에서 선택 하거나 취소 하 는 동작 을 받 습 니 다.

public void setLastChooseDate(int day, boolean flag) {
    HashSet<Integer> days = chooseDate.get(currentPosition);
    if (flag) {
      if (days == null) {
        days = new HashSet<>();
        chooseDate.put(currentPosition, days);
      }
      days.add(day);
    } else {
      days.remove(day);
    }
  }
그 다음 에 월 전환 과정 에서 저 장 된 날짜 데이터 에 따라 해당 하 는 Monthview 를 리 셋 하여 선택 한 상태의 회복 을 실현 하 는데 이것 은 여섯 번 째 점 과 유사 합 니 다.
9.지 정 된 날짜 로 이동
지정 한 날짜 로 이동 하려 면 먼저 날짜 의 연월 에 따라 목표 MonthView 가 달력 에 있 는 position 를 계산 해 야 합 니 다.

public static int dateToPosition(int year, int month, int startY, int startM) {
    return (year - startY) * 12 + month - startM;
  }
ViewPager 는 setCurrent Item(int item,boolean smoothScroll)방법 이 있 습 니 다.그러면 position 에 대응 하 는 MonthView 로 이동 한 다음 여섯 번 째 방법 과 결합 하여 해당 하 는 날짜 View 를 선택 할 수 있 습 니 다.이렇게 달력 설정 날짜 범위 내 에서 하루 를 뛰 어 넘 는 것 은 문제 가 없다.
10.사용자 정의 달력 스타일
현재 CalendarView 에서 제공 하 는 사용자 정의 속성 은 다음 과 같 습 니 다.

<declare-styleable name="CalendarView">
    <!--    -->
    <attr name="multi_choose" format="boolean" />
    <!--      -->
    <attr name="show_lunar" format="boolean" />
    <!--         -->
    <attr name="show_last_next" format="boolean" />
    <!--       -->
    <attr name="show_holiday" format="boolean" />
    <!--      -->
    <attr name="show_term" format="boolean" />
    <!--    (1990.1)-->
    <attr name="date_start" format="string" />
    <!--    (2020.12)-->
    <attr name="date_end" format="string" />
    <!--    、     (2016.10.1)-->
    <attr name="date_init" format="string" />
    <!--                -->
    <attr name="disable_before" format="boolean" />
    <!--       -->
    <attr name="color_solar" format="color" />
    <!--       -->
    <attr name="size_solar" format="integer" />
    <!--       -->
    <attr name="color_lunar" format="color" />
    <!--       -->
    <attr name="size_lunar" format="integer" />
    <!--      -->
    <attr name="color_holiday" format="color" />
    <!--         -->
    <attr name="color_choose" format="color" />
    <!--       (  )-->
    <attr name="day_bg" format="reference" />
    <!--       ,         -->
    <attr name="switch_choose" format="boolean" />
  </declare-styleable>
기본적으로 일상적인 수 요 를 만족 시 킬 수 있 습 니 다.기본 적 인 날짜 구 조 는 양력,음력 을 수직 으로 배열 하고 휴일 은 음력 에 덮어 표 시 됩 니 다.이것 은 위의 정적 캡 처 를 통 해 알 수 있 습 니 다.수평 배열 등 다른 배열 방식 을 사용 하려 면 자체 레이아웃 을 제공 해 야 합 니 다(단,현재 두 개의 TextView 디 스 플레이 만 지원 합 니 다).예 를 들 면:

calendarView.setOnCalendarViewAdapter(R.layout.item_layout, new CalendarViewAdapter() {
      @Override
      public TextView[] convertView(View view, DateBean date) {
        TextView solarDay = (TextView) view.findViewById(R.id.solar_day);
        TextView lunarDay = (TextView) view.findViewById(R.id.lunar_day);
        return new TextView[]{solarDay, lunarDay};
      }
    });
CalendarView 에 인 터 페 이 스 를 연결 하고 lauoyt 에 전송 한 다음 양력 과 음력 을 대표 하 는 TextView 배열 을 되 돌려 줍 니 다.
11.WeekView
날짜 와 요일 의 디 스 플레이 기능 을 분리 하기 때문에 CalendarView 는 요일 의 디 스 플레이 를 책임 지지 않 습 니 다.WeekView 는 요일 에 표시 되 는 사용자 정의 View 입 니 다.일요일 부터 월요일 부터 토요일 까지 사용자 정의 속성 을 통 해 요일 의 디 스 플레이 문자 와 문자 의 색상,사 이 즈 를 설정 할 수 있 습 니 다.이것 은 상대 적 으로 간단 합 니 다.구체 적 으로 Github 의 사용 설명 을 볼 수 있 습 니 다.
소결
여기 서 우 리 는 달력 의 기본 적 인 실현 원리 와 관건 적 인 점 만 소개 했다.사실은 이런 실현 방식 은 상대 적 으로 간단 하고 이해 하기 쉽다.물론 부족 한 점 이 있 을 수 밖 에 없다.그 다음 에 수요 에 따라 점차적으로 보완 하고 확대 하 자.비록 Github 에는 이미 만들어 진 Calendar 가 많 지만 스스로 하 나 를 실현 하 는 것 은 많은 것 을 얻 을 수 있 습 니 다.간단 해 보 이 는 것 은 직접 시도 해 봐 야 그 맛 을 느 낄 수 있 습 니 다.마지막 으로 여러분 에 게 도움 이 되 었 으 면 좋 겠 습 니 다!

좋은 웹페이지 즐겨찾기