I'm trying to implement the viewmodel observer with generics, doing this I wont need 7 observers since I'm collecting data from 7 different Fragments.
Since this 7 fragments store the data inside different ArrayLists types I made a generic ViewModel class to pass that data to my main Activity (fragments holder)
class SharedViewModel<T>: ViewModel() {
var data:MutableLiveData<ArrayList<T>> = MutableLiveData()
fun setData(anyData:ArrayList<T>){
data.value = anyData
}
val getAnyData:LiveData<ArrayList<T>>
get() = data
}
Doing this, I just set any array data type inside each fragment
(activity as MainActivity).getViewModelInstance().setData(xArray)
And in my MainActivity I want to check if that xArray data type is the same type as the one declared globally at MainActivity, if they are equal it should assing the current array data values to the new empty array
private var xArray = arrayListOf<Xclass>()
onCreate()
...
viewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)
viewModel.getAnyData.observe(this, Observer { it:ArrayList<out Any?>
if(it == xArray){
xArray.addAll(it)
}
})
If I do this, I prevent doing 7 observers in my MainActivity for each fragment and just update one observer with different array types, comparing them and reasigning them will be much less code and easier for me for the architecture.
There are two problems
ArrayLists
of the same typeSecond error is this
(activity as MainActivity).getViewModelInstance().setData(xArray)
At .setData(xArray)
its highlighted red saying Required: Nothing, Found:ArrayList<Xclass>
. Thats weird because the setData of SharedViewModel needs Any ArrayList to be passed.
Thanks
After some thought, I came up with a simple, but not fail proof method by utilizing a Class Type combined with the generics for full functionality. I believe this will handle your issues with reusability and removing redundant listeners in the way you were hoping.
class SharedViewModel <T> (val listType: Class<T>) : ViewModel() {
var data: MutableLiveData<ArrayList<T>> = MutableLiveData()
fun setData(anyData: ArrayList<T>) {
data.value = anyData
}
inline fun <reified K> isOfInternalType(checkType: Class<K>): Boolean = checkType.typeName == listType.typeName
}
class Xclass
var xArray = arrayListOf<Xclass>()
val viewModel = (activity as MainActivity).getViewModelInstance()
if (viewModel.isOfInternalType(Xclass::class.java) {
viewModel.setData(Xclass)
}
class Xclass
private var xArray = arrayListOf<Xclass>()
onCreate()
...
viewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)
viewModel.getAnyData.observe(this, Observer { data: ArrayList<out Any?> ->
if (viewModel.isOfInternalType(Xclass::class.java) {
xArray.addAll(it)
}
})
// Examples of use cases outside of initial question
class ViewOne: SharedView<ClassOne>(ClassOne::class.java)
val testClass = ViewOne()
ViewOne().isOfInternalType(ClassTwo::class.java) // returns false
ViewOne().isOfInternalType(testClass::class.java) // Error: Cannot use captured type as reified parameter
ViewOne().isOfInternalType(ClassOne::class.java) // returns true
ViewOne().isOfInternalType(ClassOne().javaClass) // returns true
testClass.isOfInternalType(ClassOne::class.java) // returns true
What I have done is created an instance of the View's listType expected at instantiation. This needed to be done because classes do not support type reification like an inline function can. This implementation assumes each View will have a static listType... This can be modified to resemble the following:
class SharedViewModel(listType: Class<*>) : ViewModel() {
var listType: Class<*> = listType
private set(value) {
field = value
}
fun setData(anyData: ArrayList<*>) {
data.value = anyData
}
fun updateListType(newType: Class<*>) {
listType = newType
}
inline fun <reified K> isOfInternalType(checkType: Class<K>): Boolean =
checkType.typeName == listType.typeName
}
class ViewOne: SharedView(ClassOne::class.java)
val testClass = ViewOne()
testClass.isOfInternalType(ClassOne::class.java) // returns true
testClass.updateListType(ClassTwo::class.java)
testClass.isOfInternalType(ClassOne::class.java) // returns false now
testClass.isOfInternalType(ClassTwo::class.java) // returns true now
With this implementation of the variable typing, you would want to chain your isOfInternalType
with the setData
so that you ensure your type safety
This should have enough functionality for you to be able to accomplish your architecture goals for this project, please let me know if I've misunderstood anything and can help further.