【问题标题】:How to set LiveData from Adapter?如何从适配器设置 LiveData?
【发布时间】:2020-03-23 22:59:25
【问题描述】:

我有两个片段:(1)图书馆片段,(2)书籍片段

图书馆片段通过 RecyclerView 显示所有可用的书籍。用户可以在每个 RecyclerView 项目上进行选项卡,这会将 LiveData 设置为相应的书。同时会打开书的片段并显示该书的内容。

我在 Library Fragment 的 RecyclerView.Adapter 内的 ViewHolder 类中设置了一个 onClickListener。因此,当单击一个项目时,将设置 livedata,然后通过导航组件导航到 Book 片段。 Book Fragment 在实时数据上有一个观察者并显示它。

正如您在以下代码中看到的,我将 viewmodel 实例传递给适配器,这是不正确的......?或者是吗?这应该怎么做?

库片段代码片段

class LibraryFragment : Fragment() {
    [...]
    private val model: SharedViewModel by activityViewModels()
    private lateinit var gridlayoutManager: GridLayoutManager
    private lateinit var thumbnailAdapter: ThumbnailAdapter
    private lateinit var thumbnailRecyclerView: RecyclerView
    private lateinit var selectedFolder: Uri

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = layoutInflater.inflate(R.layout.fragment_thumbnail_viewer, container, false)
        initRecyclerView(view)
        view.allFolderPicker.setOnClickListener {
            pickFolder()
        }
        view.switchFragment.setOnClickListener {
            findNavController().navigate(R.id.action_allFoldersFragment_to_oneFolderFragment)
        }
        return view
    }

    private fun initRecyclerView(view: View) {
        thumbnailRecyclerView = view.findViewById(R.id.thumbnail_recycler_view)
        gridlayoutManager =
            GridLayoutManager(requireContext(), 3, LinearLayoutManager.VERTICAL, false)
        thumbnailRecyclerView.layoutManager = gridlayoutManager
        thumbnailAdapter = ThumbnailAdapter(model)
        thumbnailAdapter.setThumbnailList(listOf())
        thumbnailRecyclerView.adapter = thumbnailAdapter
    }
    [...]
}

适配器类代码片段

class ThumbnailAdapter(model: SharedViewModel) :
    RecyclerView.Adapter<ThumbnailAdapter.CustomViewHolder>() {

    private var thumbnailList = listOf<Pair<String, File>>()
    private val myModel = model

    inner class CustomViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        private val thumbnail = view.thumbnail
        private val title = view.title

        fun bind(item: Pair<String, File>) {
            val titleToSet = item.first
            val bitmapToSet = Util.uriToBitmap(item.second)
            val resizedBitmapToSet = Bitmap.createScaledBitmap(bitmapToSet, 150, 150, false)
            thumbnail.setImageBitmap(resizedBitmapToSet)
            title.text = titleToSet
            itemView.setOnClickListener {
                val folderWithImages = thumbnailList[adapterPosition].second.parentFile
                folderWithImages?.let {
                    myModel.setLibraryFolderList(it)
                    itemView.findNavController()
                        .navigate(R.id.action_allFoldersFragment_to_oneFolderFragment)
                }
            }
        }
    }
    [...]
}

图书片段代码片段

[...]

class BookViewFragment : Fragment() {

    private lateinit var viewPager: ViewPager2
    private lateinit var viewPagerAdapter: ViewPager2Adapter
    private val model: SharedViewModel by activityViewModels()


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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selectedBookFile.observe(viewLifecycleOwner, androidx.lifecycle.Observer {
            viewPagerAdapter.setImageList(getImageList(it))
            viewPagerAdapter.notifyDataSetChanged()
        })
    }

    private fun initRecyclerView(view: View) {
        viewPager = view.findViewById(R.id.main_image)
        viewPagerAdapter = ViewPager2Adapter()
        viewPagerAdapter.setImageList(listOf())
        viewPager.adapter = viewPagerAdapter
    }

    [...]
}

【问题讨论】:

  • 根据谷歌的说法,在前几段here,你应该只在onCreateView()中使用observe()。

标签: android android-fragments kotlin android-adapter android-livedata


【解决方案1】:

你不应该那样做,这里有一个关于RecyclerView的课程,从显示到处理clickListener。

这是他们的定义:

虽然ViewHolder 是监听点击的好地方,但它通常不是处理点击的正确位置。您通常应该处理ViewModel 中的点击,因为ViewModel 可以访问数据和逻辑来确定响应点击需要发生的事情。

所以你应该这样做:

适配器:

class ThumbnailAdapter(model: SharedViewModel, val clickListener: ThumbnailListener) :
    RecyclerView.Adapter<ThumbnailAdapter.CustomViewHolder>() {

    private var thumbnailList = listOf<Pair<String, File>>()
    private val myModel = model

    override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
        val item = getItem(position)
        holder.itemView.setOnClickListener {
            listener.onClick(item)
        }
        holder.bind(marsProperty)
    }

    inner class CustomViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        private val thumbnail = view.thumbnail
        private val title = view.title

        fun bind(item: Pair<String, File>) {
            val titleToSet = item.first
            val bitmapToSet = Util.uriToBitmap(item.second)
            val resizedBitmapToSet = Bitmap.createScaledBitmap(bitmapToSet, 150, 150, false)
            thumbnail.setImageBitmap(resizedBitmapToSet)
            title.text = titleToSet
        }
    }

    class ThumbnailListener(val clickListener: (libraryId: Long) -> Unit) {
        fun onClick(val library: Library) = clickListener(library.id)
    }

    [...]
}

视图模型:

private val _navigateToBook = MutableLiveData<Long>()
val navigateToBook: LiveData<Long>()
   get() = _navigateToBook

fun onLibraryClicked(libraryId: Long) {
    // your stuff
    _navigateToBook.value = libraryId
}

片段:

thumbnailAdapter = ThumbnailAdapter(model, ThumbnailListener { libraryId ->
    viewModel.onLibraryClicked(libraryId)
})

viewModel.navigateToBook.observe(viewLifecycleOwner, Observer { book ->
    book?.let {
        this.findNavController().navigate(YOUR_DESTINATION)
    }
})

这一次设置监听器的第二种可能性:

xml项目文件:

<data>

<variable
    name="listener"
    type="Your.Package.To.ThumbnailListener" />


</data>

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="170dp"
    android:onClick="@{() -> listener.onClick(property)}">

    ...

</FrameLayout>

适配器:

fun bind(item: Pair<String, File>, listener: ThumbnailListener) {
            val titleToSet = item.first
            val bitmapToSet = Util.uriToBitmap(item.second)
            val resizedBitmapToSet = Bitmap.createScaledBitmap(bitmapToSet, 150, 150, false)
            thumbnail.setImageBitmap(resizedBitmapToSet)
            title.text = titleToSet
            itemView.clickListener = listener
        }

【讨论】:

  • 很高兴知道onClickListener触发的动作不应该由适配器处理,但不幸的是,它没有回答将ViewModel传递给适配器的问题。此外,无法将 ThumbnailListener 分配给 itemView.setOnClick 方法(itemView.clickListener = listener doesn't work eiter)
  • 抱歉,回答迟了,不,您不应该将viewmodel 传递给您的适配器,使用第一种方法,您可以直接从您的碎片/活动中访问viewmodel。我不确定你是怎么做到的,因为holder.itemView.setOnClickListener 正在我的项目中工作(请注意,由于holder,你选择了ìtemView)
【解决方案2】:

只需在 Adapter 类中使用您的 Activity 引用而不是 viewLifecycleOwner 即可使用

class ThumbnailAdapter(

    private val activity: FragmentActivity

) : RecyclerView.Adapter<ThumbnailAdapter.CustomViewHolder>() {

    // ... Other functions

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        model.selectedBookFile.observe(activity, androidx.lifecycle.Observer {
            // Your code goes here
        })
    }

    // ... Other functions

}

【讨论】:

    【解决方案3】:

    我知道现在回答为时已晚。 但我希望它能帮助其他开发人员寻找类似问题的解决方案。

    看看LiveAdapter。

    你只需要在 Gradle 中添加最新的依赖即可。

    dependencies {
        implementation 'com.github.RaviKoradiya:LiveAdapter:1.3.4'
        // kapt 'com.android.databinding:compiler:GRADLE_PLUGIN_VERSION' // this line only for Kotlin projects
    }
    

    并将适配器与您的 RecyclerView 绑定

    // Kotlin sample
    LiveAdapter(
            data = liveListOfItems,
            lifecycleOwner = this@MainActivity,
            variable = BR.item )
           .map<Header, ItemHeaderBinding>(R.layout.item_header) {
               onBind{
    
               }
               onClick{
    
               }
               areContentsTheSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
               areItemSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
           }
           .map<Point, ItemPointBinding>(R.layout.item_point) {
               onBind{
    
               }
               onClick{
    
               }
               areContentsTheSame { old: Point, new: Point ->
                   return@areContentsTheSame old.id == new.id
               }
               areItemSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
           }
           .into(recyclerview)
    

    就是这样。无需为适配器的实现编写额外的代码,观察 LiveData 并通知适配器。

    此外,已实现 DiffUtil,因此只有更改的项目才会在 RecyclerView 上更新,而不是全部更新。

    【讨论】:

      猜你喜欢
      • 2016-06-22
      • 1970-01-01
      • 2019-06-21
      • 2020-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-04
      相关资源
      最近更新 更多