본문 바로가기
안드로이드 코틀린

[Kotlin][Android] 달력 만들기

by teamnova 2021. 6. 17.

안드로이드에서 제공하는 calendarview를 사용하여 달력을 만들고 특정 날짜에 일정을 파일에 저장하는 기능을 사용하도록 하겠습니다

 

calendarview 란?

안드로이드에서 제공하는 달력 위젯 입니다.

 

참고)

developer.android.com/reference/android/widget/CalendarView


먼저 예제에 사용할 화면을 만들어보겠습니다.

 

 

<activity_main.xml>

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <CalendarView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/calendarView"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginLeft="8dp" android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
        app:layout_constraintHorizontal_bias="0.488" android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/title"/>

    <TextView
        android:id="@+id/diaryTextView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity= "center"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@+id/calendarView" app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
        app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp" android:textAppearance="@style/TextAppearance.AppCompat.Large"/>

    <EditText
        android:id="@+id/contextEditText"
        android:layout_width="0dp"
        android:layout_height="116dp"
        android:inputType="textMultiLine"
        android:ems="10"
        app:layout_constraintTop_toBottomOf="@+id/diaryTextView" android:layout_marginTop="16dp"
        android:hint="내용을 입력하세요." android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginRight="8dp" android:layout_marginStart="8dp"
        app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp"
        android:visibility="invisible"/>

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/diaryContent"
        app:layout_constraintTop_toTopOf="@+id/contextEditText"
        app:layout_constraintBottom_toBottomOf="@+id/contextEditText"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
        app:layout_constraintStart_toStartOf="@+id/contextEditText" android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp" android:visibility="invisible"/>

    <TextView
        android:id="@+id/title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:gravity="center"
        android:text="달력일기장"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        android:textColor="#9E28B3"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:text="저장"
        android:layout_width="0dp"
        android:layout_marginBottom="20dp"
        android:layout_height="wrap_content"
        android:id="@+id/saveBtn"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@+id/contextEditText"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp"
        android:visibility="invisible"/>

    <Button
        android:text="수정"
        android:layout_width="180dp"
        android:layout_height="wrap_content"
        android:id="@+id/updateBtn"
        app:layout_constraintBaseline_toBaselineOf="@+id/saveBtn"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp"
        app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:visibility="invisible"/>

    <Button
        android:text="삭제"
        android:layout_width="176dp"
        android:layout_height="wrap_content"
        android:id="@+id/deleteBtn"
        app:layout_constraintBaseline_toBaselineOf="@+id/updateBtn"
        app:layout_constraintEnd_toEndOf="@+id/saveBtn"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:visibility="invisible">
    </Button>

</androidx.constraintlayout.widget.ConstraintLayout>

 


다음 예제에 사용할 코드를 작성하겠습니다.

 

<MainActivity.kt>

 

1. 예제에 사용할 UI 정보 값 생성

 

ui 정보를 수정하기 위해 xml에 생성해둔 ui 정보를 호출해서 변수로 생성합니다.

 

 

import android.os.Bundle
import android.widget.Button
import android.widget.CalendarView
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {
    var userID: String = "userID"
    lateinit var fname: String
    lateinit var str: String
    lateinit var calendarView: CalendarView
    lateinit var updateBtn: Button
    lateinit var deleteBtn:Button
    lateinit var saveBtn:Button
    lateinit var diaryTextView: TextView
    lateinit var diaryContent:TextView
    lateinit var title:TextView
    lateinit var contextEditText: EditText


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // UI값 생성
        calendarView=findViewById(R.id.calendarView)
        diaryTextView=findViewById(R.id.diaryTextView)
        saveBtn=findViewById(R.id.saveBtn)
        deleteBtn=findViewById(R.id.deleteBtn)
        updateBtn=findViewById(R.id.updateBtn)
        diaryContent=findViewById(R.id.diaryContent)
        title=findViewById(R.id.title)
        contextEditText=findViewById(R.id.contextEditText)

        title.text = "달력 일기장"
    }
}

 

2. 달력 클릭 이벤트 추가

달력에 일정을 관리하기 위해 이벤트를 추가합니다.

 

스틱코드를 활용한다면, 클래스에서 'cale' 까지만 작성했을 때 '달력 클릭 이벤트 추가' 이벤트가 나타납니다.

 

 

'달력 클릭 이벤트 추가' 이벤트를 누를 경우 코드가 자동으로 완성됩니다.

코드가 완성되면 아래 사진과 같이 에러가 발생할텐데, 이 부분은 다음 코드를 추가하면 해결됩니다.

 

 

3. 달력 기능 추가

달력 관리 하기 위해 기능을 추가합니다.

 

스틱코드를 활용한다면, 클래스에서 'calf' 까지만 작성했을 때 '달력 조회, 수정, 추가, 삭제 기능' 이벤트가 나타납니다.

 

 

'달력 조회, 수정, 추가, 삭제 기능' 이벤트를 누를 경우 코드가 자동으로 완성됩니다.

코드들이 생기면서 에러가 사라지는 것을 확인할 수 있습니다.

 

 

<최종코드>

import android.annotation.SuppressLint
import java.io.FileInputStream
import java.io.FileOutputStream

import android.view.View
import android.os.Bundle
import android.widget.Button
import android.widget.CalendarView
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity



class MainActivity : AppCompatActivity() {
    var userID: String = "userID"
    lateinit var fname: String
    lateinit var str: String
    lateinit var calendarView: CalendarView
    lateinit var updateBtn: Button
    lateinit var deleteBtn:Button
    lateinit var saveBtn:Button
    lateinit var diaryTextView: TextView
    lateinit var diaryContent:TextView
    lateinit var title:TextView
    lateinit var contextEditText: EditText


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // UI값 생성
        calendarView=findViewById(R.id.calendarView)
        diaryTextView=findViewById(R.id.diaryTextView)
        saveBtn=findViewById(R.id.saveBtn)
        deleteBtn=findViewById(R.id.deleteBtn)
        updateBtn=findViewById(R.id.updateBtn)
        diaryContent=findViewById(R.id.diaryContent)
        title=findViewById(R.id.title)
        contextEditText=findViewById(R.id.contextEditText)

        title.text = "달력 일기장"

        calendarView.setOnDateChangeListener { view, year, month, dayOfMonth ->
            diaryTextView.visibility = View.VISIBLE
            saveBtn.visibility = View.VISIBLE
            contextEditText.visibility = View.VISIBLE
            diaryContent.visibility = View.INVISIBLE
            updateBtn.visibility = View.INVISIBLE
            deleteBtn.visibility = View.INVISIBLE
            diaryTextView.text = String.format("%d / %d / %d", year, month + 1, dayOfMonth)
            contextEditText.setText("")
            checkDay(year, month, dayOfMonth, userID)
        }

        saveBtn.setOnClickListener {
            saveDiary(fname)
            contextEditText.visibility = View.INVISIBLE
            saveBtn.visibility = View.INVISIBLE
            updateBtn.visibility = View.VISIBLE
            deleteBtn.visibility = View.VISIBLE
            str = contextEditText.text.toString()
            diaryContent.text = str
            diaryContent.visibility = View.VISIBLE
        }
    }

    // 달력 내용 조회, 수정
    fun checkDay(cYear: Int, cMonth: Int, cDay: Int, userID: String) {
        //저장할 파일 이름설정
        fname = "" + userID + cYear + "-" + (cMonth + 1) + "" + "-" + cDay + ".txt"

        var fileInputStream: FileInputStream
        try {
            fileInputStream = openFileInput(fname)
            val fileData = ByteArray(fileInputStream.available())
            fileInputStream.read(fileData)
            fileInputStream.close()
            str = String(fileData)
            contextEditText.visibility = View.INVISIBLE
            diaryContent.visibility = View.VISIBLE
            diaryContent.text = str
            saveBtn.visibility = View.INVISIBLE
            updateBtn.visibility = View.VISIBLE
            deleteBtn.visibility = View.VISIBLE
            updateBtn.setOnClickListener {
                contextEditText.visibility = View.VISIBLE
                diaryContent.visibility = View.INVISIBLE
                contextEditText.setText(str)
                saveBtn.visibility = View.VISIBLE
                updateBtn.visibility = View.INVISIBLE
                deleteBtn.visibility = View.INVISIBLE
                diaryContent.text = contextEditText.text
            }
            deleteBtn.setOnClickListener {
                diaryContent.visibility = View.INVISIBLE
                updateBtn.visibility = View.INVISIBLE
                deleteBtn.visibility = View.INVISIBLE
                contextEditText.setText("")
                contextEditText.visibility = View.VISIBLE
                saveBtn.visibility = View.VISIBLE
                removeDiary(fname)
            }
            if (diaryContent.text == null) {
                diaryContent.visibility = View.INVISIBLE
                updateBtn.visibility = View.INVISIBLE
                deleteBtn.visibility = View.INVISIBLE
                diaryTextView.visibility = View.VISIBLE
                saveBtn.visibility = View.VISIBLE
                contextEditText.visibility = View.VISIBLE
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }


    // 달력 내용 제거
    @SuppressLint("WrongConstant")
    fun removeDiary(readDay: String?) {
        var fileOutputStream: FileOutputStream
        try {
            fileOutputStream = openFileOutput(readDay, MODE_NO_LOCALIZED_COLLATORS)
            val content = ""
            fileOutputStream.write(content.toByteArray())
            fileOutputStream.close()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }


    // 달력 내용 추가
    @SuppressLint("WrongConstant")
    fun saveDiary(readDay: String?) {
        var fileOutputStream: FileOutputStream
        try {
            fileOutputStream = openFileOutput(readDay, MODE_NO_LOCALIZED_COLLATORS)
            val content = contextEditText.text.toString()
            fileOutputStream.write(content.toByteArray())
            fileOutputStream.close()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }
}

프로젝트를 실행하면 아래 사진같이 달력이 나오는것을 확인할 수 있습니다.