이 글은 Room Database기능을 사용하고 있다고 가정하고,
Fts4를 사용하여 DB의 검색기능을 사용하는 예제입니다.
Room과 LiveData를 MVVM패턴으로 구현한 예제는 다음글을 참고하세요
[Android/이론] - MVVM 시작하기(1) ― LiveData, Room을 MVVM패턴으로 사용해보자
[Android/이론] - MVVM 시작하기(2) ― Room Entity, Dao, Database 만들기
[Android/이론] - MVVM 시작하기(3) ― ViewModel, 데이터 바인딩(Data Binding)
[Android/이론] - MVVM 시작하기(4) ― Repository, Model-View 연동
먼저, 검색을 대상이 될 기존의 Entity는 다음과 같습니다.
@Entity(tableName = "tool_table")
data class ToolEntity(
@PrimaryKey
@ColumnInfo(name="name")
val name: String
)
String 변수 name을 가지고 있고, 이 name을 검색할 것입니다.
그리고 FtsEntity를 만들어 줍니다.
@Entity(tableName = "toolsFts")
@Fts4(contentEntity = ToolEntity::class)
class ToolFtsEntity(
val name: String
)
검색 대상이 될 Entity를 contentEntity로 하고, 검색하고 싶은 변수를 전부 추가해 주면 됩니다.
그리고 이 Entity를 데이터베이스에 추가해주어야 합니다.
@Database(entities = [ToolEntity::class, ToolFtsEntity::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun toolDao(): ToolDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(
...
entities = [ToolEntity::class, ToolFtsEntity::class]
FtsEntity를 데이터베이스에 추가해주세요.
이제 Dao에 검색 메서드를 만들어줍니다.
@Query("SELECT tool_table.* FROM tool_table JOIN toolsFts ON (tool_table.name = toolsFts.name) WHERE toolsFts MATCH :query")
fun searchAllTools(query: String?): LiveData<List<ToolEntity>>
- " SELECT tool_table.* FROM tool_table JOIN toolsFts ON (tool_table.name = toolsFts.name) WHERE toolsFts MATCH :query "
JOIN은 두개이상의 테이블을 가로방향으로 붙인다는 뜻입니다. ON (tool_table.name = toolsFts.name) 을 통해 tool_table의 name과 Fts table의 name을 맞춰 붙인다고 할 수 있습니다.
rowid를 사용하여 ON (tool_table.rowid = toolFts.rowid) 로도 사용 가능합니다.
또다른 자세한 사항은 SQLite의 문법을 알아야 합니다..
Dao에 위와같이 추가하고, Repository에 메서드를 추가하였습니다.
private val toolDao = mDatabase.toolDao()
...
fun searchTools(query: String?): LiveData<List<ToolEntity>> {
return toolDao.searchAllTools(query)
}
그다음 ViewModel에서 LiveData ToolEntity 변수 allTools를 Transformation을 이용하여 다시 정의하였습니다.
/** Use the savedStateHandle.getLiveData() as the input to switchMap,
* allowing us to recalculate what LiveData to get from the DataRepository
* based on what query the user has entered
*/
var allTools: LiveData<List<ToolEntity>> = Transformations.switchMap<CharSequence?, List<ToolEntity>>(
savedStateHandle.getLiveData("QUERY", null),
Function<CharSequence?, LiveData<List<ToolEntity>>> { query: CharSequence? ->
if (TextUtils.isEmpty(query)) {
return@Function repository.allTools
}
else return@Function repository.searchTools("*$query*")
}
)
savedStateHandle을 이용하여 검색 query를 저장시키고, 만약 저장된 query가 있다면 search를 결과를 반환하게 됩니다.
버튼을 누르면 EditText의 text를 가져와 저장시켰습니다.
fun onClickSearch(searchTxt: String){
setQuery(searchTxt)
Log.d("DiscoverViewModel", "Search $searchTxt")
}
fun setQuery(query: CharSequence?) {
/** Save the user's query into the SavedStateHandle.
* This ensures that we retain the value across process death
* and is used as the input into the Transformations.switchMap above
*/
savedStateHandle.set("QUERY",query)
}
ViewModel의 전체 소스와, 데이터 바인딩을 이용한 layout은 다음과 같습니다.
class MyViewModel(application: Application, val savedStateHandle: SavedStateHandle)
: BaseViewModel(application) {
private val repository: Repository = Repository(AppDatabase.getDatabase(application,viewModelScope))
var allTools: LiveData<List<ToolEntity>> = Transformations.switchMap<CharSequence?, List<ToolEntity>>(
savedStateHandle.getLiveData("QUERY", null),
Function<CharSequence?, LiveData<List<ToolEntity>>> { query: CharSequence? ->
if (TextUtils.isEmpty(query)) {
return@Function repository.allTools
}
else return@Function repository.searchTools("*$query*")
}
)
fun onClickSearch(searchTxt: String){
setQuery(searchTxt)
Log.d("DiscoverViewModel", "Search $searchTxt")
}
fun insert(toolEntity: ToolEntity) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(toolEntity)
}
fun setQuery(query: CharSequence?) {
// Save the user's query into the SavedStateHandle.
// This ensures that we retain the value across process death
// and is used as the input into the Transformations.switchMap above
savedStateHandle.set("QUERY",query)
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.Myapp.viewmodel.MyViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/search_text"
android:layout_marginLeft="5dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="10"
android:hint="검색어를 입력 후 검색 버튼을 눌러주세요." />
<ImageButton
android:layout_marginRight="5dp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_search"
android:onClick="@{()->viewModel.onClickSearch(searchText.getText().toString())}"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
tools:listitem="@layout/measurement_item" />
</LinearLayout>
</layout>
이는 앞서 링크했던 MVVM시작하기의 소스의 연장선입니다.
기본 기능 구현은 앞의 링크를 참조해주세요.
[Android/이론] - MVVM 시작하기(1) ― LiveData, Room을 MVVM패턴으로 사용해보자
검색 기능 구현 소스 결과는 다음과 같습니다.
'Android > Function' 카테고리의 다른 글
[Android Test] Espresso 로 View의 Visibility 설정 하기 (0) | 2020.08.18 |
---|---|
[안드로이드] 데이터 바인딩으로 EditText의 Text를 onClick 함수 parameter로 가져오기 (0) | 2020.08.18 |
by viewModels() 사용하는 법, by viewModels() 종속성 추가 (0) | 2020.07.27 |
[안드로이드] application context 어디서나 쉽게 가져오기 ― companion object에서 context 사용 (0) | 2020.07.17 |