【问题标题】:Reflect data change in realtime using Kotlin & Livedata is not working使用 Kotlin 实时反映数据变化 & Livedata 不起作用
【发布时间】:2021-06-01 11:23:18
【问题描述】:

我正在实施一个解决方案,其中 a 会看到联系人列表以及当前优先级 - 高、中和低。然后,用户可以使用对话框通过优先级更改状态,并且房间数据库已更新,但 UI 未按预期更新。只有当我重新创建活动时才会出现更改。我没有使用 ViewModel。

我需要在下面的代码中更改什么才能发生同样的情况?`

class CrmContacts : Fragment(), SearchView.OnQueryTextListener {

private lateinit var progressBar: ProgressDialog
var firebaseUser: FirebaseUser? = null //the used id of the user using the app
var managecontactsadapter: ManageContactsAdapter? = null
var reference: DatabaseReference? = null
var rvCRM: RecyclerView? = null
private val _contsCRMLiveData = MutableLiveData<ArrayList<Contact>>()
private val crmContactsLiveData: LiveData<ArrayList<Contact>> = _contsCRMLiveData

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

    val srchView: SearchView = view.findViewById(R.id.search_user_contacts)

    //Search
    val searchPlateId: Int = srchView.context.resources
        .getIdentifier("android:id/search_plate", null, null)
    val searchPlate: View =
        srchView.findViewById(searchPlateId)
    searchPlate.setBackgroundColor(Color.TRANSPARENT)

    val linearLayout1 = srchView.getChildAt(0) as LinearLayout
    val linearLayout2 = linearLayout1.getChildAt(2) as LinearLayout
    val linearLayout3 = linearLayout2.getChildAt(1) as LinearLayout
    val autoComplete = linearLayout3.getChildAt(0) as AutoCompleteTextView
    autoComplete.textSize = 14f

    srchView.setOnQueryTextListener(this)
    srchView.isFocusable = false

    //Progress Bar
    progressBar = ProgressDialog(activity)
    progressBar.setCancelable(false)
    progressBar.setMessage("Loading...")


    return view
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

    rvCRM = view.findViewById(R.id.rvCRM)
    rvCRM!!.setHasFixedSize(true)
    rvCRM!!.layoutManager = LinearLayoutManager(activity)
    displayContacts()
    crmContactsLiveData.observe(viewLifecycleOwner, Observer {
        rvCRM!!.adapter = managecontactsadapter
    })

    contact_fab.setOnClickListener {
        val intent = Intent(activity, EditContact::class.java)
        intent.putExtra("Edit", false)
        this.startActivity(intent)
    }

    crmFilter.setOnClickListener {
        val options = arrayOf<CharSequence>(
            "Lead",
            "Qualified",
            "Proposal",
            "Client",
            "Invoiced",
            "Unpaid",
            "Clear Filter"
        )
        var salesTag = ""
        val builder: AlertDialog.Builder = AlertDialog.Builder(activity)
        builder.setTitle("Filter by?")
        builder.setItems(options, DialogInterface.OnClickListener { _, which ->
            if (which == 0) {
                salesTag = "High"
                filtercontactby(salesTag)
            }
            if (which == 1) {
                salesTag = "Medium"
                filtercontactby(salesTag)
            }
            if (which == 2) {
                salesTag = "Low"
                filtercontactby(salesTag)
            }
        })
        builder.show()
    }

}

private fun filtercontactby(salesTag: String) {

    val mupdateList = ArrayList<Contact>()
    val db = AppDatabase.getDatabase(requireContext())
    val contact = db.contactsDao().getFilterContacts(salesTag)
    if(contact.isEmpty())
    {
        val nocontacts = view?.findViewById<TextView>(R.id.noContacts)
        nocontacts!!.visibility = View.VISIBLE
        val notice = "uh oh! No Contacts have been tagged as " + "$salesTag!"
        nocontacts.text = notice
    }
    mupdateList.clear()
    for (i in contact) {
        mupdateList.add(i)
    }

    managecontactsadapter = ManageContactsAdapter(requireContext(), mupdateList)
    rvCRM!!.adapter = managecontactsadapter

}

private fun displayContacts() {

    val mupdateList = ArrayList<Contact>()
    val db = AppDatabase.getDatabase(requireContext())
    val contact = db.contactsDao().getContacts()
    val nocontacts = view?.findViewById<TextView>(R.id.noContacts)
    if(contact.isEmpty())
    {
        nocontacts!!.visibility = View.VISIBLE
        nocontacts.text = "uh oh! Please use the add clients/contacts button below!"
    } else {
        nocontacts!!.visibility = View.GONE
    }
    for (i in contact) {
        mupdateList.add(i)
    }
    managecontactsadapter = ManageContactsAdapter(requireContext(), mupdateList)
    rvCRM!!.adapter = managecontactsadapter
}

private fun searchForContacts(searchString: String) {

    val mupdateList = ArrayList<Contact>()
    val nocontacts = view?.findViewById<TextView>(R.id.noContacts)
    val db = AppDatabase.getDatabase(requireContext())
    val contact = db.contactsDao().getBySearch(searchString)
    if(contact.isEmpty())
    {
        nocontacts!!.visibility = View.VISIBLE
        nocontacts.text = "uh oh! Search is empty, try again?!"
    } else {
        nocontacts!!.visibility = View.GONE
    }
    mupdateList.clear()
    for (i in contact) {
        mupdateList.add(i)
    }

    managecontactsadapter = ManageContactsAdapter(requireContext(), mupdateList)
    rvCRM!!.adapter = managecontactsadapter
}

//Search Contacts using SearchView
override fun onQueryTextSubmit(query: String?): Boolean {
    managecontactsadapter!!.notifyDataSetChanged()
    if (query != null) {
        searchForContacts("%${query}%")
    }
    return true
}

override fun onQueryTextChange(keyWord: String?): Boolean {

    if (keyWord != null) {
        searchForContacts("%${keyWord}%")
    }
    return true
}

}

RoomDAO 返回一个对象列表 - LiveData

适配器类代码:

class ManageContactsAdapter(
mContext: Context,
mupdateList: ArrayList<Contact>
) : 

RecyclerView.Adapter<ManageContactsAdapter.MyViewHolder?>() {
    private val layoutInflater = mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
    private val mContext: Context
    private val mupdateList: List<Contact>

    init {
        this.mContext = mContext
        this.mupdateList = mupdateList
    }
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var contLabel: TextView? = null
    var ContactName: TextView? = null
    var ContactDetails: LinearLayout? = null
    private var ContactProfileImage: ShapeableImageView
    private var contFavEdit: ImageButton
    private lateinit var salesLabel: CardView

    init {
        contLabel = itemView.findViewById(R.id.contLabel)
        ContactProfileImage = itemView.findViewById(R.id.ContactProfileImage)
        ContactName = itemView.findViewById(R.id.ContactName)
        ContactDetails = itemView.findViewById(R.id.ContactDetails)
        contFavEdit = itemView.findViewById(R.id.contFavEdit)
        salesLabel = itemView.findViewById(R.id.salesLabel)
    }
}

override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): MyViewHolder {
    return MyViewHolder(layoutInflater.inflate(R.layout.contacts_row, parent, false))
}

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


override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    val contact = mupdateList[position]

    with(holder.itemView) {
        ContactName.text = contact.name
        if(contact.label != null){
            contLabel.text = contact.label
            if (contact.label == "High" || contact.label == "high"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorPurple))
            } else if (contact.label == "Medium"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorBlueNote))
            } else if (contact.label == "Low"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorMaroon))
            } 
        }

        if(contact.favorite == true){
            holder.itemView.contFavEdit.setImageResource(R.drawable.ic_favsel)
        } else {
            holder.itemView.contFavEdit.setImageResource(R.drawable.ic_fav)
        }

        Picasso.get().load(contact.photoUri)
            .placeholder(R.drawable.ic_baseline_whatshot_24).fit().centerCrop().into(
                ContactProfileImage
            )
        ContactDetails.removeAllViews()
        contact.numbers.forEach {
            val detail = layoutInflater.inflate(
                R.layout.row_contact_data,
                ContactDetails,
                false
            )
            detail.imgIcon.setImageResource(R.drawable.ic_local_phone_black_24dp)
            detail.ContactData.text = it
            ContactDetails.addView(detail)
        }
        contact.emails.forEach {
            val detail = layoutInflater.inflate(
                R.layout.row_contact_data,
                ContactDetails,
                false
            )
            detail.imgIcon.setImageResource(R.drawable.ic_mail_black_24dp)
            detail.ContactData.text = it
            ContactDetails.addView(detail)
        }
    }
    holder.itemView.setOnClickListener {
        val intent = Intent(mContext, ContactDetailsHome::class.java)
        intent.putExtra("contact", contact as Serializable)
        intent.putExtra("Edit", true)
        mContext.startActivity(intent)
    }

    holder.itemView.contFavEdit.setOnClickListener {
       if(contact.favorite == true){
           contact.favorite = false
           holder.itemView.contFavEdit.setImageResource(R.drawable.ic_fav)
           updatefav(contact,false)
       } else {
           contact.favorite = true
           holder.itemView.contFavEdit.setImageResource(R.drawable.ic_favsel)
           updatefav(contact,true)
       }
    }

    holder.itemView.setOnLongClickListener {

        val options = arrayOf<CharSequence>(
            "High",
            "Medium",
            "Low",
        )
        var salesTag = ""
        val builder: AlertDialog.Builder = AlertDialog.Builder(mContext)
        builder.setTitle("Contact is a?")
        builder.setItems(options, DialogInterface.OnClickListener { _, which ->
            if (which == 0) {
                salesTag = "High"
                updateDB(contact, salesTag)
            }
            if (which == 1) {
                salesTag = "Medium"
                updateDB(contact, salesTag)
            }
            if (which == 2) {
                salesTag = "Low"
                updateDB(contact, salesTag)
            }
        })
        builder.show()
        true
    }

}

private fun updateDB(contactL: Contact, salesTag: String) {
    val db = AppDatabase.getDatabase(mContext)
    val ev = Contact(
        contactL.id,
        contactL.name,
        contactL.lookupKey,
        contactL.photoUri,
        salesTag,
        null,
        contactL.favorite
    )
    db.contactsDao().updateContact(ev)
}

private fun updatefav(contactL: Contact, fav: Boolean) {
    val db = AppDatabase.getDatabase(mContext)
    db.contactsDao().updatefav(contactL.id, fav)
}

inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

}

【问题讨论】:

  • 你能粘贴你的适配器类的代码吗?
  • 嗨 che10,已添加相同的代码...

标签: android kotlin android-room android-livedata


【解决方案1】:

代码看起来很好,我觉得你的适配器没有被更新,而不是每次都重新分配适配器,你可以做的是在你的适配器类中添加一个函数来更新列表数据并如下刷新。

private fun updateContacts(contactList: ArrayList<Contact>){
        this.mupdateList.clear()
        this.mupdateList.addAll(contactList)
        notifyDataSetChanged()
    }

然后无论你在哪里更新你的列表,比如在 filtercontactby() 函数中,你都可以简单地去喜欢

managecontactsadapter?.updateContacts(mupdateList)

【讨论】:

  • 但是调用 updateContacts 函数意味着我需要在适配器中再次读取数据库...?我将如何重新生成联系人列表?更改的 notifydatset 也不起作用...
  • @madhall 不,你理解错了,当一些用户选择一个过滤器时,你会像这样调用filtercontactby(salesTag),你需要做的是在你的适配器中添加updateContacts函数和在filtercontactby() 函数中使用managecontactsadapter?.updateContacts(mupdateList)。您实际上不需要每次都创建一个适配器,只需像您已经完成的那样在顶部创建一个并使用它。
  • 是的,这将是一个解决方案,但我的过滤器运行良好。问题是当我使用在项目上设置的 AlertDialog 将状态从高更改为中时 - 此代码存在于适配器中并更新数据库但无法在那里刷新...
  • @madhall 代码现在很乱,即使你不想使用ViewModel,我也建议你将业务逻辑分离并保留到活动本身。从技术上讲,您的适配器应该只显示提供给它的数据,不需要其他东西。所以你可以做的是当你改变状态时,在活动中点击你的查询,获取结果并将其传递给适配器,方法是创建一个与我在答案中相同的方法。
  • 现在听起来不错,可以弄清楚如何从适配器调用片段函数!哇
【解决方案2】:

notifyDatasetChange() 不再推荐使用,自 2021 年 6 月起在大多数情况下都不起作用。我解决此问题的方法是使用提供的额外通知功能。在我的情况下,只有一个项目正在更新,所以我必须申请

notifyItemChanged(位置)

这仅更新了回收站视图中的相关项目!因此,如果您的 notifyDatasetChange() 在适配器中不起作用 - 那是因为您可能必须应用特定于项目的通知。

【讨论】:

    猜你喜欢
    • 2019-07-23
    • 1970-01-01
    • 1970-01-01
    • 2020-05-22
    • 2013-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    相关资源
    最近更新 更多