[Android] 반복되는 레이아웃 재사용하기 - include 태그(Kotlin)
앱개발을 하다보면 여러개의 레이아웃에서 반복되어 사용되는 레이아웃이 생긴다.
나의 경우에는 앱 레이아웃에 전체적으로 툴바가 존재한다.
이번 글에서는 공통적으로 사용되는 코드를 따로 만들어서 재사용하는 방법을 작성하려고 한다.
거기서 끝나는 것이 아니라 dataBinding을 사용하여 응용하는 방법까지 알아보자
현재 진행하고 있는 프로젝트에서는 이런식으로 레이아웃 상단에 툴바가 존재한다.
툴바에는 기본적으로 뒤로가기 버튼과 제목이 있고, 옵션사항으로 오른쪽에 추가 기능이 있기도하다.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingConstraints">
<TextView
android:id="@+id/title"
style="@style/neo_bold_black_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/manage_staff"
android:textSize="20sp" />
</androidx.appcompat.widget.Toolbar>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/toolbar">
...
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
이 코드는 위 스크린샷의 코드 일부이다.( activity_manage_staff.xml)
거의 모든 레이아웃에 존재하고있는 이 코드를 따로 빼서 재사용하려고한다.
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingConstraints">
<TextView
android:id="@+id/title"
style="@style/neo_bold_black_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/manage_staff"
android:textSize="20sp" />
</androidx.appcompat.widget.Toolbar>
1. 먼저, 새로운 레이아웃을 생성하자(layout_toolbar.xml)
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_back"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_back_arrow_black" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:text="title" />
</androidx.appcompat.widget.Toolbar>
<TextView
android:id="@+id/btn_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:text="option"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2. 해당 레이아웃을 넣어야하는 레이아웃(activity_manage_staff.xml)에 include 태그로 추가한다.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/layout_toolbar"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/toolbar">
...
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
여기까지 하면 뒤로가기버튼과 title이라는 텍스트가 있는 툴바가 적용된다.
나는 옵션기능까지 추가해주었다. 뒤에서 데이터바인딩을 이용해 visibility를 변경할 것이다.
하지만 우리는 title이라는 텍스트가 각 레이아웃별로 변경되기를 원한다.
데이터바인딩을 활용하면 이런 변경사항을 적용할 수 있다.
데이터 바인딩 활용하기
위의 툴바 예시에서 'title'에 해당하는 부분이 위의 스크린샷처럼 '직원 관리'로 변경한다고 하자.
데이터바인딩을 이용하면 텍스트뷰의 text에 들어갈 값을 지정할 수 있다.
1. 위에서 만든 툴바 레이아웃(layout_toolbar.xml)에서 변경해야할 부분에 대한 변수를 지정한다.
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<!-- title 텍스트 지정 -->
<variable
name="titleText"
type="java.lang.String" />
<!-- 오른쪽 텍스트 지정 -->
<variable
name="rightText"
type="java.lang.String" />
</data>
<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="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_back"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_back_arrow_black" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:text="@{titleText == null ? `title` : titleText}"/>
</androidx.appcompat.widget.Toolbar>
<TextView
android:id="@+id/btn_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:text="@{rightText == null ? `option` : rightText}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
2. include하는 부분에서 include 태그 내부로 해당 변수를 전달한다.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/layout_toolbar"
app:titleText="@{`직원 관리`}"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/toolbar">
...
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
데이터를 전달할때는 데이터바인딩으로만 전달 가능하다.
즉, app:titleText="직원 관리" 형식으로 사용할 수 없고, app:titleText="@{`직원 관리`}"와 같이 사용해야 한다.
만약 string value를 지정했다면 app:titleText="@{@string/manage_staff}"처럼 사용할 수 있다.
여기까지 하면 기본적인 것은 다 설명한 것 같다.
이제 추가적으로 내가 필요했던 내용을 정리해보려고 한다.
나에게 남은 과제는 option 기능의 visibility를 지정하는 것이다.
변수로 rightText값을 전달받지 않으면 visibility를 Gone으로 하려고 한다.
추가1) DataBinding으로 visibility 처리하기
<data>
<import type="android.view.View"/>
<variable
name="rightText"
type="java.lang.String"/>
</data>
<FrameLayout android:visibility="@{rightText == null ? View.GONE : View.VISIBLE}"/>
추가2) textColor 변경하기
특별케이스로 option 의 텍스트 색이 다른 경우가 생겼다.
그래서 rightTextColor라는 Boolean 변수를 만들고, 만약 값을 전달받지 않거나, false이면 @color/disabled_text,
true이면 @color/app_color로 지정해주었다.
최종적인 코드이다.(layout_toolbar.xml)
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<import type="android.view.View" />
<!-- title 텍스트 지정 -->
<variable name="titleText" type="java.lang.String" />
<!-- 오른쪽 텍스트 지정 -->
<variable name="rightText" type="java.lang.String" />
<!-- 오른쪽 텍스트 컬러지정 - true: app_color, false 또는 null : b1 -->
<variable name="rightTextColor" type="Boolean" />
</data>
<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="wrap_content">
<androidx.appcompat.widget.Toolbar .../>
<TextView
android:id="@+id/btn_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:text="@{rightText == null ? `option` : rightText}"
android:textColor="@{rightTextColor == null || !rightTextColor ? @color/disabled_text : @color/app_color}"
android:visibility="@{rightText == null ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>