将 ViewModel 与 ViewPagers 相结合
Combining ViewModels With ViewPagers
我正在制作一个简单的应用程序,它包含一个计时器列表并允许用户通过 ViewPager 在这些计时器之间滚动。 Android 架构组件原则在这里生效:ViewModel 保存应用程序的状态,Fragments/Activities 只处理 UI 交互。
在这种情况下,自定义的ViewPager 和FragmentStatePagerAdapter 已经制作完成。我的问题是,让 ViewPager/adapter 响应 ViewModel 中的变化的最佳方式是什么?
下面的当前代码工作正常,但是有更好的方法吗?目前,ViewPager 和 Adapter 都需要引用 ViewModel,这感觉与 MVVM 应该实现的相反。
ViewPager
class VerticalTimerViewPager(context: Context, attributeSet: AttributeSet): ViewPager(context, attributeSet){
var homeViewModel: HomeViewModel?=null
init {
setPageTransformer(true, VerticalPageTransformer())
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev ?: return false
val intercepted = super.onInterceptTouchEvent(swapXYOnMotionEvent(ev))
swapXYOnMotionEvent(ev)
return intercepted
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
homeViewModel ?: throwNoViewModelException()
when(ev?.action){
MotionEvent.ACTION_DOWN -> homeViewModel?.onSwipeDown()
MotionEvent.ACTION_UP -> homeViewModel?.onSwipeUp()
}
return super.onTouchEvent(swapXYOnMotionEvent(ev ?: return false))
}
private fun swapXYOnMotionEvent(motionEvent: MotionEvent): MotionEvent{
with(motionEvent){
val newX = (y/height)*width
val newY = (x/width) * height
setLocation(newX, newY)
}
return motionEvent
}
private fun throwNoViewModelException():Boolean{
throw RuntimeException("${this.javaClass.simpleName}: HomeViewModel must be set by setting the variable homeViewModel")
}
}
FragmentStatePagerAdapter
class VerticalTimerViewPager(context: Context, attributeSet: AttributeSet): ViewPager(context, attributeSet){
var homeViewModel: HomeViewModel?=null
init {
setPageTransformer(true, VerticalPageTransformer())
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev ?: return false
val intercepted = super.onInterceptTouchEvent(swapXYOnMotionEvent(ev))
swapXYOnMotionEvent(ev)
return intercepted
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
homeViewModel ?: throwNoViewModelException()
when(ev?.action){
MotionEvent.ACTION_DOWN -> homeViewModel?.onSwipeDown()
MotionEvent.ACTION_UP -> homeViewModel?.onSwipeUp()
}
return super.onTouchEvent(swapXYOnMotionEvent(ev ?: return false))
}
private fun swapXYOnMotionEvent(motionEvent: MotionEvent): MotionEvent{
with(motionEvent){
val newX = (y/height)*width
val newY = (x/width) * height
setLocation(newX, newY)
}
return motionEvent
}
private fun throwNoViewModelException():Boolean{
throw RuntimeException("${this.javaClass.simpleName}: HomeViewModel must be set by setting the variable homeViewModel")
}
}
在 Activity onCreate()
中调用的实例化方法
private fun setupPagerAdapter() {
val pagerAdapter = TimerSlidePagerAdapter(viewModel = viewModel, fragmentManager = supportFragmentManager)
with(pager){
adapter = pagerAdapter
homeViewModel = viewModel
}
}
看起来您正在使用 ViewModel
从您的 适配器 & [= 为 onTouchEvent(ev: MotionEvent?)
进行通信32=]view pager,如果你想让你的适配器和view pager独立于ViewModel,我建议你采用以下方式:
创建一个具有方法的接口 onSwipeDown()
& onSwipeUp()
(或者为 ViewModel 创建任意 N 个方法)
在您的视图寻呼机和适配器中使用此接口对象而不是 ViewModel
。
将此接口实现到您的 ViewModel
(这将在 ViewModel 中公开接口方法).
将您的界面投射到 ViewModel
,您将在此处将 ViewModel 传递给适配器并查看寻呼机。
瞧!您已经使您的适配器和视图寻呼机独立于 ViewModel 的直接引用。
Jeel Vankhede 的回答被标记为解决方案并已实施。为了以后参考,解决方案代码将粘贴在下面,以显示如何成功使 ViewPager 和 Adapter 独立于 ViewModel 的细节:
ViewPager:
注意我们现在有一个接口对象。滑动ViewPager时,如果没有界面对象,则抛出异常。
//Overriding default touch events and swapping x/y coordinates prior to handling
class VerticalTimerViewPager(context: Context, attributeSet: AttributeSet): ViewPager(context, attributeSet){
var timerViewPagerEventListener: TimerViewPagerEvent? = null
init {
setPageTransformer(true, VerticalPageTransformer())
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev ?: return false
val intercepted = super.onInterceptTouchEvent(swapXYOnMotionEvent(ev))
swapXYOnMotionEvent(ev)
return intercepted
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
timerViewPagerEventListener ?: throwInterfaceExeption()
when(ev?.action){
MotionEvent.ACTION_DOWN -> timerViewPagerEventListener?.onViewPagerSwipeDown()
MotionEvent.ACTION_UP -> timerViewPagerEventListener?.onViewPagerSwipeUp()
}
return super.onTouchEvent(swapXYOnMotionEvent(ev ?: return false))
}
private fun throwInterfaceExeption():Boolean {
throw RuntimeException("${this.javaClass.simpleName}: Parent Activity must implement TimerViewPagerEvent interface" )
}
private fun swapXYOnMotionEvent(motionEvent: MotionEvent): MotionEvent{
with(motionEvent){
val newX = (y/height)*width
val newY = (x/width) * height
setLocation(newX, newY)
}
return motionEvent
}
interface TimerViewPagerEvent{
fun onViewPagerSwipeUp()
fun onViewPagerSwipeDown()
}
}
适配器:现在获取对象列表(在本例中为计时器)
class TimerSlidePagerAdapter(fragmentManager: FragmentManager, private val timers: List<TimerEntity>?):
FragmentStatePagerAdapter(fragmentManager){
override fun getItem(position: Int): Fragment {
if (count > 0) {
return makeTimerFragment(position)
}
return NoDataFragment()
}
override fun getCount(): Int = timers?.size ?: 0
//Internal Functions
private fun makeTimerFragment(position: Int): Fragment {
timers ?: return NoDataFragment()
val timeInMS = timers[position].timeInMS
return if (timeInMS > 0) {
TimerFragment.newInstance(timeInMS)
} else {
NoDataFragment()
}
}
}
Activity 实现:
private fun setupPagerAdapter() {
val pagerAdapter = TimerSlidePagerAdapter(
fragmentManager = supportFragmentManager,
timers = viewModel?.timers?.value)
with(pager){
adapter = pagerAdapter
timerViewPagerEventListener = this@MainActivity
}
}
private fun observeTimers(){
viewModel.timers.observe(this, Observer {timerList->
timerList ?: Log.e(this.javaClass.simpleName, "List of timers is null")
.also {return@Observer}
setupPagerAdapter()
})
}
我正在制作一个简单的应用程序,它包含一个计时器列表并允许用户通过 ViewPager 在这些计时器之间滚动。 Android 架构组件原则在这里生效:ViewModel 保存应用程序的状态,Fragments/Activities 只处理 UI 交互。
在这种情况下,自定义的ViewPager 和FragmentStatePagerAdapter 已经制作完成。我的问题是,让 ViewPager/adapter 响应 ViewModel 中的变化的最佳方式是什么?
下面的当前代码工作正常,但是有更好的方法吗?目前,ViewPager 和 Adapter 都需要引用 ViewModel,这感觉与 MVVM 应该实现的相反。
ViewPager
class VerticalTimerViewPager(context: Context, attributeSet: AttributeSet): ViewPager(context, attributeSet){
var homeViewModel: HomeViewModel?=null
init {
setPageTransformer(true, VerticalPageTransformer())
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev ?: return false
val intercepted = super.onInterceptTouchEvent(swapXYOnMotionEvent(ev))
swapXYOnMotionEvent(ev)
return intercepted
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
homeViewModel ?: throwNoViewModelException()
when(ev?.action){
MotionEvent.ACTION_DOWN -> homeViewModel?.onSwipeDown()
MotionEvent.ACTION_UP -> homeViewModel?.onSwipeUp()
}
return super.onTouchEvent(swapXYOnMotionEvent(ev ?: return false))
}
private fun swapXYOnMotionEvent(motionEvent: MotionEvent): MotionEvent{
with(motionEvent){
val newX = (y/height)*width
val newY = (x/width) * height
setLocation(newX, newY)
}
return motionEvent
}
private fun throwNoViewModelException():Boolean{
throw RuntimeException("${this.javaClass.simpleName}: HomeViewModel must be set by setting the variable homeViewModel")
}
}
FragmentStatePagerAdapter
class VerticalTimerViewPager(context: Context, attributeSet: AttributeSet): ViewPager(context, attributeSet){
var homeViewModel: HomeViewModel?=null
init {
setPageTransformer(true, VerticalPageTransformer())
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev ?: return false
val intercepted = super.onInterceptTouchEvent(swapXYOnMotionEvent(ev))
swapXYOnMotionEvent(ev)
return intercepted
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
homeViewModel ?: throwNoViewModelException()
when(ev?.action){
MotionEvent.ACTION_DOWN -> homeViewModel?.onSwipeDown()
MotionEvent.ACTION_UP -> homeViewModel?.onSwipeUp()
}
return super.onTouchEvent(swapXYOnMotionEvent(ev ?: return false))
}
private fun swapXYOnMotionEvent(motionEvent: MotionEvent): MotionEvent{
with(motionEvent){
val newX = (y/height)*width
val newY = (x/width) * height
setLocation(newX, newY)
}
return motionEvent
}
private fun throwNoViewModelException():Boolean{
throw RuntimeException("${this.javaClass.simpleName}: HomeViewModel must be set by setting the variable homeViewModel")
}
}
在 Activity onCreate()
中调用的实例化方法 private fun setupPagerAdapter() {
val pagerAdapter = TimerSlidePagerAdapter(viewModel = viewModel, fragmentManager = supportFragmentManager)
with(pager){
adapter = pagerAdapter
homeViewModel = viewModel
}
}
看起来您正在使用 ViewModel
从您的 适配器 & [= 为 onTouchEvent(ev: MotionEvent?)
进行通信32=]view pager,如果你想让你的适配器和view pager独立于ViewModel,我建议你采用以下方式:
创建一个具有方法的接口
onSwipeDown()
&onSwipeUp()
(或者为 ViewModel 创建任意 N 个方法)在您的视图寻呼机和适配器中使用此接口对象而不是
ViewModel
。将此接口实现到您的
ViewModel
(这将在 ViewModel 中公开接口方法).将您的界面投射到
ViewModel
,您将在此处将 ViewModel 传递给适配器并查看寻呼机。
瞧!您已经使您的适配器和视图寻呼机独立于 ViewModel 的直接引用。
Jeel Vankhede 的回答被标记为解决方案并已实施。为了以后参考,解决方案代码将粘贴在下面,以显示如何成功使 ViewPager 和 Adapter 独立于 ViewModel 的细节:
ViewPager: 注意我们现在有一个接口对象。滑动ViewPager时,如果没有界面对象,则抛出异常。
//Overriding default touch events and swapping x/y coordinates prior to handling
class VerticalTimerViewPager(context: Context, attributeSet: AttributeSet): ViewPager(context, attributeSet){
var timerViewPagerEventListener: TimerViewPagerEvent? = null
init {
setPageTransformer(true, VerticalPageTransformer())
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev ?: return false
val intercepted = super.onInterceptTouchEvent(swapXYOnMotionEvent(ev))
swapXYOnMotionEvent(ev)
return intercepted
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
timerViewPagerEventListener ?: throwInterfaceExeption()
when(ev?.action){
MotionEvent.ACTION_DOWN -> timerViewPagerEventListener?.onViewPagerSwipeDown()
MotionEvent.ACTION_UP -> timerViewPagerEventListener?.onViewPagerSwipeUp()
}
return super.onTouchEvent(swapXYOnMotionEvent(ev ?: return false))
}
private fun throwInterfaceExeption():Boolean {
throw RuntimeException("${this.javaClass.simpleName}: Parent Activity must implement TimerViewPagerEvent interface" )
}
private fun swapXYOnMotionEvent(motionEvent: MotionEvent): MotionEvent{
with(motionEvent){
val newX = (y/height)*width
val newY = (x/width) * height
setLocation(newX, newY)
}
return motionEvent
}
interface TimerViewPagerEvent{
fun onViewPagerSwipeUp()
fun onViewPagerSwipeDown()
}
}
适配器:现在获取对象列表(在本例中为计时器)
class TimerSlidePagerAdapter(fragmentManager: FragmentManager, private val timers: List<TimerEntity>?):
FragmentStatePagerAdapter(fragmentManager){
override fun getItem(position: Int): Fragment {
if (count > 0) {
return makeTimerFragment(position)
}
return NoDataFragment()
}
override fun getCount(): Int = timers?.size ?: 0
//Internal Functions
private fun makeTimerFragment(position: Int): Fragment {
timers ?: return NoDataFragment()
val timeInMS = timers[position].timeInMS
return if (timeInMS > 0) {
TimerFragment.newInstance(timeInMS)
} else {
NoDataFragment()
}
}
}
Activity 实现:
private fun setupPagerAdapter() {
val pagerAdapter = TimerSlidePagerAdapter(
fragmentManager = supportFragmentManager,
timers = viewModel?.timers?.value)
with(pager){
adapter = pagerAdapter
timerViewPagerEventListener = this@MainActivity
}
}
private fun observeTimers(){
viewModel.timers.observe(this, Observer {timerList->
timerList ?: Log.e(this.javaClass.simpleName, "List of timers is null")
.also {return@Observer}
setupPagerAdapter()
})
}