使用 ViewModel 和数据绑定显示 Room 数据库中的数据时出现问题
Problem displaying data from Room database with ViewModel and Databinding
我正在构建一个 android 应用程序,我在 Detailfragment 的数据库中添加产品并在主要片段中显示它们:
这是产品 class:
@Parcelize
@Entity(tableName = "product_table")
data class Product (
@ColumnInfo(name = "name")
var name:String = "product",
):Parcelable{@PrimaryKey(autoGenerate = true)
var id:Long = 0L}
这是我正在使用的 ProductViewModel
class ProductViewModel(val database: ProductDatabaseDao) : ViewModel() {
val products = database.getAllProducts()
val productsString = products.value?.get(0)?.name
private var _navigateToProductsList = MutableLiveData<Boolean>()
val navigateToProductsList : LiveData<Boolean>
get() = _navigateToProductsList
private suspend fun insert(product: Product) {
database.insert(product)
}
fun onAddProduct(){
viewModelScope.launch {
val product = Product()
product.name = "toy"
insert(product)
}
_navigateToProductsList.value = true
}
fun doneNavigation(){
_navigateToProductsList.value = false
}
}
这是 ProductDetailFragment:
class ProductDetailFragment : Fragment() {
private val productViewModel: ProductViewModel by activityViewModels {
ProductViewModelFactory(
getDatabase()
)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val binding = DataBindingUtil.inflate<FragmentProductDetailBinding>(
inflater,
R.layout.fragment_product_detail,
container,
false
)
binding.lifecycleOwner = this
binding.productViewModel = productViewModel
productViewModel.navigateToProductsList.observe(viewLifecycleOwner, Observer {
if (it == true) {
this.findNavController()
.navigate(ProductDetailFragmentDirections.actionProductDetailFragmentToProductsListFragment())
// Reset state to make sure we only navigate once, even if the device
// has a configuration change
productViewModel.doneNavigation()
}
})
return binding.root
}
fun getDatabase(): ProductDatabaseDao {
val application = requireNotNull(this.activity).application
val dataSource = ProductDatabase.getInstance(application).productDatabaseDao
return dataSource
}
}
这是product_detail_fragment
<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="productViewModel"
type="com.github.mariemmezghani.inventory.viewModel.ProductViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/product_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:background="@drawable/round_button"
android:text="@string/save_button"
android:textColor="@color/white"
android:onClick="@{()->productViewModel.onAddProduct()}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/product_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
这里我在save_Button
中调用了productViewmodel.onAddProduct()
这是 products_list_fragment
<?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 to make available to the XML via data binding. In this case,
the whole ViewModel, so that we can access the LiveData,
click handlers, and state variables. -->
<data>
<variable
name="viewModel"
type="com.github.mariemmezghani.inventory.viewModel.ProductViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ProductsListFragment">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:clickable="true"
app:backgroundTint="@color/color_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@android:drawable/ic_input_add" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:text="@{viewModel.productsString}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
在实现recyclerView显示用户添加的产品之前,我想测试产品是否正确添加到数据库中,所以我只是添加了一个产品并给它命名为“toy”并尝试显示它在 product_list_fragment.
的 TextView 中
我不明白为什么这不起作用,当我按 save_Button
时屏幕上没有显示任何内容
感谢您的帮助
您的视图模型可能会在数据库操作完成之前销毁,因此您可以尝试下面的方法
fun onAddProduct(){
viewModelScope.launch {
val product = Product()
product.name = "toy"
insert(product)
_navigateToProductsList.postValue(true)
}
}
我找到了问题的解决方案。
问题是 products.value?
(类型 LiveData)从 Room 返回 null。我设法通过使用 Transformations.map 解决了这个问题。
我改变了这个:
val productsString = products.value?.get(0)?.name
对此:
val productsString = Transformations.map(products){products -> products.get(0).name}
我正在构建一个 android 应用程序,我在 Detailfragment 的数据库中添加产品并在主要片段中显示它们: 这是产品 class:
@Parcelize
@Entity(tableName = "product_table")
data class Product (
@ColumnInfo(name = "name")
var name:String = "product",
):Parcelable{@PrimaryKey(autoGenerate = true)
var id:Long = 0L}
这是我正在使用的 ProductViewModel
class ProductViewModel(val database: ProductDatabaseDao) : ViewModel() {
val products = database.getAllProducts()
val productsString = products.value?.get(0)?.name
private var _navigateToProductsList = MutableLiveData<Boolean>()
val navigateToProductsList : LiveData<Boolean>
get() = _navigateToProductsList
private suspend fun insert(product: Product) {
database.insert(product)
}
fun onAddProduct(){
viewModelScope.launch {
val product = Product()
product.name = "toy"
insert(product)
}
_navigateToProductsList.value = true
}
fun doneNavigation(){
_navigateToProductsList.value = false
}
}
这是 ProductDetailFragment:
class ProductDetailFragment : Fragment() {
private val productViewModel: ProductViewModel by activityViewModels {
ProductViewModelFactory(
getDatabase()
)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val binding = DataBindingUtil.inflate<FragmentProductDetailBinding>(
inflater,
R.layout.fragment_product_detail,
container,
false
)
binding.lifecycleOwner = this
binding.productViewModel = productViewModel
productViewModel.navigateToProductsList.observe(viewLifecycleOwner, Observer {
if (it == true) {
this.findNavController()
.navigate(ProductDetailFragmentDirections.actionProductDetailFragmentToProductsListFragment())
// Reset state to make sure we only navigate once, even if the device
// has a configuration change
productViewModel.doneNavigation()
}
})
return binding.root
}
fun getDatabase(): ProductDatabaseDao {
val application = requireNotNull(this.activity).application
val dataSource = ProductDatabase.getInstance(application).productDatabaseDao
return dataSource
}
}
这是product_detail_fragment
<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="productViewModel"
type="com.github.mariemmezghani.inventory.viewModel.ProductViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/product_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:background="@drawable/round_button"
android:text="@string/save_button"
android:textColor="@color/white"
android:onClick="@{()->productViewModel.onAddProduct()}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/product_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
这里我在save_Button
中调用了productViewmodel.onAddProduct()这是 products_list_fragment
<?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 to make available to the XML via data binding. In this case,
the whole ViewModel, so that we can access the LiveData,
click handlers, and state variables. -->
<data>
<variable
name="viewModel"
type="com.github.mariemmezghani.inventory.viewModel.ProductViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ProductsListFragment">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:clickable="true"
app:backgroundTint="@color/color_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@android:drawable/ic_input_add" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:text="@{viewModel.productsString}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
在实现recyclerView显示用户添加的产品之前,我想测试产品是否正确添加到数据库中,所以我只是添加了一个产品并给它命名为“toy”并尝试显示它在 product_list_fragment.
的 TextView 中我不明白为什么这不起作用,当我按 save_Button
感谢您的帮助
您的视图模型可能会在数据库操作完成之前销毁,因此您可以尝试下面的方法
fun onAddProduct(){
viewModelScope.launch {
val product = Product()
product.name = "toy"
insert(product)
_navigateToProductsList.postValue(true)
}
}
我找到了问题的解决方案。
问题是 products.value?
(类型 LiveData)从 Room 返回 null。我设法通过使用 Transformations.map 解决了这个问题。
我改变了这个:
val productsString = products.value?.get(0)?.name
对此:
val productsString = Transformations.map(products){products -> products.get(0).name}