안드로이드 앱을 만들다 보면 처음으로 알아 챌 수 있는게 있다. 안드로이드는 기본적으로 'App bar' 이라는 툴을 제공한다. 여기에는 앱의 이름이 써 있을 수도 있고, 개발자가 원하는 기능을 추가하여 쓸 수도 있다.
아래 사진에서 위쪽에 보이는 보라색 바(DreamCatcher)라고 보이는 부분이 'App bar' 이다.
그렇다면 여기서 저 위의 바에 기능을 추가 할 순 없을까? 이 예제에서는 위의 바에 + 버튼을 추가하여 꿈의 목록을 추가하는 기능을 구현 해 보겠다!
앱 바의 오른쪽 상단 영역은 앱 바의 메뉴를 위해 원래부터 예약되어 있다. 이 메뉴는 작업 항목(가끔은 메뉴 항목으로도 불림)으로 구성되어 있으며, 현재 화면이나 전체 앱에 대해 작업을 수행할 수 있다. 새 작업 항목에는 레이블을 위한 문자열 자원이 필요하다. res/values/strings.xml을 열고 새 작업을 설명하는 문자열 레이블을 추가하자!
우리가 사용하는 대부분의 앱은 하나의 화면으로 이뤄져 있지 않다. 앱이 새로운 기능을 추가함에 따라, 개발자들은 그 기능을 수용할 새로운 화면을 만든다. 간단한 예를들어, 카카오톡의 친구 목록에 가면 친구의 리스트들이 보이고, 친구의 프로필 사진을 보고싶으면 친구의 프로필을 눌러 사진을 봐야한다. 얼핏 보면 간단한 작업처럼 보이지만, 속을 보면 그렇지 않다.
친구 목록을 보여주는 화면에서 친구의 프로필을 눌러 친구 프로필에 대한 정보를 보려면 단순히 친구 프로필을 띄우는게 아니라 다른 페이지로 이동해서 그 새로운 창을 유저에게 보여줘야 한다.(카톡의 정확한 UI로직은 나도 모른다 그냥 예를 든것 뿐!)
Jetpack 라이브러리의 Navigation 구성 요소는 화면을 정의하고 그들 사이의 경로를 정의한 다음 그 탐색을 수행할 도구를 제공한다. 이 라이브러리의 핵심은 탐색 그래프에 의존하는데, 이 그래프는 화면 목적지 그룹과 그들 사이의 경로를 정의한다. 탐색 그래프는 XML 파일에 포함되어 있으며, 아래 예시를 보자.
먼저, 우리는 하나의 액티비티와 여러개의 fragment가 있다고 가정해보자. 각각의 fragments(조각)들은 각 화면의 역할을 한다. 즉, 조각 1은 친구 목록을 보여주는 화면 역할을 할 수 있고, 조각 2는 친구의 세부 정보를 보여주는 화면일 수 있고, 조각 3은 또 다른 화면을 보여주는 조각이라고 생각 할 수 있다. 이때, 존재하는 단 하나의 액티비티는 조각들을 담아서 화면에 보여주는 역할을 하는 '컨테이너' 역할을 한다.
자, 그럼 이제 자유자재로 이 프래그먼트들을 넘나들며 UI를 화면에 보여주는 실습을 해 보자.
를 추가해주고, 동기화를 바로바로 해준다.(안드로이드 스튜디오에서 앱을 개발할땐 의존성을 추가하거나 gradle 파일에 어떠한 변경 사항이 만들어지면 즉시 동기화를 해주는걸 잊지 말자!
2. 탐색 그래프를 저장할 파일을 생성한다.
Navigation 파일 생성
위와 같이 res 폴더를 우클릭 -> Android Resource File을 누르고 위와 같이 설정한다. Resource type을 꼭 Navigation으로 해줘야 하는걸 잊지 말자!
OK버튼을 누르면 안드로이드 스튜디오는 자동으로 nav_graph.xml을 자동으로 열어줄 것이다. 오른쪽 위에 3개의 옵션중 'Design'을 선택하면 아래와 같이 보일것이다.
Navigation 탐색 그래프
현재 이 탐색 그래프는 비어있다 - 사용자에게 표시하거나 탐색할 수 있는 화면이 없다 :( . 탐색 그래프를 더 유용하게 만들려면 사용자에게 표시할 수 있는 화면을 정의하는 목적지를 그래프에 추가해야 한다. 에디터 중앙의 텍스트에 나와 있는대로, 에디터의 왼쪽 상단에 있는 "An empty navigation graph Add Destination" 아이콘을 클릭한다.
아이콘을 클릭하면 아래와 같은 모습이 보일텐데, 여기서는 CrimeListFragment를 먼저 navigation destination에 추가 할 것이다. 이CrimeListFragment가 맨 처음 추가된 destination이므로, 이 CrimeListFragment 화면이 유저들에게 가장 먼저 보이는 화면이 될 것이다.
이렇게 하면 기본적인 Navigation과 destination이 추가 된 것이다. 포스팅이 너무 길어지므로 다음 포스팅에서 계속 하도록 하겠다!
ViewModel을 사용하는 이유는 간단하다. MainActivity에 데이터를 저장하는 대신 ViewModel에 데이터를 저장하는 가장 큰 이유는 화면 회전을 좀 더 효율적으로 지원하고, 프로세스 종료시 데이터를 유지하고 관리하기 위함이다. ViewModel을 사용하면 화면이 필요로 하는 모든 데이터를 한 곳에 모아 데이터를 포맷하고 최종 결과에 쉽게 액세스할 수 있다.
Step 1
먼저, viewmodel을 사용하기전 2개의 라이브러리를 추가해야한다. Android 앱 프로젝트에서는 Gradle 빌드 시스템을 사용한다. Gradle은 프로젝트를 빌드하고 필요한 라이브러리를 관리하는 도구 중 하나이고, 'dependencies' 블록은 프로젝트가 필요로 하는 외부 라이브러리를 지정하는 부분으로, 이 라이브러리들은 해당 프로젝트의 소스 코드에서 사용될 수 있다. 그래들 스크립트 파일에서 'dependencies' 블록을 수정하면, 프로젝트가 사용하는 라이브러리를 추가, 업데이트, 또는 제거할 수 있다.
보면 여기 dependencies 블럭에 우리가 추가하고싶은 라이브러리를 추가해주면 된다(ViewModel support주석 아래 두줄). 앱이 컴파일 될때 gradle은 해당 dependencies들을 찾아내고, 알아서 다운로드 해서 포함시켜준다!(얼마나 간단한가? 수많은 개발자들이 갈려나갔을것이다,,,)
***gradle파일을 수정한 후에는 "동 기 화" 하는것을 잊지 말자!!!***
Step2
ViewModel의 하위 클래스인 QuizViewModel을 만들자.
package edu.vt.mobiledev.multiquiz
import androidx.lifecycle.ViewModel
import android.util.Log
private const val TAG = "QuizViewModel"
class QuizViewModel : ViewModel(){
init {
Log.d(TAG, "ViewModel instance created")
}
override fun onCleared() {
super.onCleared()
Log.d(TAG, "ViewModel instance about to be destroyed")
}
}
위의 태그나 로그출력 코드들은 전부 다 디버깅을 하기 위함이다.
이제 MainActivity로 가서 ViewModel들을 추가한다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val quizViewModel: QuizViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
...
setContentView(binding.root)
Log.d(TAG, "Got a QuizViewModel: $quizViewModel")
binding.trueButton.setOnClickListener { view: View ->
checkAnswer(true)
}
...
}
Kotlin에서 `by` 키워드는 프로퍼티를 구현하는 방법 중 하나인데 프로퍼티의 기능을 외부 코드 단위에 위임하는 방법이다. Kotlin에서 흔히 사용되는 프로퍼티 델리게이트 중 하나는 `lazy`이다. `lazy` 프로퍼티 델리게이트를 사용하면 프로퍼티가 액세스될 때까지 초기화를 지연시켜 자원을 절약할 수 있다.
또한, 여기서 소개된 `viewModels()` 프로퍼티 델리게이트도 비슷한 방식으로 작동한다. QuizViewModel은 액세스되지 않는 한 초기화되지 않는다. 로깅 메시지에서 참조함으로써 프로퍼티를 초기화하고 동시에 값을 로깅할 수 있다.
`viewModels()` 프로퍼티 델리게이트는 내부에서 여러 작업을 처리한다. Activity가 처음으로 QuizViewModel을 쿼리할 때, `viewModels()`는 새로운 QuizViewModel 인스턴스를 생성하고 반환한다. 구성 변경 후에 QuizViewModel을 다시 쿼리할 때는 처음에 생성된 인스턴스를 반환한다. 활동이 종료될 때(화면에서 앱을 닫을 때), ViewModel-Activity 쌍은 메모리에서 제거된다.
활동 내에서 QuizViewModel을 직접으로 인스턴스화하지 말아야 한다. 대신에 `viewModels()`을 쓰자. 직접 ViewModel을 인스턴스화하는 것이 동일하게 작동할 것처럼 보일 수 있지만, 활동의 구성이 변경된 후에 동일한 인스턴스가 반환되는 이점을 잃게 된다.
이렇게 복잡하게 ViewModel을 Acitivity 에서 구현하지 않고 따로 구현하는 이유는 간단하다. MainActivity의 간결함과 MainActivity는 화면에 display하는 역할만 수행 할 뿐, 복잡한 로직이나 세부적인 데이터 내용을 숨겨 유지보수를 위함이다.
저번 시간에는 findViewById를 이용해 xml파일에 있는 문자열의 아이디값을 참조했다. 하지만, 이 방법은 좋은 방법은 아니다.
`findViewById`와 `ViewBinding`의 `binding` 객체를 사용하는 것은 안드로이드 앱에서 뷰를 참조하는 두 가지 다른 방법이다.
1. **findViewById:** - `findViewById`는 기존에 사용되던 방식으로, XML 레이아웃에서 정의한 뷰의 ID를 사용하여 뷰를 찾아온다. - 예를 들어, `findViewById(R.id.true_button)`은 XML 레이아웃에서 `true_button` ID를 가진 뷰를 찾아온다. - 이 방식은 뷰를 찾아오는 과정에서 캐스팅이 필요하며, 레이아웃의 변경 시에 레이아웃에 대한 일일이 변경이 필요하다.
trueButton = findViewById(R.id.true_button)
2. **ViewBinding:** - `ViewBinding`은 안드로이드 스튜디오에서 자동으로 생성해주는 바인딩 클래스(`ActivityMainBinding`과 같은)를 사용하여 뷰에 직접 접근한다.
-binding이 자동으로 만들어준 binding요소 파일에 접근하려면 android View가 아니라 project View를 통해 app/build/generated/..을 통해 접근한다. 이 자동완성 파일은 "빌드할때 만들어진다!" - 이 방식은 레이아웃의 변경에 유연하게 대처할 수 있고, 캐스팅이 필요하지 않다. - 예를 들어, `binding.trueButton`은 바인딩 클래스에서 생성된 `trueButton`에 직접 접근한다.
layoutInflator를 인자로 넘겨 inflate를 해야한다. .inflate는 xml레이아웃 파일을 실제 뷰 객체로 변환하고, 이를 이용해 액티비티의 화면에 표시하는 역할을 한다. 그리고 root는 해당 xml파일의 최상단 뷰를 나타낸다. 이로써 최상위 뷰 아래 생성된 하위 뷰와 구성 요소들이 액티비티 화면에 표시된다!
먼저, ActivityMainBinding타입의 binding이라는 변수를 선언해준다. 그 다음, 예를들어 brisbaneButton.updateColor()을 해야한다고 해보자. 원래대로 findViewById()를 쓰려면, 먼저 문자열의 아이디를 가져온 후 변수에 할당해줘야 한다.
var brisBtId = findViewById(R.id.brisbaneButton)
brisBtId.updateColor()
저번 포스팅에서 언급됐던 res 라는 상위 폴더는 resource의 줄임말이다. 그렇다면 resource는 무엇일까? resource의 정의는 "A piece of your application that is not code – things like image files, audio files, and XML files." 이다. 즉, 코드가 아닌 다른 형태의 자원들을 일컫는 말이다.
그렇다면, 코드가 아닌 리소스들을 어떻게 참조할까?
정답은 resource ID를 이용해 참조하는것이다.(여기서 또 해시테이블 생각이 났다). 레이아웃을 위한 리소스 아이디는
R.layout.activity_main. 이다. 여기서 R은 자동으로 컴파일때 생성되는 클래스이고, 이 클래스에는 모든 리소스 아이디가 정수 형태로 나열되어 있다. 이 아이디는 우리가 직접 지정해주지 않아도 컴파일을 하면 build process가 자동으로 우리에게 리소스 아이디를 부여해준다. [사실, build process는 모든 리소스에 리소스 아이디를 자동으로 부여해준다]
R.layout.activity_main 을 참조할때, activity_main이라고 이름 지어진 정수형 아이디를 R 클래스 하위의 layout에서 참조하는것이다.
자바의 R(resource) 클래스
package com.bignerdranch.android.geoquiz;
public final class R {
public static final class anim {
...
}
...
public static final class id {
...
}
public static final class layout {
...
public static final Int activity_main=0x7f030017;
}
public static final class mipmap {
public static final Int ic_launcher=0x7f030000;
}
public static final class string {
...
public static final Int app_name=0x7f0a0010;
public static final Int false_button=0x7f0a0012;
public static final Int question_text=0x7f0a0014;
public static final Int true_button=0x7f0a0015;
}
}
위의 string의 정적 클래스를 살펴보자. 우리는 아무런 아이디를 부여하지 않았지만, build process가 이렇게 자동으로 아이디를 부여해줬다.
Android은 전체 레이아웃 및 각 문자열에 대한 리소스 ID를 생성했지만, activity_main.xml의 개별 "뷰"에 대한 리소스 ID는 생성하지 않았다. 모든 뷰가 리소스 ID가 필요한 것은 아니다! 이 장에서는 코드에서 두 버튼만 상호 작용하므로 그 두 버튼만 리소스 ID가 필요하다.
뷰에 대한 리소스 ID를 생성하려면 뷰의 정의에 android:id 속성을 포함해야 한다. activity_main.xml에서 각 버튼에 android:id 속성을 추가해보자.
id에 대한 값에는 + 기호가 있지만 android:text에 대한 값에는 + 기호가 없다. 이것은 리소스 ID를 생성하고 문자열을 참조하기만 하기 때문이다.
이제 버튼에도 아이디가 주어졌으니(참조 가능), 버튼들과 상호작용 해보도록 해보자!
이제 MainActivity에서 참조 할 수 있다.
MainActivity.kt 파일에 아래와 같이 코드를 작성한다. 위에서 설명했듯 R.id.true_button은 "Resource클래스의 id라는 정적 클래스의 true버튼의 아이디를 참조할것이고, findViewById라는 메서드를 통해 id를 리턴받고 trueButton변수에 아이디를 부여한다.
class MainActivity : AppCompatActivity() {
private lateinit var trueButton: Button
private lateinit var falseButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
trueButton = findViewById(R.id.true_button)
falseButton = findViewById(R.id.false_button)
}
}
findViewById 메서드는 View타입을 리턴받지만, 더 자세하게 말하면 우리가 원하는 View의 하위 타입으로 캐스팅 되어 리턴된다. 위의 경우에는 View의 Button을 리턴한다.
위의 코드에서는 버튼의 리소스 ID를 사용하여 팽창된 객체를 검색하고 이를 뷰 속성에 할당한다. 뷰 객체는 onCreate(…)에서 setContentView(…)이 호출된 후에 메모리에 팽창되어 사용 가능하기 때문에 속성 선언에서 lateinit을 사용하여 해당 속성의 내용을 사용하기 전에 비어 있지 않은 뷰 값을 제공할 것이라고 컴파일러에 알린다.그런 다음 onCreate(…)에서 뷰 객체를 찾아 적절한 속성에 할당한다.
※ 이제, 버튼이 "상호작용" 할 수 있도록 Event Listener를 추가해줘야 한다.
안드로이드 애플리케이션은 일반적으로 이벤트 기반이다. 명령 줄 프로그램이나 스크립트와 달리, 이벤트 기반 애플리케이션은 시작하고 나서 사용자가 버튼을 누르는 등의 이벤트를 기다린다.
애플리케이션이 특정 이벤트를 기다리고 있을 때 해당 이벤트를 "듣고 있는" 것이라고 한다. 이벤트에 응답하기 위해 생성하는 객체를 리스너(listener)라고하며, 리스너는 해당 이벤트를 위한 리스너 인터페이스를 구현한다.
안드로이드 SDK에는 다양한 이벤트에 대한 리스너 인터페이스가 제공되므로 직접 작성할 필요가 없다! 이 경우, 듣고 싶은 이벤트는 버튼이 눌렸을 때 또는 "클릭"되었을 때이므로 리스너는 View.OnClickListener 이다.
자 그러면, trueButton의 이벤트 리스너를 추가해주자.
MainActivity.kt 파일로 가서
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
trueButton = findViewById(R.id.true_button)
falseButton = findViewById(R.id.false_button)
trueButton.setOnClickListener { view: View ->
// Do something in response to the click here
}
}
혹시 view-> 같은 식을 처음 본다면 람다식을 검색해보자
이런식으로 이벤트 리스너를 추가해주자. {} 괄호 사이에는 버튼이 클릭되었을때 어떤 일을 해야할지 알려주면 된다.
같은 방법으로 false button에도 이벤트 리스너를 추가하자!
※Toast 사용하기
우리가 만들 앱은 퀴즈 앱이고, 문제가 나왔을때 true, false를 눌러 정답인지 아닌지 '팝업'을 띄우는 것이다. 이 팝업을 띄울때 사용하는 속성이 toast이다.(왜 토스트지? 토스트기에서 빵이 시간되면 팍 하고 튀어올라오는 모습을 생각한건가?)
토스트는 사용자에게 어떤 사실을 알리지만 입력이나 조치를 필요로하지 않는 간단한 팝업이다(즉, 메세지를 보여주는 팝업만 띄우는거지 거기에 사용자한테 입력을 받거나 하지는 않는다).
1. 다시 strings.xml 파일로 들어가 우리가 팝업창에 보여줄 문자열을 생성한다.
<resources>
<string name="app_name">GeoQuiz</string>
<string name="question_text">Canberra is the capital of Australia.</string>
<string name="true_button">True</string>
<string name="false_button">False</string>
<string name="correct_toast">Correct!</string>
<string name="incorrect_toast">Incorrect!</string>
</resources>
2. 이번엔 다시 MainActivity.kt로 들어가 아래 코드를 써준다
override fun onCreate(savedInstanceState: Bundle?) {
...
trueButton.setOnClickListener { view: View ->
// Do something in response to the click here
Toast.makeText(
this,
R.string.correct_toast,
Toast.LENGTH_SHORT
).show()
}
falseButton.setOnClickListener { view: View ->
// Do something in response to the click here
Toast.makeText(
this,
R.string.incorrect_toast,
Toast.LENGTH_SHORT
).show()
}
}
토스트를 생성하기 위해서는 정적함수 Toast.makeText(Context, Int, Int)를 호출한다.
자바에서의 정적 함수는 선언 당시에 메모리에 할당되어 객체를 만들지 않고도 함수명만을 호출하여 함수의 기능을 한다!
두번째 인자로 받은 Int는 토스트가 팝업에 보여줄 문자열의 resource ID 이다. Context 파라미터는 토스트 클래스가 문자열의 resource ID를 찾고 사용하기 위해 필요하다. 마지막 int파라미터는 팝업이 얼마나 오랫동안 보여지는지를 결정하는 파라미터이다. 토스트를 생성한 후, Toast.show()메서드를 이용하여 화면에 보여줄것이다.
자, 그럼 이제 우리가 만든 앱이 화면에 어떻게 보이는지 Emulator를 실행해서 보자!
우리는 안드로이드 가상 기기를 통해 확인을 할건데, 가상 기기를 구축하는법은 Tools - AVD manager - create virtual device - emulate a Pixel 4 를 선택해준다.
그 다음 창에서는 API 32 버전을 다운로드 해준다!
그러면 이제 편집기 오른쪽에 안드로이드 폰 모양이 보일것인데, 우리가 만든 GeoQuiz 앱이 보일것이다
짜잔!! 저기서 TRUE 버튼과 False 버튼을 누르면 이제 우리가 위에서 Toast로 만들었던 팝업이 보일것이다.
짝짝짝!!
정말 별건 아니지만, 그래도 내 손으로 만든 첫 앱이 되었으니 뭔가 뿌듯하다. 이 앱을 만들면서 새로운 지식들도 습득하였고(리소스, 아이디, 코틀린, 스트링, 리소스 아이디, 이벤트리스너 등등), 재밌었다. 다음 포스팅에는 이것보다 더 많은 기능을 구현하는 다른 앱을 한번 만들어 보겠다!!
이번 학기에 시작한 안드로이드 개발 수업! 아직 아무것도 모르겠고 어색하지만 언제나 그래왔듯 다시 한번 시작해보겠다.
원래 나는 무언가를 배우기 시작할때 이론적인것부터 들이밀고 공부하라고 하면 진절머리가 나는 스타일이다. 직접 간단한 앱을 만들면서 차근차근 더 깊이 배워보도록 하겠다.
사용 언어는 Kotlin을 사용하고, 코틀린은 자바에 대한 이해가 있으면 코딩하기가 좀 더 수월할거라고 들었다. 다행히 자료구조 알고리즘을 자바로 배운터라 크게 걱정하진 않았다. 앱을 만드는데 쓰는 프로그램은 Android Studio를 썼고, Mac M1 pro에서 사용중이다. 이번에 만들어볼 앱은 정말 간단한 퀴즈 맵이다. 문제가 나오고 True/ False를 선택 할 수 있는 앱이다. 한번 시작해보자!
먼저, Android Studio를 실행하고 New Project(Empty)를 누르면 아래와 같은 창이 나온다.
중요한 태그 몇개만 짚고 넘어가겠다.
Name : 말 그대로 "앱 이름" 이다. 여기서는GeoQuiz라고 칭하겠다.
Language : 사용 언어는 위에서 말했듯 코틀린이다.
Minimum SDK(최소 지원 API레벨) : 이건 현재 만드려는 앱이 어디까지 지원 할 수 있는지를 나타내는 좌표인데, API뒤의 숫자가 커질수록 최신 API이며, 그만큼 현존하는 기기들과 호환되지 않는 경우가 많다. 일반적으로는 낮은 API를 사용한다.
그 다음, 이러한 창이 나오면 왼쪽 아래 망치버튼을 누르면 콘솔에 알 수 없는 명령어들이 나오면서 BUILD SUCCESSFUL이라는 문구가 뜬다.
왼쪽 위에 보면, project view를 설정 할 수 있는데, Android Studio의 디폴트 view는 Android view이다[개발자들이 디폴트값을 이렇게 설정 해놓은데에는 항상 이유가 있다]. Android view는 실제 진행하는 프로젝트의 디렉토리를 숨겨 우리가 현재 진행중인 안드로이드 프로젝트에만 더 집중 할 수 있도록 해준다.
Android view와 Project view의 차이
한눈에 봐도 왼쪽이 훨씬 더 간결해보인다.
그 다음, 아래와 같이 Empty Views Activity를 누른다. 되게 복잡해 보이고 처음 해보는 사람들은 헷갈리겠지만, 지금은 하나하나가 뭔지 알 필요 없다!
그러면 아래와 같이 처음 보는 코드들이 보일것이다.
이제, 안드로이드에게 앱을 시작할때 화면에 무엇을 로드할지 알려주어야 한다. [UI 레이아웃]
그 다음, 왼쪽 에디터에 보이는 AndroidManifest.xml파일을 연다. 그러면 처음 보는 코드들이 쓰여있는데, 거기서 <activity/> 태그로 감싸진 부분을 수정해준다. 디폴트는 아래와 같지만
그런데 여기서 문제가 하나 생긴다. 위의 코드를 작성하면 오른쪽 위에 IDE Warning이 뜰것이다. 버튼에 대한 warning인데, 여러 버튼들이 horizontal layout에 인접해 있으면 뜨는 오류이다. 해결 방법은 간단하다. 버튼 태그 안에 style="?android:buttonStyle" 만 추가해주면 된다.
코드를 살펴보면 Textview, Button 는 android : text라는 속성이 있다. 이 속성은 view에게 어떤 텍스트를 보여줄건지 명령하는 속성이다. 하지만 여기서 중요한 부분이 있다. 이 text속성의 값은 raw 문자열이 아닌 대신에, @string/ syntax를 이용하여 string값을 참조해야 한다. 단순히 hardcoding으로 android : text = "True" 라고 할 수 있지만, 하드코딩은 좋은 방법이 아니다(유지보수 문제). 대신 문자열을 별도의 파일에 넣고 참조하는것이 좋은 방법이다.
자 그럼 이제, 참조할 문자열 파일을 만들어보자!
모든 프로젝트는 디폴트 문자열 파일 res/values/strings.xml 이 포함되어있다. 이 파일을 열고, 우리가 view에 전달해야 할 문자열을 MainAcrtivity가 참조 할 수 있도록 문자열을 추가해주자.
strings.xml파일에 문자열을 더한 모습
문자열 파일은 이름이 꼭 strings가 아니여도 되고, 여러가지 문자열 파일을 만들수도 있다.(단, 무조건 res라는 상위폴더 아래에 만들어야 한다) 각 분야마다 문자열 파일을 따로 만들어 놓는다면(직관적인 문자열 파일 이름과 함께) 유지보수가 더욱 더 쉬워질 것이다.