主頁 > .NET開發 > 問題在Recyclerview上使用LiveData、Retrofit、Coroutine實作更新UI:配接器Recyclerview未更新

問題在Recyclerview上使用LiveData、Retrofit、Coroutine實作更新UI:配接器Recyclerview未更新

2022-05-23 05:46:42 .NET開發

我是在我的開發應用程式 android 上使用 Kotlin 的新手,現在,我逐步學習在 Recyclerview 上使用 LiveData、Retrofit、Coroutine 實作更新 UI。我的應用程式:

MainActivity > MainFragment with 3 Tab fragment > HomeFragment, DashboardFragment, and SettingsFragment

我在onCreateView HomeFragment上呼叫函式從服務器獲取資料,并在加載時在我的 Recylerview 上顯示閃光資料來觀察這一點,成功時嘗試更新 RecyclerView,并在錯誤時顯示視圖加載失敗按鈕重繪 。

問題是:

  • 成功從服務器獲取資料時配接器 Recyclerview 不更新。配接器仍顯示微光資料
  • 出現案例錯誤(沒有互聯網),我顯示視圖加載失敗,按鈕重繪 。點擊重繪 ,我重新呼叫函式來獲取資料服務器,但函式不能正常作業。Recyclerview 顯示最后的資料,不再顯示加載失敗。

咆哮我的代碼

首頁片段


private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
private lateinit var adapterNews: NewsAdapter
private var shimmerNews: Boolean = false
private var itemsDataNews = ArrayList<NewsModel>()

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
// Inflate the layout for this fragment
_binding = FragmentHomeBinding.inflate(inflater, container, false)
......

newsViewModel = ViewModelProvider(requireActivity()).get(NewsViewModel::class.java)
//news
binding.frameNews.rvNews.setHasFixedSize(true)
binding.frameNews.rvNews.layoutManager = llmh
adapterNews = NewsAdapter(itemsDataNews, shimmerNews)
binding.frameNews.rvNews.adapter = adapterNews
// Observe

//get News
newsViewModel.refresh()

newsViewModel.newsList.observe(
            viewLifecycleOwner,
            androidx.lifecycle.Observer { newsList ->
                newsList?.let {
                    binding.frameNews.rvNews.visibility = View.VISIBLE
                    binding.frameNews.rvNews.isNestedScrollingEnabled = true
                    binding.frameNews.itemNewsLayoutFailed.visibility = View.GONE
                    if (it.size == 0)
                        binding.frameNews.root.visibility = View.GONE
                    else
                        getDataNews(it)
                }
            })
        newsViewModel.loading.observe(viewLifecycleOwner) { isLoading ->
            isLoading?.let {
                binding.frameNews.rvNews.visibility = View.VISIBLE
                binding.frameNews.rvNews.isNestedScrollingEnabled = false
                binding.frameNews.itemNewsLayoutFailed.visibility = View.GONE
                getDataNewsShimmer()
            }
        }

        newsViewModel.loadError.observe(viewLifecycleOwner) { isError ->
            isError?.let {
                binding.frameNews.rvNews.visibility = View.INVISIBLE
                binding.frameNews.itemNewsLayoutFailed.visibility = View.VISIBLE
                binding.frameNews.btnNewsFailed.setOnClickListener {
                    newsViewModel.refresh()
                }

            }
        }
....

return binding.root
}

@SuppressLint("NotifyDataSetChanged")
    private fun getDataNewsShimmer() {
        shimmerNews = true
        itemsDataNews.clear()
        itemsDataNews.addAll(NewsData.itemsShimmer)
        adapterNews.notifyDataSetChanged()
    }

    @SuppressLint("NotifyDataSetChanged")
    private fun getDataNews(list: List<NewsModel>) {
        Toast.makeText(requireContext(), list.size.toString(), Toast.LENGTH_SHORT).show()
        shimmerNews = false
        itemsDataNews.clear()
        itemsDataNews.addAll(list)
        adapterNews.notifyDataSetChanged()
    }

override fun onDestroyView() {
        super.onDestroyView()
        _binding=null
    }

新聞視圖模型

class NewsViewModel: ViewModel() {

    val newsService = KopraMobileService().getNewsApi()
    var job: Job? = null
    val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        one rror("Exception handled: ${throwable.localizedMessage}")
    }

    val newsList = MutableLiveData<List<NewsModel>>()
    val loadError = MutableLiveData<String?>()
    val loading = MutableLiveData<Boolean>()

    fun refresh() {
        fetchNews()
    }

    private fun fetchNews() {
        loading.postValue(true)
        job = CoroutineScope(Dispatchers.IO   exceptionHandler).launch {
            val response = newsService.getNewsList()
            withContext(Dispatchers.Main) {
                if (response.isSuccessful) {
                    newsList.postValue(response.body()?.data)
                    loadError.postValue(null)
                    loading.postValue(false)
                } else {
                    one rror("Error : ${response.message()} ")
                }
            }
        }
        loadError.postValue("")
        loading.postValue( false)
    }

    private fun onError(message: String) {
        loadError.postValue(message)
        loading.postValue( false)
    }

    override fun onCleared() {
        super.onCleared()
        job?.cancel()
    }

}

新聞配接器

NewsAdapter(
var itemsCells: List<NewsModel?>  = emptyList(),
    var shimmer: Boolean ,
) :
    RecyclerView.Adapter<ViewHolder>() {

    private lateinit var context: Context

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsHomeViewHolder {
        context = parent.context
        return NewsHomeViewHolder(
                NewsItemHomeBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            )
        

    }


    override fun onBindViewHolder(holder: NewsHomeViewHolder, position: Int) {
     
            holder.bind(itemsCells[position]!!)
        
    }

    inner class NewsHomeViewHolder(private val binding: NewsItemHomeBinding) :
        ViewHolder(binding.root) {
        fun bind(newsItem: NewsModel) {
          
            binding.newsItemFlat.newsTitle.text = newsItem.name
            binding.newsItemFlatShimmer.newsTitle.text = newsItem.name

            binding.newsItemFlat.newsSummary.text = newsItem.name
            binding.newsItemFlatShimmer.newsSummary.text = newsItem.name

            if (shimmer) {
                binding.layoutNewsItemFlat.visibility = View.INVISIBLE
                binding.layoutNewsItemFlatShimmer.visibility = View.VISIBLE
                binding.layoutNewsItemFlatShimmer.startShimmer()
            } else {

                binding.layoutNewsItemFlat.visibility = View.VISIBLE
                binding.layoutNewsItemFlatShimmer.visibility = View.INVISIBLE
            }
        }
    }

    override fun getItemCount(): Int {
        return itemsCells.size
    }

我希望有人可以幫助我解決問題。謝謝,對不起我的英語。

uj5u.com熱心網友回復:

你有3個不同LiveData的s,對吧?一個帶有新聞資料串列,一個帶有加載錯誤訊息,一個帶有加載狀態。

您為其中的每一個設定了一個,并且每當' 的值更新Observer時都會呼叫該觀察者函式這很重要,因為當您的請求成功時會發生以下情況,并且您會獲得一些新資料:LiveData

if (response.isSuccessful) {
    newsList.postValue(response.body()?.data)
    loadError.postValue(null)
    loading.postValue(false)
}

這意味著newsList更新,然后 loadError更新,然后 loading更新。

因此,您的每個LiveDatas 的觀察者函式按該順序運行。但是您沒有測驗新值(空檢查除外),因此每個值中的代碼總是在值更新時運行。因此,當您的回應成功時,會發生這種情況:

  • newsList觀察者運行,顯示為成功,呼叫getDataNews
  • loadError觀察者運行,值為,所以什么也沒有發生
  • loading觀察者運行,值為false但未檢查,顯示為 loading,呼叫getDataNewsShimmer

所以即使回應成功,你做的最后一件事就是顯示加載狀態


loading在嘗試顯示狀態之前,您需要檢查狀態(如)。但是如果你檢查這loading真的,你會有一個錯誤fetchNews——設定loading = true,啟動一個稍后完成的協程,然后立即設定loading = false

我建議嘗試創建一個代表不同狀態的類,其中一個LiveData代表當前狀態。像這樣的東西:

// a class representing the different states, and any data they need
sealed class State {
    object Loading : State()
    data class Success(val newsList: List<NewsModel>?) : State()
    data class Error(val message: String) : State()
}

// this is just a way to keep the mutable LiveData private, so it can't be updated
private val _state = MutableLiveData<State>()
val state: LiveData<State> get() = _state

private fun fetchNews() {
    // initial state is Loading, until we get a response
    _state.value = State.Loading
    job = CoroutineScope(Dispatchers.IO   exceptionHandler).launch {
        val response = newsService.getNewsList()

        // if you're using postValue I don't think you need to switch to Dispatchers.Main?
        _state.postValue(
            // when you get a response, the state is now either Success or Error
            if (response.isSuccessful) State.Success(response.body()?.data)
            else State.Error("Error : ${response.message()} ")
        )
    }
}

然后你只需要觀察state

// you don't need to create an Observer object, you can use a lambda!
newsViewModel.state.observe(viewLifecycleOwner) { state ->
    // Handle the different possible states, and display the current one

    // this lets us avoid repeating 'binding.frameNews' before everything
    with(binding.frameNews) {
        // You could use a when block, and describe each state explicitly,
        // like your current setup:
        when(state) {
            // just checking equality because Loading is a -singleton object instance-
            State.Loading -> {
                rvNews.visibility = View.VISIBLE
                rvNews.isNestedScrollingEnabled = false
                itemNewsLayoutFailed.visibility = View.GONE
                getDataNewsShimmer()
            }
            // Error and Success are both -classes- so we need to check their type with 'is'
            is State.Error -> {
                rvNews.visibility = View.INVISIBLE
                itemNewsLayoutFailed.visibility = View.VISIBLE
                btnNewsFailed.setOnClickListener {
                    newsViewModel.refresh()
                }
            }
            is State.Success -> {
                rvNews.visibility = View.VISIBLE
                rvNews.isNestedScrollingEnabled = true
                itemNewsLayoutFailed.visibility = View.GONE

                // Because we know state is a Success, we can access newsList on it
                // newsList can be null - I don't know how you want to handle that,
                // I'm just treating it as defaulting to size == 0
                // (make sure you make this visible when necessary too)
                if (state.newsList?.size ?: 0 == 0) root.visibility = View.GONE
                else getDataNews(state.newsList)
            }
        }

        // or, if you like, you could do this kind of thing instead:
        itemNewsLayoutFailed.visibility = if (state is Error) VISIBLE else GONE
    }
}

您可能還想將該顯示代碼分解為單獨的函式(如showError()showList(state.newsList))并從 的分支呼叫它們when,如果這使其更具可讀性

我希望這是有道理的!當您有一個表示狀態的值時,使用起來會容易得多 - 隨著事物的變化設定當前狀態,并通過更新顯示讓您的觀察者處理每個可能的 UI 狀態。加載時,讓它看起來像這樣當出現錯誤時,讓它看起來像這樣

這應該有助于避免您為多個事物多次更新,試圖協調一切的錯誤。我不確定為什么在發生錯誤后重新加載時會看到該問題,但這樣做可能有助于修復它(或者更容易查看導致它的原因)

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/479634.html

標籤:安卓 科特林 android-recyclerview kotlin 协程 可变实时数据

上一篇:readLine()在以readLine()為條件的while回圈后回傳任何內容

下一篇:翻譯編輯器安卓

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more