GDSC HUFS 4기/Kotlin Team #2

[2팀] 코틀린으로 분 단위 계산기 만들기

ヽ(°ᴥ°)ノ 2022. 10. 26. 21:41

 

이 글은 유데미 강의 Android 12 및 Kotlin 개발 완전 정복을 참고하여 작성하였습니다.

작성자 : 유희수

개발환경은 Windows, Android Studio입니다. 

 

 

이번 글에서 다룰 것 : 분 단위 계산기를 만드는 전 과정

 

 

 

 # 전체적인 폴더 구조

● res/layout/activity_main.xml : 앱의 레이아웃 즉, 화면의 배치를 전담하는 파일 (버튼, 이미지, 텍스트 입력창 등)

   ▷레이아웃 xml로 화면 구성 : 화면을 구성하는데 필요한 요소들을 xml의 태그로 명시해 화면 구성하는 방법

   ▷ 디자인탭으로 화면 구성 : 위 사진의 오른쪽처럼 직접 요소를 끌어서 배치하는 방법 

   

 java/com/example/dobcalc/MainActivity.kt : xml에 배치된 요소들에 기능을 구현하는 부분

 


1. 앱 기초 UI 구성하기

 

# LinearLayout 

: 레이아웃 클래스에 다른 뷰를 포함해 화면 구성

: LinearLayout은 안드로이드 화면 구성을 x축이나 y축 방향으로 나열할 때 사용

  • android:gravity="center_horizontal" : 하위 뷰들에 대한 배치 방향을 x축 중앙 정렬되게
  • android:orientation="vertical" : 하위 뷰들을 수직 방향으로 배치
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity"
        android:orientation="vertical"
        android:background="@color/bgColor"
        android:gravity="center_horizontal"
        android:padding="16dp" >
        
        <TextView ... />
        <Button ... />
        <TextView ... />
        
</LinearLayout>

 

# TextView : 문자열을 출력하는 뷰

(디자인 탭에서) textview 클릭후 attributes에서 id, text, color 등 여러가지 속성 설정 가능

 

# Button : 이벤트 처리를 위한 클릭 기능이 포함되어 있는 뷰

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="IN MINIUTES"
        android:textColor="#264653"
        android:textSize="25sp"
        android:textStyle="bold"
        android:layout_marginTop="16dp" />
<Button
        android:text="SELECT DATE"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnDatePicker"
        android:textColor="@color/lightgray"
        android:backgroundTint="@color/btnBgColor"/>
  • layout_width, layout_height
    • wrap_content : 포함된 내용(글자 or 이미지크기)을 보여줄 수 있는 크기로 설정
    • match_parent : 부모 뷰의 길이에 맞춤
      • Button의 경우 가로가 match_parent 라서 화면 전체 넓이에 맞춰서 버튼 크기가 지정됨
  • id 
    • id를 설정해서 기능 구현할때 view에 접근해야할 때 id로 보통 접근
    • XML 파일에서 :   android:id = "@+id/button_name"
    • Kt 파일에서 : 위젯 변수 = (위젯 명) findViewById(R.id.위젯_id);
  • textColor, backgroundColor .. 
    • 직접 color 원하는대로 설정 가능
    • 자주 쓰는 색깔의 경우 res/values/colors.xml 파일에 저장해서 사용
      • <color name="lightgray">#b7b7a4</color>
  • padding, margin

  • dp, sp, px 
    • px : 전체 화면의 크기와 상관없이 지정한 pixel만큼 표시되는 절대적 표시 단위이다.
    • dp : 다양한 기기에서 UI 요소의 실제 크기에 일관성을 부여
    • sp : dp 단위와 같지만, 사용자의 글꼴 크기 환경설정에 의해 확장되기도 합니다
  • res/values/strings.xml : app_name 수정 가능

2. MainActivity.kt에서 기능 구현

 

# DatePickerDialog : 달력 dialog 생성

  • 2번째 파라미터로 OnDateSetListener로 달력에서 날짜를 선택했을 시 동작을 선언해주며 '년/월/일'을 '${selectedYear}/${selectedMonth+1}/${selectedDayOfMonth}'로 읽을 수 있다.
  • 이때 월을 나타내는 m은 0~11까지의 수를 갖기 때문에 +1을 해줘야 월을 제대로 얻을 수 있다
  • 3~5번째 파라미터로 준 year, month, day는 달력 호출 시 디폴트로 선택되어 있는 날짜를 지정하며 밑에 코드처럼 Calendar 인스턴스를 생성해 현재 날짜를 디폴트 값으로 주게 했다
val myCalender = Calendar.getInstance() // 캘린더 인스턴스 생성
val year = myCalender.get(Calendar.YEAR) // 현재 날짜 가져오기
val month = myCalender.get(Calendar.MONTH)
val day = myCalender.get(Calendar.DAY_OF_MONTH)

val dpd = DatePickerDialog(this,
            DatePickerDialog.OnDateSetListener{ view, selectedYear, selectedMonth, selectedDayOfMonth ->
            ...
            },
            year,
            month,
            day
            )

 

# 버튼 클릭시 Toast 메세지 띄우기

Toast.makeText(this,
    "btnDataPicker pressed", Toast.LENGTH_LONG).show() // 나타낼 문자와 얼마나 길게 메세지를 보여줄 것인지

 

 

class MainActivity : AppCompatActivity() {

    var tvSelectedDate : TextView? = null
    var tvAgeInMinutes : TextView? = null

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

        val btnDatePicker : Button = findViewById(R.id.btnDatePicker)
        tvSelectedDate = findViewById(R.id.tvSelectedDate)
        tvAgeInMinutes = findViewById(R.id.tvAgeInMinutes)

        btnDatePicker.setOnClickListener {
            clickDatePicker()
        }
    }

    fun clickDatePicker(){

        val myCalender = Calendar.getInstance()
        val year = myCalender.get(Calendar.YEAR)
        val month = myCalender.get(Calendar.MONTH)
        val day = myCalender.get(Calendar.DAY_OF_MONTH)

        val dpd = DatePickerDialog(this,
            DatePickerDialog.OnDateSetListener{ view, selectedYear, selectedMonth, selectedDayOfMonth ->
                Toast.makeText(this,
                    "btnDataPicker pressed", Toast.LENGTH_LONG).show()

                val selectedDate = "${selectedYear}/${selectedMonth+1}/${selectedDayOfMonth}"

                //tvSelectedDate?.setText(selectedDate)
                tvSelectedDate?.text = selectedDate // view 요소에 선택된 날짜를 보여줄 수 있도록 text 변경

                val sdf = SimpleDateFormat("yyyy/MM/dd", Locale.KOREAN) // 원하는 날짜 형식 포맷 작성

                val theDate = sdf.parse(selectedDate) // string -> (SimpleDateFormat) -> Date 형식으로 변경
                
                theDate?.let { // 이렇게하면 날짜 선택 여부를 확인하고 날짜가 선택됐을때만 실행됨
                    
                    // millisecond 기준이기 때문에 분 단위를 구하려면 1000(1초)*60 -> 1분
                    val selectedDateInMinutes = theDate.time / 60000 

                    val currentDate = sdf.parse(sdf.format(System.currentTimeMillis()))
                    
                    currentDate?.let {
                        val currentDateInMinutes = currentDate.time / 60000

                        val differenceInMinutes = currentDateInMinutes - selectedDateInMinutes

                        tvAgeInMinutes?.text = differenceInMinutes.toString()
                    }

                }

            },
            year,
            month,
            day
            )
		
        // 미래의 값은 선택할 수 없도록 설정
        dpd.datePicker.maxDate = System.currentTimeMillis() - 86400000 // 86400000 : 1일
        dpd.show()


    }
}