【问题标题】:Navigate to another fragment on network call success导航到网络调用成功的另一个片段
【发布时间】:2020-07-06 13:15:58
【问题描述】:

我想通过按下按钮从片段进行网络调用,如果网络调用成功,则想要导航到另一个片段,如果没有,则留在当前片段中并在吐司。

我通过点击satrtGameButtonReviewGameRulesFragment 进行网络调用,它看起来像这样:

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
    
    viewModel = ViewModelProvider(requireActivity()).get(SetupGameViewModel::class.java)
    
    binding = DataBindingUtil.inflate<FragmentReviewGameRulesBinding>(
                inflater,
                R.layout.fragment_review_game_rules,
                container,
                false
            )
    
    binding.startGameButton.setOnClickListener {
                viewModel.createGame(this.view)
            }
}

createGame 函数位于SetupGameViewModel 文件中,如下所示:

fun createGame() {
        coroutineScope.launch {
            val createGameRequest = CreateGameRequest(
                gamePrice = gamePrice,
                gameRules = gameRules,
                playerOneGameName = playerOneGameName
            )
            var getPropertiesDeferred =
                ParagonApi.retrofitService.createGameAsync(createGameRequest)
            try {
                _createGameResponse.value = getPropertiesDeferred.await()
            } catch (e: Exception) {
            }
        }
    }

现在 - 我想在此调用成功时导航到另一个片段,或者在失败时留在那里。这样做的正确方法是什么?

【问题讨论】:

  • 看看我的回答,我尽量详细解释

标签: android kotlin retrofit2 kotlin-coroutines


【解决方案1】:

你可以使用LiveData来观察状态,当它是成功的时候就通知已经发生了成功

为您的州创建课程

sealed class NetworkLoadingState {
    object OnLoading : NetworkLoadingState()
    object OnSuccess : NetworkLoadingState()
    data class OnError(val message: String) : NetworkLoadingState()
}

在你的 SetupGameViewModel 中创建一个 LiveData 实例来观察

class SetupGameViewModel : ViewModel() {

   private var _loadState = MutableLiveData<Event<NetworkLoadingState>>()
    val loadState: LiveData<Event<NetworkLoadingState>>
        get() = _loadState

//Instead of `coroutineScope` use `viewModelScope`
fun createGame() {
       // coroutineScope.launch {
        viewModelScope.launch {
            
              _loadState.value = Event(NetworkLoadingState.OnLoading)
            
            val createGameRequest = CreateGameRequest(
                gamePrice = gamePrice,
                gameRules = gameRules,
                playerOneGameName = playerOneGameName
            )
            var getPropertiesDeferred =
                ParagonApi.retrofitService.createGameAsync(createGameRequest)
            try {
                _createGameResponse.value = getPropertiesDeferred.await()
              //Here is code
              _loadState.value = Event(NetworkLoadingState.OnSuccess)
            } catch (e: Exception) {
              _loadState.value = Event(NetworkLoadingState.OnError("Exception message here"))
            }
        }
    }


}

现在在你的片段中

 binding.startGameButton.setOnClickListener {
            viewModel.createGame(this.view)
            observeLoadingState()
        }

 private fun observeLoadingState() =
        viewModel.loadState.observe(viewLifecycleOwner, EventObserver {
            when (it) {
                is NetworkLoadingState.OnLoading -> println("You can show loading indicator here or whatever to inform user that data is being loaded")
                is NetworkLoadingState.OnSuccess -> println("Success now you can navigate")
                is NetworkLoadingState.OnError -> println(it.errorMessage)
            }
        })

这是处理单个事件的事件类,否则当你的成功片段从后台堆栈弹出时,你将导航回成功屏幕

Event.kt

/*
 a* Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package your.package.name

import androidx.lifecycle.Observer

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {

    @Suppress("MemberVisibilityCanBePrivate")
    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    //fun peekContent(): T = content
}

/**
 * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
 * already been handled.
 *
 * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
 */
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
    override fun onChanged(event: Event<T>?) {
        event?.getContentIfNotHandled()?.let {
            onEventUnhandledContent(it)
        }
    }
}

想了解更多,可以visit my repo,喜欢就给个star吧。

【讨论】:

  • 您的回答帮助我解决了我的 viewModel 在导航到另一个片段后触发 onBackPress 时发起网络调用的问题 - 您的 Notes 存储库很棒,谢谢
【解决方案2】:

您应该在您的活动/片段中观察您的数据,如果它返回成功则转到下一个屏幕。您可以使用 liveData 和 mutableLiveData 来观察您的数据。

【讨论】:

    猜你喜欢
    • 2013-05-12
    • 2021-09-08
    • 2021-05-02
    • 2013-12-19
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 2020-09-03
    • 1970-01-01
    相关资源
    最近更新 更多