ABOUT ME

작은 디테일에 집착하는 개발자

Today
-
Yesterday
-
Total
-
  • [Android] counter(계수기, 숫자 세기) 앱 (feat. 화면 회전 시 초기화, 화면 회전 시 UI 가려지기, weight)
    IT Study/Android 2024. 1. 21. 15:09
    728x90

    안드로이드에서 counter를 만드는 것은 아주 간단합니다.

    계수기를 생각하면 더 쉽게 느껴지죠.

    계수기
    1. + 버튼을 클릭할 경우, 숫자 1이 증가한다.
    2. 초기화 버튼 클릭할 경우, 숫자 0으로 초기화된다.

     

    🧮 Counter

    코드, UI는 아래의 내용을 통해 확인하시죠. 저의 계수기는 아래와 같이 생겼습니다.

     

    (1) MainActivity.kt

    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.util.Log
    import android.widget.Button
    import android.widget.TextView
    
    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 initBtn = findViewById<Button>(R.id.initBtn)
            val plusBtn = findViewById<Button>(R.id.plusBtn)
    
            initBtn.setOnClickListener {
                number = 0
                numberTextView.text = number.toString()
                Log.d("Main - onClick", "init $number")
            }
            plusBtn.setOnClickListener {
                number++
                numberTextView.text = number.toString()
                Log.d("Main - onClick", "plus $number")
            }
        }
    }

     

    (2) UI (사용자 인터페이스)

     

     

    🤔 의문점

    counter를 만들며, 아래와 같은 의문점이 생겼습니다.

    1. 화면 방향이 바뀌면 (회전하면) 왜 초기화될까?
    2. 화면 방향이 바뀌더라도 값을 유지하려면 어떻게 해야할까?
    3. 화면 방향이 바뀌더라도 (화면 방향에 무관하게) 늘 버튼이 보이도록 하려면 어떻게 해야할까?
    4. 왜 xml 파일의 weight에 0dp를 넣을까?

     

     

    😮 의문점 해결

     

    (1) 화면 방향이 바뀌면 (회전하면) 왜 초기화될까?

    안드로이드에서 화면 방향이 바뀔 때 (즉, 기기가 세로에서 가로로 또는 그 반대로 회전할 때) 시스템은 현재의 액티비티를 파괴하고 재생성하게 됩니다. 이는 화면 구성이 방향에 따라 달라질 수 있기 때문입니다. 이 과정에서 액티비티의 모든 상태 정보가 초기화되는데, 그래서 화면 회전 시 데이터가 초기화되는 것입니다.

     

    (2) 화면 방향이 바뀌더라도 값을 유지하려면 어떻게 해야할까?

    (2) - 1. onSaveInstanceState()와 onRestoreInstanceState() 사용

    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 initBtn = findViewById<Button>(R.id.initBtn)
            val plusBtn = findViewById<Button>(R.id.plusBtn)
    
            savedInstanceState?.let {
                number = it.getInt("number")
                numberTextView.text = number.toString()
            }
    
            initBtn.setOnClickListener {
                number = 0
                numberTextView.text = number.toString()
                Log.d("Main - onClick", "init $number")
            }
            plusBtn.setOnClickListener {
                number++
                numberTextView.text = number.toString()
                Log.d("Main - onClick", "plus $number")
            }
        }
    
        override fun onSaveInstanceState(outState: Bundle) {
            super.onSaveInstanceState(outState)
            outState.putInt("number", number)
        }
    }

     

    (2) - 2. ViewModel 사용

    class MainViewModel : ViewModel() {
        var number = 0
    }
    
    class MainActivity : AppCompatActivity() {
        private val viewModel: MainViewModel by viewModels()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val numberTextView = findViewById<TextView>(R.id.numberTextView)
            val initBtn = findViewById<Button>(R.id.initBtn)
            val plusBtn = findViewById<Button>(R.id.plusBtn)
    
            numberTextView.text = viewModel.number.toString()
    
            initBtn.setOnClickListener {
                viewModel.number = 0
                numberTextView.text = viewModel.number.toString()
                Log.d("Main - onClick", "init ${viewModel.number}")
            }
            plusBtn.setOnClickListener {
                viewModel.number++
                numberTextView.text = viewModel.number.toString()
                Log.d("Main - onClick", "plus ${viewModel.number}")
            }
        }
    }

     

    (2) - 3. setRetainInstance(true) 사용

    프래그먼트에서만 사용 가능한 메소드로, 프래그먼트 인스턴스가 재생성되지 않고 유지되도록 설정하는 메서드입니다.

    class CounterFragment : Fragment(R.layout.fragment_layout) {
        private var number = 0
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            setRetainInstance(true) // 화면 방향이 바뀌어도 Fragment 인스턴스 재생성 X (유지)
    
            val numberTextView = view.findViewById<TextView>(R.id.numberTextView)
            val initBtn = view.findViewById<Button>(R.id.initBtn)
            val plusBtn = view.findViewById<Button>(R.id.plusBtn)
    
            numberTextView.text = number.toString()
    
            initBtn.setOnClickListener {
                number = 0
                numberTextView.text = number.toString()
            }
            plusBtn.setOnClickListener {
                number++
                numberTextView.text = number.toString()
            }
        }
    }

     

    3. 화면 방향이 바뀌더라도 (화면 방향에 무관하게) 늘 버튼이 보이도록 하려면 어떻게 해야할까?

    (3) - 1. ScrollView 사용

    화면이 회전하거나 크기가 변경되어서 버튼이 화면 밖으로 나가버리는 경우, ScrollView를 사용하여 화면을 스크롤할 수 있게 만들 수 있습니다. 이 방법은 단순하고 효과적이며, 여러 개의 뷰가 모두 화면에 보일 수 있도록 합니다.

    <ScrollView 
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <LinearLayout 
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
    
            <!-- ... -->
    
        </LinearLayout>
    
    </ScrollView>

     

    (3) - 2. 레이아웃 가중치 사용

    LinearLayout에서는 layout_weight 속성을 사용하여 뷰 간의 공간 분배 비율을 지정할 수 있습니다. 이를 통해 화면 크기와 방향에 관계없이 버튼이 항상 보이게 할 수 있습니다.

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal">
    
            <!-- ... -->
    
        </LinearLayout>
    
    </LinearLayout>

     

    (3) - 3. ConstraintLayout 사용

    ConstraintLayout은 화면 비율에 맞출 수 있는 유동적인 레이아웃이며, 각 요소를 화면에 고정시키거나 서로 상대적으로 위치시키는 것이 가능합니다. 이를 통해 화면 방향이 바뀔 때 버튼이 항상 보이도록 할 수 있습니다.

     

    4. 왜 xml 파일의 weight에 0dp를 넣을까?

    XML 파일에서 weight 속성을 사용할 때 0dp를 넣는 이유는 뷰의 너비나 높이를 뷰의 weight 값에 따라 동적으로 변경하려는 의도 때문입니다. LinearLayout에서 weight 속성은 해당 뷰가 차지하는 공간의 비율을 지정합니다. 너비나 높이를 0dp로 설정하면, 해당 뷰는 크기를 weight 값에 따라 결정하게 됩니다. 따라서 화면 크기 및 방향에 관계 없이 동적으로 뷰의 크기를 조절할 수 있습니다.

Designed by Tistory.