You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何复用BottomSheet并绑定对应Fragment的ViewModel?

这个问题的核心是用面向接口编程的思路,把BottomSheet需要的操作抽象成通用接口,让不同的ViewModel都实现这个接口,这样BottomSheet就不用绑定具体的ViewModel了,自然就能彻底避免冗余代码。下面一步步教你怎么改:

1. 定义统一的选择行为接口

先创建一个接口,把BottomSheet里需要调用的所有方法都放进去——也就是你现在XML里绑定的oneSelected()和twoSelected():

interface BottomSheetSelectionHandler {
    fun oneSelected()
    fun twoSelected()
}
2. 让现有ViewModel实现这个接口

修改你的ViewModelA和ViewModelB,让它们都实现上面的接口,保留各自原本的业务逻辑就行:

// ViewModelA的修改
class ViewModelA : ViewModel(), BottomSheetSelectionHandler {
    override fun oneSelected() {
        // 保留你原来的实现逻辑
    }

    override fun twoSelected() {
        // 保留你原来的实现逻辑
    }

    // ViewModelA原本的其他方法不受影响
}

// ViewModelB的修改
class ViewModelB : ViewModel(), BottomSheetSelectionHandler {
    override fun oneSelected() {
        // 保留你原来的实现逻辑
    }

    override fun twoSelected() {
        // 保留你原来的实现逻辑
    }

    // ViewModelB原本的其他方法不受影响
}
3. 修改BottomSheet的布局XML

把布局里的viewModel变量类型换成刚才定义的接口,这样不管是哪个ViewModel,只要实现了接口就能绑定:

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <!-- 把具体的ViewModel类型换成通用接口 -->
        <variable 
            name="selectionHandler" 
            type="com.yourpackage.BottomSheetSelectionHandler" />
    </data>
    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content">
        <Button 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content" 
            android:onClick="@{()->selectionHandler.oneSelected()}" />
        <Button 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content" 
            android:onClick="@{()->selectionHandler.twoSelected()}" />
    </LinearLayout>
</layout>

(这里把变量名改成selectionHandler是为了语义更清晰,当然你也可以保留viewModel,只要类型是接口就行)

4. 重构BottomSheetSample代码

让BottomSheet依赖这个通用接口,而不是具体的ViewModel。获取宿主Fragment的ViewModel后,直接转成接口类型即可:

class BottomSheetSample : BottomSheetDialogFragment() {

    // 通过参数接收宿主Fragment的导航图ID(如果不同Fragment属于不同导航图)
    private val navGraphId by lazy {
        arguments?.getInt("NAV_GRAPH_ID") 
            ?: throw IllegalArgumentException("必须传入导航图ID")
    }

    // 获取导航图对应的ViewModel,泛型指定为接口类型
    private val selectionHandler: BottomSheetSelectionHandler by navGraphViewModels(navGraphId) { viewModelFactory }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        val binding: BottomSheetSampleBinding = DataBindingUtil.inflate(
            inflater, 
            R.layout.bottom_sheet_sample, 
            container, 
            false
        )
        binding.selectionHandler = selectionHandler
        binding.lifecycleOwner = viewLifecycleOwner
        return binding.root
    }

    // 静态方法方便外部调用传参
    companion object {
        fun newInstance(navGraphId: Int) = BottomSheetSample().apply {
            arguments = Bundle().apply {
                putInt("NAV_GRAPH_ID", navGraphId)
            }
        }
    }
}
5. 在Fragment中直接复用同一个BottomSheet

现在不管是FragmentA还是FragmentB,都可以直接调用同一个BottomSheetSample,完全不需要再创建重复的BottomSheet类:

// 在FragmentA中显示BottomSheet
BottomSheetSample.newInstance(R.id.nav_A_graph)
    .show(parentFragmentManager, "BottomSheetSample")

// 在FragmentB中显示BottomSheet
BottomSheetSample.newInstance(R.id.nav_B_graph)
    .show(parentFragmentManager, "BottomSheetSample")

这种方式的优势非常明显:后续不管你要在多少个Fragment中复用这个BottomSheet,只要对应的ViewModel实现了BottomSheetSelectionHandler接口,就能直接复用,完全不会产生冗余代码。如果以后需要给BottomSheet加新按钮,只需要在接口里新增方法,再让ViewModel实现即可,扩展性极强。

内容的提问来源于stack exchange,提问作者hushed_voice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 19:37:52