我的应用程序使用Google Places API,我稍后使用该API从openweather获取天气数据。我有一个SearchFragment
和RecyclerView
在这里发生。
在SearchFragment
中,我观察到我得到的列表:
viewModel.predictions.observe(viewLifecycleOwner) {
citiesAdapter.submitList(it)
}
<...>
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_fragment_weather, menu)
<...>
searchView.onQueryTextChanged {
viewModel.searchQuery.value = it
}
}
MyviewModel
:
class SearchViewModel @Inject constructor(
private val repository: AutocompleteRepository,
private val weatherRepository: WeatherRepository
) : ViewModel() {
fun provideClient(client: PlacesClient) {
repository.provideClient(client)
}
val searchQuery = MutableStateFlow("")
private val autocompleteFlow = searchQuery.flatMapLatest {
repository.getPredictions(it)
}
val predictions = autocompleteFlow.asLiveData()
fun onAddPlace(place: PlacesPrediction, added: Boolean) {
viewModelScope.launch {
repository.update(place, added)
if (added) weatherRepository.addWeather(place)
else weatherRepository.delete(place)
}
}
fun onDestroy() = viewModelScope.launch {repository.clearDb()}
}
在adapter
中,我像这样绑定我的条目:
inner class CityViewHolder(private val binding: ItemCityToAddBinding) : RecyclerView.ViewHolder(binding.root) {
init {
binding.apply {
btnAdd.setOnClickListener {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
val place = getItem(position)
btnAdd.animate().rotation(if (place.isAdded) 45f else 0f).start()
println("Current item state (isAdded): ${place.isAdded}")
listener.onAddClick(place, !place.isAdded)
}
}
}
}
fun bind(prediction : PlacesPrediction) {
binding.apply {
val cityName = prediction.fullText.split(", ")[0]
locationName.text = cityName
fullName.text = prediction.fullText
btnAdd.animate().rotation(if (prediction.isAdded) 45f else 0f).start()
}
}
}
其中listener
作为参数从我的片段传递给我的适配器:
override fun onAddClick(place: PlacesPrediction, isAdded: Boolean) {
viewModel.onAddPlace(place, isAdded)
println("Parameter passed to onClick: $isAdded, placeId = ${place.placeId}")
}
<...>
val citiesAdapter = CitiesAdapter(this)
我的repository
的update()
方法看起来像这样:
suspend fun update(place: PlacesPrediction, added: Boolean) =
database.dao().update(place.copy(isAdded = added))
最后,我的dao
是update
:
@Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun update(prediction: PlacesPrediction)
这些都是在PlacesPrediction
类上绑定的,在这里:
@Entity(tableName = "autocomplete_table")
data class PlacesPrediction(
val fullText: String,
val latitude: Double,
val longitude: Double,
val placeId: String,
val isAdded: Boolean = false
) {
@PrimaryKey(autoGenerate = true) var id: Int = 0
}
所以,我的问题是,PlacesPrediction
的条目在我的数据库没有得到更新。实际上,我想用上面提供的代码更新的唯一字段是isAdded
,但在我按下列表项的btnAdd
后,它保持不变。我使用Android Studio的数据库检查器来验证。
我试着用@Insert
代替,像这样:
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(prediction: PlacesPrediction)
suspend fun update(place: PlacesPrediction, added: Boolean) =
database.dao().insert(place.copy(isAdded = added))
但奇怪的是,它只插入place
的副本,我点击的原始项目保持不变。
解决方案
我只有通过自己的方式才能得到想要的行为:
@Entity(tableName = "autocomplete_table")
data class PlacesPrediction(
val fullText: String,
val latitude: Double,
val longitude: Double,
val placeId: String,
var isAdded: Boolean = false,
@PrimaryKey(autoGenerate = true) var id: Int = 0
)
suspend fun update(place: PlacesPrediction, added: Boolean) =
database.dao().insert(place.copy(isAdded = added, id = place.id))
我一点也不喜欢这个方案。所以我的问题是:我如何使@Update
工作?
您可能已经理解,数据类生成的copy
方法忽略了在构造函数外部声明的所有成员。因此,place.copy(isAdded = added)
将生成所有构造函数参数的副本,但将id保留为默认的0,这意味着应该插入一个新元素,而不是更新现有的元素。
现在这是我个人的观点:
将id作为构造函数参数是最优雅的解决方案,因为更新将开箱即用。
然而,如果你不喜欢它,也许一个扩展函数可以帮助你:
inline fun PlacesPrediction.preserveId(copyBuilder: PlacesPrediction.() -> PlacesPrediction): PlacesPrediction{
val copy = copyBuilder(this)
copy.id = this.id
return copy
}
//usage
suspend fun update(place: PlacesPrediction, added: Boolean) =
database.dao().insert(place.preserveId { copy(isAdded = added) })