[Android/Kotlin] strings.xml 문자열 리소스 제대로 활용하기 (feat. 데이터바인딩)

코코아의 개발일지·2024년 10월 9일
0

Android-Kotlin

목록 보기
33/36
post-thumbnail

오늘은 Android Studio의 strings.xml에 저장해놓은 문자열을 어떻게 사용할 수 있을지 정리해보겠다!
앱 내에서 사용되는 문자열을 strings.xml에 미리 저장해놓으면 중복되는 텍스트를 쉽게 관리할 수 있고, 하드코딩도 줄일 수 있다는 장점이 있다. 그렇지만 어정쩡하게 활용하면 안하느니만 못할 수 있다. 문자열 안에 변수를 어떻게 넣고, 데이터바인딩을 어떻게 적용할 수 있을지까지 다뤄보겠다.

프로젝트에서의 문자열 관리 예시

1️⃣ xml 레이아웃

1) Basic

  • strings.xml
<string name="next_btn">다음</string>
  • layout.xml
<androidx.appcompat.widget.AppCompatButton
            android:id="@+id/route_create_next_btn"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:text="@string/next_btn"
            android:layout_marginHorizontal="20dp"
            android:layout_marginBottom="28dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            style="@style/default_large_button"/>

가장 기본적으로는 TextView, Button 등 텍스트가 들어갈 수 있는 위젯에
android:text="@string/next_btn"
식으로 미리 저장된 리소스를 넣을 수 있다!

이 이후에 작성되는 레이아웃 코드에서는, 편의상 텍스트를 지정하는 부분을 android:text="@string/next_btn" 식으로만 표시하도록 하겠다. 택스트가 들어갈 수 있는 위젯에 모두 공통적으로 쓰일 수 있음을 의미한다고 생각해주면 좋겠다.


2) 변수를 넣을 때

타 프로그래밍 언어에서 문자열 내에 변수를 넣어 출력하고 싶을 때 Int 형식이면 %d, 문자열 형식이면 %s 등으로 표시하는 걸 본 적 있을 것이다. 이와 유사한 개념으로 보면 된다. strings.xml에 문자열을 미리 넣어둘 때도 %d, %s 등으로 변수가 들어갈 자리를 지정할 수 있다.

  • strings.xml
<string name="search_route_result_title">\'%s\' 루트</string>
<string name="filter_look_route">%d개 루트 보기</string>

여기에서 %s, %d 자리에 문자열을 넣어줄 수 있다.

  • layout.xml
android:text="@{@string/search_route_result_title(`hi`)}"
android:text="@{@string/filter_look_route(111)}"

** 문자열(%s)의 경우: xml 안에서는 큰 따옴표(")나 작은 따옴표(')가 안 먹기 때문에, 문자열을 꼭`로 감싸서 사용해야 함.

그치만! 이것 또한 하드코딩과 별다를 바 없기 때문에 보통은 데이터바인딩과 함께 사용한다. (<- 사용자가 입력한 데이터나, 서버에서 받아온 정보를 받아서 텍스트에 보여주기가 용이함)

%s 자리에 문자열 'hi'가 들어감 %d 자리에 정수 111이 들어감



3) 데이터바인딩에서의 사용 예시

android:text="@{@string/search_route_result_title(viewModel.routeSearchKeyWord)}"
android:text="@{@string/filter_look_route(viewModel.searchResultNum)}"

레이아웃의 xml 파일에서 미리 variable를 정의하고, 이를 활용해 텍스트에 변수를 전달할 수 있다.

<?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="viewModel"
            type="com.daval.routebox.presentation.ui.seek.search.FilterViewModel" />
    </data>
</layout>

참고로 뷰모델에서는 변수가 아래와 같이 정의되어 있다.

  • routeSearchKeyWord
@HiltViewModel
class SearchViewModel @Inject constructor(
    val repository: SeekRepository
): ViewModel() {
    private val _routeSearchKeyWord = MutableLiveData<String>("") // 검색 결과 타이틀 입력용
    val routeSearchKeyWord: LiveData<String> = _routeSearchKeyWord
    
    val searchWord = MutableLiveData<String>("") // 사용자가 입력한 검색어
    
    // 루트 검색
    fun inputRouteSearchWord() {
        Log.d("SearchViewModel", "입력한 검색어: ${searchWord.value}")
        // 검색 결과 수정
        _routeSearchKeyWord.value = searchWord.value
        // 루트 검색 결과 조회
        viewModelScope.launch {
            _searchResultRoutes.value = repository.searchRoute(
                searchWord = searchWord.value!!,
                sortBy = _selectedOrderOption.value!!.serverEnum,
                // ...
            )
        }
    }
}

searchWord는 EditText와 양방향 바인딩이 걸려있는 변수이고, 이 EditText에 입력한 검색어를 '검색 버튼을 누르면' 조회를 진행하기 때문에 버튼 클릭 후의 텍스트를 저장하기 위해 routeSearchKeyWord를 사용했다.

  • searchResultNum
@HiltViewModel
class FilterViewModel @Inject constructor(
    val repository: SeekRepository
): ViewModel() {

    // 필터링을 적용한 검색 결과 개수
    private val _searchResultNum = MutableLiveData<Int>(0)
    val searchResultNum: LiveData<Int> = _searchResultNum
    
    // 루트를 조회해서 결과 개수 확인
    fun inquirySearchResultNum() {
        Log.d("FilterViewModel", "받아온 검색어: $searchWord")
        viewModelScope.launch {
            _searchResultNum.value = repository.searchRoute(
                searchWord = searchWord,
                sortBy = OrderOptionType.ORDER_RECENT.serverEnum,
                // ...
            ).size
        }
    }
}
%s 자리에 뷰모델의 routeSearchKeyWord가 들어감 %d 자리에 뷰모델의 searchResultNum이 들어감



4) 변수가 여러개인 경우

변수가 여러개인 경우에는 그냥 콤마(,)로 구분해준다.

  • strings.xml
<string name="weather_update_date">%s %s 업데이트\n제공: 기상청</string>
  • layout.xml
android:text="@{@string/weather_update_date(date, time)}"

괄호 안에 들어갈 변수를 순서대로 넣어주면 된다.



2️⃣ Activity/Fragment Kotlin 코드

Activity나 Fragment 코드에서 사용할 수 있는 방법이다.

<다양한 예시 참고>
🔗 [Android/Kotlin] 안드로이드 스튜디오에서 JDBC를 이용해 MySQL 연동하기 (3/3) - 통신편

1) Basic

기본적으로 strings.xml 코드를 불러오는 방식은

resources.getString(R.string.db_url)

형태이다.
텍스트로 넣어주고 싶다면

binding.nextBtn.text = ContextCompat.getString(this, R.string.next_btn)

식으로 넣어줄 수 있다.

2) 변수를 넣을 때

strings.xml 파일에서 문자열 작성 방법은 xml에서 한 것과 동일하다.

  • strings.xml
<string name="signup_complete">%s,\n루트박스에 온 걸 환영해요!</string>

%s 자리에 회원가입 시에 작성한 유저의 닉네임을 넣어주면 된다. 하드코딩할 경우, 아래와 같이 작성할 수 있다.

  • Fragent/Activity.kt
binding.termTitle.text = String.format(resources.getString(R.string.signup_complete), "코코아")

변수를 넣어줘야 하는 경우
String.format({resources.getString({리소스_id}), {넘겨줄 값}) 식으로 작성해줄 수 있다.

3) 데이터바인딩에서의 사용 예시

위와 같은 동일한 string 리소스에서 하드 코딩 대신 뷰모델에서 사용한 변수를 전달할 수 있다.

  • Fragent/Activity.kt
binding.termTitle.text = String.format(resources.getString(R.string.signup_complete), viewModel.nickname.value)

4) 변수가 여러개인 경우

strings.xml 파일 작성은 마찬가지로 앞선 xml에서와 동일하다. 이번엔 3개를 넣어 본다고 하자.

  • strings.xml
// 팔로워, 팔로잉 목록 조회
<string name="query_select_follow_list">SELECT user_id, user_name, name, profileImage_url FROM user WHERE user_id IN (SELECT %1$s FROM %2$s WHERE user_id = %3$d)</string>

%1$1, %2$s, %3%d 식으로 변수가 들어갈 순서도 지정해 줄 수 있다.

  • Fragment/Activity.kt
String.format(resources.getString(R.string.query_select_follow_list), targetId, table, userId)

📚 참고 자료

profile
안드로이드 개발자를 꿈꾸는 학생입니다

0개의 댓글