RaisePropertyChanaged in Mvvmcross from one class to another class - android

public class CartItemViewModel : MvxNotifyPropertyChanged
{
public double SubTotal
{
get { return UnitPrice * Quantity; }
set
{
//RaisePropertyChanged("TotalValue")
}
}
}
public class CartViewModel : MvxViewModel
{
public double TotalValue
{
get
{
foreach (var item in cartlist)
{
totalvalue += item.UnitPrice;
}
return totalvalue;
}
set
{
TotalValue = value;
}
}
private double totalvalue;
}
I want to modify TotalValue property when SubTotal property is changed. Both are in different classes. How to make it possible?
It is not working when I pop up Raispropertychanged("TotalValue") in SubTotal.
Please Help!

You should handle the PropertyChanged event for all the instances of CartItemViewModel inside the CartViewModel.
You receive PropertyChangedEventArgs which has a member called PropertyName. When its value is "SubTotal" then you can call Raispropertychanged(TotalValue).
The code is completely added to CartViewModel.

Related

I am facing a problem i have duplicates in array list with same objects when i make changes in one duplicate object it updates others as well

i am using singleton class to add data from activity and then get the arralist in fragments to add to cart function when i update the quantity by position based it updates the other duplicates quantity as well
MODEL CLASS
public class UTIL {
public static List productModels ;
public static List<ProductModel> getModel()
{
if(productModels==null)
{
productModels = new ArrayList<>();
}
return productModels;
}
public static void setModel(ProductModel productModel)
{
if(productModels==null)
{
productModels = new ArrayList<>();
productModels.add(productModel);
}
else {
productModels.add(productModel);
}
}
public static void replace(List<ProductModel> productModel)
{
if(productModels==null)
{
productModels = productModel;
}
else {
productModels=productModel;
}
}
}
MAKING CHANGES
UTIL.getModel().get(0).setNumberofitem("!");
now if i update index 0 it also updates the other duplicates objects in it
What does it mean "duplicates"?. The same object? If I understand right, your list contains a few links to the same object.

How to set up a listener for a variable in Kotlin

How to I set up an interface listener for detecting a variable change in Kotlin. I successful implemented the following in Java, but am running into issues doing it in Kotlin:
Interface:
public interface InterfaceRefreshList
{
public void refreshListRequest();
}
Class containing listener:
public class SignalChange
{
private static boolean refreshListSwitch;
private static List<InterfaceRefreshList> refreshListListeners = new ArrayList<>();
public static void setRefreshList(boolean value)
{
refreshListSwitch = value;
for(InterfaceRefreshList l : refreshListListeners)
l.refreshListRequest();
}
public static void addRefreshListListener(InterfaceRefreshList l)
{
refreshListListeners.add(l);
}
}
Class where listener is listening:
public class FragmentBrowse extends Fragment
{
public FragmentBrowse() /// Constructor
{
SignalChange.addRefreshListListener(() -> refreshList());
}
refreshList()
{
// do something
}
}
To signal a change:
SignalChange.setRefreshList(true);
I can set up the interface and the signal class:
class SignalChange
{
private var refreshListSwitch: Boolean = false
var setSwitch: Boolean
get() = refreshListSwitch
set(value)
{
refreshListSwitch = value
}
private var refreshListListeners = ArrayList<InterfaceRefreshPersonsList>()
fun sendRefreshSignal()
{
for(l in refreshListListeners) l.refreshPersonsList()
}
fun addRefreshListListener(l: InterfaceRefreshPersonsList)
{
refreshListListeners.add(l)
}
}
But I cannot setup the listener in the FragmentBrowse class. The fragment class doesn't allow constructors.
You can use built-in Kotlin delegates, for example:
object SignalChange {
var refreshListListeners = ArrayList<InterfaceRefreshList>()
// fires off every time value of the property changes
var property1: String by Delegates.observable("initial value") { property, oldValue, newValue ->
// do your stuff here
refreshListListeners.forEach {
it.refreshListRequest()
}
}
}
interface InterfaceRefreshList {
fun refreshListRequest()
}
Add listeners like this:
SignalChange.refreshListListeners.add(object : InterfaceRefreshList {
override fun refreshListRequest() {
refreshList()
}
})
OR
Intead of interface you can use lambda:
object SignalChange {
var refreshListListeners = ArrayList<() -> Unit>()
// fires off every time value of the property changes
var property1: String by Delegates.observable("initial value") { property, oldValue, newValue ->
// do your stuff here
refreshListListeners.forEach {
it()
}
}
}
And to add listener just call:
SignalChange.refreshListListeners.add(::refreshList)
//or
SignalChange.refreshListListeners.add { refreshList() }
fun refreshList() {
}
The simple way to Listen to variable in KOTLIN
private var myVariableName by Delegates.observable(0) { property, oldValue, newValue ->
Log.d(TAG,"New Value $newValue")
Log.d(TAG,"Old Value $oldValue")
}
for more info read https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/

Binding double to android:text

Using two way data binding I'm trying to fit a double(Double) inside a EditText type field.
I have tried with converter functions (with #InverseMethod) and also tried to write a #BindingAdapter with #InverseBindingAdapter.
I think I may be missing something crucial cause:
"#={`` + muObject.myDecimal}"
reveals 'null' in the EditText field.
The #InverseFunction method crashes, and the binding adapter way did not work either...
Could someone please point me in the right direction?
Thanks
ViewModel code:
Note that the BaseObservableViewModel extends ViewModel from architecture components and contains the contents of the BaseObservable class (tip from Yigit Boyar).
Also note that none of the fields in the QualityControl class are observable.
At last: Note that the getter/setter for measurementKm are a test. measurementKm is Double field in the QualityControl class and I would prefer to bind directly to that field.
public final class QualityControlViewModel extends BaseObservableViewModel {
#SuppressWarnings("unused")
private static final String TAG = "QualityControlVM";
private QualityControl qualityControl;
private int position;
public String measurementKm = "";
QualityControlViewModel(Application application) {
super(application);
}
public QualityControl getQualityControl() {
return qualityControl;
}
public void setQualityControl(QualityControl qualityControl) {
this.qualityControl = qualityControl;
// Initialize massMeasurementPlaceSelected
if (isValidMassMeasurementPlace()) massMeasurementPlaceSelected = true;
setMeasurementKm(qualityControl.getMeasurementKm());
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
private Double getMeasurementKm() {
if (this.measurementKm.length() > 0) {
return Double.parseDouble(this.measurementKm);
} else {
return 0.0;
}
}
private void setMeasurementKm(Double measurementKm) {
if (qualityControl.getMeasurementKm() != null) {
this.measurementKm = String.valueOf(qualityControl.getMeasurementKm());
}
notifyChange();
}
}

Android Rxjava subscribe to a variable change

I am learning Observer pattern, I want my observable to keep track of a certain variable when it changes it's value and do some operations, I've done something like :
public class Test extends MyChildActivity {
private int VARIABLE_TO_OBSERVE = 0;
Observable<Integer> mObservable = Observable.just(VARIABLE_TO_OBSERVE);
protected void onCreate() {/*onCreate method*/
super();
setContentView();
method();
changeVariable();
}
public void changeVariable() {
VARIABLE_TO_OBSERVE = 1;
}
public void method() {
mObservable.map(value -> {
if (value == 1) doMethod2();
return String.valueOf(value);
}).subScribe(string -> System.out.println(string));
}
public void doMethod2() {/*Do additional operations*/}
}
But doMethod2() doesn't get called
Nothing is magic in the life : if you update a value, your Observable won't be notified. You have to do it by yourself. For example using a PublishSubject.
public class Test extends MyChildActivity {
private int VARIABLE_TO_OBSERVE = 0;
Subject<Integer> mObservable = PublishSubject.create();
protected void onCreate() {/*onCreate method*/
super();
setContentView();
method();
changeVariable();
}
public void changeVariable() {
VARIABLE_TO_OBSERVE = 1;
// notify the Observable that the value just change
mObservable.onNext(VARIABLE_TO_OBSERVE);
}
public void method() {
mObservable.map(value -> {
if (value == 1) doMethod2();
return String.valueOf(value);
}).subScribe(string -> System.out.println(string));
}
public void doMethod2() {/*Do additional operations*/}
}
If interested here a Kotlin version of Variable class, which lets subscribers to be updated after every variable change.
class Variable<T>(private val defaultValue: T) {
var value: T = defaultValue
set(value) {
field = value
observable.onNext(value)
}
val observable = BehaviorSubject.createDefault(value)
}
Usage:
val greeting = Variable("Hello!")
greeting.observable.subscribe { Log.i("RxKotlin", it) }
greeting.value = "Ciao!"
greeting.value = "Hola!"
This will print:
"Hello!"
"Ciao!"
"Hola!"
#dwursteisen Nothing is magic, no, but I think we can get it a little more magic than that... 😊
How about using an Rx BehaviourSubject in this way:
import rx.functions.Action1;
import rx.subjects.BehaviorSubject;
public class BehaviourSubjectExample {
public BehaviourSubjectExample() {
subject.skip(1).subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
System.out.println("The value changed to " + integer );
}
});
}
public final BehaviorSubject<Integer> subject = BehaviorSubject.create(0);
public int getValue() { return subject.getValue(); }
public void setValue(int value) { subject.onNext(value); }
}
Remove the .skip(1) if you want the observing code to see the initial value.
The variable backing remains with the BehaviourSubject and can be accessed through conventional Java Getter/Setter. This is a toy example of course: If your use case were really this simple there'd be no excuse for not just writing:
private int value = 0;
public int getValue() { return value; }
public void setValue(int value) {
this.value = value;
System.out.println("The value changed to " + value );
}
...but the use of BehaviourSubject lets you bridge changes to other Rx data-streams inside your class for composing more advanced behaviours.

MVVMCross Get SelectedItem from a MvxBindableListView

Little problem with my Android application and I don't know how to solve it with MVVM Cross.
Here is my ViewModel:
public class AddressesShowViewModel : MvxViewModel
{
public List<Address> Addresses { get; set; }
public AddressesShowViewModel(string addressesForListView)
{
Addresses = JsonConvert.DeserializeObject<List<Address>>(addressesForListView);
}
public IMvxCommand ShowItemCommand
{
get
{
//return new MvxRelayCommand<Type>((type) => this.RequestNavigate(type));
return new MvxRelayCommand(DoShowContact);
}
}
private Address selectedItem;
public Address SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; FirePropertyChanged(() => SelectedItem); }
}
private void DoShowContact()
{
RequestNavigate<AddressShowViewModel>();
}
}
My AddressesShow.axml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/INMobileCRM4Android.INMobileCRM4Android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Mvx.MvxBindableListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'Addresses'},'ItemClick':{'Path':'ShowItemCommand'}, 'SelectedItem':{'Path':'SelectedItem'}}"
local:MvxItemTemplate="#layout/addresslistitem"
/>
</FrameLayout>
I would like to know, how I can get the SelectedItem from the ListView in AddressesShow.axml.. I tried to create a Property 'SelectedItem'.. But its getting called at the beginning, when the ViewModel is created (and is obviously returning null), not when the Item is clicked.. Its btw a type of Address, not just a String or something.. Maybe any suggestions?
The lack of SelectedItem in Droid was identified as an issue last week during preparation for Daniel's talk at Build.
To workaround it, there were a couple of quick answers:
1 There is SelectedItemPosition you can use for binding - this is an int
2 You can use a Click ICommand/IMvxCommand binding instead of using SelectedItem - in your example, this would be the same axml but
public IMvxCommand ShowItemCommand
{
get
{
return new MvxRelayCommand<Address>(address => DoShowContact(address));
}
}
To be clear this Click option above is what I would use.
If SelectedItem really is needed...
Then for a complete answer, Daniel and I prototyped a new binding. This binding was registered using:
registry.RegisterFactory(new MvxCustomBindingFactory<MvxBindableListView>("SelectedItem", adapterView => new MvxAdapterViewSelectedItemTargetBinding(adapterView)));
and contained the logic:
using System;
using Android.Widget;
using Cirrious.MvvmCross.Binding.Droid.Views;
using Cirrious.MvvmCross.Binding.Interfaces;
using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics;
namespace Cirrious.MvvmCross.Binding.Droid.Target
{
#warning This needs to be redone for all adapterviews not just list view!
#warning The use of ItemClick instead of ItemSelected needs to be reinvestigated here!
public class MvxAdapterViewSelectedItemTargetBinding : MvxBaseAndroidTargetBinding
{
private readonly MvxBindableListView _view;
private object _currentValue;
public MvxAdapterViewSelectedItemTargetBinding(MvxBindableListView view)
{
_view = view;
((ListView)_view).ItemClick += OnItemClick;
}
private void OnItemClick(object sender, AdapterView.ItemClickEventArgs itemClickEventArgs)
{
var container = (_view.GetItemAtPosition(itemClickEventArgs.Position) as MvxJavaContainer);
if (container == null)
{
MvxBindingTrace.Trace(MvxTraceLevel.Warning, "Missing MvxJavaContainer in MvxAdapterViewSelectedItemTargetBinding");
return;
}
var newValue = container.Object;
if (!newValue.Equals(_currentValue))
{
_currentValue = newValue;
FireValueChanged(newValue);
}
}
public override void SetValue(object value)
{
#warning Sort out Equals test here
if (value != null && value != _currentValue)
{
var index = _view.Adapter.GetPosition(value);
if (index < 0)
{
MvxBindingTrace.Trace(MvxTraceLevel.Warning, "Value not found for spinner {0}", value.ToString());
return;
}
_currentValue = value;
_view.SetSelection(index);
}
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
public override Type TargetType
{
get { return typeof(object); }
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
((ListView)_view).ItemClick -= OnItemClick;
}
base.Dispose(isDisposing);
}
}
}
To test this worked, I used the Tutorial PullToRefresh code adapted using:
<Mvx.MvxBindableListView android:id="#android:id/list" android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'Emails'},'ItemClick':{'Path':'ShowItemCommand'},'SelectedItem':{'Path':'TheSelectedEmail'}}"
local:MvxItemTemplate="#layout/listitem_email"
/>
and:
public class SimpleEmail
{
public string From { get; set; }
public string Header { get; set; }
public string Message { get; set; }
}
private ObservableCollection<SimpleEmail> _emails;
public ObservableCollection<SimpleEmail> Emails
{
get { return _emails; }
private set { _emails = value; RaisePropertyChanged(() => Emails); }
}
private SimpleEmail _email;
public SimpleEmail TheSelectedEmail
{
get { return _email; }
set
{
_email = value;
MvxTrace.Trace(MvxTraceLevel.Error, "HELLO {0} ", value == null ? "null" : value.From);
}
}
One thing to be careful about in all this work is that a listview selected item in Android is slightly different to a listbox selected item in Silverlight/wp - e.g. it can be quite hard to get a listview in android to highlight the current selection and it can be quite hard to get the listview to generate selection changed events.
Note: I've logged an issue on Droid SelectedItem to https://github.com/slodge/MvvmCross/issues/52 - I'll make sure the binding is added to the core library in the near future

Categories

Resources