문제 URL : https://solvesql.com/problems/summary-of-artworks-in-3-years/

 

https://solvesql.com/problems/summary-of-artworks-in-3-years/

 

solvesql.com

 

* 문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

select classification,
sum(case when strftime('%Y', acquisition_date) = '2014' then 1 else 0 end) as '2014',
sum(case when strftime('%Y', acquisition_date) = '2015' then 1 else 0 end) as '2015',
sum(case when strftime('%Y', acquisition_date) = '2016' then 1 else 0 end) as '2016'
from artworks
group by classification
order by classification

 

문제를 풀면서 2014, 2015, 2016 컬럼을 구하는 계산이 각각 다른데, 이 부분을 어떻게 하나의 테이블 안에 표시할 것인가에 대해 고민했음.

각 컬럼에 대한 값을 구한 별개의 테이블을 만들어서 조인을 해야하나 싶었지만, 그러면 독립된 테이블이 3개여서 쿼리문도 좀 지저분하고 '이게 맞나'싶을 것 같아서 ..

근데 생각해보니까 이미 문제명에 답이 있었음. 집계 함수 쓰면 해결될 것을 ....

 

CASE WHEN 구문은 조건에 따른 값을 활용하여 새로운 열 생성 / 열 집계를 도움 (여기선 열 집계의 역할로 사용)

이것과 집계 함수 SUM을 활용하면 문제 내 필요한 값들을 구할 수 있음

조건에 해당하면 카운팅 +1이 되는 식으로 구현함

문제 URL : https://solvesql.com/problems/max-row/

 

https://solvesql.com/problems/max-row/

 

solvesql.com

 

* 문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

select id from points
where x == (select max(x) from points)
or y == (select max(y) from points)
order by id

 

조건에서 사용될 각 열의 최댓값을 구하기 위해 서브쿼리 사용

값이 A열과 B열 중 하나에서라도 최댓값과 동일하면 포함시키기 위해 OR 연산자를 사용

 

문제 URL

https://www.acmicpc.net/problem/2655

 

 

 

문제 설명 요약

  • n개의 네모 모양 벽돌 정보가 주어짐
  • 주어진 정보는 '밑면의 넓이'와 '높이' 그리고 '무게'
  • 구해야 하는 것은 주어진 벽돌들 중 높이가 최대가 되도록 쌓을 때, 쓰인 벽돌의 개수와 각 벽돌의 순서
    (단, 순서를 출력할 때에는 제일 위에 쌓인 벽돌의 순서부터 출력해야함)
  • 벽돌을 쌓을 때의 규칙은 아래와 같음
    1. 벽돌은 회전시킬 수 없다. 즉, 옆면을 밑면으로 사용할 수 없다.
    2. 밑면의 넓이가 같은 벽돌은 없으며, 또한 무게가 같은 벽돌도 없다.
    3. 벽돌들의 높이는 같을 수도 있다.
    4. 탑을 쌓을 때 밑면이 좁은 벽돌 위에 밑면이 넓은 벽돌은 놓을 수 없다.
    5. 무게가 무거운 벽돌을 무게가 가벼운 벽돌 위에 놓을 수 없다

문제 풀이

 

  • 벽돌을 쌓을 수 있을지 없을지를 결정하는 요소가 '밑면의 넓이'와 '무게'이므로, 우선 둘 중에 하나를 기준으로 배열을 정렬해둠
  • 정렬 이후 Dynamic Programming(동적 계획법)을 활용하여, 쌓을 수 있는지 없는지에 대한 판단을 요소별로 쪼개어 수행

 

 

Swift를 활용한 풀이

 

import Foundation

let n = Int(readLine()!)! // 벽돌의 개수
var origin:[[Int]] = [] // 벽돌들의 정보

// 벽돌 정보 입력 받기
for _ in 0..<n {
    origin.append(readLine()!.split(separator: " ").map{Int(String($0))!})
}
// 벽돌의 밑면의 넓이를 기준으로 내림차순 정렬
var sorted_list = origin.sorted { $0[0] > $1[0] }

var dy:[Int] = [] // 높이합 최대 기록 배열
var hist:[Int:[Int]] = [:] // 각 항목 별 쌓을 수 있는 벽돌의 무게 정보 - [s:[w1, w2, ...]]

// 밑면이 가장 넓은 벽돌(맨 앞의 벽돌)에 대한 정보는 기본적으로 저장 (비교 대상이 없기 때문)
dy.append(sorted_list[0][1])
hist[sorted_list[0][0]] = [sorted_list[0][2]]

// 최대값에 관한 정보는 모두 밑면이 가장 넓은 벽돌(맨 앞의 벽돌)의 값으로 초기화
// (0과 빈 배열로 초기화해두면, 0번째 값을 고려하는 경우가 없어지기 때문)
var max_h_sum = dy[0] // 높이 합의 최대값
var max_h_list:[Int] = hist[sorted_list[0][0]]! // 높이 합이 최대인 경우의 높이값 요소 배열

// 2번째 벽돌 정보부터 살펴보면서, 이전 벽돌 중 자신이 위에 쌓일 수 있을지를 판단
for i in 1..<n {
    var max_h = 0
    var temp:[Int] = []
    for j in (0..<i).reversed() {
        // 이전 벽돌 중 자신보다 무게가 무거운 벽돌이면서, 높이의 합이 최대치인 경우의 정보를 임시 저장
        if (sorted_list[i][2] < sorted_list[j][2] && dy[j] > max_h) {
            max_h = dy[j]
            temp = hist[sorted_list[j][0]]!
        }
    }
    // 바로 위 for문에 의해 결정된 '높이의 합이 최대이면서, 무게가 자기자신보다 무거운 벽돌'의 정보를 기반으로 dy와 hist 초기화
    temp.append(sorted_list[i][2])
    hist[sorted_list[i][0]] = temp
    dy.append(max_h + sorted_list[i][1])

    // dy와 hist의 정보가 추가될 때마다, 각 높이의 합과 그것을 이루는 높이 요소값 배열을 갱신
    if (max_h_sum < dy[i]) {
        max_h_sum = dy[i]
        max_h_list = hist[sorted_list[i][0]]!
    }
}

// 결정된 최대 높이값의 요소 배열을 뒤집고 출력
// (무게를 기준으로 내림차순 된 배열에서 값을 찾았기때문)
max_h_list = max_h_list.reversed()
print(max_h_list.count)
for w in max_h_list {
    for i in 0..<origin.count {
        if(origin[i][2] == w) { print(i+1) }
    }
}

 

전체 코드 : https://github.com/ryr0121/AndroidPractice/tree/main/stopwatchApp

 

구현 결과

 

주요 기능

  • 카운트다운
  • 스톱워치 (시작/일시정지/정지)
  • 중간 기록 추가

 

ConstraintLayout으로 view 배치

Dialog / ProgressBar 를 통한 카운트다운 설정 및 현황 파악

// AlertDialog를 이용한 카운트다운 설정창 열기

private fun showCountdownSettingDialog() {
    AlertDialog.Builder(this).apply {
        val dialogBinding = DialogCountdownSettingBinding.inflate(layoutInflater)
        with(dialogBinding.countdownSecondPicker) {
            maxValue = 20
            minValue = 0
            value = countdownSecond
        }
        setView(dialogBinding.root)
        setTitle("카운트다운 설정")
        setPositiveButton("확인") { _, _ ->
            countdownSecond = dialogBinding.countdownSecondPicker.value
            currentCountdownDeciSecond = countdownSecond * 10
            binding.countdownTextView.text = String.format("%02d", countdownSecond)
        }
        setNegativeButton("취소",null)
    }.show()
}
// ProgressBar 추가

<ProgressBar
    android:id="@+id/countdownProgressBar"
    style="@style/Widget.AppCompat.ProgressBar.Horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="30dp"
    app:layout_constraintBottom_toTopOf="@id/timeTextView"
    app:layout_constraintStart_toStartOf="parent" />


// ProgressBar 설정

private fun start() {
    timer = timer(initialDelay = 0, period = 100) {
        if (currentCountdownDeciSecond == 0) {
            ...
            
        } else {
            currentCountdownDeciSecond -= 1
            val sec = currentCountdownDeciSecond/10
            val progress = (currentCountdownDeciSecond/(countdownSecond * 10f)) * 100

            binding.root.post {
				...
                binding.countdownProgressBar.progress = progress.toInt()
            }
        }
        ...
    }
}

 

ScrollViewLinearLayout을 활용한 시간 기록 view 구성

<ScrollView
    android:id="@+id/lapScrollView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginTop="50dp"
    android:padding="16dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/guideline">

    <LinearLayout
        android:id="@+id/lapContainerLinearLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" />

</ScrollView>

 

코드를 활용한 시간 기록 TextView 생성 및 ScrollView에 추가

private fun lap() {
    if (currentDeciSecond == 0) return
    val container = binding.lapContainerLinearLayout
    TextView(this).apply {
        textSize = 20f
        gravity = Gravity.CENTER

        val min = currentDeciSecond.div(10)/60
        val sec = currentDeciSecond.div(10)%60
        val deciSec = currentDeciSecond%10

        text = "${container.childCount.inc().toString()}. " + String.format(
            "%02d:%02d:%02d",
            min,
            sec,
            deciSec
        )
        setPadding(30)
    }.let { lapTextView ->
        container.addView(lapTextView, 0)
    }
}

 

workThread와 runOnUiThread / post 메소드를 이용한 UI 작업 비동기 처리

private fun start() {
    timer = timer(initialDelay = 0, period = 100) {
        if (currentCountdownDeciSecond == 0) {
        	// UI 업데이트가 아닌 다른 작업의 비동기 처리
            currentDeciSecond += 1

            val min = currentDeciSecond.div(10)/60
            val sec = currentDeciSecond.div(10)%60
            val deciSec = currentDeciSecond%10
            
            // runOnUiThread를 활용한 UI 업데이트 작업 진행
            runOnUiThread {
                binding.timeTextView.text = String.format("%02d:%02d", min, sec)
                binding.tickTextView.text = deciSec.toString()

                binding.countdownGroup.isVisible = false
            }
        } else {
        	// UI 업데이트가 아닌 다른 작업의 비동기 처리
            currentCountdownDeciSecond -= 1
            val sec = currentCountdownDeciSecond/10
            val progress = (currentCountdownDeciSecond/(countdownSecond * 10f)) * 100

			// post 메소드를 활용한 UI 업데이트 작업 진행
            binding.root.post {
                binding.countdownTextView.text = String.format("%02d", sec)
                binding.countdownProgressBar.progress = progress.toInt()
            }
        }
        ...
    }
}

 

ToneGenerator를 활용한 알림음 추가

val toneType = if(currentCountdownDeciSecond == 0) ToneGenerator.TONE_CDMA_HIGH_L else ToneGenerator.TONE_CDMA_ANSWER
ToneGenerator(AudioManager.STREAM_ALARM, ToneGenerator.MAX_VOLUME)
    .startTone(toneType, 100)

'Android' 카테고리의 다른 글

[Android] "계산기 앱" 구현  (0) 2024.06.30
[Android] "응급 의료정보 앱" 구현  (0) 2024.06.30
[Android] "단위 변환기 앱" 구현  (4) 2024.06.26
[Android] "숫자세기 앱" 구현  (7) 2024.06.25

전체 코드 : https://github.com/ryr0121/AndroidPractice/tree/main/calculatorApp

 

구현 결과

 

주요 기능

  • 정수형 숫자의 더하기, 빼기 연산

FlowLayout을 통해 숫자 및 연산자 버튼 배치

<androidx.constraintlayout.helper.widget.Flow
    android:id="@+id/keypadFlow"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintHeight_percent="0.7"
    app:layout_constraintVertical_bias="1"
    app:flow_maxElementsWrap="4"
    app:flow_wrapMode="chain"
    android:padding="8dp"
    app:flow_horizontalGap="8dp"
    app:constraint_referenced_ids="button_1, button_2, button_3, button_clear,
    button_4, button_5, button_6, button_plus,
    button_7, button_8, button_9, button_minus,
    button_0, button_equal"
    />

 

layout_constraintHorizontal_weight 값 지정을 통해 한 행에 관하여 얼만큼의 비율로 view를 차지하게 할 것인지를 지정

<Button
    android:id="@+id/button_0"
    style="@style/numberKeypad"
    android:text="0"
    android:onClick="numberClicked"
    app:layout_constraintHorizontal_weight="1"
    tools:ignore="MissingConstraints" />

<Button
    android:id="@+id/button_equal"
    style="@style/operatorKeypad"
    android:text="="
    android:onClick="equalClicked"
    app:layout_constraintHorizontal_weight="3"
    tools:ignore="MissingConstraints" />

 

setOnClickListen 메소드 대신 view 내의 onClick 속성값을 통해 클릭 이벤트 지정 (실습 상으로 해봄)

<Button
    android:id="@+id/button_1"
    style="@style/numberKeypad"
    android:text="1"
    android:onClick="numberClicked"
    tools:ignore="MissingConstraints" />

 

'Android' 카테고리의 다른 글

[Android] "스톱워치 앱" 구현  (0) 2024.07.02
[Android] "응급 의료정보 앱" 구현  (0) 2024.06.30
[Android] "단위 변환기 앱" 구현  (4) 2024.06.26
[Android] "숫자세기 앱" 구현  (7) 2024.06.25

4월 27일에 실기 시험보고 6월 18일에 결과 듣고 이제서야 남기는 정처기 합격 후기,,,,ㅎ

 

필기는 이미 작년 7월에 봤고 합격해둔 상태였음

필기 공부는 거의 시험 3일 전에 시작했고, 최근 기출 5회정도 풀어보고 들어갔음 (교재,강의 안 봄)

 

실기는 거의 시험 2주 전부터 시작했고 수제비 교재로만 공부했음 (따로 유튜브 강의나 이런 건 안 들었음)

시험 2주 전에는 위 책 2번 정도 봤음

1회독 : 이해 안 되도 그냥 읽고, 각 챕터 별 문제 다 풀고 틀린 문제는 해설 한 번씩 손으로 써보면서 머리에 넣으려고 함

2회독 : 이미 머리에 들어간 내용은 빠르게 읽고 넘김 / 이해 안 되는 부분 있으면 일단 머리에 넣으려고 그 부분만 몇 번 읽고 넘어감 / 문제는 다시 풀지는 않고 문제/해설 다시 읽음

 

시험 1주 전에는 이전에 공부하면서 '아 이 파트는 진짜 머리에 어지간히도 안 들어가네 뭔소린데 이게'라는 부분들만 추려서 계속 읽음

추가로 수제비에서 나온 파이널 모고 교재 있길래 그거 사서 기출 부분만 풀었음

(기본 교재는 문제가 좀 적은 편이라고 느꼈고, 내가 알고 있는 줄 알았는데 막상 문제에 나오면 생각 안 나는 부분까지 체크하려고 삼)

 

2주의 공부 과정을 다시 요약하면,,

[1] 일단 그냥 슥 읽기 + 다 틀려도 되니까 챕터 별 문제 다 풀어보기

[2] 아는 부분은 빠르게 읽고 넘기기, 잘 모르겠다 싶은 부분은 3~5번 읽기 + 이미 풀었던 문제들 다시 읽어보기

[3] 앞 과정에서 와 여기 진짜 왤케 어렵냐 싶었던 부분들만 인덱싱해놓고 계속 읽기 + 파이널 교재에 있던 기출 모음 부분만 풀기

전체 코드 : https://github.com/ryr0121/AndroidPractice/tree/main/sosInfoApp 

 

구현 결과 : 

 

주요 기능

  • 의료정보(이름, 생년월일, 혈액형, 비상 연락처) 조회/입력/수정/삭제
  • 비상 연락처 탭하면 전화 앱으로 연결

ConstraintLayout으로 view 배치

RadioButton, Spinner를 이용한 범위 내 값 선택 구현

// Rh+, Rh- 중 하나를 선택하는 RadioButton 그룹

<RadioGroup
    android:id="@+id/bloodTypeRadioGroup"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintBottom_toBottomOf="@id/bloodTypeTextView"
    app:layout_constraintEnd_toStartOf="@id/bloodTypeSpinner"
    app:layout_constraintStart_toStartOf="@id/guideline"
    app:layout_constraintTop_toTopOf="@id/bloodTypeTextView">

    <RadioButton
        android:id="@+id/bloodTypePlus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Rh+" />

    <RadioButton
        android:id="@+id/bloodTypeMinus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Rh-" />
</RadioGroup>
// A, B, AB, O형 중 하나를 선택하는 Spinner

<Spinner
    android:id="@+id/bloodTypeSpinner"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="@id/bloodTypeTextView"
    app:layout_constraintEnd_toEndOf="@+id/nameEditText"
    app:layout_constraintTop_toTopOf="@id/bloodTypeTextView" />

 

 

Intent를 이용한 화면 전환 및 전화앱 연결 구현

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.moveToInputActivity.setOnClickListener {
            val intent = Intent(this, EditActivity::class.java)
            startActivity(intent)
        }

        ...

        binding.moveToSosCallLayer.setOnClickListener {
            with(Intent(Intent.ACTION_VIEW)) {
                val phoneNum = binding.sosPhoneValueTextView.text.toString().replace("-","")
                data = Uri.parse("tel:$phoneNum")
                startActivity(this)
            }
        }
    }

 

SharedPreferences를 이용해 의료정보 데이터 관리

  • 데이터 조회
with(getSharedPreferences(USER_INFO, Context.MODE_PRIVATE)) {
    binding.nameValueTextView.text = getString(NAME, "미입력")
    binding.birthValueTextView.text = getString(BIRTH, "미입력")
    binding.bloodTypeValueTextView.text = getString(BLOOD_TYPE, "미입력")
    binding.sosPhoneValueTextView.text = getString(SOS_PHONE, "미입력")
    val notiInfo = getString(NOTI_INFO, "")

    binding.notiInfoTextView.isVisible = notiInfo.isNullOrEmpty().not()
    binding.notiInfoValueTextView.isVisible = notiInfo.isNullOrEmpty().not()

    if(!notiInfo.isNullOrEmpty()) {
        binding.notiInfoValueTextView.text = notiInfo
    }
}
  • 데이터 수정 및 저장
with(getSharedPreferences(USER_INFO, Context.MODE_PRIVATE).edit()) {
    putString(NAME, binding.nameEditText.text.toString())
    putString(BLOOD_TYPE, getBloodType())
    putString(SOS_PHONE, binding.sosPhoneValueTextView.text.toString())
    putString(BIRTH, binding.birthEditText.text.toString())
    putString(NOTI_INFO, getNotiInfo())
    apply()
}
  • 데이터 삭제
with(getSharedPreferences(USER_INFO, Context.MODE_PRIVATE).edit()) {
    clear()
    apply()
    getDataAndUiUpdate() // 데이터 재조회 및 UI 업데이트
}

'Android' 카테고리의 다른 글

[Android] "스톱워치 앱" 구현  (0) 2024.07.02
[Android] "계산기 앱" 구현  (0) 2024.06.30
[Android] "단위 변환기 앱" 구현  (4) 2024.06.26
[Android] "숫자세기 앱" 구현  (7) 2024.06.25

전체 코드 : https://github.com/ryr0121/AndroidPractice/tree/main/unitConversionApp

 

구현 결과

 

주요 기능

  • cm <-> m 간의 값 변환 (초기 상태 : cm단위의 값 입력 시, m의 값으로 변환)
  • 버튼을 통한 변환 단위 교체

ConstraintLayout으로 view 배치

ConstraintLayout은 상대적 제약조건을 통해 배치가 이루어지므로 렌더링 속도 향상 등의 이점 존재

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

   	...

</androidx.constraintlayout.widget.ConstraintLayout>

 

구현을 위해 사용한 컴포넌트는 아래와 같음

각 컴포넌트들에 대해 layout_..._...의 속성값 지정을 통해 상대적인 제약조건을 추가하여 UI 배치 진행

  • EditText : 변환시킬 정수값을 입력받기 위한 컴포넌트
    • hint : 입력값에 대한 정보를 나타내는 텍스트
    • maxLength : 입력값의 최대 길이 지정
    • inputType : 입력값의 타입 지정 (number : 정수 / numberDecimal : 소수를 포함한 숫자)
<EditText
        android:id="@+id/inputEditText"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:gravity="end"
        android:hint="자연수를 입력해주세요"
        android:inputType="numberDecimal"
        android:maxLength="7"
        android:textColorHint="@color/purple"
        android:textSize="20sp"
        android:textStyle="italic"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.4"
        app:layout_constraintHorizontal_bias="0.2" />
  • TextView : 단위가 변환된 값 / 입,출력 값의 단위 표시를 위한 컴포넌트
<TextView
        android:id="@+id/outputTextView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="end"
        android:text="0"
        android:textColor="@color/black"
        android:textSize="30sp"
        android:layout_marginTop="30dp"
        app:layout_constraintEnd_toEndOf="@+id/inputEditText"
        app:layout_constraintStart_toStartOf="@id/inputEditText"
        app:layout_constraintTop_toBottomOf="@id/inputEditText" />

    <TextView
        android:id="@+id/inputUnitTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="cm"
        android:textSize="20sp"
        android:layout_marginStart="16dp"
        app:layout_constraintStart_toEndOf="@+id/inputEditText"
        app:layout_constraintBaseline_toBaselineOf="@id/inputEditText" />

    <TextView
        android:id="@+id/outputUnitTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="m"
        android:textSize="20sp"
        android:layout_marginStart="16dp"
        app:layout_constraintStart_toEndOf="@+id/outputTextView"
        app:layout_constraintBaseline_toBaselineOf="@id/outputTextView" />
  • ImageButton : 단위 교체를 위한 컴포넌트
    • src 속성값 지정을 통해 버튼에 사용할 이미지 선택
<ImageButton
        android:id="@+id/swapImgButton"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@drawable/baseline_swap_vert_24"
        app:layout_constraintStart_toEndOf="@id/inputUnitTextView"
        app:layout_constraintTop_toTopOf="@id/inputEditText"
        app:layout_constraintBottom_toBottomOf="@id/outputTextView"
        android:layout_marginStart="16dp" />

 

MainActivity 내에서 ViewBinding을 통해 findById 메소드 사용의 단점을 보완

(예. 중복된 ID 값으로 인한 문제, ID값 분실, 지정된 xml에서의 뷰 포함 여부 확인 어려움 등)

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        val outputTextView = binding.outputTextView
        val outputUnitTextView = binding.outputUnitTextView
        val inputEditText = binding.inputEditText
        val inputUnitTextView = binding.inputUnitTextView
        val swapImgButton = binding.swapImgButton

        ...
    }

    ...
}

 

EditText의 addTextChangedListener 메소드를 통해 입력값의 변화가 감지될 때마다 관련된 이벤트 처리 가능

inputEditText.addTextChangedListener { text ->
    inputNumber = if (text.isNullOrEmpty()) { 0 } else { text.toString().toInt() }

    if (cmToM) {
        outputTextView.text = inputNumber.times(0.01).toString()
    } else {
        outputTextView.text = inputNumber.times(100).toString()
    }
}

 

onSaveInstanceState 메소드와 onRestoreInstanceState 메소드를 통해, 단위 변환 상태에 대한 '임시 UI 상태 저장 및 복원' 가능
(공식 문서 : https://developer.android.com/guide/components/activities/activity-lifecycle?hl=ko)

override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
    outState.putBoolean("cmToM", cmToM)

    super.onSaveInstanceState(outState, outPersistentState)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    cmToM = savedInstanceState.getBoolean("cmToM")
    binding.inputUnitTextView.text = if(cmToM) "cm" else "m"
    binding.outputUnitTextView.text = if(cmToM) "m" else "cm"
    
    super.onRestoreInstanceState(savedInstanceState)
}

'Android' 카테고리의 다른 글

[Android] "스톱워치 앱" 구현  (0) 2024.07.02
[Android] "계산기 앱" 구현  (0) 2024.06.30
[Android] "응급 의료정보 앱" 구현  (0) 2024.06.30
[Android] "숫자세기 앱" 구현  (7) 2024.06.25

전체 코드 : https://github.com/ryr0121/AndroidPractice/tree/main/countingApp

 

구현결과

주요 기능

  • 화면 상단에 카운팅된 숫자 표시
  • '초기화' 버튼으로 숫자를 0으로 변경
  • '+' 버튼으로 숫자를 1씩 증가

 

xml을 통해 UI 구현을 진행했고, LinearLayout을 통해 view를 배치함

LinearLayout은 배치 방향(orientation)의 값에 따라 세로 방향(vertical) 혹은 가로 방향(horizontal)으로 view 배치가 이루어짐

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    ...


</LinearLayout>

 

포함시킨 view는 TextView와 Button임

여러 디바이스에서 동등한 크기로 표시하기 위해 고안된 단위인 "밀도 독립적 픽셀(DP)" 사용

(픽셀 단위를 사용할 경우 디바이스 별로 전체 픽셀이 다르기 때문에 같은 값이 서로 다른 크기로 표현될 수 있기 때문)

id값 지정을 통해 이후 activity에서 버튼 이벤트 발생 시 변경된 숫자값을 표시하는 것이 가능해짐

<TextView
        android:id="@+id/numberTextView"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:text="0"
        android:textSize="100sp"
        android:textColor="@color/blue"
        android:textStyle="bold|italic"
        android:gravity="center"
        />

 

'초기화' 버튼과 '+' 버튼을 수평 방향으로 나란히 배치하기 위해 orientation이 horizontal인 LinearLayout을 사용함

id값 지정을 통해 이후 activity에서 사용자의 버튼 클릭 이벤트 처리가 가능해짐

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

        <Button
            android:id="@+id/resetButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="초기화"
            android:layout_weight="1"
            android:layout_marginTop="16dp"
            android:layout_marginBottom="16dp"
            android:layout_marginStart="16dp"
            />
        <Button
            android:id="@+id/plusButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="+"
            android:layout_weight="1"
            android:layout_margin="16dp"
            />
    </LinearLayout>

 

MainActivity 내에서 카운팅 현황을 나타내는 number 변수 선언 및 활용

findViewById 메소드를 활용하여 지정된 id의 view를 찾아와 활용

button의 setOnClickListener를 람다로 정의하여 사용자가 버튼 클릭 시 일어날 내용을 작성 (숫자를 0 혹은 +1 하여 변경)

class MainActivity : AppCompatActivity() {
    private var number = 0

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

        val numberTextView = findViewById<TextView>(R.id.numberTextView)
        val resetButton = findViewById<Button>(R.id.resetButton)
        val plusButton = findViewById<Button>(R.id.plusButton)

        resetButton.setOnClickListener {
            number = 0
            numberTextView.text = number.toString()
        }
        plusButton.setOnClickListener {
            number += 1
            numberTextView.text = number.toString()
        }
    }
}

 

 

아요랑 다른데 비슷해,,,비슷한데 다른 늒김,,,,,암튼 첫 구현 도전 성공,,

'Android' 카테고리의 다른 글

[Android] "스톱워치 앱" 구현  (0) 2024.07.02
[Android] "계산기 앱" 구현  (0) 2024.06.30
[Android] "응급 의료정보 앱" 구현  (0) 2024.06.30
[Android] "단위 변환기 앱" 구현  (4) 2024.06.26

1. UIKit를 사용하는 새 프로젝트 생성

2. Main.storyboard 파일 삭제

3. info.plistStoryboard Name 제거

 

4. 프로젝트 Target 내 Build Setting UIKit Main Storyboard File Base Name 제거

 

5. SceneDelegate.swift 파일 내 willConnectTo 메소드 내용 수정

* UINavigationController(rootViewController: MainViewController()) 중 'MainViewController'는 처음 시작할 ViewController명으로 지정

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: windowScene)
        window?.rootViewController = UINavigationController(rootViewController: MainViewController())
        window?.backgroundColor = .white
        window?.makeKeyAndVisible()
    }
    
}

+ Recent posts