이 글은 이후에 캘린더 관련 기능을 추가할 때 스스로 참고하기 위해 만든 글이다. 코드의 주석과 간단한 설명은 있지만 자세한 내요은 없다.
<MonthItem.java\>
package io.swnomad.samplecalendar;
public class MonthItem {
private int dayValue;
MonthItem(int dayValue){
this.dayValue = dayValue;
}
public int getDay(){
return dayValue;
}
}
아이템의 데이터를 위한 클래스이다. 달력이므로 간단하게 날짜 값 하나만을 멤버로 가진다.
<MonthItemView.java\>
package io.swnomad.samplecalendar;
import android.content.Context;
import android.graphics.Color;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import android.view.Gravity;
public class MonthItemView extends AppCompatTextView {
private MonthItem item;
public MonthItemView(Context context){
super(context);
init();
}
public MonthItemView(Context context, AttributeSet attrs){
super(context, attrs);
init();
}
private void init(){
setBackgroundColor(Color.WHITE);
}
public MonthItem getItem(){
return item;
}
public void setItem(MonthItem item){
this.item = item;
int day = item.getDay();
if (day != 0) {
setText(String.valueOf(day));
setGravity(Gravity.CENTER);
setTextColor(Color.BLACK);
setTextSize(20);
}else{
setText("");
}
}
}
그리드뷰의 각 아이템의 뷰를 위한 코드이다. 단지 텍스트뷰로 충분하므로 XML 레이아웃 파일은 따로 만들지 않았다.
<MonthAdapter.java\>
package io.swnomad.samplecalendar;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import java.util.Calendar;
public class MonthAdapter extends BaseAdapter {
Calendar cal;
Context mContext;
MonthItem[] items;
int curYear;
int curMonth;
MonthAdapter(Context context){
super();
mContext = context;
init();
}
MonthAdapter(Context context, AttributeSet attrs){
super();
mContext = context;
init();
}
public void init(){
cal = Calendar.getInstance(); //Calendar 객체 가져오기
items = new MonthItem[7*6]; //아이템 크기 결정
calculate();//날짜 계산해서 items[] 배열 값 설정
}
public void calculate(){
for(int i=0; i<items.length; i++){ //items[] 모든 값 0으로 초기화
items[i] = new MonthItem(0);
}
cal.set(Calendar.DAY_OF_MONTH, 1); //1일로 설정
int startDay = cal.get(Calendar.DAY_OF_WEEK); //현재 달 1일의 요일 (1: 일요일, . . . 7: 토요일)
int lastDay = cal.getActualMaximum(Calendar.DATE); //달의 마지막 날짜
int cnt = 1;
for(int i=startDay-1; i<startDay-1+lastDay; i++){ /* 1일의 요일에 따라 시작위치 다르고 마지막 날짜까지 값 지정*/
items[i] = new MonthItem(cnt);
cnt++;
}
curYear = cal.get(Calendar.YEAR);
curMonth = cal.get(Calendar.MONTH);
}
public void setPreviousMonth(){ //한 달 앞으로 가고 다시 계산
cal.add(Calendar.MONTH, -1);
calculate();
}
public void setNextMonth(){
cal.add(Calendar.MONTH, 1); //한 달 뒤로가고 다시 계산
calculate();
}
@Override
public int getCount() {
return items.length;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MonthItemView view = new MonthItemView(mContext);
MonthItem item = items[position];
view.setItem(item); //날짜 값이 0이면 ""으로, 아니면 날짜값으로 TextView의 Text 지정
if(position%7==0){ //일요일은 날짜 색 빨간색으로
view.setTextColor(Color.RED);
}
GridView.LayoutParams params = new GridView.LayoutParams( GridView.LayoutParams.MATCH_PARENT,150);
view.setLayoutParams(params);
return view; //뷰 뿌려주기
}
@Override
public Object getItem(int position) {
return items[position];
}
@Override
public long getItemId(int position) {
return 0;
}
public int getCurYear(){
return curYear;
}
public int getCurMonth(){
return curMonth;
}
}
가장 중요한 어댑터 클래스이다. 생성자가 만들어질 때 init()
메소드를 통해 초기화를 해준다.
init()
메소드는 Calendar.getInstance()
메소드를 통해서 캐린더 객체를 참조해주고 items[] 배열의 크기를 초기화한다. 그리고 처음으로 calculate()
메소드를 통해 달력 화면 초기화를 해주어야한다.
calculate()
메소드의 구성은 아래와 같다.
- 어댑터의 데이터 자료인 items[] 배열의 모든 값을 0으로 초기화
- 달의 1일의 요일과 달의 마지막 날짜를 가져와서 계산을 통해 배열에 날짜를 집어넣는다.
나름대로 머리를 써서 만든 날짜 계산 방식인데, 더 효율적인 방법을 찾아보면 최적화가 더 될 것 같다.
그리고 setPreviousMonth()
메소드와 setNextMonth()
메소드는 이전달과 다음달로 달력의 화면을 전환시키는 메소드이다. 코드 내부를 보면 달을 넘기는 Calendar.add()
메소드가 호출된다. 이어서 calculate()
메소드를 통해 넘어온 달에서 다시 날짜를 계산한다.
위에서 계산된 날짜를 토대로 getView()
메소드에서는 뷰를 구성하여 화면에 뿌린다. 이 메소드 내에서 뷰의 setItem()
메소드를 호출하는데 이 메소드는 날짜가 0이냐 아니냐에 따라 텍스트를 설정한다.
그리고 텍스트뷰의 크기를 세팅하고 일요일은 빨간색으로 표시하기위한 코드도 추가되었다.
<activity_main.xml\>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffffff">
<Button
android:id="@+id/monthPrevious"
android:layout_width="100dp"
android:layout_height="30dp"
android:gravity="center_horizontal"
android:layout_alignParentLeft="true"
android:background="@drawable/back"/>
<TextView
android:id="@+id/monthText"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="MonthView"
android:textSize="24dp"
android:textStyle="bold"
android:gravity="center_horizontal"
android:layout_centerInParent="true"/>
<Button
android:id="@+id/monthNext"
android:layout_width="100dp"
android:layout_height="30dp"
android:gravity="center_horizontal"
android:layout_alignParentRight="true"
android:background="@drawable/next"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#22000000">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="일"
android:textSize="30dp"
android:textStyle="bold"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="월"
android:textSize="30dp"
android:textStyle="bold"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="화"
android:textSize="30dp"
android:textStyle="bold"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="수"
android:textSize="30dp"
android:textStyle="bold"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="목"
android:textSize="30dp"
android:textStyle="bold"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="금"
android:textSize="30dp"
android:textStyle="bold"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="토"
android:textSize="30dp"
android:textStyle="bold"
android:gravity="center_horizontal"/>
</LinearLayout>
<GridView
android:id="@+id/monthView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="7"
android:stretchMode="columnWidth"
android:horizontalSpacing="1dp"
android:verticalSpacing="1dp"
android:background="#ffbbbbbb">
</GridView>
</LinearLayout>
가장 상위에는 양옆에 이전달과 다음달로 넘어가기 위한 버튼 2개가 있고 가운데에는 현재 년도와 달을 표시하는 텍스트뷰가 있다. 그리고 요일을 표시하기 위한 텍스트뷰 7개, 마지막으로 가장 중요한 그리드뷰가 있다.
이제 이 그리드뷰에 어댑터를 설정하여 화면을 구성하기 위한 코드를 보겠다.
<MainActivity.java\>
package io.swnomad.samplecalendar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.GridView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
GridView monthView;
TextView monthText;
MonthAdapter adt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
monthView = findViewById(R.id.monthView); //그리드뷰 객체 참조
adt = new MonthAdapter(this); //어댑터 객체 생성
monthView.setAdapter(adt); //그리드뷰에 어댑터 설정
monthText = findViewById(R.id.monthText);
setMonthText();
Button monthPrevious = findViewById(R.id.monthPrevious);
Button monthNext = findViewById(R.id.monthNext);
// 뒤로가기 버튼 이벤트 리스너 설정
monthPrevious.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adt.setPreviousMonth();
adt.notifyDataSetChanged(); //어댑터 데이터 갱신하고 뷰 다시 뿌리기
setMonthText();
}
});
// 앞으로 가기 버튼에 이벤트 리스너 설정
monthNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adt.setNextMonth();
adt.notifyDataSetChanged(); //어댑터 데이터 갱신하고 뷰 다시 뿌리기
setMonthText();
}
});
}
public void setMonthText(){
int curYear = adt.getCurYear();
int curMonth = adt.getCurMonth();
monthText.setText(curYear+"년 "+(curMonth+1)+"월");
}
}
그리드 뷰에 어댑터를 설정했다. 이어서 이전 달과 다음 달로 넘어가기 위한 버튼에 클릭 이벤트 리스너 객체를 설정해주었다. 버튼을 누르면 달을 넘기는 메소드가 호출이 된다. 이어서 notifyDataSetChanged()
메소드가 호출된다. 이 메소드는 어댑터의 데이터를 최신으로 갱신하고 다시 그리드뷰에 뷰를 뿌려주는 메소드이다. 이것을 하지 않으면 새로 갱신된 달력의 데이터와 그에따른 새로운 화면을 가져올 수 없으므로 꼭 추가해주어야한다. 마지막으로 setTextMonth()
메소드를 통해 위에있는 텍슽뷰에 현재의 년도와 달을 표시해준다.
아래는 앱의 실행 화면이다.
'안드로이드' 카테고리의 다른 글
(안드로이드) 43 - 그래픽(Graphic)2 - 비트맵(Bitmap) (0) | 2020.05.19 |
---|---|
(안드로이드) 42 - 그래픽(Graphic) 1 - Canvas, Paint, Drawable (0) | 2020.05.19 |
(안드로이드) 40 - 복합 위젯(Composite Widget) (0) | 2020.05.19 |
(안드로이드) 39 - 그리드뷰(GridView) (0) | 2020.05.19 |
(안드로이드) 38 - 스피너(Spinner) (0) | 2020.05.19 |