Search code examples
androidkotlincanvasviewmodelandroid-custom-view

Kotlin/Android : Cannot pass LiveData to customView with Canvas


I've instanciated a Graph object in ViewModel and Im trying to pass it to the custom canvas view I made but I keep getting the messages that "Attempt to invoke virtual method ... on a null object reference"

Bellow I post my code and error log.

Custom Canvas View

Class DesignCanvasView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
    ) : View(context) {

private lateinit var extraCanvas: Canvas
private lateinit var extraBitmap: Bitmap
lateinit var graph : Graph

Fragment class DesignFragment : Fragment() {

    private lateinit var binding : DesignFragmentBinding
    private lateinit var viewModel: DesignViewModel

    companion object {
        fun newInstance() = DesignFragment()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        binding = DataBindingUtil.inflate(inflater, R.layout.design_fragment, container, false)
        viewModel = ViewModelProvider(this).get(DesignViewModel::class.java)

        viewModel.graph.observe(viewLifecycleOwner, Observer {
            binding.designCanvasView.graph = it
        })

        return binding.root
    }

}

ViewModel

class DesignViewModel : ViewModel() {
    val graph = MutableLiveData<Graph>(Graph())
}

Error MSG Error

EDIT Layout code

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".screens.design.DesignFragment">

        <com.example.druggame.screens.design.DesignCanvasView
            android:id="@+id/designCanvasView"
            android:layout_width="match_parent"
            android:layout_height="450sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.4"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.5">

        </com.example.druggame.screens.design.DesignCanvasView>


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Thanks for any help in advance.


Solution

  • The issue isn't actually really data binding related but is because of your custom view. Your constructor takes in an AttributeSet as required for data binding, but you never pass it to the superclass (View). So instead it should look like this:

    class DesignCanvasView @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : View(context, attrs, defStyleAttr)
    

    Just a couple of other notes. You are recommended to use the individual binding classes to inflate your bindings, not the generic DataBindingUtil unless you have a specific reason to do so.

    Secondly it is generally good practice to have all your view-related logic that isn't the initial binding in onViewCreated instead of onCreateView.

    Then finally, although in your case the binding doesn't need a lifecycle owner, you should always stick to using the view's owner retreived with viewLifecycleOwner for your bindings (however, again, only if you don't have a specific not to).