我的问题在于如何初始化要在 @Preview @Composable 函数中使用的视图模型中的接口。由于以下堆栈跟踪,我的预览未显示在 Android Studio 中,当我在模拟器或设备中运行时,我的下一个活动(可组合项)也未显示。
https://1drv.ms/w/s!AnsFOFM6YEPJqKYPTlOnwwfwZ1NNAQ?e=QieFPV
以下代码显示了问题区域:
@Composable
fun WishListViewModelData(fbVM: FirebaseWishListViewModel) {
val fb = Firebase
val loggedInCurrentUserEmail = fb.auth.currentUser?.email.toString()
val loggedInEmail: String = loggedInCurrentUserEmail
val recipState: State<RecipientData> = fbVM.uiRecipientState.collectAsState()
val purchaserState: State<PurchaserData> = fbVM.uiPurchaserState.collectAsState()
RecipientWishList(
loggedInEmail = loggedInEmail,
recipState = recipState,
purchaserState = purchaserState,
fbVM = fbVM
)
}
@Preview(showBackground = true, apiLevel = 33)
@Composable
fun WishListBody() {
lateinit var contactsRepository: ContactsRepository
val repoContacts = contactsRepository
val viewModel = FirebaseWishListViewModel(repoContacts)
WishListViewModelData(fbVM = viewModel)
}
@SuppressLint("SuspiciousIndentation")
@OptIn(ExperimentalGlideComposeApi::class)
@Composable
fun RecipientWishList(
loggedInEmail: String?,
recipState: State<RecipientData>,
purchaserState: State<PurchaserData>,
fbVM: FirebaseWishListViewModel
) {
val requestManager = Glide.with(LocalContext.current)
val imageFileName: String? = null
val showDialog = remember {mutableStateOf(false)}
var selectedRecipientProfile: MutableMap<String,Any> = mutableMapOf()
val purchaserInfo = purchaserState.value
val purchaserImages = purchaserState.value.purchaserImage?.value!!
Box(
Modifier
.wrapContentHeight(Alignment.CenterVertically)
.wrapContentWidth(Alignment.CenterHorizontally)
)
{
Column (
modifier = Modifier
.fillMaxWidth(1f)
.fillMaxHeight(1f)
)
{
// Purchaser Icon Button
Button(
onClick = { showDialog.value = true },
modifier = Modifier
.wrapContentWidth(Alignment.CenterHorizontally)
.wrapContentHeight(Alignment.CenterVertically),
) {
if (showDialog.value) {
ShowPopUp(purchaserImages = purchaserImages, fbVM = fbVM)
}
}
// Active recipient picture with gift items
Row (modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)){
val recipList = fbVM.getAllRecipientsInfo(loggedInEmail)
SetRecipientImage(recipientMap = recipList, rm = requestManager, imageURL =
recipState.value.recipientImage.toString() )
val productMap: MutableMap<String, String> = mutableMapOf()
GetListItems(products = productMap)
}
//List of all recipients that belongs to signed in user
LazyRow(state = rememberLazyListState()){
val numRecipients = fbVM.getAllRecipientsInfo(loggedInEmail.toString()).count()
items(numRecipients) {
ElevatedButton(onClick = {
selectedRecipientProfile =
loggedInEmail?.let { it1 -> switchRecipientProfile(it1,
fbVM.getAllRecipientsInfo(loggedInEmail)) }!!
}) {
GlideImage(model = recipState.value.recipientImage!!,
contentDescription = "${recipState.value.recipientFirstName}"
.plus("")
.plus("${recipState.value.recipientLastName}")
)
Text (recipState.value.recipientFirstName.toString())
Text (recipState.value.recipientLastName.toString())
Text (recipState.value.recipientAddress.toString())
Text (recipState.value.recipientCity.toString())
Text (recipState.value.recipientState.toString())
Text (recipState.value.recipientZipCode.toString())
Text (recipState.value.recipientPhone.toString())
Text (recipState.value.recipientEmail.toString())
}
}
}
}
}
}
My view model looks like the following:
package com.tritongames.shoppingwishlist.data.viewmodels
import android.util.Log
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.google.firebase.Firebase
import com.google.firebase.auth.auth
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.firestore
import com.google.firebase.storage.FirebaseStorage
import com.tritongames.shoppingwishlist.data.repository.contacts.ContactsRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.launch
import java.io.File
import javax.inject.Inject
data class PurchaserData(
val purchaserImage: MutableState<MutableMap<String, String>>? = null,
val purchaserFirstName: MutableState<String?>? = null,
val purchaserLastName: MutableState<String?>? = null,
val purchaserAddress: MutableState<String?>? = null,
val purchaserCity: MutableState<String?>? = null,
val purchaserState: MutableState<String?>? = null,
val purchaserZipCode: MutableState<String?>? = null,
val purchaserEmail: MutableState<String?>? = null,
val purchaserPhone: MutableState<String?>? = null,
val purchaserPassword: MutableState<String?>? = null,
val purchaserUserName: MutableState<String?>? = null,
)
data class RecipientData(
val recipientImage: MutableState<String?>? = null,
val recipientFirstName: MutableState<String?>? = null,
val recipientLastName: MutableState<String?>? = null,
val recipientAddress: MutableState<String?>? = null,
val recipientCity: MutableState<String?>? = null,
val recipientState: MutableState<String?>? = null,
val recipientZipCode: MutableState<String?>? = null,
val recipientEmail: MutableState<String?>? = null,
val recipientPhone: MutableState<String?>? = null,
val recipientPassword: MutableState<String?>? = null,
val recipientUserName: MutableState<String?>? = null,
)
@HiltViewModel
class FirebaseWishListViewModel @Inject constructor(private val contactsRepository:
ContactsRepository) : ViewModel() {
fun repoContacts(): ContactsRepository {
return contactsRepository
}
private val _uiPurchaserState = MutableStateFlow(PurchaserData())
val uiPurchaserState: StateFlow<PurchaserData> = _uiPurchaserState.asStateFlow()
private val _uiRecipientState = MutableStateFlow(RecipientData())
val uiRecipientState: StateFlow<RecipientData> = _uiRecipientState.asStateFlow()
companion object {
@Suppress("UNCHECKED_CAST")
class FirebaseWishListViewModelFactory(private val contactsRepository:
ContactsRepository):
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T =
with(modelClass){
lateinit var vm: T
when{
isAssignableFrom(FirebaseWishListViewModel::class.java) ->
FirebaseWishListViewModel(contactsRepository)
else -> { throw IllegalArgumentException("Unknown ViewModel class:
${modelClass.name}")}
}
} as T
}
}
fun updateImageFile(imageFilePath: String, email: String) {
val fb = Firebase.firestore
val imageUpdateRef = fb.collection("purchasers").document(email)
imageUpdateRef.update("Image",imageFilePath)
}
fun getAllRecipientsInfo(loggedInEmail: String?): List<DocumentSnapshot> {
var recipients = listOf<DocumentSnapshot>()
viewModelScope.launch(Dispatchers.IO) { recipients = loggedInEmail?.let {
contactsRepository.getAllRecipients(it) }!!
}
return recipients
}
fun getAllPurchaserPictures(loggedInEmail: String): MutableMap<String,String> {
val purchaserImagesMap: MutableMap<String,String> = mutableMapOf()
viewModelScope.launch(Dispatchers.IO) {
val purchPictures = _uiPurchaserState.getAndUpdate {
val pics =
mutableStateOf(contactsRepository.getPurchaserPictures(loggedInEmail))
PurchaserData(pics)
}
purchPictures.purchaserImage?.value?.let { purchaserImagesMap.putAll(it) }
}
return purchaserImagesMap
}
fun saveExistingUserImageFiles(images: MutableMap<String,String>, loggedInEmail:
String?) {
val fb = Firebase.firestore
val purchaserReferenceImages =
fb.collection("purchasers").document(loggedInEmail.toString())
for (key in images.keys) {
purchaserReferenceImages.update("Image", images[key].toString() )
}
}
fun selectImageDownload(items: MutableMap<String,String>, picName: String): String? {
var chosenImageFilePath: String? = null
val fbStorage = FirebaseStorage.getInstance()
val purchaserImagesReference =
fbStorage.reference.child("purchasers/loggedInEmail/Images")
val localFile = File.createTempFile(items[picName].toString(), "jpg")
purchaserImagesReference.getFile(localFile)
.addOnSuccessListener {
chosenImageFilePath = it.storage.path
}
.addOnFailureListener {
Log.d("FirebaseWishListViewModel", it.message.toString())
}
return chosenImageFilePath
}
fun getPurchaserInfo(loggedInEmail: String?): MutableMap<String, String> {
val purchaserMap: MutableMap<String, String> = mutableMapOf()
val fbAuthEmail = Firebase.auth.currentUser?.email
Log.d("FirebaseListViewModel", "Firebase Auth Email: ${fbAuthEmail.toString()}")
Log.d("FirebaseListViewModel", "Logged in Email: ${loggedInEmail.toString()}")
if (fbAuthEmail != null && fbAuthEmail == loggedInEmail) {
val fb = Firebase.firestore
fb.collection("purchasers")
.document(loggedInEmail)
.get()
.addOnCompleteListener {
purchaserMap.putAll(it.result.data as MutableMap<String,String>)
}
.addOnFailureListener {
Log.d("Wish List", it.message.toString())
}
}
return purchaserMap
}
}
就像 documentation 所说的 android 预览有一些限制。
“在可组合项中使用 ViewModel 时,预览受到限制。预览系统无法构造传递给 ViewModel 的所有参数,例如存储库、用例、管理器或类似参数。”
但是,您可以轻松解决这个问题,只需使组件与 ViewModel 对象解耦即可。(文档显示了其他示例)
示例:
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = viewModel()) {
ProfileScreenContent(
name = viewModel.name,
onNameChanged = viewModel::onNameChanged
)
}
@Composable
fun ProfileScreenContent(name: String, onNameChanged: (name: String) -> Unit) {
//TODO ...view...
}
@Preview
@Composable
fun ProfileScreenContentPreview() { // And here you can mock whatever you want
ProfileScreenContent(
name = "Test",
onNameChanged = {})
}