繁体   English   中英

如何在 MainActivity 中使用 Adapter.notifyDataSetChanged()?

[英]How to use Adapter.notifyDataSetChanged() in MainActivity?

我已经创建了一个带有 RecyclerView 的列表,并且希望现在能够在 MainActvitiy 中更新它,上下文来自一个来自 MainActivity 的片段,但我就是不能使用

Adapter.notifyDataSetChanged()

在它工作的片段中,它更新列表,我如何从片段中获取上下文 bz 我如何在 MainActvitiy 中使用Adapter.notifyDataSetChanged()

适配器.kt

class Adapter(val context: Context) : RecyclerView.Adapter<Adapter.ViewHolder>() {

    var dataList = emptyList<VolvicsModel>()

    internal fun setDataList(dataList: List<VolvicsModel>) {
        this.dataList = dataList
        notifyDataSetChanged()
    }

    class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
        var title : TextView
        var name : TextView
        var image : ImageView
        var liters: TextView
        var quantity: TextView

        init {
            title = itemView.findViewById(R.id.textView9)
            image = itemView.findViewById(R.id.imageView)
            liters = itemView.findViewById(R.id.liters)
            quantity = itemView.findViewById(R.id.quantity)
            name = itemView.findViewById(R.id.name)

            itemView.setOnLongClickListener {
                Toast.makeText(itemView.context, "${title.text}", Toast.LENGTH_SHORT).show()
                return@setOnLongClickListener true
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.card_user_volvics, parent, false)
        return ViewHolder(view)
    }
    override fun getItemCount() = dataList.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val data = dataList[position]

        holder.title.text = data.name
        holder.liters.text = data.liters
        holder.quantity.text = data.quantity
        holder.name.text = data.sort
        Glide.with(context).load(data.image).into(holder.image)
        
    }

}

数据列表.kt

var dataList = mutableListOf<VolvicsModel>()

片段.kt

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    val view = inflater.inflate(R.layout.fragment_your_volvics, container, false)

    recycleView = view.findViewById(R.id.recyclerview_your_volvics)
    recycleView.layoutManager = GridLayoutManager(view.context, 2)
    recycleView.setHasFixedSize(true)

    VolvicsAdapter = Adapter(view.context)
    recycleView.adapter = VolvicsAdapter

    progressBar = view.findViewById(R.id.progressBar)
    LinearLayout = view.findViewById(R.id.linearLayout)

    database = FirebaseDatabase.getInstance().getReference("Userdata").child(uid!!)
    database.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            if(snapshot.child("volvics").exists()) {
                dataRequest()
                Adapter.notifyDataSetChanged()
                database.removeEventListener(this)
            }
        }
        override fun onCancelled(error: DatabaseError) {
            println("Error")
        }
    })

    return view
}

通常,全局可访问的可变状态(如您的dataList )使用起来很危险。 它引入了挑战,就像您观察到的那样,它可以从应用程序的其他地方更改,而其他组件不知道它已更改。 与其尝试在 Activity 和 Fragment 之间添加更多的依赖关系,还不如尝试修复原始问题。

更好的选择是将数据存储在Activity ViewModel中,Activity 和 Fragment 都可以访问以触发更改,并且 Fragment 可以观察和响应。 当片段被通知数据更改时,它可以更新其适配器中的数据。

这是一个示例,其中 Activity ViewModel 包含数据(字符串)列表,并且 Activity 和 Fragment 都可以将内容添加到该列表中。 Fragment 观察列表并在数据发生变化时更新适配器。 这使用ListAdapter ,因此不需要调用notifyDataSetChanged

查看模型

ViewModel 包含您要显示的数组。 它是私有数据,需要显示该数据的东西可以观察 LiveData。 任何想要更改该数据的东西都应该通过这里。

class MainViewModel : ViewModel() {
    private val dataListLiveData = MutableLiveData<List<String>>()
    val dataList: LiveData<List<String>>
        get() = dataListLiveData

    // This is the source of truth - anything that changes it should go through
    // the ViewModel so that its observers can be notified
    private val myData = mutableListOf<String>()

    // Anything that triggers a change to the data, either by calling an API
    // like firebase or locally editing it, should call through the ViewModel
    fun addEntry(e: String) {
        myData.add(e)
        dataListLiveData.postValue(myData)
    }

    fun getDataFromFirebase() {
        // asynchronous calls to Firebase could be triggered here, and when
        // complete they can add data to the list and post the updated list
        // to the LiveData
    }
}

活动

这里的 Activity 获取 ViewModel 并在协程中延迟添加一些数据。 此处的 ViewModel 实例将与 Fragment 获得的实例相同。

class MainActivity : AppCompatActivity() {

    private val model: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        if (savedInstanceState == null) {
            supportFragmentManager
                .beginTransaction()
                .setReorderingAllowed(true)
                .add(R.id.fragment, ListFragment())
                .commit()
        }

        // slowly add data to the list, one entry every 2 seconds
        lifecycleScope.launch {
            for(i in 1..5) {
                delay(2000)
                model.addEntry("Data $i from the activity")
            }
        }
    }
}

分段

Fragment 检索 Activity ViewModel 并观察 LiveData。 当数据发生变化时,Fragment 会更新适配器/视图。 Fragment 还可以通过调用 ViewModel 上的方法来更新数据。

class ListFragment : Fragment() {

    private var _binding: FragmentListBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentListBinding.inflate(inflater, container, false)
        return binding.root
    }

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

        val model: MainViewModel by activityViewModels()
        val adapter = MyListAdapter()
        binding.dataList.adapter = adapter
        binding.dataList.layoutManager = LinearLayoutManager(requireContext())

        // The fragment observes the list LiveData in the
        // ViewModel and updates the adapter when new data
        // is posted
        model.dataList.observe(viewLifecycleOwner) { newList ->
            // put it in the adapter. Using ListAdapter means
            // we don't need to call notifyDataSetChanged, it figures
            // that out automatically
            adapter.submitList(newList.toList())
        }

        lifecycleScope.launch {
            for(i in 1..3) {
                delay(1200)
                model.addEntry("Data $i from the fragment")
            }
        }

    }

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

适配器

这里的adapter继承自ListAdapter,所以有新数据时可以使用submitList ,不需要手动调用notifyDataSetChanged

class MyListAdapter : ListAdapter<String, MyListAdapter.ViewHolder>(DIFF_CALLBACK) {

    class ViewHolder(val binding: ListRowBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ListRowBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.binding.item.text = getItem(position)
    }

    companion object {
        val DIFF_CALLBACK = object : DiffUtil.ItemCallback<String>() {
            override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
                return oldItem == newItem
            }

            override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
                return oldItem == newItem
            }
        }
    }
}

摇篮

Gradle 文件依赖项是非常标准的,但如果您缺少-ktx那些,则上述某些方法将不存在。 您还需要激活视图绑定才能使用此示例。

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.8.0'
    implementation 'androidx.appcompat:appcompat:1.4.2'
    implementation 'com.google.android.material:material:1.6.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.activity:activity-ktx:1.4.0'
    implementation 'androidx.fragment:fragment-ktx:1.4.1'
}

XML

  • activity_main.xml - 包含名为R.id.fragmentFragmentContainer的活动布局 - 映射到 ActivityMainBinding
  • fragment_list.xml - 包含名为R.id.data_listRecyclerView的片段布局 - 映射到 FragmentListBinding
  • list_row.xml - 包含名为R.id.itemTextView的 RecyclerView 行布局 - 映射到 ListRowBinding

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM