我有一个 Android Kotlin MVVM 项目,首次启动 Fragment 时未加载 ViewModel。但如果我刷新它加载数据并显示在 UI 中
HomeFragment.kt UI
class HomeFragment : Fragment() {
private val publicationViewModel: ListViewModel by viewModels()
private lateinit var magazineList: RecyclerView
private lateinit var paperList: RecyclerView
private var magazineListAdapter = PaperAdapter(arrayListOf())
private var paperListAdapter = PaperAdapter(arrayListOf())
private lateinit var paperSkeletonScreen: LinearLayout
private lateinit var magazineSkeletonScreen: LinearLayout
private var pullToRefresh: SwipeRefreshLayout? = null
var page: Int = 1
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
val view: View = LayoutInflater.from(container!!.context)
.inflate(R.layout.fragment_main_home, container, false)
findAll()
paperList = view.findViewById(R.id.today_newspaper_list)
paperList.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false).apply {
paperList.layoutManager
}
paperList.apply {
adapter = paperListAdapter
}
paperSkeletonScreen = view.findViewById(R.id.newspaper_loader)
magazineList = view.findViewById(R.id.magazine_list)
magazineList.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false).apply {
magazineList.layoutManager
}
magazineList.apply {
adapter = magazineListAdapter
}
magazineSkeletonScreen = view.findViewById(R.id.magazine_loader)
pullToRefresh = view.findViewById(R.id.refresh_page) as SwipeRefreshLayout
pullToRefresh?.setOnRefreshListener {
TrackerService.trackUserEvent(requireActivity(), "refresh_home")
refreshPage()
}
observeViewModelPublications()
return view
}
private fun observeViewModelPublications() {
try {
activity?.let { it ->
publicationViewModel.apply {
todayPapers.observe(it) { pubs ->
pubs?.let {
paperListAdapter.getPapers(it)
paperSkeletonScreen.visibility = View.GONE
}
}
}
publicationViewModel.apply {
magazine.observe(it) { pubs ->
pubs?.let { magazine ->
magazineListAdapter.getPapers(magazine)
magazineSkeletonScreen.visibility = View.GONE
}
}
}
publicationViewModel.viewModelLoadError.observe(it) { error ->
if (!error.isNullOrEmpty()) {
ToastHelper.showToast(requireActivity(), error, 1)
}
}
}
} catch (_: Exception) {
ToastHelper.showToast(
requireActivity(), requireActivity().getString(R.string.something_wrong), 1
)
}
}
private fun refreshPage() {
try {
pullToRefresh?.isRefreshing = false
arrayOf(
paperSkeletonScreen, magazineSkeletonScreen
).forEach { it.visibility = View.VISIBLE }
findAll()
Handler(Looper.getMainLooper()).postDelayed({
observeViewModelPublications()
}, 5000)
} catch (_: Exception) {
ToastHelper.showToast(requireActivity(), getString(R.string.something_wrong), 1)
}
}
private fun findAll() {
publicationViewModel.findPublications("Token", "KE", 1)
}
companion object {
fun newInstance(): MainHomeFragment {
return MainHomeFragment()
}
}
}
与ApiService交互进行api调用并更新UI ListViewModel.kt
class ListViewModel : ViewModel() {
private val userData = ApiClient.ApiService()
private var job: Job? = null
// Publications
val magazine = MutableLiveData<PublicationModal>()
val todayPapers = MutableLiveData<List<PaperModal>>()
// Error Handling
val viewModelLoadError = MutableLiveData<String?>()
val loading = MutableLiveData<Boolean>()
fun findPublications(bearer: String?, country: String?, page: Int?) {
fetchTodayPapers(country)
fetchMagazine(country)
}
private fun fetchTodayPapers(country: String?) {
viewModelScope.launch {
when (val result =
RequestService().viewModelFetch(loading) { userData.findTodayPapers(country) }) {
is RequestService.Result.Success -> {
todayPapers.value = result.data?.data
viewModelLoadError.value = null
}
is RequestService.Result.Error -> onError(result.message)
}
}
}
private fun fetchMagazine(country: String?) {
viewModelScope.launch {
when (val result = RequestService().viewModelFetch(loading) {
userData.getTodayMagazine(
"2",
country
)
}) {
is RequestService.Result.Success -> {
magazine.value = result.data?.data
viewModelLoadError.value = null
}
is RequestService.Result.Error -> onError(result.message)
}
}
}
private fun onError(
message: String,
) {
viewModelLoadError.value = message
loading.value = false
}
override fun onCleared() {
super.onCleared()
job?.cancel()
}
}
用于进行 api 调用的扩展服务会折叠其中的所有内容以删除重复的代码 此外,
val response = fetchDataFunction()
在开始时返回 null,但当您刷新它时返回数据。
请求服务.kt
class RequestService {
suspend fun <T> viewModelFetch(
loadingLiveData: MutableLiveData<Boolean>,
fetchDataFunction: suspend () -> Response<T>,
): Result<T> {
return try {
loadingLiveData.postValue(true)
val response = fetchDataFunction()
if (response.isSuccessful) {
loadingLiveData.postValue(false)
return Result.Success(response.body())
} else {
val responseBody = response.errorBody()?.toPrettyJson()
if (responseBody == null) {
loadingLiveData.postValue(true)
Result.Success(response.body())
} else {
val errorMessage =
responseBody.optString("message")
?: responseBody.optString("error")
loadingLiveData.postValue(false)
return Result.Error(errorMessage)
}
}
} catch (e: SocketTimeoutException) {
loadingLiveData.postValue(false)
return Result.Error("Request is taking too long to complete, try again later")
} catch (e: IOException) {
loadingLiveData.postValue(false)
return Result.Error("Network error, check your internet connection")
} catch (e: Exception) {
loadingLiveData.postValue(false)
return Result.Error("Network error, check your internet connection")
}
}
sealed class Result<out T> {
data class Success<out T>(val data: T?) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
}
}
ApiClient.kt 调用我的 Api 并返回数据
@GET("today")
suspend fun findTodayPapers(
@Query("country") country: String?,
): Response<PaperData>
@GET("latest")
suspend fun getTodayMagazine(
@Query("country") country: String?,
): Response<PaperData>
上面的代码有什么问题,请帮忙
我在您的代码中看到的一个可能的问题是您正在 onCreateView() 中观察 LiveData。首次创建 Fragment 时,会调用 onCreateView(),并且在该方法完成之前 UI 尚未准备好。这也许可以解释为什么你的 UI 最初没有加载。
要解决此问题,您应该将 LiveData 观察移至 onViewCreated() 或 onActivityCreated() 而不是 onCreateView()。这些生命周期方法在视图创建后调用,通过观察其中的 LiveData,您可以确保只要 LiveData 发出数据,您的 UI 就会更新。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
observeViewModelPublications()
}