서론
보통 지금까지 개발을 하면서, Android 클래스에서 뷰에 접근하려고 할 때 findViewById 방식을 사용해 왔다.
위의 방식을 사용하면서 접근해야 할 View가 많아지면 코드의 양도 많아지고 불편했다.
자바로만 안드로이드 개발을 하다 새롭게 코틀린을 접하게 되면서 바인딩이라는 개념에 대해 접하게 되었는데, findViewById의 방식보다는 어렵지만 필요성을 느껴 정리해보려고 한다.
View 접근
가장 흔하게 사용해왔던 방식인, findViewById에 관해 먼저 이야기해 보자면, xml 파일(Layout)에서 뷰에 Id를 설정해준 뒤 소스에서 그 Id를 통해 접근하는 방식이다.
TextView text1 = findViewById(R.id.text1);
TextView text2 = findViewById(R.id.text2);
Button btn1 = findViewById(R.id.btn1);
ImageView img1 = findViewById(R.id.img1);
위 방식의 단점은 필요한 뷰의 개수가 많아질수록 소스 코드의 양이 많아진다는 것이다. 위의 불편함을 해결하기 위한 다양한 방법들이 있다. 뭐, Butter knife라는 라이브러리도 있지만, 현재는 아래의 방식들을 더 많이 사용하기 때문에 넘어가도록 하겠다.
Kotlin Android Extensions
Kotlin Android Extensions를 사용하면 findViewById를 사용하지 않고 바로 뷰에 접근이 가능해진다.
그러나, 구글에서는 위의 방식을 사용하는 것을 권장하지 않는다. 왜냐하면 Kotlin에서만 사용할 수 있고, 다른 뷰와 이름이 겹치게 될 경우, 또는 코틀린 파일에서 잘못된 뷰에 접근하는 경우 발생할 수 있는 에러 때문이다. 또한 리싸이클러뷰에서 사용할 경우 성능 이슈가 있다고 한다.
그럼 또 findViewById를 사용해야 하는 걸까? 구글 문서를 살펴보면, 현재 구글에서는 ViewBinding과 DataBinding을 사용하라고 권장한다. 오늘은 이 두 가지 방식을 알아보려고 한다.
ViewBinding
ViewBinding을 통해 뷰에 접근이 가능하다. ViewBinding은 XML의 View Component에 접근하는 object를 반환받아 view에 접근하는 방식이다. 그냥 쉽게 findViewById를 반복해서 작성해야 하는 번거로움을 덜어주는 방식으로 생각하자.
위의 방식을 사용하면 Null반환 문제를 해결할 수 있고, 반환 타입이 일치하지 않는 것에 대한 예외 문제를 예방할 수 있다.
사용법
build.gradle(Module..)
android {
buildFeatures {
viewBinding true
}
}
접근할 View의 소스 파일
class MainActivity : AppCompatActivity() {
//ViewBinding
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.btnArea.setOnClickListener {
binding.btnAraea.text = "바인딩 끝"
}
}
}
액티비티에서 ViewBinding은 위와 같이 간단하게 해결할 수 있다.
fragment에서 ViewBinding은 아래와 같이 사용한다.
class TestFragment : Fragment() {
private var _binding : FragmentTestBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentTestBinding.inflate(inflater, container, false)
val view = binding.root
// Inflate the layout for this fragment
return view
}
//메모누리 누수 예방
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
fragment에서 ViewBinding을 사용할 경우 Fragment는 View보다 오래 지속되기 때문에, Fragment의 LifeCycle로 인해 메모리 누수가 발생할 수 있기 때문에 onDestroyView에서 null로 만들어주어야 한다.
DataBinding
예를 들면 TextView의 text를 동적으로 바꾸려고 하면, 소스 코드에서 처리하는 경우가 많았다. 하지만, 데이터 바인딩을 활용하면 xml 코드에서 처리해줄 수 있다. 이렇게 하면, 소스코드에서는 로직처리만 해주고 뷰와 관련된 작업은 레이아웃 파일에서 할 수 있다. 데이터와 뷰를 연결하는 작업을 소스코드가 아닌 xml(레이아웃) 파일에서 처리하는 것을 DataBinding이라고 한다.
DataBinding은 ViewBinding의 역할도 할 수 있고, 동적 UI 콘텐츠 선언, 양방향 데이터 결합도 지원한다.
그런데, 항상 데이터 바인딩이 효율이 좋은 것은 아니다. Viewbinding이 상대적으로 간단하고 효율이 적고 용량이 절약된다는 장점도 있다. 예를 들면, 단순히 findViewById를 사용하기 원한다면 DataBinding보다 ViewBinding이 효율적일 수 있다는 것이다. 필요한 상황에 맞게 사용하면 된다.
사용법
build.gradle(Module..)
android {
buildFeatures {
dataBinding = true
}
}
data class
data class Person (
val name : String,
val age : Int
)
xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="user"
type="com.pys.databinding2.Person" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
/>
<TextView
android:id="@+id/textAge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{Integer.toString(user.age)}"
/>
<TextView
android:id="@+id/textEx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.age > 30 ? `나이 많음` : `나이 적음`}"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Btn"
android:onClick="myClick"
/>
</LinearLayout>
</layout>
소스코드
class MainActivity : AppCompatActivity() {
//데이터 결합을 위해 데이터 바인딩 활용
private lateinit var binding : ActivityMainBinding
var testCount = 20
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// 기존의 방법
binding.textV.text = "변경"
// 데이터 결합
val person = Person("동동이", 26)
}
fun myClick(view : View) {
testCount++
val person = Person("동동이", testCount)
binding.user = person
}
}
마치며
오늘은 뷰에 접근하는 다양한 방법을 알아보았다. 지금까지 자바로 대부분의 코드를 작성해왔던 나는 findViewById만 사용했었다. 뷰바인딩과 데이터바인딩 등 새로운 방법들에 접근해 보았는데, 사실 아직도 어색한 느낌이다. 위의 내용들은 기본적인 내용이므로 확실하게 알아두려고 한다.
ps) DataBinding은 ViewModel, LiveData와 결합해서 많이 쓰인다고 하여 꾸준히 공부하려고 한다.
참고
뷰 결합 | Android 개발자 | Android Developers
뷰 결합 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 뷰 결합 기능을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있습니다. 모듈에서 사용 설정
developer.android.com
데이터 결합 라이브러리 | Android 개발자 | Android Developers
컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 데이터 결합 라이브러리 Android Jetpack의 구성요소. 데이터 결합 라이브러리는 프로그래매틱 방식이 아니라 선
developer.android.com
[중급편] 친절한 JETPACK 개론 <상> (Android Kotlin) - 인프런 | 강의
코틀린으로 안드로이드 개발을 위한 젯팩(JETPACK) 개론입니다. 안드로이드 개발을 하면서 사용할 수 있는 다양한 테크닉을 연습할 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com