728x90

GridCellAdapter 대신에 RecyclerView 기반 CalendarAdapter를 만들면 어떤 사항들이 변경되는지 알아보자.


RecyclerView 는 ListView 보다 메모리, 성능이 개선된 것으로 데이터 양이 많은 경우 스크롤을 효율적으로 수행할 수 있는 위젯이다.


앱 build.gradle 수정사항

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'gun0912.ted:tedpermission:2.0.0'
    implementation 'androidx.multidex:multidex:2.0.0' // minSdkVersion 이 21 이하인 경우 사용
    implementation files('libs/icu4j-4_8_2.jar')
}



activity_main.xml

<GridView
    android:id="@+id/calendar"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:scrollbars="horizontal"
    android:numColumns="7" >
</GridView>

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/calendar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerVertical="true"
    android:adjustViewBounds="false"
    android:focusableInTouchMode="true"
    android:scrollbars="vertical" />


MainActivity.java

    private GridView calendarView;
    private GridCellAdapter adapter;


        calendarView = (GridView) this.findViewById(R.id.calendar);

        makeCalendarList(month, year);

        // Initialised
        adapter = new GridCellAdapter(mContext, calList);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
    }

    private void setGridCellAdapterToDate(int month, int year) {
        makeCalendarList(month, year);
        adapter = new GridCellAdapter(mContext, calList);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
        // 상단 화면 날짜 출력
        cal.set(year, month -1, cal.get(Calendar.DAY_OF_MONTH));
        currentMonth.setText(DateFormat.format(dateTemplate, cal.getTime()));
    }

    private RecyclerView calendarView;
    private RecyclerView.Adapter adapter;

        calendarView = this.findViewById(R.id.calendar);
        calendarView.setHasFixedSize(true);
        calendarView.setLayoutManager(new GridLayoutManager(mContext,7));

        makeCalendarList(month, year);

        // Initialised
        adapter = new CalendarAdapter(mContext, calList);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
    }

    private void setGridCellAdapterToDate(int month, int year) {
        makeCalendarList(month, year);
        adapter = new CalendarAdapter(mContext, calList);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
        // 상단 화면 날짜 출력
        cal.set(year, month -1, cal.get(Calendar.DAY_OF_MONTH));
        currentMonth.setText(DateFormat.format(dateTemplate, cal.getTime()));
    }


이제 CalendarAdapter 만드는 방법이다.

public class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.CustomViewHolder> {

    @NonNull
    @Override
    public CalendarAdapter.CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull CalendarAdapter.CustomViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }

    public class CustomViewHolder extends RecyclerView.ViewHolder {
        public CustomViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }
} 


아래 이미지와 같은 과정을 거치면 위와 같은 코드가 만들어진다.








이제 완성된 코드를 적는다.

package com.link2me.android.adpater;

import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.link2me.android.calendar.R;
import com.link2me.android.item.Calendar_Item;
import com.link2me.android.util.CalendarHelper;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;

public class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.CustomViewHolder> {
    static final String tag = CalendarAdapter.class.getSimpleName();
    Context mContext;

    private LinkedHashMap<String, Calendar_Item> calList;
    private String[] mKeys;

    private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd");

    public CalendarAdapter(Context context, LinkedHashMap<String, Calendar_Item> list) {
        mContext = context;
        calList = list;
        mKeys = calList.keySet().toArray(new String[list.size()]);
    }

    @NonNull
    @Override
    public CalendarAdapter.CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.calendar_cell, parent, false);
        CustomViewHolder holder = new CustomViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull final CalendarAdapter.CustomViewHolder holder, int position) {
        String theday = calList.get(mKeys[position]).getDay();
        String themonth = calList.get(mKeys[position]).getMonth();
        String theyear = calList.get(mKeys[position]).getYear();
        String holiday = calList.get(mKeys[position]).getEvent();

        holder.gridcell_Day.setText(theday);
        holder.gridcell_LunarDay.setText(CalendarHelper.Sol2Lun(theyear, String.valueOf(Integer.parseInt(themonth)-1),theday)); //0 ~ 11월로 인식하므로 - 1
        if(holiday.length()>0){
            holder.gridcell_Event.setText(holiday);
        } else {
            holder.gridcell_Event.setText("");
        }
        holder.gridcell_layout.setTag(theday + "-" + themonth + "-" + theyear);

        if(calList.get(mKeys[position]).getColor().equals("GRAY")){
            holder.gridcell_Day.setTextColor(Color.LTGRAY);
        }
        if(calList.get(mKeys[position]).getColor().equals("BLACK")){
            holder.gridcell_Day.setTextColor(Color.BLACK);
        }
        if(calList.get(mKeys[position]).getColor().equals("RED")){
            holder.gridcell_Day.setTextColor(Color.RED);
            holder.gridcell_Event.setTextColor(Color.RED);
        }
        if(calList.get(mKeys[position]).getColor().equals("BLUE")){
            holder.gridcell_Day.setTextColor(Color.BLUE);
        }
        if(calList.get(mKeys[position]).getColor().equals("CYAN")){
            holder.gridcell_layout.setBackgroundColor(Color.CYAN);
        }

        String tagData = theyear+themonth+theday;
        holder.itemView.setTag(tagData);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String date_month_year = (String) view.getTag();
                Log.e("Selected date", date_month_year);
                try {
                    Date parsedDate = dateFormatter.parse(date_month_year);
                    Log.d(tag, "Parsed Date: " + parsedDate.toString());

                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return calList.size();
    }

    public class CustomViewHolder extends RecyclerView.ViewHolder {
        protected LinearLayout gridcell_layout;
        protected TextView gridcell_Day;
        protected TextView gridcell_LunarDay;
        protected TextView gridcell_Event;

        public CustomViewHolder(@NonNull View itemView) {
            super(itemView);
            gridcell_layout = (LinearLayout) itemView.findViewById(R.id.calendar_day_gridcell);
            gridcell_Day = itemView.findViewById(R.id.dayTV);
            gridcell_LunarDay = itemView.findViewById(R.id.lunardayTV);
            gridcell_Event = itemView.findViewById(R.id.eventTV);
        }
    }
}
 


커스텀 달력 만드는 방법에 대한 충분한 설명이 되었을 것이라고 본다.


https://www.youtube.com/watch?v=kNq9w1_nhL4 를 강좌를 보면 RecyclerView 에 대한 설명을 잘 해주고 있으니 사용법에 대해 잘 모르면 보면 도움될 수 있다.

블로그 이미지

Link2Me

,
728x90

이번에는 MainActivity.java 에서

ArrayList<HashMap<String, String>> calList = new ArrayList<HashMap<String, String>>();

LinkedHashMap<String, Calendar_Item> calList = new LinkedHashMap<String, Calendar_Item>();

로 변경하면 어떤 것들을 수정해줘야 할까?


먼저 https://link2me.tistory.com/1717 게시글을 읽고 나서 아래 비교 내용을 보면 좀 더 이해하는데 도움이 될 것으로 본다.

HashMap 을 사용하면 입력한 순서대로 출력이 될 것을 기대하지만 뒤죽박죽으로 결과를 보여준다.


MainActivity.java 수정사항

                if(CurrentMonth-1 == thisMonth){ // 현재월이면
                    if(i == thisDay){
                        Log.e(TAG, "key := " + key);
                        int index = getIndexOfCalList(key);
                        HashMap<String, String> item = calendarItem(String.valueOf(yy),String.valueOf(CurrentMonth),String.valueOf(i),weekday,"CYAN","",key);
                        calList.set(index,item);
                    }
                }


    private HashMap<String, String> calendarItem(String year, String month, String day, int weekday, String color, String name, String key){
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("year",year);
        map.put("month",month);
        map.put("day",day);
        map.put("weekday", String.valueOf(weekday));
        map.put("color",color);
        map.put("event",name);
        map.put("key",key);
        return map;
    }

    private void addCalendarItem(String year, String month, String day, int weekday, String color,String name, String key){
        HashMap<String, String> item = calendarItem(year,month,day,weekday,color,name,key);
        calList.add(item);
    }

    private int getIndexOfCalList(String search_key) {
        for (int temp = 0; temp < calList.size(); temp++) {
            String key = calList.get(temp).get("key");
            if (key != null && key.equals(search_key)) {
                return temp;
            }
        }
        return -1;
    }

                if(CurrentMonth-1 == thisMonth){ // 현재월이면
                    if(i == thisDay){
                        Log.e(TAG, "key := " + key);
                        addCalendarItem(String.valueOf(yy),String.valueOf(CurrentMonth),String.valueOf(i),weekday,"CYAN","",key);
                    }
                }

    private Calendar_Item calendarItem(String year, String month, String day, int weekday, String color, String name, String key){
        Calendar_Item item = new Calendar_Item();
        item.setYear(year);
        item.setMonth(month);
        item.setDay(day);
        item.setWeekday(weekday);
        item.setColor(color);
        item.setEvent(name);
        return item;
    }

    private void addCalendarItem(String year, String month, String day, int weekday, String color,String name, String key){
        Calendar_Item item = calendarItem(year,month,day,weekday,color,name,key);
        calList.put(key,item);
    }


GridCellAdapter.java 수정사항

    private final ArrayList<HashMap<String, String>> calList;

    public GridCellAdapter(Context context, ArrayList<HashMap<String, String>> list) {
        mContext = context;
        calList = list;
    }

    public HashMap<String, String> getItem(int position) {
        return calList.get(position);
    }

        String theday = calList.get(position).get("day");
        int themonth = Integer.parseInt(calList.get(position).get("month"));
        String theyear = calList.get(position).get("year");
        String holiday = calList.get(position).get("event");

    private final LinkedHashMap<String, Calendar_Item> calList;
    private String[] mKeys;

    public GridCellAdapter(Context context, LinkedHashMap<String, Calendar_Item> list) {
        mContext = context;
        calList = list;
        mKeys = calList.keySet().toArray(new String[list.size()]);
    }

    public Calendar_Item getItem(int position) {
        return calList.get(mKeys[position]);
    }

        String theday = calList.get(mKeys[position]).getDay();
        int themonth = Integer.parseInt(calList.get(mKeys[position]).getMonth());
        String theyear = calList.get(mKeys[position]).getYear();
        String holiday = calList.get(mKeys[position]).getEvent();


어떤 것을 사용하든지 검색 속도가 느리지 않으면서 확장성을 고려한 메소드를 적절하게 사용하면 된다.


공휴일 등록, 기념일 등록, 일정 등록 등의 루틴은 여기에는 적지 않았다.


도움이 되셨다면 ... 해 주세요. 좋은 글 작성에 큰 힘이 됩니다.

블로그 이미지

Link2Me

,
728x90

안드로이드 달력 만들기 MainActivity.java 와 GridCellAdapter.java 파일에서 아래와 같이 ArrayList 제네릭으로 HashMap<String, String> 을 사용하는 방법이다.


ArrayList<Calendar_Item> calList = new ArrayList<>();

ArrayList<HashMap<String, String>> calList = new ArrayList<>();

로 변경하여 코딩하면 어떻게 처리해야 할까?


Calendar_Item 이 HashMap<String, String> 라는 것으로 변경된 것만 살펴보고 수정해주면 된다.


    private Calendar_Item calendarItem(String year, String month, String day, int weekday, String color, String name, String key){
        Calendar_Item item = new Calendar_Item();
        item.setYear(year);
        item.setMonth(month);
        item.setDay(day);
        item.setWeekday(weekday);
        item.setColor(color);
        item.setEvent(name);
        item.setKey(key);
        return item;
    }

    private void addCalendarItem(String year, String month, String day, int weekday, String color,String name, String key){
        Calendar_Item item = calendarItem(year,month,day,weekday,color,name,key);
        calList.add(item);
    }

    private int getIndexOfCalList(String search_key) {
        for (int temp = 0; temp < calList.size(); temp++) {
            String key = calList.get(temp).getKey();
            if (key != null && key.equals(search_key)) {
                return temp;
            }
        }
        return -1;
    }
 

    private HashMap<String, String> calendarItem(String year, String month, String day, int weekday, String color, String name, String key){
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("year",year);
        map.put("month",month);
        map.put("day",day);
        map.put("weekday", String.valueOf(weekday));
        map.put("color",color);
        map.put("event",name);
        map.put("key",key);
        return map;
    }

    private void addCalendarItem(String year, String month, String day, int weekday, String color,String name, String key){
        HashMap<String, String> item = calendarItem(year,month,day,weekday,color,name,key);
        calList.add(item);
    }

    private int getIndexOfCalList(String search_key) {
        for (int temp = 0; temp < calList.size(); temp++) {
            String key = calList.get(temp).get("key");
            if (key != null && key.equals(search_key)) {
                return temp;
            }
        }
        return -1;
    }


GridCellAdapter 에서 변경해줄 것은 아래와 같다.

    private final ArrayList<Calendar_Item> calList;


    public GridCellAdapter(Context context, ArrayList<Calendar_Item> list) {
        mContext = context;
        calList = list;
    }

    public Calendar_Item getItem(int position) {
        return calList.get(position);
    }


        String theday = calList.get(position).getDay();
        int themonth = Integer.parseInt(calList.get(position).getMonth());
        String theyear = calList.get(position).getYear();
        String holiday = calList.get(position).getEvent();
 

    private final ArrayList<HashMap<String, String>> calList;


    public GridCellAdapter(Context context, ArrayList<HashMap<String, String>> list) {
        mContext = context;
        calList = list;
    }


    public HashMap<String, String> getItem(int position) {
        return calList.get(position);
    }

        String theday = calList.get(position).get("day");
        int themonth = Integer.parseInt(calList.get(position).get("month"));
        String theyear = calList.get(position).get("year");
        String holiday = calList.get(position).get("event");
 


ArrayList<HashMap<String, String>> 를 사용하면 아래와 같은 Calendar_Item Class 를 만들 필요가 없어 코드가 좀 더 간단해질 수 있다.

public class Calendar_Item {
    private  String year; // 년
    private  String month; // 월
    private  String day; // 일
    private  int weekday; // 요일
    private  String color; // 색상
    private String event;
    private String key; // 월일 값을 key 로 사용

    public Calendar_Item() {
    }

    public Calendar_Item(String year, String month, String day, int weekday, String color) {
        this.year = year;
        this.month = month;
        this.day = day;
        this.weekday = weekday;
        this.color = color;
    }

    public String getYear() {
        return year;
    }

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

    public String getMonth() {
        return month;
    }

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

    public String getDay() {
        return day;
    }

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

    public int getWeekday() {
        return weekday;
    }

    public void setWeekday(int weekday) {
        this.weekday = weekday;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getEvent() {
        return event;
    }

    public void setEvent(String event) {
        this.event = event;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}


블로그 이미지

Link2Me

,
728x90

Calendar Class 를 사용하여 달력을 만드는데 알아둘 사항을 정리했다.

Date는 JDK1.0, Calendar는 JDK1.1 부터 제공되었다.

Calendar는 추상클래스라서 객체를 직접 생성할 수 없고, 메소드를 통해서 완전히 구현된 클래스의 인스턴스를 얻어야 한다.

Calendar cal = new Calendar(); // 에러. 추상클래스는 인스턴스를 생성할 수 없다.

Calendar cal = Calendar.getInstance(); // getInstance()는 Calendar 클래스의 인스턴스를 반환


아래 코드는 Java에서 작성한 코드다.

package Calendar;

import java.util.Calendar;

public class Calendar_EX {

    public static void main(String[] args) {
        // Calendar Class 를 사용한 달력 구현을 위한 기본사항 알아보기
        int year = 2019;
        int month = 2;
        int trailingSpaces = 0;
        int daysInPrevMonth = 0;
        int prevYear = 0;
        int prevMonth = 0;
        int DAY_OFFSET = 1;
       
        Calendar cal = Calendar.getInstance(); //Calendar 객체 생성(시스템의 현재 날짜와 시간 정보)
        System.out.println("today is "+cal.getTime()); // 현재시각을 알려주는 메소드
        System.out.println("올해 년도 : " + cal.get(Calendar.YEAR));
        System.out.println("이번 달(0~11, 0:1월): " + cal.get(Calendar.MONTH));
        System.out.println("이번 달의 오늘 날짜 : " + cal.get(Calendar.DATE));
       
        int LastDays = cal.getActualMaximum(Calendar.DATE); //해당 월 마지막 날짜
        System.out.println("이번달의 마지막날짜 : "+LastDays); // 현재시각을 알려주는 메소드
        int dayofweek = cal.get(Calendar.DAY_OF_WEEK); // 현재 요일 (일요일은 1, 토요일은 7)
        System.out.println("이번달 오늘날짜 요일 값 : "+dayofweek); // 오늘날짜의 요일값을 반환
       
        System.out.println("이번년도의 몇째 주 : "+ cal.get(Calendar.WEEK_OF_YEAR));
        System.out.println("이번달의 몇째 주 : "+ cal.get(Calendar.WEEK_OF_MONTH));
        System.out.println("이번달의 몇째 주 : " + cal.get(Calendar.DAY_OF_WEEK_IN_MONTH));
        System.out.println("현재 시간 : "+ cal.get(Calendar.HOUR)); // 현재 시간 (12시간제)
        System.out.println("현재 시간 : "+ cal.get(Calendar.HOUR_OF_DAY)); // 현재 시간 (24시간제)
        System.out.println("오전_오후(0:오전, 1:오후): " + cal.get(Calendar.AM_PM));
        System.out.println("현재 분 : "+ cal.get(Calendar.MINUTE));
        System.out.println("현재 초 : "+ cal.get(Calendar.SECOND));
        String time = String.valueOf(cal.get(Calendar.HOUR)) + "시 " +
                String.valueOf(cal.get(Calendar.MINUTE)) + "분 " +
                String.valueOf(cal.get(Calendar.SECOND)) + "초" ;
        System.out.println("현재 시간 : "+ time);
       
       
        //set메소드를 통해 연도 월 일 을 설정
        cal.set(Calendar.YEAR, Integer.valueOf(year));
        cal.set(Calendar.MONTH, Integer.valueOf(month)-1); // 0 ~ 11월이라  1을 빼줌
        cal.set(Calendar.DAY_OF_MONTH, 1); // 지정 월의 날짜
        // 년 월 일을 나누어서 표현했고, 아래는 하나로 년월일을 지정
        //cal.set(year,month-1,1); // 0 ~ 11월로 인식하므로 - 1, 입력된 년월

        LastDays = cal.getActualMaximum(Calendar.DATE); //지정한 월의 마지막 날짜
        System.out.println("SET 지정달 마지막날짜 : "+LastDays);

        dayofweek = cal.get(Calendar.DAY_OF_WEEK); // 지정날짜 요일 (일요일은 1, 토요일은 7)
        System.out.println("SET 지정달 week 값 : "+dayofweek); // 지정날짜의 1일의 요일 값을 표시
        trailingSpaces = dayofweek -1;
        prevMonth = month -1;
        prevYear = year;
        daysInPrevMonth = getLastDay(prevMonth,prevYear);
        System.out.println("SET 이전달 마지막날짜 : "+daysInPrevMonth);
        // 이번달 1일 이전 공백 부분을 채우기 위한 이전달 날짜
        for (int i = 0; i < trailingSpaces; i++) {
             System.out.println("SET 이전달 날짜 : "+ String.valueOf((daysInPrevMonth - trailingSpaces + DAY_OFFSET) + i));
        }
    }

    // 달의 마지막 일을 구함
    private static int getLastDay(int month, int year) {
        if (month == 4 || month == 6 || month == 9 || month == 11) {
            return 30;
        } else if (month == 2 && isLeapYear(year) == true) {
            return 29;
        } else if (month == 2 && isLeapYear(year) == false) {
            return 28;
        } else {
            return 31;
        }
    }

    // 해당 년도가 윤년인지 판별
    private static boolean isLeapYear(int year) {
        return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));
    }
}


블로그 이미지

Link2Me

,
728x90

안드로이드 달력 화면에 뿌려줄 GridCellAdapter 코드는 아래와 같다.


package com.link2me.android.adpater;

import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.link2me.android.calendar.R;
import com.link2me.android.item.Calendar_Item;
import com.link2me.android.util.CalendarHelper;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

public class GridCellAdapter extends BaseAdapter implements View.OnClickListener {
    private static final String tag = GridCellAdapter.class.getSimpleName();
    private final Context mContext;

    private final ArrayList<Calendar_Item> calList;
    private LinearLayout gridcell_layout;
    private TextView gridcell_Day;
    private TextView gridcell_LunarDay;
    private TextView gridcell_Event;
    private final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd-MMM-yyyy");

    public GridCellAdapter(Context context, ArrayList<Calendar_Item> list) {
        mContext = context;
        calList = list;
    }

    public Calendar_Item getItem(int position) {
        return calList.get(position);
    }

    @Override
    public int getCount() {
        return calList.size();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = convertView;
        if (view == null) {
            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.calendar_cell, parent, false);
        }

        // Get a reference to the Day gridcell_layout
        gridcell_layout = (LinearLayout) view.findViewById(R.id.calendar_day_gridcell);
        gridcell_Day = view.findViewById(R.id.dayTV);
        gridcell_LunarDay = view.findViewById(R.id.lunardayTV);
        gridcell_Event = view.findViewById(R.id.eventTV);
        gridcell_layout.setOnClickListener(this);

        String theday = calList.get(position).getDay();
        int themonth = Integer.parseInt(calList.get(position).getMonth());
        String theyear = calList.get(position).getYear();
        String holiday = calList.get(position).getEvent();

        // Set the Day GridCell
        gridcell_Day.setText(theday);
        gridcell_LunarDay.setText(CalendarHelper.Sol2Lun(theyear, String.valueOf(themonth-1),theday)); //0 ~ 11월로 인식하므로 - 1
        if(holiday.length()>0){
            gridcell_Event.setText(holiday);
        } else {
            gridcell_Event.setText("");
        }
        gridcell_layout.setTag(theday + "-" + themonth + "-" + theyear);

        if(calList.get(position).getColor().equals("GRAY")){
            gridcell_Day.setTextColor(Color.LTGRAY);
        }
        if(calList.get(position).getColor().equals("BLACK")){
            gridcell_Day.setTextColor(Color.BLACK);
        }
        if(calList.get(position).getColor().equals("RED")){
            gridcell_Day.setTextColor(Color.RED);
            gridcell_Event.setTextColor(Color.RED);
        }
        if(calList.get(position).getColor().equals("BLUE")){
            gridcell_Day.setTextColor(Color.BLUE);
        }
        if(calList.get(position).getColor().equals("CYAN")){
            gridcell_layout.setBackgroundColor(Color.CYAN);
        }
        return view;
    }

    @Override
    public void onClick(View view) {
        String date_month_year = (String) view.getTag();
        Log.e("Selected date", date_month_year);
        try {
            Date parsedDate = dateFormatter.parse(date_month_year);
            Log.d(tag, "Parsed Date: " + parsedDate.toString());

        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

}


calendar_cell.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/calendar_day_gridcell"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/day_cell_bg"
    android:orientation="vertical"
    android:padding="7dp">

    <TextView
        android:id="@+id/dayTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="8"
        android:textStyle="bold"
        android:textSize="24sp" />

    <TextView
        android:id="@+id/lunardayTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="00.16"
        android:textSize="10sp" />

    <TextView
        android:id="@+id/eventTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="1"
        android:text="기념일"
        android:textColor="#ff0000" />

</LinearLayout>



블로그 이미지

Link2Me

,
728x90

CalendarHelper Class 를 생성하여 양력/음력 변환, 음력/양력 변환, 한달의 마지막날짜 구하기 등을 하는 메소드를 구현한다.


package com.link2me.android.util;

import com.ibm.icu.util.ChineseCalendar;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class CalendarHelper {
    /**
     * 음력날짜를 양력날짜로 변환
     * @param 음력날짜 (yyyyMMdd)
     * @return 양력날짜 (yyyyMMdd)
     */
    public static String Lunar2Solar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim();
        if (date.length() != 8) {
            if (date.length() == 4)
                date = date + "0101";
            else if (date.length() == 6)
                date = date + "01";
            else if (date.length() > 8)
                date = date.substring(0, 8);
            else
                return "";
        }

        cc.set(ChineseCalendar.EXTENDED_YEAR, Integer.parseInt(date.substring(0, 4)) + 2637);
        cc.set(ChineseCalendar.MONTH, Integer.parseInt(date.substring(4, 6)) - 1);
        cc.set(ChineseCalendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)));

        cal.setTimeInMillis(cc.getTimeInMillis());

        int y = cal.get(Calendar.YEAR);
        int m = cal.get(Calendar.MONTH) + 1;
        int d = cal.get(Calendar.DAY_OF_MONTH);

        StringBuffer ret = new StringBuffer();
        ret.append(String.format("%04d", y));
        ret.append(String.format("%02d", m));
        ret.append(String.format("%02d", d));

        return ret.toString();
    }

    /**
     * 양력날짜를 음력날짜로 변환
     * @param 양력날짜 (yyyyMMdd)
     * @return 음력날짜 (yyyyMMdd)
     */
    public static String Solar2Lunar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim() ;
        if( date.length() != 8 ) {
            if( date.length() == 4 )
                date = date + "0101" ;
            else if( date.length() == 6 )
                date = date + "01" ;
            else if( date.length() > 8 )
                date = date.substring(0,8) ;
            else
                return "" ;
        }

        cal.set( Calendar.YEAR, Integer.parseInt(date.substring(0,4)) ) ;
        cal.set( Calendar.MONTH, Integer.parseInt(date.substring(4,6))-1 ) ;
        cal.set( Calendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)) ) ;

        cc.setTimeInMillis( cal.getTimeInMillis() ) ;

        // ChinessCalendar.YEAR 는 1~60 까지의 값만 가지고 ,
        // ChinessCalendar.EXTENDED_YEAR 는 Calendar.YEAR 값과 2637 만큼의 차이를 가진다.
        int y = cc.get(ChineseCalendar.EXTENDED_YEAR)-2637 ;
        int m = cc.get(ChineseCalendar.MONTH)+1 ;
        int d = cc.get(ChineseCalendar.DAY_OF_MONTH) ;

        StringBuffer ret = new StringBuffer() ;
        if( y < 1000 )          ret.append( "0" ) ;
        else if( y < 100 )      ret.append( "00" ) ;
        else if( y < 10 )       ret.append( "000" ) ;
        ret.append( y ) ;

        if( m < 10 ) ret.append( "0" ) ;
        ret.append( m ) ;

        if( d < 10 ) ret.append( "0" ) ;
        ret.append( d ) ;

        return ret.toString() ;
    }

    public static int WeekendValue(String date) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Calendar cal = Calendar.getInstance();
        cal.setTime(sdf.parse(date));
        return cal.get(Calendar.DAY_OF_WEEK);
        // Calendar.SUNDAY : 1
        // Calendar.SATURDAY : 7
    }

    /**
     * 양력날짜의 요일을 리턴
     * @param 양력날짜 (yyyyMMdd)
     * @return 요일(int)
     */
    public static int getDayOfWeek(String day) {
        int y = Integer.parseInt(day.substring(0, 4));
        int m = Integer.parseInt(day.substring(4, 6)) - 1;
        int d = Integer.parseInt(day.substring(6));
        Calendar c = Calendar.getInstance();
        c.set(y, m, d);
        return c.get(Calendar.DAY_OF_WEEK);
    }

    public static String Sol2Lun(String year, String month, String day) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (year == null || month == null || day == null)
            return "";

        cal.set( Calendar.YEAR, Integer.parseInt(year) ) ;
        cal.set( Calendar.MONTH, Integer.parseInt(month)) ;
        cal.set( Calendar.DAY_OF_MONTH, Integer.parseInt(day)) ;

        cc.setTimeInMillis( cal.getTimeInMillis() ) ;

        // ChinessCalendar.YEAR 는 1~60 까지의 값만 가지고 ,
        // ChinessCalendar.EXTENDED_YEAR 는 Calendar.YEAR 값과 2637 만큼의 차이를 가진다.
        int y = cc.get(ChineseCalendar.EXTENDED_YEAR)-2637 ;
        int m = cc.get(ChineseCalendar.MONTH)+1 ;
        int d = cc.get(ChineseCalendar.DAY_OF_MONTH) ;

        StringBuffer ret = new StringBuffer() ;
        if( y < 1000 )          ret.append( "0" ) ;
        else if( y < 100 )      ret.append( "00" ) ;
        else if( y < 10 )       ret.append( "000" ) ;
        ret.append( y ) ;

        if( m < 10 ) ret.append( "0" ) ;
        ret.append( m ) ;

        if( d < 10 ) ret.append( "0" ) ;
        ret.append( d ) ;

        return ret.toString().substring(4,6)+"."+ret.toString().substring(6,8);
    }

    // 해당 달의 첫 요일을 구해서 돌려줌.
    private static int getStartDay(int year, int month) {
        int monthSum = 0;
        int leapYear = 0;
        int daySum = 1;

        for (int i = 1; i < year; i++) {
            monthSum += 365;
            if (isLeapYear(i) == true) {
                leapYear += 1;
            }
        }

        for (int j = 1; j < month; j++) {
            daySum += getLastDay(year, j);
        }

        return (monthSum + leapYear + daySum) % 7;

    }

    // 달의 마지막 일을 구함
    public static int getLastDay(int month, int year) {

        if (month == 4 || month == 6 || month == 9 || month == 11) {
            return 30;
        } else if (month == 2 && isLeapYear(year) == true) {
            return 29;
        } else if (month == 2 && isLeapYear(year) == false) {
            return 28;
        } else {
            return 31;
        }
    }

    // 해당 년도가 윤년인지 판별
    private static boolean isLeapYear(int year) {
        return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));
    }
}


CalendarHelper 수정본

package com.link2me.android.util;

import com.ibm.icu.util.ChineseCalendar;
import com.link2me.android.item.Holidays_Item;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;


public class CalendarHelper {
    static HashMap<String, Holidays_Item> holidaysArrayList = new HashMap<String,Holidays_Item>();

    /**
     * 음력날짜를 양력날짜로 변환
     * @param 음력날짜 (yyyyMMdd)
     * @return 양력날짜 (yyyyMMdd)
     */
    public static String Lunar2Solar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim();
        if (date.length() != 8) {
            if (date.length() == 4)
                date = date + "0101";
            else if (date.length() == 6)
                date = date + "01";
            else if (date.length() > 8)
                date = date.substring(0, 8);
            else
                return "";
        }

        cc.set(ChineseCalendar.EXTENDED_YEAR, Integer.parseInt(date.substring(0, 4)) + 2637);
        cc.set(ChineseCalendar.MONTH, Integer.parseInt(date.substring(4, 6)) - 1);
        cc.set(ChineseCalendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)));

        cal.setTimeInMillis(cc.getTimeInMillis());

        int y = cal.get(Calendar.YEAR);
        int m = cal.get(Calendar.MONTH) + 1;
        int d = cal.get(Calendar.DAY_OF_MONTH);

        StringBuffer ret = new StringBuffer();
        ret.append(String.format("%04d", y));
        ret.append(String.format("%02d", m));
        ret.append(String.format("%02d", d));

        return ret.toString();
    }

    /**
     * 양력날짜를 음력날짜로 변환
     * @param 양력날짜 (yyyyMMdd)
     * @return 음력날짜 (yyyyMMdd)
     */
    public static String Solar2Lunar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim() ;
        if( date.length() != 8 ) {
            if( date.length() == 4 )
                date = date + "0101" ;
            else if( date.length() == 6 )
                date = date + "01" ;
            else if( date.length() > 8 )
                date = date.substring(0,8) ;
            else
                return "" ;
        }

        cal.set( Calendar.YEAR, Integer.parseInt(date.substring(0,4)) ) ;
        cal.set( Calendar.MONTH, Integer.parseInt(date.substring(4,6))-1 ) ;
        cal.set( Calendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)) ) ;

        cc.setTimeInMillis( cal.getTimeInMillis() ) ;

        // ChinessCalendar.YEAR 는 1~60 까지의 값만 가지고 ,
        // ChinessCalendar.EXTENDED_YEAR 는 Calendar.YEAR 값과 2637 만큼의 차이를 가진다.
        int y = cc.get(ChineseCalendar.EXTENDED_YEAR)-2637 ;
        int m = cc.get(ChineseCalendar.MONTH)+1 ;
        int d = cc.get(ChineseCalendar.DAY_OF_MONTH) ;

        StringBuffer ret = new StringBuffer() ;
        if( y < 1000 )          ret.append( "0" ) ;
        else if( y < 100 )      ret.append( "00" ) ;
        else if( y < 10 )       ret.append( "000" ) ;
        ret.append( y ) ;

        if( m < 10 ) ret.append( "0" ) ;
        ret.append( m ) ;

        if( d < 10 ) ret.append( "0" ) ;
        ret.append( d ) ;

        return ret.toString() ;
    }


    public static HashMap<String,Holidays_Item> holidayArray(String yyyy){
        holidaysArrayList.clear(); // 데이터 초기화
        // 양력 휴일
        addHolidaysItem(yyyy,"0101" ,"신정");
        addHolidaysItem(yyyy,"0301" ,"삼일절");
        addHolidaysItem(yyyy,"0505" ,"어린이날");
        addHolidaysItem(yyyy,"0606" ,"현충일" );
        addHolidaysItem(yyyy,"0815" ,"광복절");
        addHolidaysItem(yyyy,"1003" ,"개천절");
        addHolidaysItem(yyyy,"1009" ,"한글날");
        addHolidaysItem(yyyy,"1225" ,"성탄절");

        // 음력 휴일
        String prev_seol = String.valueOf(Integer.parseInt(Lunar2Solar(yyyy+"0101")) -1);
        addHolidaysItem(yyyy,prev_seol.substring(4) ,"");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0101"),"설날");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0102"),"");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0408"),"석탄일");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0814"),"");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0815"),"추석");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0816"),"");

        try {
            // 어린이날 대체공휴일 검사 : 어린이날은 토요일, 일요일인 경우 그 다음 평일을 대체공유일로 지정
            int childDayChk = WeekendValue(yyyy+"0505");
            if(childDayChk == 1) addHolidaysItem(yyyy,"0506" ,"대체공휴일");
            if(childDayChk == 7) addHolidaysItem(yyyy,"0507" ,"대체공휴일");

            // 설날 대체공휴일 검사
            if(WeekendValue(Lunar2Solar(yyyy+"0101"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0103"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0101"))==2) addHolidaysItem(yyyy,SolarDays(yyyy, "0103"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0102"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0103"),"대체공휴일");

            // 추석 대체공휴일 검사
            if(WeekendValue(Lunar2Solar(yyyy+"0814"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0817"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0815"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0817"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0816"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0817"),"대체공휴일");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        //Collections.sort(holidaysArrayList); // 오름차순 정렬

        return holidaysArrayList;
    }

    private static String SolarDays(String yyyy, String date){
        return Lunar2Solar(yyyy+date).substring(4);
    }

    private static void addHolidaysItem(String year, String date, String name ){
        Holidays_Item item = new Holidays_Item();
        item.setYear(year);
        item.setDate(date);
        item.setName(name);
        holidaysArrayList.put(date,item);
    }

    public static int WeekendValue(String date) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Calendar cal = Calendar.getInstance();
        cal.setTime(sdf.parse(date));
        return cal.get(Calendar.DAY_OF_WEEK);
        // Calendar.SUNDAY : 1
        // Calendar.SATURDAY : 7
    }

    /**
     * 양력날짜의 요일을 리턴
     * @param 양력날짜 (yyyyMMdd)
     * @return 요일(int)
     */
    public static int getDayOfWeek(String day) {
        int y = Integer.parseInt(day.substring(0, 4));
        int m = Integer.parseInt(day.substring(4, 6)) - 1;
        int d = Integer.parseInt(day.substring(6));
        Calendar c = Calendar.getInstance();
        c.set(y, m, d);
        return c.get(Calendar.DAY_OF_WEEK);
    }

    public static String Sol2Lun(String year, String month, String day) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (year == null || month == null || day == null)
            return "";

        cal.set( Calendar.YEAR, Integer.parseInt(year) ) ;
        cal.set( Calendar.MONTH, Integer.parseInt(month)) ;
        cal.set( Calendar.DAY_OF_MONTH, Integer.parseInt(day)) ;

        cc.setTimeInMillis( cal.getTimeInMillis() ) ;

        // ChinessCalendar.YEAR 는 1~60 까지의 값만 가지고 ,
        // ChinessCalendar.EXTENDED_YEAR 는 Calendar.YEAR 값과 2637 만큼의 차이를 가진다.
        int y = cc.get(ChineseCalendar.EXTENDED_YEAR)-2637 ;
        int m = cc.get(ChineseCalendar.MONTH)+1 ;
        int d = cc.get(ChineseCalendar.DAY_OF_MONTH) ;

        StringBuffer ret = new StringBuffer() ;
        if( y < 1000 )          ret.append( "0" ) ;
        else if( y < 100 )      ret.append( "00" ) ;
        else if( y < 10 )       ret.append( "000" ) ;
        ret.append( y ) ;

        if( m < 10 ) ret.append( "0" ) ;
        ret.append( m ) ;

        if( d < 10 ) ret.append( "0" ) ;
        ret.append( d ) ;

        return ret.toString().substring(4,6)+"."+ret.toString().substring(6,8);
    }

    // 해당 달의 첫 요일을 구해서 돌려줌.
    private static int getStartDay(int year, int month) {
        int monthSum = 0;
        int leapYear = 0;
        int daySum = 1;

        for (int i = 1; i < year; i++) {
            monthSum += 365;
            if (isLeapYear(i) == true) {
                leapYear += 1;
            }
        }

        for (int j = 1; j < month; j++) {
            daySum += getLastDay(year, j);
        }

        return (monthSum + leapYear + daySum) % 7;

    }

    // 달의 마지막 일을 구함
    public static int getLastDay(int month, int year) {

        if (month == 4 || month == 6 || month == 9 || month == 11) {
            return 30;
        } else if (month == 2 && isLeapYear(year) == true) {
            return 29;
        } else if (month == 2 && isLeapYear(year) == false) {
            return 28;
        } else {
            return 31;
        }
    }

    // 해당 년도가 윤년인지 판별
    private static boolean isLeapYear(int year) {
        return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));
    }



블로그 이미지

Link2Me

,
728x90

달력 데이터를 생성하는 것은 ArrayList 또는 Hashmap 을 이용한다.


먼저 달력 데이터 Class 를 구현한다.

package com.link2me.android.item;

public class Calendar_Item {
    private  String year; // 년
    private  String month; // 월
    private  String day; // 일
    private  int weekday; // 요일
    private  String color; // 색상
    private String event;
    private String key; // 월일 값을 key 로 사용

    public Calendar_Item() {
    }

    public Calendar_Item(String year, String month, String day, int weekday, String color) {
        this.year = year;
        this.month = month;
        this.day = day;
        this.weekday = weekday;
        this.color = color;
    }

    public String getYear() {
        return year;
    }

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

    public String getMonth() {
        return month;
    }

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

    public String getDay() {
        return day;
    }

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

    public int getWeekday() {
        return weekday;
    }

    public void setWeekday(int weekday) {
        this.weekday = weekday;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getEvent() {
        return event;
    }

    public void setEvent(String event) {
        this.event = event;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}


먼저 작성했던 MainActivity 에서 makeCalendarList(int CurrentMonth, int yy) 메소드를 추가하면서 필요한 코드를 작성한다.

여기서는 공휴일 데이터를 추가하는 루틴을 생략했다.

공휴일 데이터 추가 샘플은 https://link2me.tistory.com/1699 게시글과 다음 게시글인 CalendarHelper 를 같이 참조하며 CalendarHelper 에 필요한 사항을 추가하고 수정해서 구현하도록 개발자의 몫으로 남겼다.


class package com.link2me.android.calendar;

import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import com.link2me.android.adpater.GridCellAdapter;
import com.link2me.android.item.Calendar_Item;
import com.link2me.android.item.Holidays_Item;
import com.link2me.android.util.CalendarHelper;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = MainActivity.class.getSimpleName();
    private Context mContext;

    private TextView currentMonth;
    private ImageView prevYear;
    private ImageView prevMonth;
    private ImageView nextMonth;
    private ImageView nextYear;
    private ImageView selectDate;

    private GridView calendarView;
    private GridCellAdapter adapter;
    private Calendar cal;
    private int month, year;
    private static final String dateTemplate = "yyyy MMMM";

    private ArrayList<Calendar_Item> calList = new ArrayList<>();
    private static final int DAY_OFFSET = 1;
    private final String[] weekdays = new String[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    private int lastDays;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = MainActivity.this;

        cal = Calendar.getInstance(Locale.getDefault());
        month = cal.get(Calendar.MONTH) + 1;
        year = cal.get(Calendar.YEAR);

        prevYear = (ImageView) this.findViewById(R.id.prevYear);
        prevYear.setOnClickListener(this);

        prevMonth = (ImageView) this.findViewById(R.id.prevMonth);
        prevMonth.setOnClickListener(this);

        currentMonth = (TextView) this.findViewById(R.id.currentMonth);
        currentMonth.setText(year +"년 " + month + "월");
        currentMonth.setOnClickListener(this);

        nextMonth = (ImageView) this.findViewById(R.id.nextMonth);
        nextMonth.setOnClickListener(this);

        nextYear = (ImageView) this.findViewById(R.id.nextYear);
        nextYear.setOnClickListener(this);

        selectDate = (ImageView) this.findViewById(R.id.selectDate);
        selectDate.setOnClickListener(this);

        calendarView = (GridView) this.findViewById(R.id.calendar);

        makeCalendarList(month, year);

        // Initialised
        adapter = new GridCellAdapter(mContext, calList);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
    }

    private void setGridCellAdapterToDate(int month, int year) {
        makeCalendarList(month, year);
        adapter = new GridCellAdapter(mContext, calList);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
        // 상단 화면 날짜 출력
        currentMonth.setText(year +"년 " + month + "월");
    }

    private void makeCalendarList(int CurrentMonth, int yy) {
        int trailingSpaces = 0;
        int daysInPrevMonth = 0;
        int prevMonth = 0;
        int prevYear = 0;
        int nextMonth = 0;
        int nextYear = 0;

        calList.clear();

        Calendar cal = Calendar.getInstance();

        int thisYear = cal.get(Calendar.YEAR);

        int thisMonth = cal.get(Calendar.MONTH); // 현재 월
        int thisDay = cal.get(Calendar.DAY_OF_MONTH); // 오늘 날짜
        cal.set(yy,CurrentMonth-1,1); // 0 ~ 11월로 인식하므로 - 1, 입력된 년월
        Log.e(TAG, "Calendar SET := " + cal.getTime().toString());
        lastDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH);

        if (CurrentMonth == 12) {
            prevMonth = CurrentMonth - 1;
            nextMonth = 1;
            prevYear = yy;
            nextYear = yy + 1;
            daysInPrevMonth = CalendarHelper.getLastDay(prevMonth,prevYear);
        } else if (CurrentMonth == 1) {
            prevMonth = 12;
            prevYear = yy - 1;
            nextYear = yy;
            nextMonth = CurrentMonth + 1;
            daysInPrevMonth = CalendarHelper.getLastDay(prevMonth,prevYear);
        } else {
            prevMonth = CurrentMonth - 1;
            nextMonth = CurrentMonth + 1;
            nextYear = yy;
            prevYear = yy;
            daysInPrevMonth = CalendarHelper.getLastDay(prevMonth,prevYear);
        }

        int currentWeekDay = cal.get(Calendar.DAY_OF_WEEK) - 1;
        trailingSpaces = currentWeekDay;

        // 이전월 데이터 채우기 위한 리스트 추가
        for (int i = 0; i < trailingSpaces; i++) {
            String key = String.format("%02d",prevMonth)+String.format("%02d",(daysInPrevMonth - trailingSpaces + DAY_OFFSET) + i);
            addCalendarItem(String.valueOf(prevYear),String.valueOf(prevMonth),String.valueOf((daysInPrevMonth - trailingSpaces + DAY_OFFSET) + i),
                    0,"GRAY","",key);
        }

        // 현재월 데이터 리스트 추가
        for (int i = 1; i <= lastDays; i++) {
            String date = String.valueOf(yy)+String.format("%02d",CurrentMonth)+String.format("%02d",i);
            String key = String.format("%02d",CurrentMonth)+String.format("%02d",i);
            try {
                int weekday = CalendarHelper.WeekendValue(date);
                if(weekday == 1) // 일요일
                    addCalendarItem(String.valueOf(yy),String.valueOf(CurrentMonth),String.valueOf(i),weekday,"RED","",key);
                else if(weekday == 7) // 토요일
                    addCalendarItem(String.valueOf(yy),String.valueOf(CurrentMonth),String.valueOf(i),weekday,"BLUE","",key);
                else
                    addCalendarItem(String.valueOf(yy),String.valueOf(CurrentMonth),String.valueOf(i),weekday,"BLACK","",key);
                if(CurrentMonth-1 == thisMonth){ // 현재월이면
                    if(i == thisDay){
                        Log.e(TAG, "key := " + key);
                        int index = getIndexOfCalList(key);
                        Calendar_Item item = calendarItem(String.valueOf(yy),String.valueOf(CurrentMonth),String.valueOf(i),weekday,"CYAN","",key);
                        calList.set(index,item);
                    }
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }

        // 다음달 데이터 채우기 리스트 추가
        for (int i = 0; i < calList.size() % 7; i++) {
            String key = String.format("%02d",nextMonth)+String.format("%02d",i+1);
            addCalendarItem(String.valueOf(nextYear),String.valueOf(nextMonth),String.valueOf(i+1),0,"GRAY",
                    "",key);
        }

    }

    private Calendar_Item calendarItem(String year, String month, String day, int weekday, String color, String name, String key){
        Calendar_Item item = new Calendar_Item();
        item.setYear(year);
        item.setMonth(month);
        item.setDay(day);
        item.setWeekday(weekday);
        item.setColor(color);
        item.setEvent(name);
        item.setKey(key);
        return item;
    }

    private void addCalendarItem(String year, String month, String day, int weekday, String color,String name, String key){
        Calendar_Item item = calendarItem(year,month,day,weekday,color,name,key);
        calList.add(item);
    }

    private int getIndexOfCalList(String search_key) {
        for (int temp = 0; temp < calList.size(); temp++) {
            String key = calList.get(temp).getKey();
            if (key != null && key.equals(search_key)) {
                return temp;
            }
        }
        return -1;
    }

    private String getWeekDayAsString(int i) {
        return weekdays[i];
    }
}


참고 도움될 자료 : https://www.toptal.com/android/android-customization-how-to-build-a-ui-component-that-does-what-you-want


다음은 CalendarHelper Class 를 만들어서 음력/양력 변환, 공휴일 처리, 한달의 마지막 날짜 구하기 등 메소드를 구현한다.

블로그 이미지

Link2Me

,
728x90

Layout 을 화면에 뿌릴 MainActivity.java 코드를 아래와 같이 구현했다.


package com.link2me.android.calendar;

import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import com.link2me.android.adpater.GridCellAdapter;

import java.util.Calendar;
import java.util.Locale;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = MainActivity.class.getSimpleName();
    private Context mContext;

    private TextView currentMonth;
    private ImageView prevYear;
    private ImageView prevMonth;
    private ImageView nextMonth;
    private ImageView nextYear;
    private ImageView selectDate;

    private GridView calendarView;
    private GridCellAdapter adapter;
    private Calendar cal;
    private int month, year;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = MainActivity.this;

        cal = Calendar.getInstance(Locale.getDefault());
        month = cal.get(Calendar.MONTH) + 1;
        year = cal.get(Calendar.YEAR);

        prevYear = (ImageView) this.findViewById(R.id.prevYear);
        prevYear.setOnClickListener(this);

        prevMonth = (ImageView) this.findViewById(R.id.prevMonth);
        prevMonth.setOnClickListener(this);

        currentMonth = (TextView) this.findViewById(R.id.currentMonth);
        currentMonth.setText(year +"년 " + month + "월");
        currentMonth.setOnClickListener(this);

        nextMonth = (ImageView) this.findViewById(R.id.nextMonth);
        nextMonth.setOnClickListener(this);

        nextYear = (ImageView) this.findViewById(R.id.nextYear);
        nextYear.setOnClickListener(this);

        selectDate = (ImageView) this.findViewById(R.id.selectDate);
        selectDate.setOnClickListener(this);

        calendarView = (GridView) this.findViewById(R.id.calendar);

        // Initialised
        adapter = new GridCellAdapter(mContext, month, year);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
    }

    private void setGridCellAdapterToDate(int month, int year) {
        adapter = new GridCellAdapter(mContext, month, year);
        adapter.notifyDataSetChanged();
        calendarView.setAdapter(adapter);
        // 상단 화면 날짜 출력
        currentMonth.setText(year +"년 " + month + "월");
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.prevYear:
                year--;
                setGridCellAdapterToDate(month, year);
                break;

            case R.id.prevMonth:
                if (month <= 1) {
                    month = 12;
                    year--;
                } else {
                    month--;
                }
                setGridCellAdapterToDate(month, year);
                break;

            case R.id.currentMonth:
                cal = Calendar.getInstance(Locale.getDefault()); // 현재날짜와 시간으로 설정된다.
                month = cal.get(Calendar.MONTH) + 1; // 0 ~ 11월을 반환하므로 + 1을 해준다.
                year = cal.get(Calendar.YEAR);
                setGridCellAdapterToDate(month, year);
                break;

            case R.id.nextMonth:
                if (month > 11) {
                    month = 1;
                    year++;
                } else {
                    month++;
                }
                setGridCellAdapterToDate(month, year);
                break;

            case R.id.nextYear:
                year++;
                setGridCellAdapterToDate(month, year);
                break;

            case R.id.selectDate:
                LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
                final View layout = inflater.inflate(R.layout.select_calendar_input,null);
                AlertDialog.Builder cal_confirm = new AlertDialog.Builder(mContext);
                cal_confirm.setTitle("검색할 년도 입력");
                cal_confirm.setView(layout);
                final EditText etYear = layout.findViewById(R.id.selectYear);
                final EditText etMonth = layout.findViewById(R.id.selectMonth);
                // 확인 버튼 설정
                cal_confirm.setPositiveButton("검색", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String selectYear = etYear.getText().toString().trim();
                        if(selectYear.length() == 0) {
                            AlertDialog.Builder phoneNO_confirm = new AlertDialog.Builder(mContext);
                            phoneNO_confirm.setMessage("년도를 입력하세요.").setCancelable(false).setPositiveButton("확인",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) { // 'YES'
                                            dialog.dismiss();
                                        }
                                    });
                            AlertDialog alert = phoneNO_confirm.create();
                            alert.show();
                            return;
                        }

                        String selectMonth = etMonth.getText().toString().trim();
                        if(selectMonth.length() == 0) {
                            AlertDialog.Builder phoneNO_confirm = new AlertDialog.Builder(mContext);
                            phoneNO_confirm.setMessage("월(月)을 입력하세요.").setCancelable(false).setPositiveButton("확인",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) { // 'YES'
                                            dialog.dismiss();
                                        }
                                    });
                            AlertDialog alert = phoneNO_confirm.create();
                            alert.show();
                            return;
                        }
                        month = Integer.parseInt(selectMonth);
                        year = Integer.parseInt(selectYear);
                        setGridCellAdapterToDate(month, year);
                    }
                });
                cal_confirm.setNegativeButton("취소", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
                cal_confirm.show();
                break;
        }
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "Destroying View ...");
        super.onDestroy();
    }
}


화면에 출력할 데이터는 GridCellAdapter 를 만들어서 뿌려주면 된다.

블로그 이미지

Link2Me

,
728x90

만들고 싶은 달력의 모습은 아래와 같다.

이미지 파일은 https://www.iconfinder.com/ 에서 free 이미지를 받아서 이미지 크기를 줄여서 사용했다.


 



AndroidManifest.xml

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28


    defaultConfig {
        applicationId "com.link2me.android.calendar"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation files('libs/icu4j-4_8_2.jar')
}


Android 달력 만들기 Layout 구성을 위한 XML 파일 구조다.

전년도, 이전달, 현재 월, 다음달, 다음년도, 특정 년월 검색

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/lightgray"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/buttonlayout"
        android:layout_width="fill_parent"
        android:layout_height="60sp"
        android:background="@drawable/navibar_bg"
        android:gravity="left|top"
        android:height="60sp"
        android:orientation="horizontal" >

        <LinearLayout
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center">

            <ImageView
                android:id="@+id/prevYear"
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                android:layout_gravity="center"
                android:src="@drawable/double_arrow_left" >
            </ImageView>
        </LinearLayout>

        <LinearLayout
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center">
            <ImageView
                android:id="@+id/prevMonth"
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                android:layout_gravity="center"
                android:src="@drawable/arrow_left" >
            </ImageView>
        </LinearLayout>

        <TextView
            android:id="@+id/currentMonth"
            android:layout_width="fill_parent"
            android:layout_height="60sp"
            android:layout_weight="0.6"
            android:gravity="center"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textColor="#FFFFFF" >
        </TextView>

        <LinearLayout
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center">

            <ImageView
                android:id="@+id/nextMonth"
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                android:layout_gravity="center"
                android:src="@drawable/arrow_right" >
            </ImageView>

        </LinearLayout>

        <LinearLayout
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center">
            <ImageView
                android:id="@+id/nextYear"
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                android:layout_gravity="center"
                android:src="@drawable/double_arrow_right" >
            </ImageView>
        </LinearLayout>

        <LinearLayout
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center">
            <ImageView
                android:id="@+id/selectDate"
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                android:layout_gravity="center"
                android:src="@drawable/box_arrow" >
            </ImageView>
        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="30dp"
        android:background="#e0e7ee"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="일"
            android:textColor="#ff0000"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="월"
            android:textColor="#969a9b"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="화"
            android:textColor="#969a9b"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="수"
            android:textColor="#969a9b"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="목"
            android:textColor="#969a9b"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="금"
            android:textColor="#969a9b"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="토"
            android:textColor="#ff0000"
            android:textSize="14sp"
            android:textStyle="bold" />
    </LinearLayout>

    <GridView
        android:id="@+id/calendar"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:numColumns="7" >
    </GridView>

</LinearLayout>


select_calendar_input.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:text="년도"
            android:layout_marginLeft="20dp"
            android:textSize="12sp" />

        <EditText
            android:id="@+id/selectYear"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_marginRight="20dp"
            android:singleLine="true"
            android:inputType="number"
            android:textSize="16sp"
            android:textColor="#000000" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:text="월(月)"
            android:textSize="12sp" />

        <EditText
            android:id="@+id/selectMonth"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_marginRight="20dp"
            android:singleLine="true"
            android:inputType="number"
            android:textSize="16sp"
            android:textColor="#000000" />

    </LinearLayout>
</LinearLayout>


블로그 이미지

Link2Me

,
728x90

자바(Java) 음력 일자 변환 라이브러리 설치법은 https://link2me.tistory.com/1695 를 참조하면 된다.

음력에 대한 자세한 내용을 알고 싶다면 http://oops.org/project/manse/https://github.com/OOPS-ORG-PHP/Lunar 를 읽어보면 도움이 될 것이다. 고영창 씨께서 고생해서 자료와 알고리즘을 만들었던 거 같다.

음력 앱, 프로그램 등을 테스트하다보면 음력일자가 약간 틀리게 나오는 경우도 있다. 정확한 데이터를 기준으로 해야 하는데, 데이터의 기준이 이것이 정확하다고 하는 걸 찾는게 어려운 거 같다.

음력 데이터를 MySQL 버전으로 DB화를 해 놓은 걸 SQLite 로 포팅하는 걸 시도해봐야 하나 고민되기도 한다.


자바(Java)에서 사용하는 라이브러리가 맞다는 가정하에 이 코드를 기준으로 음력과 양력을 변환하여 법정공휴일 처리를 하도록 했다. 음력을 지원하는 앱을 실행해보면서 2019년도 추석, 설 데이터, 어린이날 대체공휴일 등이 맞는 걸로 봐서 음력 데이터가 정확한 거 같아서 아래 코드를 노가다 수준으로 구현해서 테스트 했다.


법정공휴일 처리를 위해서 Holidays Class 를 만들었다.

ArrayList를 사용해서 입력 연도에 해당되는 법정공휴일 처리를 하려고 한다.

public class Holidays implements Comparable<Holidays>{
    // ArrayList의 type이 Comparable을 implements한 경우에만 sort 메소드의 정렬 기능을 사용할 수 있다
    private String year; // 연도
    private String date; // 월일
    private String name; // 휴일 명칭

    public Holidays() {
    }

    public Holidays(String year, String date, String name) {
        this.year = year;
        this.date = date;
        this.name = name;
    }

    public String getYear() {
        return year;
    }

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

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(Holidays o) {
        return this.date.compareTo(o.date);
    }
}


이제 법정공휴일 양력과 음력을 처리할 ArrayList<Holidays> holidayArray(String yyyy) 메소드를 만들었다.

ArrayList return 메소드 만드는 예제는 https://link2me.tistory.com/1698 를 참조하면 된다.

사용법은 ArrayList<Holidays> arr = LunarCalendar.holidayArray("2019"); 로 결과를 얻어서 처리하면 된다.

import com.ibm.icu.util.ChineseCalendar;
import com.tistory.link2me.item.Holidays;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;

public class LunarCalendar {
    static ArrayList<Holidays> holidaysArrayList = new ArrayList<>();

    /**
     * 음력날짜를 양력날짜로 변환
     * @param 음력날짜 (yyyyMMdd)
     * @return 양력날짜 (yyyyMMdd)
     */
    public static String Lunar2Solar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim();
        if (date.length() != 8) {
            if (date.length() == 4)
                date = date + "0101";
            else if (date.length() == 6)
                date = date + "01";
            else if (date.length() > 8)
                date = date.substring(0, 8);
            else
                return "";
        }

        cc.set(ChineseCalendar.EXTENDED_YEAR, Integer.parseInt(date.substring(0, 4)) + 2637);
        cc.set(ChineseCalendar.MONTH, Integer.parseInt(date.substring(4, 6)) - 1);
        cc.set(ChineseCalendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)));

        cal.setTimeInMillis(cc.getTimeInMillis());

        int y = cal.get(Calendar.YEAR);
        int m = cal.get(Calendar.MONTH) + 1;
        int d = cal.get(Calendar.DAY_OF_MONTH);

        StringBuffer ret = new StringBuffer();
        ret.append(String.format("%04d", y));
        ret.append(String.format("%02d", m));
        ret.append(String.format("%02d", d));

        return ret.toString();
    }

    /**
     * 양력날짜를 음력날짜로 변환
     * @param 양력날짜 (yyyyMMdd)
     * @return 음력날짜 (yyyyMMdd)
     */
    public static String Solar2Lunar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim() ;
        if( date.length() != 8 ) {
            if( date.length() == 4 )
                date = date + "0101" ;
            else if( date.length() == 6 )
                date = date + "01" ;
            else if( date.length() > 8 )
                date = date.substring(0,8) ;
            else
                return "" ;
        }

        cal.set( Calendar.YEAR, Integer.parseInt(date.substring(0,4)) ) ;
        cal.set( Calendar.MONTH, Integer.parseInt(date.substring(4,6))-1 ) ;
        cal.set( Calendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)) ) ;

        cc.setTimeInMillis( cal.getTimeInMillis() ) ;

        // ChinessCalendar.YEAR 는 1~60 까지의 값만 가지고 ,
        // ChinessCalendar.EXTENDED_YEAR 는 Calendar.YEAR 값과 2637 만큼의 차이를 가진다.
        int y = cc.get(ChineseCalendar.EXTENDED_YEAR)-2637 ;
        int m = cc.get(ChineseCalendar.MONTH)+1 ;
        int d = cc.get(ChineseCalendar.DAY_OF_MONTH) ;

        StringBuffer ret = new StringBuffer() ;
        if( y < 1000 )          ret.append( "0" ) ;
        else if( y < 100 )      ret.append( "00" ) ;
        else if( y < 10 )       ret.append( "000" ) ;
        ret.append( y ) ;

        if( m < 10 ) ret.append( "0" ) ;
        ret.append( m ) ;

        if( d < 10 ) ret.append( "0" ) ;
        ret.append( d ) ;

        return ret.toString() ;
    }


    public static ArrayList<Holidays> holidayArray(String yyyy){
        holidaysArrayList.clear(); // 데이터 초기화
        // 양력 휴일
        addHolidaysItem(yyyy,"0101" ,"신정");
        addHolidaysItem(yyyy,"0301" ,"삼일절");
        addHolidaysItem(yyyy,"0505" ,"어린이날");
        addHolidaysItem(yyyy,"0606" ,"현충일" );
        addHolidaysItem(yyyy,"0815" ,"광복절");
        addHolidaysItem(yyyy,"1003" ,"개천절");
        addHolidaysItem(yyyy,"1009" ,"한글날");
        addHolidaysItem(yyyy,"1225" ,"성탄절");

        // 음력 휴일
        String prev_seol = String.valueOf(Integer.parseInt(Lunar2Solar(yyyy+"0101")) -1);
        addHolidaysItem(yyyy,prev_seol.substring(4) ,"");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0101"),"설날");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0102"),"");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0408"),"석탄일");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0814"),"");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0815"),"추석");
        addHolidaysItem(yyyy,SolarDays(yyyy, "0816"),"");

        try {
            // 어린이날 대체공휴일 검사 : 어린이날은 토요일, 일요일인 경우 그 다음 평일을 대체공유일로 지정
            int childDayChk = WeekendValue(yyyy+"0505");
            if(childDayChk == 1) addHolidaysItem(yyyy,"0506" ,"대체공휴일");
            if(childDayChk == 7) addHolidaysItem(yyyy,"0507" ,"대체공휴일");

            // 설날 대체공휴일 검사
            if(WeekendValue(Lunar2Solar(yyyy+"0101"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0103"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0101"))==2) addHolidaysItem(yyyy,SolarDays(yyyy, "0103"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0102"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0103"),"대체공휴일");

            // 추석 대체공휴일 검사
            if(WeekendValue(Lunar2Solar(yyyy+"0814"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0817"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0815"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0817"),"대체공휴일");
            if(WeekendValue(Lunar2Solar(yyyy+"0816"))==1) addHolidaysItem(yyyy,SolarDays(yyyy, "0817"),"대체공휴일");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Collections.sort(holidaysArrayList); // 오름차순 정렬

        return holidaysArrayList;
    }

    private static String SolarDays(String yyyy, String date){
        return Lunar2Solar(yyyy+date).substring(4);
    }

    private static void addHolidaysItem(String year, String date, String name ){
        Holidays item = new Holidays();
        item.setYear(year);
        item.setDate(date);
        item.setName(name);
        holidaysArrayList.add(item);
    }

    private static int WeekendValue(String date) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Calendar cal = Calendar.getInstance();
        cal.setTime(sdf.parse(date));
        return cal.get(Calendar.DAY_OF_WEEK);
        // Calendar.SUNDAY : 1
        // Calendar.SATURDAY : 7
    }

}


사용 샘플 예제

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.tistory.link2me.item.Holidays;
import com.tistory.link2me.util.Lunar2Solar;
import com.tistory.link2me.util.LunarCalendar;

import java.util.ArrayList;
import java.util.Calendar;

public class CalendarActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_calendar);

        System.out.println("Calendar.SUNDAY : "+Calendar.SUNDAY);
        System.out.println("Calendar.SATURDAY : "+Calendar.SATURDAY);

        ArrayList<Holidays> arr = LunarCalendar.holidayArray("2019");
        for(int i=0; i < arr.size();i++){
            System.out.println(arr.get(i).getYear() +"년 " + arr.get(i).getDate() +", " + arr.get(i).getName() );
        }

    }
}


결과 Logcat 화면


블로그 이미지

Link2Me

,
728x90

[ 태음력의 조정 ]
    235회합월은 일수로는 6940일(days)이다(235 x 29.530588일 = 6939.68818일).
    6940일의 1/19은 6940일 / 19 = 365.263157894737일(365 + 1/4 + 1/76일)이다.
    365.263157894737일은 12회합월(synodic months)인
    12 x 29.530588일 = 354.367056일보다 10.896101894737일(365.263157894737 - 354.367056) 더 크다.
    이것이 뜻하는 바는 음력으로 1년이 지나면 태양력과는 약 11일의 차이가 난다는 것을 의미한다.
    따라서 음력으로 3년이 지나면 태양력과는 약 33일의 차이가 나게 된다.
    따라서 3년마다 음력에 1달을 추가하며 13개의 음력 달들이 있게 하면,
    태음력은 어느 정도 근사하게 태양력과 일치하게 된다.
    태양력 19년 동안에 이런 작업을 총 7번(19년/3년 = 6.33) 행하면 된다.
    이런 이유로, 메톤 주기를 19년 7윤법이라고도 하였다.
 
메톤은 19태양년을 6,940일로 보고,
이것을 큰달(30일) 125개월과 작은달(29일) 110개월의 총 235개월 6,940일로 배분하였다.
이 방법은 평년(12개월) 12년과 윤년(13개월) 7년의 총 235개월로
태음력 달력을 구성하는 것과 동일하므로 19년 7윤법이라고도 하였다.

메톤주기의 존재를 알 수 있는 예로,
특정한 양력 날짜와 그에 대응하는 음력 날짜는 19년이 지나면 다시 일치한다는 사실을 찾을 수 있다.

하지만 정확히 맞물리는 건 아니라서 하루 정도 어긋나기도 한다. 


음력 날짜는 어느 경도를 기준으로 했느냐에 따라 하루 정도 차이가 있어서 때려 맞춰야 한다는 얘기가 있다.

한국천문연구원에서 계산한 2050년까지 음양력 자료들이 수록된 만세력을 기준으로 음력 날짜를 맞추면 될 거 같다.

음력을 알아내는 일정한 규칙은 없다고 한다.

아래 변환 코드로 내 생년월일, 딸래미 생년월일을 양력 → 음력 변환을 했을 때는 정확하게 결과가 도출되었다.


검색한 걸 참고하면 도움될 거 같아서 링크를 걸어둔다.

http://lifesci.net/pod/bbs/board.php?bo_table=B01&wr_id=1917



PHP 달력 만들기 https://link2me.tistory.com/1545 코드에 있는 함수를 자바 코드로 변환을 했다.

자동 변환이 아니라 코드에 맞게 함수를 찾아서 수정했다.

음력이 지원되는 Custom 달력을 만들어 볼 목적으로 코드를 변환하여 테스트 해보는 중이다.

PHP 원본 코드와 Java 로 변환한 코드를 같이 첨부했다.

Java 변환 코드는 Android 에서 활용하기 위한 목적이라 Android Studio 에서 작성했다.


PHP 코드

lun2sol.php



Lunar2Solar.java

Lunar2Solar.java

Lunar2Solar.java


자바 음력/양력 변환 코드

public class Lunar2Solar {
    // 음력 데이터 (평달 - 작은달 :1,  큰달:2 )
    // (윤달이 있는 달 - 평달이 작고 윤달도 작으면 :3 , 평달이 작고 윤달이 크면 : 4)
    // (윤달이 있는 달 - 평달이 크고 윤달이 작으면 :5,  평달과 윤달이 모두 크면 : 6)
    final static String[] LunarInfo = new String[]
            {"1212122322121","1212121221220","1121121222120","2112132122122","2112112121220",
                "2121211212120","2212321121212","2122121121210","2122121212120","1232122121212",
                "1212121221220","1121123221222","1121121212220","1212112121220","2121231212121",
                "2221211212120","1221212121210","2123221212121","2121212212120","1211212232212",
                "1211212122210","2121121212220","1212132112212","2212112112210","2212211212120",
                "1221412121212","1212122121210","2112212122120","1231212122212","1211212122210",
                "2121123122122","2121121122120","2212112112120","2212231212112","2122121212120",
                "1212122121210","2132122122121","2112121222120","1211212322122","1211211221220",
                "2121121121220","2122132112122","1221212121120","2121221212110","2122321221212",
                "1121212212210","2112121221220","1231211221222","1211211212220","1221123121221",
                "2221121121210","2221212112120","1221241212112","1212212212120","1121212212210",
                "2114121212221","2112112122210","2211211412212","2211211212120","2212121121210",
                "2212214112121","2122122121120","1212122122120","1121412122122","1121121222120",
                "2112112122120","2231211212122","2121211212120","2212121321212","2122121121210",
                "2122121212120","1212142121212","1211221221220","1121121221220","2114112121222",
                "1212112121220","2121211232122","1221211212120","1221212121210","2121223212121",
                "2121212212120","1211212212210","2121321212221","2121121212220","1212112112210",
                "2223211211221","2212211212120","1221212321212","1212122121210","2112212122120",
                "1211232122212","1211212122210","2121121122210","2212312112212","2212112112120",
                "2212121232112","2122121212110","2212122121210","2112124122121","2112121221220",
                "1211211221220","2121321122122","2121121121220","2122112112322","1221212112120",
                "1221221212110","2122123221212","1121212212210","2112121221220","1211231212222",
                "1211211212220","1221121121220","1223212112121","2221212112120","1221221232112",
                "1212212122120","1121212212210","2112132212221","2112112122210","2211211212210",
                "2221321121212","2212121121210","2212212112120","1232212122112","1212122122110",
                "2121212322122","1121121222120","2112112122120","2211231212122","2121211212120",
                "2122121121210","2124212112121","2122121212120","1212121223212","1211212221220",
                "1121121221220","2112132121222","1212112121220","2121211212120","2122321121212",
                "1221212121210","2121221212120","1232121221212","1211212212210","2121123212221",
                "2121121212220","1212112112220","1221231211221","2212211211220","1212212121210",
                "2123212212121","2112122122120","1211212322212","1211212122210","2121121122120",
                "2212114112122","2212112112120","2212121211210","2212232121211","2122122121210",
                "2112122122120","1231212122212","1211211221220","2121121321222","2121121121220",
                "2122112112120","2122141211212","1221221212110","2121221221210","2114121221221"};

    final static int[] arrayLDAY = new int[]{31,0,31,30,31,30,31,31,30,31,30,31};

    public static String lun2sol(String yyyymmdd) {
        int YunMonthFlag = 0;
        int gf_lun2sol = 0;
        int gf_yun = 0;
        int leap = 0;
        int syear = 0;
        int smonth = 0;
        int sday = 0;

        int getYEAR = Integer.parseInt(yyyymmdd.substring(0,4));
        int getMONTH = Integer.parseInt(yyyymmdd.substring(4,6));
        int getDAY = Integer.parseInt(yyyymmdd.substring(6,8));

        String arrayYUKSTR="甲-乙-丙-丁-戊-己-庚-辛-壬-癸";
        String[] arrayYUK = arrayYUKSTR.split("-");

        String arrayGAPSTR="子-丑-寅-卯-辰-巳-午-未-申-酉-戌-亥";
        String[] arrayGAP = arrayGAPSTR.split("-");

        String arrayDDISTR="쥐-소-범-토끼-용-뱀-말-양-원숭이-닭-개-돼지";
        String[] arrayDDI = arrayDDISTR.split("-");

        String arrayWEEKSTR="일-월-화-수-목-금-토";
        String[] arrayWEEK = arrayWEEKSTR.split("-");

        if (getYEAR <= 1881 || getYEAR >= 2050) { //년수가 해당일자를 넘는 경우
            YunMonthFlag = 0;
        }

        if (getMONTH > 12) { // 달수가 13이 넘는 경우
            YunMonthFlag = 0;
        }

        int m1 = getYEAR - 1881;
        if (Integer.parseInt(LunarInfo[m1].substring(12,13)) == 0) { // 윤달이 없는 해임
            YunMonthFlag = 0;
        } else {
            if (Integer.parseInt(LunarInfo[m1].substring(getMONTH,getMONTH+1)) > 2) {
                YunMonthFlag = 1;
            } else {
                YunMonthFlag = 0;
            }
        }

        m1 = -1;
        int td = 0;

        if (getYEAR > 1881 && getYEAR < 2050) {
            m1 = getYEAR - 1882;
            for (int i=0; i <= m1; i++) {
                for (int j=0; j <= 12; j++) {
                    td = td + Integer.parseInt(LunarInfo[i].substring(j,j+1));
                }
                if (Integer.parseInt(LunarInfo[i].substring(12,13)) == 0) {
                    td = td + 336;
                } else {
                    td = td + 362;
                }
            }
        } else {
            gf_lun2sol = 0;
        }

        m1++;
        int n2 = getMONTH - 1;
        int m2 = -1;

        while(true) {
            m2++;
            if (Integer.parseInt(LunarInfo[m1].substring(m2,m2+1)) > 2) {
                td = td + 26 + Integer.parseInt(LunarInfo[m1].substring(m2,m2+1));
                n2++;
            } else {
                if (m2 == n2) {
                    if (gf_yun == 1) {
                        td = td + 28 + Integer.parseInt(LunarInfo[m1].substring(m2,m2+1));
                    }
                    break;
                } else {
                    td = td + 28 + Integer.parseInt(LunarInfo[m1].substring(m2,m2+1));
                }
            }
        }

        td = td + getDAY + 29;
        m1 = 1880;
        while(true) {
            m1++;
            if (m1 % 400 == 0 || m1 % 100 != 0 && m1 % 4 == 0) {
                leap = 1;
            } else {
                leap = 0;
            }

            if (leap == 1) {
                m2 = 366;
            } else {
                m2 = 365;
            }

            if (td < m2) break;
            td = td - m2;
        }
        syear = m1;
        arrayLDAY[1] = m2 - 337;

        m1 = 0;

        while(true) {
            m1++;
            if (td <= arrayLDAY[m1-1]) {
                break;
            }
            td = td - arrayLDAY[m1-1];
        }
        smonth = m1;
        sday = td;
        int y = syear - 1;
        td = (int)(y*365) + (int)(y/4) - (int)(y/100) + (int)(y/400);

        if (syear % 400 == 0 || syear % 100 != 0 && syear % 4 == 0) {
            leap = 1;
        } else {
            leap = 0;
        }

        if (leap == 1) {
            arrayLDAY[1] = 29;
        } else {
            arrayLDAY[1] = 28;
        }
        for (int i=0; i <= smonth-2; i++) {
            td = td + arrayLDAY[i];
        }
        td = td + sday;
        int w = td % 7;

        String sweek = arrayWEEK[w];
        gf_lun2sol = 1;

        return String.valueOf(syear+String.format("%02d", smonth)+String.format("%02d", sday));
    }

    public static String sol2lun(String yyyymmdd) {
        int gf_sol2lun = 0;
        int gf_yun = 0;

        int getYEAR = Integer.parseInt(yyyymmdd.substring(0,4));
        int getMONTH = Integer.parseInt(yyyymmdd.substring(4,6));
        int getDAY = Integer.parseInt(yyyymmdd.substring(6,8));

        String arrayYUKSTR="甲-乙-丙-丁-戊-己-庚-辛-壬-癸";
        String[] arrayYUK = arrayYUKSTR.split("-");

        String arrayGAPSTR="子-丑-寅-卯-辰-巳-午-未-申-酉-戌-亥";
        String[] arrayGAP = arrayGAPSTR.split("-");

        String arrayDDISTR="쥐-소-범-토끼-용-뱀-말-양-원숭이-닭-개-돼지";
        String[] arrayDDI = arrayDDISTR.split("-");

        String arrayWEEKSTR="일-월-화-수-목-금-토";
        String[] arrayWEEK = arrayWEEKSTR.split("-");


        Long[] dt = new Long[LunarInfo.length];
        for (int i = 0; i < LunarInfo.length; i++) {
            dt[i] = Long.parseLong(LunarInfo[i]);
        }
        for (int i=0; i <= 168; i++) {
            dt[i] = Long.valueOf(0);
            for (int j=0;j < 12;j++) {
                switch (Integer.parseInt(LunarInfo[i].substring(j,j+1))) {
                    case 1:
                        dt[i] += 29;
                        break;
                    case 3:
                        dt[i] += 29;
                        break;
                    case 2:
                        dt[i] += 30;
                        break;
                    case 4:
                        dt[i] += 30;
                        break;
                }
            }

            switch (Integer.parseInt(LunarInfo[i].substring(12,13))) {
                case 0:
                    break;
                case 1:
                    dt[i] += 29;
                    break;
                case 3:
                    dt[i] += 29;
                    break;
                case 2:
                    dt[i] += 30;
                    break;
                case 4:
                    dt[i] += 30;
                    break;
            }
        }

        int td1 = 1880 * 365 + (int)(1880/4) - (int)(1880/100) + (int)(1880/400) + 30;
        int k11 = getYEAR - 1;

        int td2 = k11 * 365 + (int)(k11/4) - (int)(k11/100) + (int)(k11/400);

        if (getYEAR % 400 == 0 || getYEAR % 100 != 0 && getYEAR % 4 == 0) {
            arrayLDAY[1] = 29;
        } else {
            arrayLDAY[1] = 28;
        }

        if (getMONTH > 13) {
            gf_sol2lun = 0;
        }

        if (getDAY > arrayLDAY[getMONTH-1]) {
            gf_sol2lun = 0;
        }

        for (int i=0;i <= getMONTH-2;i++) {
            td2 += arrayLDAY[i];
        }

        td2 += getDAY;
        int td = td2 - td1 + 1;
        Long td0 = dt[0];

        int jcount = 0;
        int ryear = 0;
        int m1 = 0;
        int m2 = 0;
        int i = 0;
        for (i=0; i <= 168; i++) {
            if (td <= td0) {
                break;
            }
            td0 += dt[i + 1];
        }
        ryear = i + 1881;
        td0 -= dt[i];
        td -= td0;

        if (Integer.parseInt(LunarInfo[i].substring(12,13)) == 0) {
            jcount = 11;
        } else {
            jcount = 12;
        }

        for (int j=0;j <= jcount;j++) { // 달수 check, 윤달 > 2 (by harcoon)
            if (Integer.parseInt(LunarInfo[i].substring(j,j+1)) <= 2) {
                m2++;
                m1 = Integer.parseInt(LunarInfo[i].substring(j,j+1)) + 28;
                gf_yun = 0;
            } else {
                m1 = Integer.parseInt(LunarInfo[i].substring(j,j+1)) + 26;
                gf_yun = 1;
            }
            if (td <= m1) {
                break;
            }
            td = td - m1;
        }

        int k1=(ryear+6) % 10;
        String syuk = arrayYUK[k1];
        int k2=(ryear+8) % 12;
        String sgap = arrayGAP[k2];
        String sddi = arrayDDI[k2];

        gf_sol2lun = 1;

        return String.valueOf(ryear+String.format("%02d", m2)+String.format("%02d", td)+"|"+syuk+sgap+"년|"+sddi+"띠");
    }

}


샘플 코드

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.tistory.link2me.util.LunarCalendar;
import java.util.Calendar;

public class CalendarActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_calendar);

        System.out.println("이순신 음력 => 양력 : "+ Lunar2Solar.lun2sol("19961112"));
        System.out.println("홍길동 양력 => 음력 : "+ Lunar2Solar.sol2lun("20010125"));
    }

}
 


Logcat

I/System.out: 이순신 음력 => 양력 : 19961222
I/System.out: 홍길동 양력 => 음력 : 2001|01|02|신사년|뱀띠

이 음력/양력 변환 코드가 자바 버전으로 돌아다니는 것은 없는거 같다.

PHP 코드를 자바로 변환한 것이지만 나름 유용하게 사용할 수 있을 것으로 본다.


이 글이 도움 되었다면 XX 클릭 해주세요. 좋은 자료 올리는데 큰 힘이 됩니다.

블로그 이미지

Link2Me

,
728x90
MySQL 로 된 데이터를 SQLite 로 변환하기 위해서 assets 를 이용할 까 해봤는데, 데이터 크기가 5MB가 넘어서 어플의 용량이 커지는 문제가 발생하고 변환하는데 시간도 걸리고 해서 포기했다.

결국에는 양력/음력 변환을 제대로 해주는 메소드를 잘 만들거나, 기존에 만들어진 다른 코드를 변환해서 이용하는 방법이 최선인 거 같다.
제대로 된 Java 라이브러리가 어디에 있는데 못찾는 것인지, 공개를 안해서 못 구하는 것인지는 모르겠다.


자바에서 제공하는 라이브러리를 이용하여 변환하는 방법을 시도했다.
인터넷 검색하면 가장 많이 나오는 방법이다.


1. https://github.com/unicode-org/icu/releases/tag/release-4-8-2 에서 jar 파일을 다운로드 한다.

http://site.icu-project.org/download/48#ICU4J-Download 페이지의 중간 정도에 Version: 4.8.2
Release Date: 2019-04-11 라고 나온다.


2. 다운로드 받은 파일 icu4j-4_8_2.jar 를 libs 폴더로 복사하거나 옮긴다.


3. dependencies 에 추가한다.

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'gun0912.ted:tedpermission:2.0.0'
    implementation 'com.github.bumptech.glide:glide:3.8.0' // 이미지 라이브러리
    implementation files('libs/icu4j-4_8_2.jar')
}

* 자동 추가 방법은 Android Studio 왼쪽 메뉴에서 Project 로 선택하고 해당 파일에서 마우스 우클릭을 하면 Add As Library... 를 선택하여 추가하면 된다. 직접 dependencies 에 적어주고 Sync Now를 해도 된다.


4. 음력/양력 변환 코드

import com.ibm.icu.util.ChineseCalendar;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;

public class LunarCalendar {
    /**
     * 음력날짜를 양력날짜로 변환
     * @param 음력날짜 (yyyyMMdd)
     * @return 양력날짜 (yyyyMMdd)
     */
    public static String Lunar2Solar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim();
        if (date.length() != 8) {
            if (date.length() == 4)
                date = date + "0101";
            else if (date.length() == 6)
                date = date + "01";
            else if (date.length() > 8)
                date = date.substring(0, 8);
            else
                return "";
        }

        cc.set(ChineseCalendar.EXTENDED_YEAR, Integer.parseInt(date.substring(0, 4)) + 2637);
        cc.set(ChineseCalendar.MONTH, Integer.parseInt(date.substring(4, 6)) - 1);
        cc.set(ChineseCalendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)));

        cal.setTimeInMillis(cc.getTimeInMillis());

        int y = cal.get(Calendar.YEAR);
        int m = cal.get(Calendar.MONTH) + 1;
        int d = cal.get(Calendar.DAY_OF_MONTH);

        StringBuffer ret = new StringBuffer();
        ret.append(String.format("%04d", y));
        ret.append(String.format("%02d", m));
        ret.append(String.format("%02d", d));

        return ret.toString();
    }

    /**
     * 양력날짜를 음력날짜로 변환
     * @param 양력날짜 (yyyyMMdd)
     * @return 음력날짜 (yyyyMMdd)
     */
    public static String Solar2Lunar(String yyyymmdd) {
        ChineseCalendar cc = new ChineseCalendar();
        Calendar cal = Calendar.getInstance();

        if (yyyymmdd == null)
            return "";

        String date = yyyymmdd.trim() ;
        if( date.length() != 8 ) {
            if( date.length() == 4 )
                date = date + "0101" ;
            else if( date.length() == 6 )
                date = date + "01" ;
            else if( date.length() > 8 )
                date = date.substring(0,8) ;
            else
                return "" ;
        }

        cal.set( Calendar.YEAR, Integer.parseInt(date.substring(0,4)) ) ;
        cal.set( Calendar.MONTH, Integer.parseInt(date.substring(4,6))-1 ) ;
        cal.set( Calendar.DAY_OF_MONTH, Integer.parseInt(date.substring(6)) ) ;

        cc.setTimeInMillis( cal.getTimeInMillis() ) ;

        // ChinessCalendar.YEAR 는 1~60 까지의 값만 가지고 ,
        // ChinessCalendar.EXTENDED_YEAR 는 Calendar.YEAR 값과 2637 만큼의 차이를 가진다.
        int y = cc.get(ChineseCalendar.EXTENDED_YEAR)-2637 ;
        int m = cc.get(ChineseCalendar.MONTH)+1 ;
        int d = cc.get(ChineseCalendar.DAY_OF_MONTH) ;

        StringBuffer ret = new StringBuffer() ;
        if( y < 1000 )          ret.append( "0" ) ;
        else if( y < 100 )      ret.append( "00" ) ;
        else if( y < 10 )       ret.append( "000" ) ;
        ret.append( y ) ;

        if( m < 10 ) ret.append( "0" ) ;
        ret.append( m ) ;

        if( d < 10 ) ret.append( "0" ) ;
        ret.append( d ) ;

        return ret.toString() ;
    }
}


5. 샘플 코드

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.tistory.link2me.util.LunarCalendar;
import java.util.Calendar;

public class CalendarActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_calendar);

        System.out.println( LunarCalendar.Solar2Lunar("20010715") ) ; // 양력을 음력으로 바꾸기
        System.out.println( LunarCalendar.Lunar2Solar("20010527") ) ; // 음력을 양력으로 바꾸기
    }


이 라이브러리를 이용하는 방법은 간단하기는 한데 정교한 맛이 없는 거 같다.

윤달인지 여부, 간지 등을 표현할 수가 없는 점이 좀 아쉽다.


그래서 구글 검색을 하여 Java Lunar Calendar (Lunar Calendar) Tools 를 찾아봤더니

https://www.cnblogs.com/harry335/p/4861598.html 가 검색된다.

이걸 테스트해봤더니 음력일자 결과가 제대로 반환되지 않고 하루가 틀리게 나온다.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Ca03 {
    private int year;
    private int month;
    private int day;
    private boolean leap;
    final static String chineseNumber[] = { "1", "2", "3", "4", "5", "6", "7","8", "9", "10", "11", "12" };
    static SimpleDateFormat chineseDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
    final static long[] lunarInfo = new long[] { 0x04bd8, 0x04ae0, 0x0a570,
            0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
            0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0,
            0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50,
            0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566,
            0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0,
            0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4,
            0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550,
            0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950,
            0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260,
            0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0,
            0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
            0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40,
            0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3,
            0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960,
            0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0,
            0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9,
            0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0,
            0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65,
            0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0,
            0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2,
            0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0 };

    // ====== 传回农历 y年的总天数
    final private static int yearDays(int y) {
        int i, sum = 348;
        for (i = 0x8000; i > 0x8; i >>= 1) {
            if ((lunarInfo[y - 1900] & i) != 0)
                sum += 1;
        }
        return (sum + leapDays(y));
    }

    // ====== 传回农历 y年闰月的天数
    final private static int leapDays(int y) {
        if (leapMonth(y) != 0) {
            if ((lunarInfo[y - 1900] & 0x10000) != 0)
                return 30;
            else
                return 29;
        } else
            return 0;
    }

    // ====== 传回农历 y年闰哪个月 1-12 , 没闰传回 0
    final private static int leapMonth(int y) {
        return (int) (lunarInfo[y - 1900] & 0xf);
    }

    // ====== 传回农历 y年m月的总天数
    final private static int monthDays(int y, int m) {
        if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0)
            return 29;
        else
            return 30;
    }

    // ====== 传回农历 y年的生肖
    final public String animalsYear() {
        final String[] Animals = new String[] { "쥐", "소", "호랑이", "토끼", "용", "뱀", "말", "양", "원숭이", "닭", "개", "돼지"};
        return Animals[(year - 4) % 12];
    }

    // ====== 传入 月日的offset 传回干支, 0=甲子
    final private static String cyclicalm(int num) {
        final String[] Gan = new String[] { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };
        final String[] Zhi = new String[] { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };
        return (Gan[num % 10] + Zhi[num % 12]);
    }

    // ====== 传入 offset 传回干支, 0=甲子
    final public String cyclical() {
        int num = year - 1900 + 36;
        return (cyclicalm(num));
    }

    public Ca03(Calendar cal) {
        int yearCyl, monCyl, dayCyl;
        int leapMonth = 0;
        Date baseDate = null;
        try {
            baseDate = chineseDateFormat.parse("1900年1月31日");
        } catch (ParseException e) {
            e.printStackTrace(); // To change body of catch statement use
                                    // Options | File Templates.
        }

        // 求出和1900年1月31日相差的天数
        int offset = (int) ((cal.getTime().getTime() - baseDate.getTime()) / 86400000L);
        dayCyl = offset + 40;
        monCyl = 14;

        // 用offset减去每农历年的天数
        // 计算当天是农历第几天
        // i最终结果是农历的年份
        // offset是当年的第几天
        int iYear, daysOfYear = 0;
        for (iYear = 1900; iYear < 2050 && offset > 0; iYear++) {
            daysOfYear = yearDays(iYear);
            offset -= daysOfYear;
            monCyl += 12;
        }
        if (offset < 0) {
            offset += daysOfYear;
            iYear--;
            monCyl -= 12;
        }
        // 农历年份
        year = iYear;

        yearCyl = iYear - 1864;
        leapMonth = leapMonth(iYear); // 闰哪个月,1-12
        leap = false;

        // 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
        int iMonth, daysOfMonth = 0;
        for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
            // 闰月
            if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
                --iMonth;
                leap = true;
                daysOfMonth = leapDays(year);
            } else
                daysOfMonth = monthDays(year, iMonth);

            offset -= daysOfMonth;
            // 解除闰月
            if (leap && iMonth == (leapMonth + 1))
                leap = false;
            if (!leap)
                monCyl++;
        }
        // offset为0时,并且刚才计算的月份是闰月,要校正
        if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
            if (leap) {
                leap = false;
            } else {
                leap = true;
                --iMonth;
                --monCyl;
            }
        }
        // offset小于0时,也要校正
        if (offset < 0) {
            offset += daysOfMonth;
            --iMonth;
            --monCyl;
        }
        month = iMonth;
        day = offset + 1;
    }

    public static String getChinaDayString(int day) {
        String chineseTen[] = { "初", "十", "廿", "卅" };
        int n = day % 10 == 0 ? 9 : day % 10 - 1;
        if (day > 30)
            return "";
        if (day == 10)
            return "初十";
        else
            return chineseTen[day / 10] + chineseNumber[n];
    }

    public String toString() {
        return year + "年 " + (leap ? "闰" : "") + chineseNumber[month - 1] + "月 " + day +"日";
    }
   
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        cal.setTimeZone(TimeZone.getDefault());
        System.out.println("양력(오늘):" + sdf.format(cal.getTime()));
       
        int year = 2001;
        int month = 1;
        int date = 25;
        cal.set(year, month-1, date);

        Ca03 lunar = new Ca03(cal);
        System.out.print("음력(오늘):");
        System.out.print(lunar.year + "年 ");
        System.out.print(lunar.month + "月 ");
        System.out.print(lunar.day+"日");

        System.out.println("\n*************\n" + lunar.animalsYear());
        System.out.println(lunar.cyclical());
        System.out.println(lunar);
    }

}



PHP 로 된 만세력 변환 코드가 올려져 있는 거 같다.

https://github.com/OOPS-ORG-PHP/Lunar/releases

이 코드를 변환해 보지는 않았다.


다음에는 PHP 에서 음력/양력 변환하는 코드를 자바로 변환해본 것을 올릴 것이다.

블로그 이미지

Link2Me

,