Android/Kotlin

<정리> 이상형월드컵 앱 만들기 5일차

re트 2023. 12. 8. 17:54
728x90

오랜만에 손대는 앱이네...

저번 4일차 이후로 4일만에 다시 할 수 있게 됐다.

그 사이에 과제하느라 정신이 없어서 여기에 힘을 쓸 여력이 없었다.

그래도 오늘은 여유가 조금 있어서 만들기를 진행했고 생각했던 기능들은 모두 구현하여 앱 만들기를 마쳤다.

 

깃허브 : https://github.com/heesoo-park/Private_IdealTypeWorldCupApp

1. 주석 달기

지금까지 주석을 거의 안 달고 진행을 하고 있었더라

4일만에 보는 것인데도 약간 헷갈려서 다른 거 하기 전에 먼저 주석을 달았다.

이번에 과제하면서 알게 된 건데

/*
*  여기에 주석 내용 쓰기
*/

이렇게 주석을 쓰면 해당 함수나 변수를 쓰는 곳에서 마우스 커서를 올리면 이 주석이 설명을 뜬다...!!

아주 좋다

 

2. 라운드 이동

라운드 이동은 걱정했던 것만큼 어렵지 않았다.

정확히는 어려운 방법을 쓰지 않았다.

바로 SelectActivty를 새로 생성하는 방법을 썼다.

새로 생성할 때 intent에 다음 라운드의 값을 넣어서 보내줬다.

// SelectActivity.kt
// 16강 -> 8강 -> 4강 -> 결승
val intent = Intent(this@SelectActivity, SelectActivity::class.java)
intent.putExtra("totalRound", totalRound / 2)
startActivity(intent)

 

그리고 결승일 때는 결과 화면으로 가게 조건문으로 처리했다.

// SelectActivity.kt
// 결승인가
if (totalRound == 2) {
    // 결과 화면으로 이동
    val intent = Intent(this@SelectActivity, ResultActivity::class.java)
    startActivity(intent)
}

결승을 판별하는 조건이 totalRound == 2인 이유는 두 장의 이미지가 한 매치가 되는 것이 결승이기 때문이다.

 

관련 전체 코드는 다음과 같다.

// SelectActivity.kt
// 다음 페이지로 전환하는 함수
fun moveToNextMatch() {
    // 마지막 페이지인가
    if (viewPager.currentItem == totalRound / 2 - 1) {
        storeImg()
        // 결승인가
        if (totalRound == 2) {
            // 결과 화면으로 이동
            val intent = Intent(this@SelectActivity, ResultActivity::class.java)
            startActivity(intent)
        } else {
            // 16강 -> 8강 -> 4강 -> 결승
            val intent = Intent(this@SelectActivity, SelectActivity::class.java)
            intent.putExtra("totalRound", totalRound / 2)
            startActivity(intent)
        }
        finish()
    } else {
        // 다음 페이지로 이동
        val nextMatch = viewPager.currentItem + 1
        if (nextMatch < (viewPager.adapter?.itemCount ?: 0)) {
            viewPager.setCurrentItem(nextMatch, true)
        }
    }
}

 

3. 이미지 선택 시 저장

이 부분도 아주 쉽게 해결이 되었다.

이미지 클릭 이벤트 안에 DataStorage의 함수들(addImg, getImg)을 사용했다.

 그렇게 함수를 통과하면 DataStorage의 selectedImg 리스트에 이미지들이 저장된다.

// SelectFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    ...
    
    // 왼쪽 이미지 클릭 이벤트
    leftImg.setOnClickListener {
        addImg(getImg(currentMatch * 2))
        (activity as? SelectActivity)?.moveToNextMatch()
    }
    // 오른쪽 이미지 클릭 이벤트
    rightImg.setOnClickListener {
        addImg(getImg(currentMatch * 2 + 1))
        (activity as? SelectActivity)?.moveToNextMatch()
    }
}

 

4. 변수 의미 변경

전에는 round를 그냥 전체적인 라운드와 사진을 선택하는 순서에 대해서 혼용했었다.

그런데 이게 오늘 코드를 작성하다보니까 많이 헷갈리더라

그래서 4강, 16강 할 때는 round, 사진2개 중 하나를 선택하는 거는 match라고 했다.

4강(4 round)일 때는 2개의 match가 있는 거다.

이로인해서 코드가 중간중간 수정되었다.(변수명, 함수명)

 

5. 라운드 진행 수정

이전 4일차 영상을 보면 4강인데 4번 선택하게 되어있다.

생각해보면 4강은 준결승전이기 때문에 2번만 선택해야하고 이후에 결승전으로 가야한다.

이것을 오늘 알아차려서 수정에 들어갔다.

// SelectActivity.kt    
// 프래그먼트에서 현재 라운드를 알 수 있도록 만들어둔 함수
fun getCurrentRound(): Int {
    return totalRound
}

// 프래그먼트에서 총 매치를 알 수 있도록 만들어둔 함수
fun getTotalMatch(): Int {
    return totalRound / 2
}
// SelectFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    // 현재 라운드를 알려주는 TextView
    val roundText: TextView = view.findViewById(R.id.selectTitle_text)
    val currentRound = (activity as? SelectActivity)?.getCurrentRound()
    roundText.text = if (currentRound == 2) "음식 이상형 월드컵 결승" else "음식 이상형 월드컵 ${currentRound}강"

    // 몇 번째 매치인지 출력해주는 TextView
    val matchText: TextView = view.findViewById(R.id.match_text)
    matchText.text = getString(R.string.match, currentMatch + 1, (activity as? SelectActivity)?.getTotalMatch())
    
    ...
}

이외에도 몇 군데 있지만 생략...ㅎ

 

6. 선택 화면의 결과와 결과화면 연결

결과화면으로 넘어오는 것은 아까 2번에 올린 코드에 적혀있다.

결과화면으로 넘어와서는 할 건 2가지였다.

1등한 이미지를 세팅하는 것과 다시하기 버튼 클릭 이벤트

1등한 이미지를 세팅하는 건 그냥 initialImg 리스트에서 가져왔고 다시하기 버튼 클릭 이벤트는 이전에 앱 결과화면 만들듯이 구현했다.

// ResultActivity.kt
// 결과 화면
class ResultActivity : AppCompatActivity() {
    // 뷰 바인딩을 위한 변수
    lateinit var binding: ActivityResultBinding

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

        // 1등한 이미지 세팅
        binding.resultImg.setImageResource(getImg(0))

        // 다시하기 버튼 클릭 이벤트
        binding.retryBtn.setOnClickListener {
            // 시작 화면으로 이동
            var intent = Intent(this@ResultActivity, MainActivity::class.java)
            // 중간에 사용했던 액티비티의 내용을 지우고 새로 시작
            intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
            startActivity(intent)
            finish()
        }
    }
}

 

이렇게 5일차만에 생각했던 기능들이 다 들어간 이상형 월드컵 앱을 만들게 되었다!!

다음 주부터는 내배캠에서 안드로이드 앱 과정이 시작되기 때문에 그거 관련으로 올리게 될 거 같지만 추가적으로 할 수 있는 여유가 있다면 이렇게 간단한 앱들을 만들어보겠다.

 

만난 에러

1. 넣었다고 생각한 이미지 리스트가 사라짐

이건 최근 과제를 하면서 겪었던 일이라 금방 느낌이 왔다.

객체를 어떤 객체로 대입하면 값으로 넘어가는 것이 참조형으로 넘어가서 넣었던 객체에 값의 변화가 있을 시에 받은 객체 또한 영향을 받는 것이었다.

그래서 나의 해결방법은 넣은 객체를 새로 초기화하여 참조를 끊어버리는 것이었다.

// DataStorage.kt
// 선택된 이미지 리스트를 다음 라운드를 위해 초기 이미지 리스트에 저장하는 함수
fun storeImg() {
    initialImg = selectedImg.shuffled().toMutableList()
    selectedImg = mutableListOf()
}

 

2. 해당 라운드 매치 이미지 누르면 튕김

이거는 조금 찾아내는데 시간이 걸렸다.

리스트에 인덱스를 넘겼다는 에러가 뜬 거였는데 도저히 코드를 봐도 넘긴 부분이 보이지 않았다.

그러다 발견한게 어댑터 쪽이었다.

다른 부분들은 라운드와 매치를 구별하기 시작하면서 2를 나눠놨는데 프래그먼트를 몇개까지 만들지 결정하는 함수에 대해서는 처리를 안 해놨던 거였다.

이 부분을 하고 나니 해결이 되었다.

// SelectActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    ...
    
    // viewPager와 fragment를 연결하기 위한 어댑터 설정
    viewPager.adapter = ViewPagerAdapter(this, totalRound / 2) <- 이 부분
    
    ...
}

 

3. 해당 라운드 매치가 종료됐는데 다음 라운드로 가지 않음

이거는 내가 라운드와 매치를 구별하면서 생긴 2번 문제와 비슷한 이유였다.

마지막 페이지인지 확인하는 부분에서 전에는 totalRound - 1을 했었는데 이제는 매치가 끝나는 걸 기준으로 해야하기 때문에 totalRound / 2 -1로 바꿨다.(round는 match의 절반)

// SelectActivity.kt
// 다음 페이지로 전환하는 함수
fun moveToNextMatch() {
    // 마지막 페이지인가
    if (viewPager.currentItem == totalRound / 2 - 1) { <- 이 부분
        ...
    } else {
        ...
    }
}

 

반응형