如何在 Kotlin 中加载
how to have loading in Kotlin
我的 MainActivity 包含一个加载 4 个片段的 ViewPager,每个片段应该从服务器加载大量数据。
所以当我的应用程序第一次想要 运行 时,它几乎需要超过 3 秒和其他时间(例如,如果你退出应用程序但没有从你的 'recently app' window 并重新打开它)它需要将近 1 秒。
加载时显示白屏。
有没有办法在数据准备好之前显示白屏,而不是显示我自己的图像?
像启动页之类的东西?
如果您在主线程上执行长时间 运行 操作,您可能会遇到 ANR crash.
每个片段的布局都应该有一个最初可见的加载视图和数据视图。像这样:
(不是代码)
FrameLayout
loading_view (can show a progress spinner or something, size is match parent)
content_view (probably a RecyclerView, initial visibility=GONE, size is match parent)
/FrameLayout
您需要在后台线程或协同程序上执行长 运行 操作,然后在数据准备好在 UI 中显示时交换这两个视图的可见性。
您不应直接在 Fragment 代码中处理数据加载,因为 Fragment 是一个 UI 控制器。 Android Jetpack 库为此提供了 ViewModel class。您可以像这样设置您的 ViewModel。在这个例子中,MyData 可以是任何东西。在你的情况下,它可能是一个列表或一组东西。
class MyBigDataViewModel(application: Application): AndroidViewModel(application) {
private val _myBigLiveData = MutableLiveData<MyData>()
val myBigLiveData: LiveData<MyData>() = _myBigLiveData
init {
loadMyBigData()
}
private fun loadMyBigData() {
viewModelScope.launch { // start a coroutine in the main UI thread
val myData: MyData = withContext(Dispatchers.Default) {
// code in this block is done on background coroutine
// Calculate MyData here and return it from lambda
// If you have a big for-loop, you might want to call yield()
// inside the loop to allow this job to be cancelled early if
// the Activity is closed before loading was finished.
//...
return@withContext calculatedData
}
// LiveData can only be accessed from the main UI thread so
// we do it outside the withContext block
_myBigLiveData.value = myData
}
}
}
然后在您的片段中,观察实时数据以在准备就绪时更新 UI。下面使用 fragment-ktx
库,您需要将其添加到您的项目中。您绝对应该阅读 documentation on ViewModel.
class MyFragment: Fragment() {
// ViewModels should not be instantiated directly, or they won't be scoped to the
// UI life cycle correctly. The activityViewModels delegate handles instantiation for us.
private val model: MyBigDataViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.myBigLiveData.observe(this, Observer<MyData> { myData ->
loading_view.visibility = View.GONE
content_view.visibility = View.VISIBLE
// use myData to update the view content
})
}
}
我的 MainActivity 包含一个加载 4 个片段的 ViewPager,每个片段应该从服务器加载大量数据。
所以当我的应用程序第一次想要 运行 时,它几乎需要超过 3 秒和其他时间(例如,如果你退出应用程序但没有从你的 'recently app' window 并重新打开它)它需要将近 1 秒。
加载时显示白屏。
有没有办法在数据准备好之前显示白屏,而不是显示我自己的图像? 像启动页之类的东西?
如果您在主线程上执行长时间 运行 操作,您可能会遇到 ANR crash.
每个片段的布局都应该有一个最初可见的加载视图和数据视图。像这样:
(不是代码)
FrameLayout
loading_view (can show a progress spinner or something, size is match parent)
content_view (probably a RecyclerView, initial visibility=GONE, size is match parent)
/FrameLayout
您需要在后台线程或协同程序上执行长 运行 操作,然后在数据准备好在 UI 中显示时交换这两个视图的可见性。
您不应直接在 Fragment 代码中处理数据加载,因为 Fragment 是一个 UI 控制器。 Android Jetpack 库为此提供了 ViewModel class。您可以像这样设置您的 ViewModel。在这个例子中,MyData 可以是任何东西。在你的情况下,它可能是一个列表或一组东西。
class MyBigDataViewModel(application: Application): AndroidViewModel(application) {
private val _myBigLiveData = MutableLiveData<MyData>()
val myBigLiveData: LiveData<MyData>() = _myBigLiveData
init {
loadMyBigData()
}
private fun loadMyBigData() {
viewModelScope.launch { // start a coroutine in the main UI thread
val myData: MyData = withContext(Dispatchers.Default) {
// code in this block is done on background coroutine
// Calculate MyData here and return it from lambda
// If you have a big for-loop, you might want to call yield()
// inside the loop to allow this job to be cancelled early if
// the Activity is closed before loading was finished.
//...
return@withContext calculatedData
}
// LiveData can only be accessed from the main UI thread so
// we do it outside the withContext block
_myBigLiveData.value = myData
}
}
}
然后在您的片段中,观察实时数据以在准备就绪时更新 UI。下面使用 fragment-ktx
库,您需要将其添加到您的项目中。您绝对应该阅读 documentation on ViewModel.
class MyFragment: Fragment() {
// ViewModels should not be instantiated directly, or they won't be scoped to the
// UI life cycle correctly. The activityViewModels delegate handles instantiation for us.
private val model: MyBigDataViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.myBigLiveData.observe(this, Observer<MyData> { myData ->
loading_view.visibility = View.GONE
content_view.visibility = View.VISIBLE
// use myData to update the view content
})
}
}