title: Combining ViewModels With ViewPagers
body: <p>I am making a simple App which holds a list of timers and allows the user to scroll between these timers via ViewPager. Android Architecture Component principles are in effect here: the ViewModel holds the state of the app, and Fragments/Activities just handle UI interactions.</p>
  
  <p>In this case, a custom ViewPager and FragmentStatePagerAdapter has been made. My question is, <strong>what is the best way to have the ViewPager/adapter respond to changes in the ViewModel?</strong></p>
  
  <p>The current code below works fine, but is there a better way to do this? Currently, both the ViewPager and Adapter require reference to the ViewModel and this feels contrary to what MVVM should implement.</p>
  
  <p><em>ViewPager</em></p>
  
  <pre><code>    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 -&gt; homeViewModel?.onSwipeDown()
              MotionEvent.ACTION_UP -&gt; 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")
      }
  }
  </code></pre>
  
  <p><em>FragmentStatePagerAdapter</em></p>
  
  <pre><code> 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 -&gt; homeViewModel?.onSwipeDown()
              MotionEvent.ACTION_UP -&gt; 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")
      }
  }
  </code></pre>
  
  <p><em>Instantiation Method called in Activity onCreate()</em></p>
  
  <pre><code>    private fun setupPagerAdapter() {
      val pagerAdapter = TimerSlidePagerAdapter(viewModel = viewModel, fragmentManager = supportFragmentManager)
      with(pager){
          adapter = pagerAdapter
          homeViewModel = viewModel
      }
  }
  </code></pre>