이미지 검색 앱을 만들어가면서 AAC에 대해 이해하는 시간을 가져보기로 했다.
이번 시간에는 검색어 입력 EditText와 Button을 가진 화면을 작성해보자.
먼저 검색어를 입력 할 EditText와 검색을 실행할 Button을 가지는 xml을 작성하자.
<!-- activity_main.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="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:orientation="horizontal"
android:padding="5dp">
<EditText
android:id="@+id/query"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="10dp"
android:maxLines="1"
android:inputType="text"
android:imeOptions="actionSearch"
android:hint="검색어"/>
<Button
android:id="@+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="검색"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
그리고 이 xml을 레이아웃으로 가지는 액티비티를 하나 생성했다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
이제 검색어를 가져와서 검색을 실행하는 코드를 작성해보자.
...
//검색어
var queryString : String = ""
...
override fun onCreate(savedInstanceState: Bundle?) {
...
//query를 id로 갖는 EditText를 찾기
val query = findViewById<EditText>(R.id.query)
//search를 id로 갖는 Button을 찾기
val search = findViewById<Button>(R.id.search)
search.setOnClickListener {
queryString = query.text.toString()
//TODO 검색어로 이미지 검색 실행
}
}
위의 코드들도 작동하는데에는 전혀 문제가 없지만 다음과 같이 개선 할 수 있을것같다.
1. DataBinding을 통해 findViewById 과정 생략하기
2. ViewModel과 DataBinding을 통해 검색어를 LiveData로 관리하고 양방향데이터바인딩을 적용하기
먼저 검색어 String과 이후 구현할 검색 결과 데이터를 관리할 ViewModel을 작성하자.
/**
* ViewModel을 Override해도 괜찮지만
* 이번 시간에는 Toast를 생성하기 위해 Context가 필요하여
* AndroidViewModel을 상속받았다.
*/
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val context : Context get() = getApplication<Application>().applicationContext
//activity_main.xml 의 query EditText와 양방향 데이터바인딩으로 묶일 검색어 LiveData
var query : MutableLiveData<String> = MutableLiveData("")
//activity_main.xml 의 search Button의 onClick으로 실행 될 function
fun getData() {
//TODO 검색어로 이미지 검색 실행
val queryString : String? = query.value
if (queryString != null) {
Toast.makeText(context, "검색어 : ${queryString}", Toast.LENGTH_SHORT).show()
}
}
}
그리고 DataBinding을 적용하기 위해 activity_main.xml을 다음과 같이 수정했다.
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!-- DataBinding을 적용하기 위해 기존 레이아웃을 <layout> 으로 감싸준다 -->
<layout>
<!-- 연결할 클래스는 <data> 태그 안에 <variable> 로 선언하여 사용할 수 있다 -->
<data>
<variable
name="myViewModel"
type="com.notegg.viewmodelexample.MyViewModel" />
</data>
<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="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:orientation="horizontal"
android:padding="5dp">
<!-- query가 변경되면 ViewModel의 LiveData값도 갱신되어야하기 때문에
양방향 데이터 바인딩으로 설정 -->
<EditText
android:id="@+id/query"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="10dp"
android:maxLines="1"
android:inputType="text"
android:imeOptions="actionSearch"
android:text="@={myViewModel.query}"
android:hint="검색어"/>
<!-- function을 호출하도록 설정할 수도 있다 -->
<Button
android:id="@+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="검색"
android:onClick="@{() -> myViewModel.getData()}"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
단순히 데이터를 표시만 하는것이 아닌, query EditText의 값이 바뀔때 LiveData도 갱신되어야 하기 때문에
단방향데이터바인딩(@{})이 아닌 양방향데이터바인딩(@={})으로 설정해주었다.
마지막으로 MainActivity도 다음과 같이 수정해주었다.
class MainActivity : AppCompatActivity() {
//DataBinding 설정
private val binding : ActivityMainBinding by lazy {
//setContentView를 대신하여 레이아웃을 설정해준다
DataBindingUtil.setContentView(this, R.layout.activity_main)
}
/** 원래는 다음과 같이 선언해야 하지만
* val myViewModel : MyViewModel by lazy {
* ViewModelProvider(this).get()
* }
* ktx를 사용하면 by viewModels()와 같이 간단하게 선언할 수 있다.
*/
private val myViewModel : MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//activity_main.xml에 설정한 myViewModel에 this.myViewModel을 할당해준다
binding.apply {
myViewModel = this@MainActivity.myViewModel
//LiveData를 사용하는 경우 잊지 말고 lifecycleOwner를 설정해주자
lifecycleOwner = this@MainActivity
}
binding.query.setOnEditorActionListener { _, _, _ ->
myViewModel.getData()
true
}
}
}
참고하면 좋은 문서
Android KTX
https://developer.android.com/kotlin/ktx?hl=ko
'Android(Kotlin)' 카테고리의 다른 글
심기일전 코틀린! - 예제 코드와 같이 Hilt + Retrofit2 이해하기 (0) | 2023.07.28 |
---|---|
심기일전 코틀린! 04. Hilt (Dependency Injection) (0) | 2023.07.26 |
심기일전 코틀린! - 03. AAC (Android Architecture Components) (0) | 2023.07.20 |
Compose에서 Tap, Gesture 감지하기 (0) | 2023.06.29 |
심기일전 코틀린! - 02. Jetpack Compose (2) (0) | 2023.06.23 |