Group Study (2023-2024)/Android 입문

[Android 입문] 2주차 스터디 -Activity와 Fragment, ViewBinding

Sominee 2023. 11. 13. 22:25

1. Activity & Intent

  • Activity
    - 앱의 단일 화면이다.
    - 초기에 MainActivity.kt와 activity_main.xml이 하나의 화면을 구성하고 있다.
    - 새로운 액티비티(서브 화면)를 만들고 싶다면 MainActivity.kt와 같은 위치에 SubActivity.kt를 만든다. 그러면 activity_main.xml과 같은 위치(res>layout)에 activity_sub.xml이 자동으로 생성된다.
  • Intent
    - 안드로이드 컴포넌트 간의 통신을 위해 사용되는 메세지 객체이다.
    - 화면 전환 및 데이터 전달을 가능하도록 한다. 메인 액티비티와 서브 액티비티 간의 이동을 도와주는 것이다.

<실습>

Main Activity 화면
Main Activity 화면(좌), Sub Activity 화면(우)

메인 화면의 "Hello World!" TextView의 id: tv_sendMsg
서브 화면의 "서브 액티비티" TextView의 id: tv_getMsg

다음은 차례로 MainActivity.kt와 SubActivity.kt 코드이다.
위의 메인 화면의 '서브 화면으로 이동' 버튼을 터치하면 서브 화면의 '서브 액티비티' TextView의 text를 메인 화면의 TextView("Hello World!")로 바꿔주도록 코드를 작성하였다.

class MainActivity : AppCompatActivity() {
    // 코틀린 익스텐션이 중단되면서 추가한 뷰바인딩
    private var mBinding: ActivityMainBinding? = null
    private val binding get() = mBinding!!

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 코틀린 익스텐션이 중단되면서 추가한 뷰바인딩
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btnA.setOnClickListener {
            val intent = Intent(this, SubActivity::class.java) // 다음 화면으로 이동하기 위한 인텐트 객체 생성.
            intent.putExtra("msg", binding.tvSendMsg.text.toString()) // Hello World! 라는 텍스트 값을 담은 뒤 msg라는 키로 잠갔다.
            startActivity(intent) // intent에 저장 되어 있는 액티비티 쪽으로 이동한다.
            finish()              // 자기 자신 액티비티를 파괴한다.
        }
    }
}

finish() 無 → 뒤로가기 클릭 시, 메인 액티비티로 돌아갈 수 있음.
finish() 有 → 뒤로가기 클릭 시, 앱이 종료됨.

 

Activity&Intent 실습 에뮬레이터 영상

 

2. Fragment

  • Fragment
    - 액티비티에 종속되어 있는 자식 개념
    - 독립적으로 존재할 수 없다.
    - 액티비티 실행 중 추가, 교체, 삭제가 가능하다.
    - 하나의 프래그먼트를 여러 액티비티에 재사용할 수 있다.
    - 하나의 액티비티에 여러 개의 프래그먼트가 있을 수 있다.
    - 전체 화면(액티비티)의 일부를 바꾸는 것으로 자원 이용량이 적어 속도가 빠르다.

<실습>

activity_main.xml에 프래그먼트를 전환할 버튼 3개와 프래그먼트를 표시할 공간(id: main_frame)을 구현한다. 같은 위치(res>layout)에 프래그먼트 xml 파일을 만든다.

다음은 Fragment1.kt 코드이다.
프래그먼트를 정의하는 코드로, 프래그먼트 2와 3도 동일하게 코드를 작성하였다.

class Fragment1 : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.frag1, container, false)

        return view
    }
}

다음은 MainActivity.kt 코드이다.
버튼을 클릭하여 해당 프래그먼트로 전환하는 것을 구현하였다. 초기에는 프래그먼트1이 나타나도록 한다.

class MainActivity : AppCompatActivity() {
    private var mBinding : ActivityMainBinding? = null
    private val binding get() = mBinding!!

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setFrag(0) // 초기에 프래그먼트 1을 표시
        binding.btnFragment1.setOnClickListener {
            setFrag(0) // 프래그먼트 1 전환
        }

        binding.btnFragment2.setOnClickListener {
            setFrag(1) // 프래그먼트 2 전환
        }

        binding.btnFragment3.setOnClickListener {
            setFrag(2) // 프래그먼트 3 전환
        }

    }

    // 프래그먼트를 설정하고 교체하는 함수
    private fun setFrag(fragNum : Int) {
        val ft = supportFragmentManager.beginTransaction()
        // 자바에서의 switch문과 동일 
        when(fragNum) { // fragNum에 따라 다른 프래그먼트로 교체
            0 -> {
                ft.replace(R.id.main_frame, Fragment1()).commit() // 프래그먼트1로 교체
            }
            1 -> {
                ft.replace(R.id.main_frame, Fragment2()).commit() // 프래그먼트2로 교체
            }
            2-> {
                ft.replace(R.id.main_frame, Fragment3()).commit() // 프래그먼트3으로 교체
            }
        }
    }
}

 

Fragment 실습 에뮬레이터 영상

 

3. ViewBinding

  • ViewBinding
    - findViewById를 생략하고 간편하게 쓸 수 있었던 Kotlin Extensions이 중단된 이후, 구글은 뷰 바인딩을 사용하도록 안내하고 있다.
    - 뷰의 요소를 불러오기 위해 사용했던 FindViewById를 대체하는 기능

    - 뷰 바인딩을 활성화하면 각 xml 파일에 대해 ViewBinding 클래스를 상속 받는 개별 뷰 바인딩 클래스가 자동으로 생성된다. 각 파일들에 선언해두었던 뷰들의 id들이 참조가 포함된다.
    - onCreate() 안에서 뷰 바인딩 클래스의 인스턴스를 생성
    - 인스턴스가 뷰의 Id를 프로퍼티로 제공하게 된다.

    - 뷰와 상호작용하는 코드를 쉽게 작성할 수 있도록 해준다. 
    - Null-safe: 뷰 바인딩은 서로 다른 layout의 같은 id를 가진 뷰를 정확히 구분할 수 있다. 만약 그럴 수 없을 경우, @Nullable로 만들어 사용할 수 없게 한다.
    - Type-safe: findViewById를 사용할 경우, 뷰에 잘못된 타입을 지정할 우려가 있는데 뷰 바인딩에서는 그런 문제가 발생하지 않는다.

뷰 바인딩은 초기 설정이 필요하다. Gradle Scrips에 있는 build.gradle.kts (Module :app) 에서 다음과 같이 viewBinding 블럭을 추가하여 수정을 해주어야 한다. 그 다음 오른쪽 위의 Sync Now를 클릭해주면 초기 설정은 끝난 것이다.

android {
    // 코드 생략
    
    // 뷰 바인딩 옵션 활성화
    viewBinding {
        enable = true
    }
}

다음은 MainActivity.java 코드이다. id가 tv_hello인 TextView의 텍스트를 "안녕하세요 !"로 바꾼다.
mbinding 바인딩 변수를 활용하여 마음껏 xml 파일 내의 뷰 id 접근을 할 수 있다.

public class MainActivity extends AppCompatActivity {
    // View Binding을 사용하여 액티비티의 레이아웃과 뷰들을 바인딩하는 객체
    // 레이아웃 파일을 따라가서 변수명이 파스칼 표기법과 카멜 표기법으로 자동으로 만들어진다. 
    private ActivityMainBinding mBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //액티비티 바인딩 객체에 할당 및 뷰 설정
        mBinding = ActivityMainBinding.inflate(getLayoutInflater());
        View view = mBinding.getRoot(); // 루트 뷰를 가져와서 액티비티의 레이아웃으로 설정
        setContentView(view);

        // View Binding을 통해 액티비티의 TextView에 텍스트 설정
        mBinding.tvHello.setText("안녕하세요 !");
    }
}

레이아웃 파일을 따라서 변수명이 파스칼 표기법과 카멜 표기법으로 자동으로 만들어지는데, 예를 들어서 레이아웃 파일명이 activity_main.xml 이면 변수명을 ActivityMainBinding으로 안드로이드 스튜디오에서 자동으로 완성해준다. 뷰 id 또한 파스칼 표기법과 카멜 표기법을 적용하여 자동 변환된다.

※ 파스칼 표기법 & 카멜 표기법

  • 파스칼 표기법
    - 단어의 첫 시작은 항상 대문자를 사용하는 표기법
    - '쌍봉낙타 표기법'이라고 불림
    - ex) activity_main.xml -> ActivityMainBinding 에서 첫 알파벳 A가 소문자에서 대문자로 바뀜
  • 카멜 표기법
    - 소문자로 시작하고 이어지는 단어들의 시작은 대문자로 작성하여 단어 간 구분에 용이한 표기법
    - '단봉낙타 표기법'이라고 불림
    - ex) activity_main.xml -> ActivityMainBinding 에서 activity와 main을 이어줄 때 _ 없이 Main으로 이어지는 단어의 첫 알파벳이 대문자로 바뀜

 

<참고>

https://www.youtube.com/watch?v=1xJmh2QhYTU

https://duckssi.tistory.com/42

https://heytech.tistory.com/294