kotlin Android Binding과 Fragment

Date:     Updated:

카테고리:

태그: , , , ,


1. 포스팅 동기

프로젝트 참조 : https://hagenti0612.github.io/project/narsha-gympti1/

학생들에게 프로젝트 멘토링을 진행 중이었다.

프로젝트 역할 분담

  • 백엔드 : SpringBoot + JPA + Swagger
  • 프론트 : React + Node Server
  • ios : Swift
  • android : kotlin
  • 머신러닝 : python

으로 진행 중이었다. 그런데 이 학생들은 고등학생들이었고 정말 대단한 친구들이었다. 학교수업, 특강 등으로는 C,JAVA 같은 기초지식만 배우고 나머지 프로그램들을 책, 인강, 인터넷 검색 등으로 자가 학습을 하여 익힌 상태였다.
체계적인 단계별 학습을 하지 않아서 필요한부분만들 검색해서 구현하고 연결하고 반복해서 작업을 하고 있었다. 그러다 보니 현업에서 당연히 익혔어야할 기초 지식이 부족했고, 에러 발생에 대한 대처 능력은 아직 미흡했었다. 그래서 나 또한 하나씩 공부하면서 해결하고 수정해나가면 프로젝트를 진행했었다. 그 중에 몇개가지 이슈가 있어서 기록을 하고자 한다.

2. Android Binding

프로젝트 언어 설정

안드로이드 초창기 부터 사용했던 언어는 Java였었다. Eclipse add 시절부터 시작해서 Android Studio까지 거의 모든 프로젝트를 Java로만 개발을 하였었다. 익숙하고 편했으니까. 그런데 학생들은 처음부터 kotlin을 원했다. Kotlin의 장단점은 책이나 블로그를 통해 읽어 보았지만 새로운 학습에 대한 두려움, 귀찮음으로 미루고 있었다. 하지만 학생의 열정에 가르치는 입장에서 외면할 수 없었다. 차이점을 익히고 하나씩 구현 하기로 했다. 학생에게 프로젝트를 생성하고 화면을 여러개로 분리해야되니 Fragment로 구현을 해보라고 숙제를 냈었다.

어디까지 학습이 되었고 익혔는지 몰랐기때문에 제안을 했지만 초반 변수 설정부터 막혔었다. 일단 하나씩 가르치면서 학습하기로 했다.

Binding 설정

예전에 안드로이드 개발하는 중에 ButterKnife 라는 훌륭한 라이브러리가 있었다. xml상의 id를 @를 동한 바인딩으로 java의 변수로 그대로 사용이 가능하게 했었다. 이로 인해 compile 단계에서 view 변수들에 nullpoint 에러를 줄일 수 있었다. 그런데 이 라이브러리는 deprecated가 되었고 더이상 사용이 불가능하다. 왜냐? 구글에서 안드로이드 자체적으로 Binding을 지원하기 시작했기 때문이다.
이전에 구현해 둔것을 가지고 와서 사용했다

android {
    release {
      ...
    }
    viewBinding {
        enabled true
    }
}

gradle이 에러가 나고 sync가 안된다.

공식문서 : https://developer.android.com/topic/libraries/view-binding

가이드문서를 보고 했는데도 안된다.

검색을 해서 찾아보니 android studio 버전이 변경되면서 명령어도 변경되었다.
(Android 공식 문서도 이중화 관리 중 이었다.)

변경 된 문서 : https://developer.android.com/topic/libraries/view-binding/migration

Android는 매번 갑작스러운 버전변경으로 외부요인이 너무 많이 발생하는 것 같다.

android studio 4.0 이상에서는 아래와 같이 사용한다.

android {
  buildTypes {
    ...
        release {
            ...
        }
        buildFeatures{
            viewBinding true
        }
    }
}

gradle이 sync 완료 후 에러가 없다면 완료된 것이다.

3. viewBinding 구현

layout을 확인해보자

<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>

root view가 중요 것이 아니다. image

android binding 공식문서의 내용은 binding을 사용 옵션을 적용 할 경우 layout.xml은 layout에 포함된 각 XML 레이아웃 파일의 bindingClass(결합 클래스)가 생성되고 각 결합 클래스에는 rootView와 ID가 있는 모든 view를 변수화 하여 생성된다. 결합 클래스의 이름은 XML 파일의 이름을 카멜 표기법으로 변환하고 끝에 ‘Binding’을 추가하여 생성됩니다.

자동생성 명명규칙

layout명 생성명
activity_main.xml MainActivityBinding
activitymain.xml MainActivityBinding
fragmentA.xml AfragmentBinding

이 class들은 자동으로 생성되며 만약 생성을 원하지 않는다면

<LinearLayout
            ...
            tools:viewBindingIgnore="true" >
        ...
    </LinearLayout>
    

viewBindingIgnore를 추가하면 된다.

준비가 되었으니 코드 구현을 해보자

private lateinit var binding: ActivityMainBinding

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

Activity binding

Activity의 경우 onCreate를 통해 화면전체를 생성하고 관리한다. 그래서 이미 bindingClass(결합 클래스)가 생성되어 있기때문에 전역변수로 처리하고 결합 클래스를 전개(inflate)를 하면 layout의 정보가 binding 변수에 연결된다. 그리고 rootView를 Activity에 연결하면 화면이 출력된다.

이후 View는 이렇게 사용하면된다.

//binding.viewId명.속성명 = 데이터
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Fragment binding

문제는 이제 Fragment다 Fragment는 Activity처럼 화면을 생성하지 못한다. Activity안에서 일부 영역을 Context를 가져와서 layout을 동적으로 생성해야한다. 즉 Fragment는 어떤 Activity안에 연결될지 실행되기전까지 알 수가 없다. 그래서 변수만 선언하고 화면은 생성하는 단계에서 변수의 값을 담아야한다.

private var _binding: MainFragmentBinding? = null
private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = MainFragmentBinding.inflate(inflater, container, false)
        val view = binding.root
        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
  1. 전역변수 선언시 값을 지정할 수 없다. null로 선언
  2. binding 변수를 readonly로 프레그먼트의 결합클래스와 연결을 선언
  3. inflate 단계에서 Activity의 inflater, container 정보로 인스턴스를 생성한다.
  4. 생성된 인스턴의 rootView를 onCreateView에서 return하여 화면을 생성한다.
  5. 프레그먼트 종료시 동적으로 연결한 _bindding 변수를 초기화한다.

나머지 사용방법은 Activity와 동일하다


4. Android Fragment 구현

layout 생성

Activity에 표시할 레이아웃을 생성한다.

<?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">

    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

중요한 포인트는 fragment에 표시할 영역의 layout의 크기와 id를 지정하는 것이다.

지금은 전체 화면과 frameLayout이라는 이름을 사용하였다.

Activity 코드 구현


private fun setFragment() {
        var transaction = supportFragmentManager.beginTransaction()
        transaction.add(R.id.frameLayout, LoginFragment())
        transaction.addToBackStack(null)
        transaction.commit()
    }

함수를 하나 만든다.

  1. android에서 지원하는 supportFragmentManager를 통해 beginTransaction를 통해 인스턴스를 생성한다.
  2. 사용한 layout명과 Fragment 클래스의 인스턴스를 생성하고 add를 통해 담아둔다.
  3. fragment의 첫화면이므로 따로 연결된 화면이 없으니 null 지정한다.
  4. commit을 통해 해당 layout에 fragment를 생성하여 연결한다.
  5. 연결된 Fragment의 onCreateView가 실행되면서 Activity에 Fragment가 표시된다.

마치며

Android는 기존것을 수정하는 작업이 아니라 새로운 프로젝트를 만들 때 매번 다양한 경험을 한다.

예전에는 gradle 버전이 바뀌면서 명령어가 변경되거나 라이브러리가 deprecated가 된적도 있었다. 이번에는 Android Studio 버전으로 gradle 옵션이 변경되었다. 시간이 걸렸지만 항상 해결하면 성취감이 느껴진다.
에러 발생부터 해결과정까지 학생들에게 리뷰를 하고 내용을 공유하였다. 어렵지만 좋은 경험을 할 수 있는 프로젝트가 될거 같다.


개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
댓글 또는 메일로 알려주시면 감사하겠습니다.

맨 위로 이동하기

kotlin 카테고리 내 다른 글 보러가기

댓글 남기기