[24.07.10] TIL - 029

🎧 0. 오늘의 추천곡

평온하고 산뜻한 하루였습니다...

💻 1. 코드 카타

오랜만에 코드 카타입니다.

오늘 풀었던 코드 카타는 삼각 달팽이입니다.

삼각형으로 쌓인 블록들을 달팽이의 껍질 무늬처럼 이동하면서 숫자를 넣어주는데,

이걸 배열로 만들어주면 됩니다. 저는 2차원 배열을 사용했습니다.

먼저 저는 3가지의 행동만 반복한다면 된다 생각했습니다.

왼쪽 아래로 내려갈 때, 밑바닥을 지나갈 때, 오른쪽 끝에서 왼쪽 위로 올라올 때

그리고 아래가 제가 작성한 코드입니다.

class Solution {
    fun solution(n: Int): IntArray {
        var answer: IntArray = intArrayOf()
        val max = n * (n+1) / 2
        var num = 0
        var array: Array<Array<Int>> = Array(n) { Array(it+1) { 0 } }
        var first = -2
        
        final@while(true) {
            first = first + 2
            val c = first / 2
            val nc1 = n - c - 1
            for(i in first until nc1) {
                array[i][c] = ++num
                if(max <= num) break@final
            }
            for(j in c until n - first) {
                array[nc1][j] = ++num
                if(max <= num) break@final
            }
            for(k in (nc1 - 1) downTo (first + 1)) {
                array[k][k-c] = ++num
                if(max <= num) break@final
            }
        }
        answer = array.flatten().toIntArray()
        return answer
    }
}

요즘 코드 카타를 풀면서 느끼는 점은, 너무 스트레스 받지 말자입니다.

이거 내가 못 풀면 어떡하지 라는 생각이 너무 강해서, 여태껏 스트레스를 받았는데

풀지 못하면 제 부족한 점을 인정하고, 아직 공부가 부족하다고 생각하고 있습니다.

이런 점은 고쳐나가면 되는 거고, 이런 걸 무시하고 고쳐나갈 생각조차 안 한다면 그게 더 안 좋은 것 같습니다.

📕 2. 안드로이드 개발반 심화 강의

오늘 목표는 끝내버리는 거였지만, 프래그먼트의 공식 문서를 뜯어보고 이해하는 과정에서 오래걸리게 됐습니다.

제가 말하는 모든 게 절대 맞는 게 아니지만, 일단 이해한 대로 써보겠습니다.

프래그먼트를 만들고 사용할 때 해당 프래그먼트를 초기 설정하는 방법은 다음과 같습니다.

supportFragmentManager.commit {
   replace<ExampleFragment>(R.id.fragment_container)
   setReorderingAllowed(true)
   addToBackStack("name") // Name can be null
}

여기서 supportFragmentManager는 액티비티에서 프래그먼트를 관리할 때 쓰는 FragmentManager입니다.

프래그먼트 매니저는 유저 상호작용에 따른 추가, 삭제, 변경에 대해 백스택을 쌓습니다.

이 부분은 공식 문서 대신 참조한 블로그를 참고하시면 좋습니다.

이런 식으로 추가, 삭제, 변경 같은 작업의 집합을 트랜잭션이라고 부릅니다.

이제 위 코드를 다시 살펴보면 supportFragmentManager가 commit을 통해 작업을 진행합니다.

replace는 프래그먼트를 교체합니다.

setReorderingAllowed(true)는 애니메이션과 전환이 올바르게 작동되록 하기 위해 재정렬을 허용합니다.

addToBackStack("") 은 백스택에 내용을 추가합니다.

기본적으로 이 3가지를 사용하며, 다른 함수들도 존재합니다.

2-1. 프래그먼트 클래스

프래그먼트를 생성하면,

class FirstFragment : Fragment()

와 같은 프래그먼트 클래스가 새로 생깁니다.

그리고, 기본적으로 override 함수들과 공동 객체, 프로퍼티가 들어가 있습니다.

private var param1: String? = null

먼저, 위의 변수는 String 값을 외부로부터 받아오기 위한 파라미터입니다.

이는 공동 객체의 newInstance() 함수에서 초기화됩니다.

companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment FirstFragment.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String) =
            FirstFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }

FirstFragment() 객체의 arguments라는 멤버 프로퍼티에 Bundle().putString()으로 값을 추가해줍니다.

여기에 값을 추가해주기 위해

FirstFragment.newInstance("아 1")

위처럼 프래그먼트의 newInstance() 함수를 호출해 arguments에 값을 넣어줍니다.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        println("온크리")
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
        }
    }

이후, 프래그먼트의 onCreate()가 내부적으로 호출되는데, 여기서 param1이 arguments에 저장됐던 값으로 초기화됩니다.

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.fragment_first, container, false)
        println("크리뷰")
        return binding.root
    }

그리고, onCreateView()가 호출됩니다. 여기서는 root 뷰를 반환값으로 두면 View를 생성해줍니다.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        println("뷰크리")
        // activity -> fragment
        binding.fragment1Text.text = param1

        binding.btnToFr2.setOnClickListener {
            val fragment = SecondFragment.newInstance("엥 2")

            requireActivity().supportFragmentManager.beginTransaction()
                .replace(R.id.fragment, fragment)
                .addToBackStack("")
                .commit()
        }
    }

마지막으로, onViewCreated()가 호출되고, 여기서 값을 설정해주거나, 버튼 리스너같은 걸 설정해줄 수 있습니다.

2-2. 프래그먼트의 데이터 전달

기본적으로 액티비티에서 프래그먼트로 데이터를 전달해주려면 위 과정처럼 newInstance로 전달해주면 됩니다.

만약, 프래그먼트와 프래그먼트 사이라면 추가적인 코드가 필요합니다.

requireActivity().supportFragmentManager.beginTransaction()
                .replace(R.id.fragment, fragment)
                .addToBackStack("")
                .commit()

위처럼 newInstance를 설정한 이후에 위와 같은 코드를 작성해주면 됩니다.

먼저, requireActivity()를 통해 이 프래그먼트와 연결된 액티비티를 가져옵니다.

그리고 그 액티비티에서 supportFragmentManager를 불러와 beginTransaction()으로 Transaction을 시작합니다.

이후, 작업 내용을 적어줍니다.

여기서 fragment를 SecondFragment로 교체했으므로, SecondFragment로 이동할 수 있습니다.

사실, 이는 액티비티 -> 프래그먼트와 똑같은 과정입니다.

다음은 프래그먼트 -> 액티비티의 과정입니다.

여기서는 프래그먼트의 생명주기에 대해 알아둘 필요가 조금 있습니다.

먼저, 인터페이스로 리스너 하나를 만들어줍니다.

interface FragmentDataListener {
    fun onDataReceived(data: String)
}

해당 리스너를 Fragment를 사용하고 있는 액티비티에서 구현해줍니다.

class FragmentActivity : AppCompatActivity(), FragmentDataListener {
...

 override fun onDataReceived(data: String) {
        Toast.makeText(this, "액티비티에 도착했습니다.", Toast.LENGTH_SHORT).show()
    }
    
}

이후, 프래그먼트에 onAttach 함수를 추가해줍니다.

onAttach는 가장 처음 진행하는 생명주기로, 아래 사진과 같습니다.

참조 블로그

위 사진을 보시면, onAttach를 가장 먼저 부릅니다.

attach의 뜻은 부착하다인데, UI를 스티커처럼 부착시켜 보여준다고 생각하면 됩니다.

어쨌든 이 맨 첫 단계를 오버라이딩한 메소드를 설정해줍니다.

private var listener: FragmentDataListener? = null
override fun onAttach(context: Context) {
        super.onAttach(context)
        if(context is FragmentDataListener) {
            listener = context
        } else {
            throw RuntimeException("오류!!!")
        }
    }

context가 FragmentDataListener를 구현하고 있는지 확인하고, 맞다면, listener를 연결합니다.

이후는 앞서 만든 프래그먼트와 같이 param이란 변수를 초기화하고 View를 연결해주는 과정을 거칩니다.

그리고나서 마지막으로 연결했던 listener의 함수를 호출해줍니다.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.fragment2Text.text = param1

        binding.btnToActivity.setOnClickListener {
            listener?.onDataReceived("도착")
        }
    }

즉, 인터페이스를 하나 만들고, 해당 인터페이스를 액티비티에서 구현해주는 방식으로 해결합니다.

어제부터 이런 방식을 자주 활용하는 것 같은데, 이에 익숙해져야겠습니다.

그리고, 이런 방식을 저도 활용해보고 싶네요.

🎯 3. 끝

오늘은 아쉽긴 하지만, 이렇게 끝났습니다.

이상하게 집중이 잘 안 되던 하루였지만, 그래도 얻어가는 게 있으니 다행입니다.

내일부터는 과제를 시작해보고 싶습니다.

끝.

profile
여기는 공부 기록용 블로그

2개의 댓글

명선님!😊 늘 추천해주시는 노래가 다 너무 좋은 것 같아요! 저도 종종 듣고 있답니다😁

코드카타는 명선님 말씀하신 것 처럼 너무 스트레스 받을 필요 없죠! 늘 말씀드린대로, 프로그래머스에서 비슷한 레벨의 다른 문제들 푸셔도 되고, 푸신 후에 다른 사람들 코드 보면서 더 가독성 좋아보이는 코드, 몰랐던 새로운 문법을 보면서 배우고 정리하는게 더 도움이 될 수도 있으니 못 풀었다는 것에 너무 피곤해 하지 않으셨으면 합니다!

명선님은 강의도 잘 들어주시고, 관련 내용도 꼼꼼히 작성해주셔서 저도 읽으면서 저절로 같이 공부가 되는 것 같아요👍이번 챕터도 같이 파이팅해봅시다!

1개의 답글