android - 如何从 Adapter 设置 LiveData?

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

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

图书馆 fragment 通过 RecyclerView 显示所有可用的书籍。用户可以点击每个 RecyclerView 项目,这会将 LiveData 设置为相应的书籍。同时将打开 Book Fragment,并显示该书的内容。

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

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

库 fragment 代码 fragment

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
    }
    [...]
}

适配器类代码 fragment

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)
                }
            }
        }
    }
    [...]
}

书籍 fragment 代码 fragment

[...]

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
    }

    [...]
}

最佳答案

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

他们是这样定义的:

While the ViewHolder is a great place to listen for clicks, it's not usually the right place to handle them. You should usually handle clicks in the ViewModel, because the ViewModel has access to the data and logic for determining what needs to happen in response to the click.

所以你应该这样做:

适配器:

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)
    }

    [...]
}

View 模型:

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

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

fragment :

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

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

第二次使用数据绑定(bind)设置监听器的可能性:

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
        }

关于android - 如何从 Adapter 设置 LiveData?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60822715/

相关文章:

java - 响应 startActivityForResult

android - 如何完整显示标题名称(未截断)以及如何将菜单元素放置在底部的操作栏中?

java - 如何修复更改类似数组列表值的 android Setters?

android - 只有在 onCreate 中设置了适配器和 RecyclerView,才会填充 Fragment 内部的 RecyclerView

android - 协程中的自定义拦截器

java - 如何下载com.sun.net.httpserver包?

android - 将 Fragment 显示为覆盖在任何 Activity 之上

android - 针对 api 21 (Android 5.0) 及更高版本时的 fragment 与支持 fragment

java - Spring PropertySources 已加载但未在测试中应用

android - 使用 Android Studio 中的 Kotlin,如何在没有 for 循环的情况下以 Kotlin 方式通过 ID 将列表添加到另一个列表而不重复项目?