как иметь загрузку в Котлине

my MainActivity содержит ViewPager, который загружает 4 фрагмента, каждый фрагмент должен загружать много данных с сервера.

поэтому, когда мое приложение хочет запускаться в первый раз, это занимает почти более 3 секунд, а в остальное время (например, если вы выходите из приложения, но не очищаете его из окна «недавно появившееся приложение» и повторно открываете его), требуется почти 1 секунда.

во время загрузки показывает белый экран.

Есть ли способ вместо того, чтобы показывать белый экран до тех пор, пока данные не будут готовы, я покажу свое собственное изображение? что-то вроде заставки?


person Mohammad    schedule 27.01.2020    source источник
comment
Ваш макет должен иметь альтернативный вид, который будет отображаться во время загрузки. Список должен быть скрыт. Затем поменяйте видимость, когда данные будут готовы к отображению. Вам необходимо загружать данные в фоновой асинхронной задаче, чтобы избежать риска ошибки ANR. Проще всего это сделать, используя сопрограммы во ViewModel для данных. Здесь слишком много, чтобы объяснять, но вы можете найти множество руководств.   -  person Tenfour04    schedule 27.01.2020
comment
спасибо за ответ, к сожалению, я не совсем знаком с андроидом и улучшаю свои навыки. вы можете предложить мне учебник?   -  person Mohammad    schedule 28.01.2020
comment
Здесь есть самые разные темы. Я бы начал с официальной документации Android по ViewModel. Я опубликую краткий обзор ниже, когда у меня будет время.   -  person Tenfour04    schedule 28.01.2020
comment
большое спасибо. @ Tenfour04   -  person Mohammad    schedule 28.01.2020


Ответы (1)


Если вы выполняете длительные действия в основном потоке, вы рискуете получить сбой ANR .

Ваш макет для каждого фрагмента должен иметь вид загрузки, который изначально виден, и ваш вид данных. Что-то вроде этого:

(не код)

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

Вам нужно выполнить длительное действие в фоновом потоке или сопрограмме, а затем поменять местами видимость этих двух представлений, когда данные готовы для отображения в пользовательском интерфейсе.

Вы не должны напрямую обрабатывать загрузку данных в коде фрагмента, поскольку фрагмент является контроллером пользовательского интерфейса. Библиотеки Android Jetpack предоставляют для этой цели класс ViewModel. Вы бы настроили свою 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
        }
    }

}

Затем в своем фрагменте вы наблюдаете за живыми данными, чтобы обновить пользовательский интерфейс, когда он будет готов. Ниже используется библиотека fragment-ktx, которую вам нужно добавить в свой проект. Вам обязательно стоит прочитать документацию по 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
        })
    }
}
person Tenfour04    schedule 28.01.2020