Search code examples
androiddagger-2scichart

Sci-Chart using Dagger2 for chart update in Android


I am building an app based on arch. MVVM+Databinding for realtime graph data. Using Sci-chart was quick and easy but trying to update app with DI using Dagger2 is generating sci-chart builder instances null while trying to update graph using SuspendUpdates(). ISuspendable interface is already bound through @Binds in the ViewModelModule.

@Module
abstract class ViewModelModule { 
@Singleton
@Binds
abstract fun getSuspendable(aSuspendable: AccurynSuspendableImpl): ISuspendable

@Binds
@IntoMap
@ViewModelKey(AViewModel::class)
abstract fun bindAViewModel(aViewModel: AViewModel): ViewModel

@Binds
internal abstract fun bindViewModelFactory(factory: AppViewModelFactory): ViewModelProvider.Factory
}

@Singleton
class AccurynSuspendableImpl @Inject constructor() : ISuspendable {


    override fun decrementSuspend() {
    }

   override fun suspendUpdates(): IUpdateSuspender? {
    return null
   }

   override fun getIsSuspended(): Boolean {
    return true
   }

    override fun resumeUpdates(p0: IUpdateSuspender?) {
    }
  }


 class AMFragment : Fragment() {
 private val acmVM by viewModel<AViewModel>()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
     mBinding = binding(inflater, R.layout.display, container)
     mBinding.viewModel =  acmVM
     mBinding.lifecycleOwner = this
     mBinding.executePendingBindings()
     return mBinding.root
   }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)
      initializeVM()
    }
    private fun initializeVM() {

       **//TODO: how to bind these xml feature through dagger ahead of time.**
        /*acmVM = AViewModel(
        mBinding.multiPaneChart.iChart,
        mBinding.multiPaneChart.uChart)*/
    }
 }

 class AViewModel @Inject constructor(
     private val iSusp: ISuspendable,
     private val uSusp: ISuspendable,
      ) :    BaseViewModel() {....

    var iRenderDataSeries = XyDataSeries<Date, Float>().apply { 
    fifoCapacity = FIFO_CAPACITY }
    var uColumnDataSeries = XyDataSeries<Date, Float>().apply { 
    fifoCapacity = FIFO_CAPACITY }
    var uLineDataSeries = XyDataSeries<Date, Float>().apply { 
    fifoCapacity = FIFO_CAPACITY }

    private val iColor = ContextCompat.getColor(mContext, 
    R.color.pink)
    private val uColumnColor = ContextCompat.getColor(mContext, 
    R.color.yellow)
    private val uLineColor = ContextCompat.getColor(mContext, 
    R.color.gray)

    val xIAxes = AxisCollection()
    val xUAxes = AxisCollection()
    val yIAxes = AxisCollection()
    val yUAxes = AxisCollection()

    val iRenderableSeries = RenderableSeriesCollection()
    val uRenderableSeries = RenderableSeriesCollection()
    private var iChartModifiers = ChartModifierCollection()
    private var uChartModifiers = ChartModifierCollection()

    init {
       initChartDisplay(context)
    }

    private fun initChartDisplay(context: Context) {
        xIAxes.add(generateXAxis(context, View.GONE, 
        AxisAlignment.Auto))
        xUAxes.add(generateXAxis(context, View.VISIBLE, 
        AxisAlignment.Top))

        yIAxes.add(generateYAxis(context, LABEL_ID_I))
        yUAxes.add(generateYAxis(context, LABEL_ID_U))

        iRenderableSeries.add(
        generateLineRenderableSeries(
            LABEL_ID_I, iRenderDataSeries, SolidPenStyle(iColor, 
            true, lineThickness, null)
            )
        )

        val uColumnSeries =
        generateColumnRenderableSeries(LABEL_ID_U, 
        uColumnDataSeries, SolidBrushStyle(uColumnColor))
        uColumnSeries.dataPointWidth = .95
        uRenderableSeries.add(uColumnSeries)

        uRenderableSeries.add(
           generateLineRenderableSeries(
           LABEL_ID_U, uLineDataSeries, SolidPenStyle(uLineColor, 
           true, lineThickness, null)
           )
        )

     }

     private fun loadData() {
         UpdateSuspender.using(iSusp) {
                iRenderDataSeries.append(Date(lastTimeStamp), 
                median)
            }
         }

       **--Based on certain condition need to update line and column 
       UpdateSuspender.using(uRenderableSeries.single()) {
                uColumnDataSeries.updateYAt(uCurIndex, hourlyTotal) 
       //hour is a current one.  We're going to update with the latest 
       // total for the hour
            }
       **-- whereas on other condition update line series over same 
       renderable series
       UpdateSuspender.using(uRenderableSeries.single()) {
            if (priorTimeStamp + GRAPH_CALC_GAP_CONSTANT < 
            lastTimeStamp)
            {
                UpdateSuspender.using(uRenderableSeries.single()) {
                    uLineDataSeries.append(Date(priorTimeStamp + 
                    GRAPH_DISPLAY_GAP_CONSTANT), Float.NaN)
                }
            }
            //line data series updates
            uLineDataSeries.append(

       Date(DateTimeUtils.toEpochMilli(mData.getRtcTimeStamp())),
                mData.uRate.toFloat()
            )
        }

  }

  XML:

   <com.xyz.widgets.ASciChart
       android:id="@+id/u_chart"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:visibility="@{graphViewModel.displayChartU ? 
               View.VISIBLE : View.GONE}"
       scichart:verticalGroup="@{graphViewModel.sharedVG}"        
      scichart:renderableSeries="@{graphViewModel.uRenderableSeries}"
      scichart:xAxes="@{graphViewModel.xUAxes}"
      scichart:yAxes="@{graphViewModel.yUAxes}"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintTop_toTopOf="parent" />

   <com.xyz.widgets.ASciChart
       android:id="@+id/i_chart"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:visibility="@{graphViewModel.displayChartI ? 
            View.VISIBLE : View.GONE}"
       scichart:verticalGroup="@{graphViewModel.sharedVG}"
       scichart:renderableSeries="@{graphViewModel.iRenderableSeries}"
       scichart:xAxes="@{graphViewModel.xIAxes}"
       scichart:yAxes="@{graphViewModel.yIAxes}"/>

How to bind this sci chart instance in the dagger2 to access it in ViewModel? Any help would be appreciated.

Apology for wrong literature.

Regards, PK


Solution

  • I think that passing SciChartSurface which is an Android View into ViewModel would violate principles of MVVM pattern.

    As I see from XML you store RenderableSeries in your ViewModel. I would suggest you to suspend updates on RenderableSeries instance which is associated with DataSeries which you need to update:

    private fun loadData() {
        // I assume you have only one renderable series in collection
        val renderableSeries = iRenderableSeries.single()
        UpdateSuspender.using(renderableSeries) {
            iRenderDataSeries.append(Date(lastTimeStamp), median)
        }
    }
    

    In this case you won't need to pass SciChartSurface into ViewModel and you'll keep your View layer separated from ViewModel