How to use DataBinding with LiveData inside RecyclerView - android-recyclerview

So DataBinding can now use LiveData in its binding. As part of this, we also have to set to the Data Binding the life cycle like so:
SampleLayoutBinding binding = DataBindingUtil.inflate(this, R.layout.sample_layout)
binding.setLifeCycleOwner(this)
My question is what is the proper way to set this lifecycle owner inside recyclerview? Or even more appropriately, do we need to set the LifeCyclerOwner when using data binding inside recyclerview?

Yes, you need to set LifeCycleOwner to your data-binding. For this purpose you need to pass Activity/Fragment to your RecyclerView.Adapter as LifeCycleOwner interface and set it when you create binding in RecyclerView.Adapter.onCreateViewHolder().
You also can use your own implementation of LifeCycleOwner

Related

ShareViewModel how to get a singleton in composable function

I have a hybrid app, I need a singleton of the Shared ViewModel class, in my composable function. I use Hilt.
my SahreViewModel class:
class SharedViewModel : ViewModel() { ... }
I use my SharedViewModel everywhere in my app, and can get the singleton it in any fragment as:
private val sharedViewModel: SharedViewModel by activityViewModels()
Same I would like to get in the composable function.
I find two workarounds for this
As you said you can fetch the singleton view model by activityViewModels() then you could simply pass it to the composable function. I imagine one needs the ViewModel inside his top-level composable functions only to make the lower-level reusable, so this solution makes much sense to me.
You can simply create another class containing all the logic that you want to be singleton and annotate with #Singleton and inject it to the ViewModel and by that, although you will have different ViewModel objects, the shared logic will be of the same object across the application.
val sharedViewModel: SharedViewModel = viewModel()
viewModel() returns an existing ViewModel or creates a new one in the given scope. The ViewModel is retained as long as the scope is alive. For example, if the composable is used in an activity, viewModel() returns the same instance until the activity is finished or the process is killed.
The viewModel() function automatically uses the ViewModel that Hilt constructs with the #HiltViewModel annotation.
Due to their lifecycle and scoping, you should access and call ViewModel instances at screen-level composables, that is, close to a root composable called from an activity, fragment, or destination of a Navigation graph. You should never pass down ViewModel instances to other composables, pass only the data they need and functions that perform the required logic as parameters.
Link

How to findViewById in RecyclerView after setting custom id to every element via adapter

I have a fragment that implements Recycler View with switches. RecyclerView sets id for every switch in id+=1 way. The problem is I can get nothing from these IDs as soon as calling throws NullableException. I understand that I have to call it correctly from parent, but I don`t get how to do it correctly.
There is the structure of code:
Fragment with ConstraintLayout(R.id.settingsScreen) -> RecylerView(R.id.recyclerView), that creates LinearLayouts(R.id.settingLayout) with switches(R.id.switch).
class ViewHolder{
val l : LinearLayout = view.findViewById(R.id.settingLayout)
}
if (holder.l.findViewById<SwitchCompat>(1100).isActivated){..}
throws java.lang.NullPointerException: Attempt to invoke virtual method 'boolean androidx.appcompat.widget.SwitchCompat.isActivated()' on a null object reference
It is very fragile to create view IDs yourself like this. They could easily collide with IDs generated by the Android build. So if you create IDs, you should be using ViewCompat.generateViewId() to do it safely.
But findViewById is something you generally want to avoid in the first place. It is slow. That's why View Binding is provided, to cache the views so you don't have to keep searching for them.
I highly recommend storing your Views in a collection instead of assigning them IDs. Then you can efficiently pull them from the collection when you need them. You just need to be sure the collection will be garbage collected when you're done with the views (don't create a collection that will outlive the screen the views are on).
You can get the view by using itemView property
Not sure of you are trying to do when you mean custom id.
You can get all views inflated by viewHolder with itemView.
this property is given to ViewHolder after you pass view as parameter in ViewHolder constructor, than ViewHolder will inflate views according with their id's.
than you can get view inside ViewHolder by:
itemView.findViewById<LinearLayout>(R.id.settingLayout)
itemView.findViewById<SwitchCompat>(R.id.switch)
You can look to see more options in google docs
If these are not what you asked for please let me know

One way binding mode, the other way around

There are four binding modes in Aurelia as documented:
.bind - Uses the default binding. One-way binding for everything but
form controls, which use two-way binding.
.one-way - Flows data one
direction: from the view-model to the view.
.two-way - Flows data
both ways: from view-model to view and from view to view-model.
.one-time - Renders data once, but does not synchronize changes after
the initial render.
I'm looking for a .one-way binding but the other way around, with the data flow from view to view-model. My use-case is dirty-bit where you want to know when the view is dirty.
ValueConverters are bidirectional and could possibly be an OK workaround until it is implemented as described in the issue Jeremy linked to.
http://aurelia.io/hub.html#/doc/article/aurelia/binding/latest/binding-value-converters/1
you can use attribute.from-view="variable" for a one way binding from view to viewmodel
https://github.com/aurelia/binding/pull/618

MVVM light - Passing multiple parameters in RelayCommand from XAML

I have more than one PasswordBox on my view and I want to pass all their SecureStrings to my view model when I click a button.
My guess is that I want to populate an instance of a custom class with all the SecureStrings and pass that object as a parameter to the RelayCommand bound to the button.
If I only knew how...
My current idea for a work around:
In the RelayCommands action for the button: send out a NotificationMessageAction with a callback taking a custom class as parameter.
Register for that message in the views code behind, and then populate an object with the SecureStrings, and then pass that object back to the view model with the help of the callback. Not very nice...
There must be a better way to do this in XAML, right?
Actually, I think what you want to do is implement event handlers, or an attached behavior on your PaswordBoxes that will push the SecureStrings to properties in the same viewmodel object that will be handling the RelayCommand's action. Then your RelayCommand won't need any parameters at all.

What do you need to implement to provide a Content Set for an NSArrayController?

Heys,
I am writing something in Xcode. I use Core Data for persistency and link the view and the model together with Cocoa Bindings; pretty much your ordinary Core Data application.
I have an array controller (NSArrayController) in my Xib. This has its managedObjectContext bound to the AppDelegate, as is convention, and tracks an entity. So far so good.
Now, the "Content Set" biding of this NSArrayController limits its content set (as you'd expect), by a keyPath from the selection in another NSArrayController (otherAc.selection.detailsOfMaster). This is the usual way to implement a Master-Detail relationship.
I want to variably change the key path at runtime, using other controls. This way, I sould return a content set that includes several other content sets, which is all advanced and beyond Interface Builder.
To achieve this, I think I should bind the Content Set to my AppDelegate instead. I have tried to do this, but don't know what methods to implement. If I just create the KVC methods (objectSet, setObjectSet), then I can provide a Content Set for the Array Controller in the contentSet method.
However, I don't think I'm binding this properly, because it doesn't "refresh". I'm new to binding; what do I need to implement to properly update the Content Set when other things, like the selection in the master NSArrayController, changes?
However, I don't think I'm binding this properly, because it doesn't "refresh".
This most often means you are assigning directly to the instance variable, not using KVC-compliant accessor methods nor posting KVO notifications.
The general solution is to create accessor methods for the property and then use them everywhere, including inside that class, except in its init and dealloc methods.