Mvvm cross binding data context - android

I am trying to implement a mvvm cross solution. I am facing an issue with bindings..
I am trying to implement the solution in xamarin.android.
Below is my Main Layout page - Main.axml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
local:MvxBind="Text Title/>
<Mvx.MvxListView
android:id="#+id/SRMTypeList"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:fastScrollEnabled="true"
local:MvxItemTemplate="#layout/list_Item"
local:MvxBind="ItemsSource PersonCollection" />
</LinearLayout>
Below is my View Model:
public class MainViewModel :MvxViewModel
{
private string title;
public String Title
{
get{ return title;}
set
{
title = value;
RaisePropertyChanged (()=> Title);
}
}
List<Person> _personCollection;
List<Person> PersonCollection
{
get { return _personCollection; }
set
{
_personCollection = value;
RaisePropertyChanged (() => PersonCollection);
}
public MainViewModel()
{
_personCollection = new List<Person>();
PersonCollection.Add(new Person{Name="Steve", Salary=10000});
PersonCollection.Add(new Person{Name="Mary", Salary=20000});
}
}
MainView.cs
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
}
The issue starts here in the item template for my list view in the main screen list_Item.axml is shown below:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<CheckBox
android:id="#+id/checkbox2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="?" />
<TextView
android:textColor="#fff"
android:textSize="16sp"
local:MvxBind="?"/>
</LinearLayout>
How to make the binding for TextView's Text and CheckBox to parent's view model (ie. in the Main View Model class)?.
Any pointer/ help to solve this will be highly appreciated.
I have gone through below links .. but being a newbie was not able to understand the implementation.
Binding button click in ListView template MvvMCross
MVVMCross changing ViewModel within a MvxBindableListView
I am not able to understand how to create the wrapper class and how to set the data context of the item template (list_Item.axml) to this wrapper class.
Is their any way in mvvm cross so that i can refer bindings in the item template directly to the parent view model in my case which is MainViewModel.
Can anyone kindly post a simpler example?
Thanks

I'm not sure if that's what you are asking, but to make a binding of the person's name to the listitem textview:
local:MvxBind="Text Name"
And instead of using a List you should use an ObservableCollection
ObservableCollection<Person> _personCollection;
ObservableCollection<Person> PersonCollection
{
get { return _personCollection; }
set
{
_personCollection = value;
RaisePropertyChanged (() => PersonCollection);
}
}
For the checkbox i would add a field to the Person class such as IsSelected and bind it to the checkbox:
local:MvxBind="Checked IsSelected"

Related

How to bind method on RadioGroup on checkChanged event with data-binding

I was searching over the internet for how to perform the new cool android data-binding over the RadioGroup and I didn't find a single blog post about it.
Its a simple scenario, based on the radio button selected, I want to attach a callback event using android data binding. I don't find any method on the xml part which allows me to define a callback.
Like here is my RadioGroup:
<RadioGroup
android:id="#+id/split_type_radio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:checkedButton="#+id/split_type_equal"
android:gravity="center"
<!-- which tag ? -->
android:orientation="horizontal">
...
</RadioGroup>
How do I attach a handler method which will be called on RadioGroup's checkChnged event will fire using data-binding?
I have tried using onClick (don't know if it is the same) in layout file and defining method in the Activity and located it using this in the layout file:
<variable
name="handler"
type="com.example.MainActivity"/>
...
<RadioGroup
android:onClick="handler.onCustomCheckChanged"
.. >
And defined method onCustomCheckChanged like this:
public void onCustomCheckChanged(RadioGroup radio, int id) {
// ...
}
But, it gives me the compilation error:
Error:(58, 36) Listener class android.view.View.OnClickListener with method onClick did not match signature of any method handler.onCustomCheckChanged
I have seen many blogs mentioning it is possible with RadioGroup but non of them really say how. How can I handle this with data-binding ?
After digging to the bunch of methods, I found this question on SO which helped me understand how to bind single methods of listeners.
Here is what to do with RadioGroup:
In RadioGroup listener you have a method onCheckedChanged(RadioGroup g, int id). So you can directly bound that method to your handler or your activity by passing an instance of it as a variable in layout file and calling a method with the same signature.
So call on layout file like this:
<RadioGroup
android:id="#+id/split_type_radio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:checkedButton="#+id/split_type_equal"
android:gravity="center"
android:onCheckedChanged="#{handler.onSplitTypeChanged}"
android:orientation="horizontal">
...
</RadioGroup>
And in my activity or handler, I need to simply provide the method with same name and signature like this:
public void onSplitTypeChanged(RadioGroup radioGroup,int id) {
// ...
}
Just make sure method is public.
NOTE: This works for any (most of, I have not tried all) listener methods. Like for EditText you can provide android:onTextChanged and so on.
I am using a string, and in this case I have bindable based on viewModel.getCommuteType() viewModel.setCommuteType(String)
<RadioGroup
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:checked="#{viewModel.commuteType.equals(Commute.DRIVING)}"
android:onClick="#{()->viewModel.setCommuteType(Commute.DRIVING)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="D"/>
<RadioButton
android:checked="#{viewModel.commuteType.equals(Commute.BICYCLE)}"
android:onClick="#{()->viewModel.setCommuteType(Commute.BICYCLE)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B"/>
<RadioButton
android:checked="#{viewModel.commuteType.equals(Commute.WALKING)}"
android:onClick="#{()->viewModel.setCommuteType(Commute.WALKING)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="W"/>
<RadioButton
android:checked="#{viewModel.commuteType.equals(Commute.BUS)}"
android:onClick="#{()->viewModel.setCommuteType(Commute.BUS)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="T"/>
After some hours I found easy way: two-way databinding in android. It's base skeleton with livedata and Kotlin. Also you can use ObservableField()
Set your viewmodel to data
Create your radiogroup with buttons as you like. Important: set all radio buttons id !!!
Set in your radio group two-way binding to checked variable (use viewmodel variable)
Enjoy)
layout.xml
<data>
<variable
name="VM"
type="...YourViewModel" />
</data>
<LinearLayout
android:id="#+id/settings_block_env"
...
>
<RadioGroup
android:id="#+id/env_radioGroup"
android:checkedButton="#={VM.radio_checked}">
<RadioButton
android:id="#+id/your_id1"/>
<RadioButton
android:id="#+id/your_id2" />
<RadioButton
android:id="#+id/your_id3"/>
<RadioButton
android:id="#+id/your_id4"/>
</RadioGroup>
</LinearLayout>
class YourViewModel(): ViewModel {
var radio_checked = MutableLiveData<Int>()
init{
radio_checked.postValue(R.id.your_id1)//def value
}
//other code
}
Often you care more about what was actually checked instead of "something was checked". In such case alternative solution is to ignore RadioGroup and bind all items as below:
<RadioGroup (...) >
<RadioButton (...)
android:checked="#={viewModel.optionA}"/>
<RadioButton (...)
android:checked="#={viewModel.optionB}"/>
<RadioButton (...)
android:checked="#={viewModel.optionC}"/>
</RadioGroup>
where optionA, optionB and optionC are defined in ViewModel like below:
public final ObservableBoolean optionA = new ObservableBoolean();
public final ObservableBoolean optionB = new ObservableBoolean();
public final ObservableBoolean optionC = new ObservableBoolean();
This is usually enough, however if you want to react immediately on click then you can add callBacks and use them like that:
OnPropertyChangedCallback userChoosedA = new OnPropertyChangedCallback() {
#Override
public void onPropertyChanged(Observable sender, int propertyId) {
(...) // basically propertyId can be ignored in such case
}
};
optionA.addOnPropertyChangedCallback(userChoosedA);
Advantage of such approach is that you don't need to compare and track "id".
In my current project, I did it like this.
I have three currency in the project and I choose one of them via RadioGroup.
It's enum with currencies:
enum class Currency(val value: Byte) {
USD(0),
EUR(1),
RUB(2);
companion object Create {
fun from(sourceValue: Byte): Currency = values().first { it.value == sourceValue }
fun from(sourceValue: String): Currency = values().first { it.toString() == sourceValue }
}
}
A piece of my ViewModel:
class BaseCurrencyViewModel : ViewModelBase<BaseCurrencyModelInterface>() {
/**
* Selected currency
*/
val currency: MutableLiveData<Currency> = MutableLiveData()
/**
*
*/
init {
currency.value = Currency.USD // Init value
}
}
Part of my layout (pay attention to binding in RadioGroup and tags of RadioButton):
<RadioGroup
android:id="#+id/currencySwitchers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:selectedCurrency = "#{viewModel.currency}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintEnd_toEndOf="parent">
<RadioButton
android:id="#+id/usdSwitcher"
android:text="USD"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:tag="USD"
/>
<RadioButton
android:id="#+id/eurSwitcher"
android:text="EUR"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:tag="EUR"
/>
<RadioButton
android:id="#+id/rubSwitcher"
android:text="RUB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:tag="RUB"
/>
</RadioGroup>
And the final part - binding adapter.
#BindingAdapter("selectedCurrency")
fun setSelectedCurrency(view: View, value: MutableLiveData<Currency>?) {
view.getParentActivity()?.let { parentActivity ->
value?.observe(parentActivity, Observer { value ->
view.findViewWithTag<RadioButton>(value.toString())
?.also {
if(!it.isChecked) {
it.isChecked = true
}
}
}
)
(view as RadioGroup).setOnCheckedChangeListener { radioGroup, checkedId ->
val currency = Currency.from(radioGroup.findViewById<RadioButton>(checkedId).tag as String)
if(value != null && value.value != currency) {
value.value = currency
}
}
}
}
In this way, I got two-way binding between RadioGroup and a property in my ViewModel.

Bind button click inside customlayout using mvvmcross and mvxlistview

i wanted to bind a button click event inside my customlayout, below is my customlayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="63dp">
<LinearLayout
android:orientation="horizontal"
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:text="Accept"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/acceptBtnOnList"
android:background="#color/green_color"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textStyle="bold"
android:layout_weight="1"
android:textColor="#android:color/background_light"
local:MvxBind="Click AcceptCommand" />
</LinearLayout>
</RelativeLayout>
below is my ListView layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/background_light">
<Mvx.MvxListView
android:id="#+id/ListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#FFDAFF7F"
local:MvxBind="ItemsSource MyList; ItemClick ShowDetailCommand"
local:MvxItemTemplate="#layout/customlayout" />
</LinearLayout>
as you can see above i have called my customlayout inside the listview layout
Below is my ViewModelClass
public class ListViewModel : BaseViewModel
{
public IListService ListService { get; set; }
private MvxCommand _acceptCommand;
private ListAcceptedResult _accepted;
private MvxCommand _detailsCommand;
private ObservableCollection<MyCustomClass> _myList = new ObservableCollection<MyCustomClass>();
public ListViewModel(IListService listService)
{
ListService = listService;
}
public ObservableCollection<MyCustomClass> MyList
{
get { return _myList; }
set
{
_myList = value;
RaisePropertyChanged(() => MyList);
}
}
public ListAcceptedResult Accepted
{
get { return _accepted; }
set
{
_accepted = value;
RaisePropertyChanged(() => Accepted);
Update();
}
}
public ICommand AcceptCommand
{
get
{
IsLoading = true;
return
new MvxCommand<MyCustomClass>(
item =>
//On Success assigning the returned value from service to Accepted Property,
error => { IsLoading = false; ReportError(error.Message); }));
}
}
public async System.Threading.Tasks.Task Update()
{
//update logic
}
}
But i am not able to bind the AcceptCommand command to my button.
i am aware that this will not work because inside my customlayout view i do not get the AcceptCommand command as it is not a part of object MyCustomClass
please help me with some example.
Thanks in advance
As you know, data binding works by binding directly to the current ViewModel/Model. You're not able to access properties or commands of the parent view model, unless you provide a property to access the parent.
Anyway, one option is to create a value converter that you use in your binding. This converter will return a MvxCommand object that when executed will use MvxMessenger to publish a message.
The parent view model will subscribe to this message and then execute the command that you want.
I've created a sample based on Stuart Lodge's N=02 example.
https://github.com/kiliman/MvxCommandToMessage
EDIT: I modified the sample to use a generic MessageToCommandValueConverter. You can now pass in the message type in the binding. You still need specific message types though since MvxMessenger.Publish() is global to your app. See the code on GitHub for the changes.
Here's the value converter:
public class KittenAcceptedMessageValueConverter : MvxValueConverter<Kitten, ICommand>
{
protected override ICommand Convert(Kitten kitten, Type targetType, object parameter, CultureInfo culture)
{
return new MvxCommand(() =>
{
var messenger = Mvx.Resolve<IMvxMessenger>();
var message = new KittenAcceptedMessage(this, kitten);
messenger.Publish(message);
});
}
}
And here's how you bind to it in your layout. Use . to pass the current object to the converter.
<Mvx.MvxImageView
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_margin="10dp"
local:MvxBind="ImageUrl ImageUrl; Click KittenAcceptedMessage(.)" />
And then finally in your ViewModel, you would subscribe to this message, and call your command:
_messenger.Subscribe<KittenAcceptedMessage>(message =>
{
KittenAcceptedCommand.Execute(message.Kitten);
});
private MvxCommand<Kitten> _kittenAcceptedCommand;
public ICommand KittenAcceptedCommand
{
get
{
_kittenAcceptedCommand = _kittenAcceptedCommand ?? new MvxCommand<Kitten>(kitten =>
{
var toast = Mvx.Resolve<IToastPlugin>();
toast.Show(string.Format("You accepted {0}", kitten.Name));
});
return _kittenAcceptedCommand;
}
}
Hope this helps.

MvxListView create binding for template layout from code

Lets say I have a simple Layout with a MvxListView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/LiivControl.Client.Droid"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Mvx.MvxListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
local:MvxBind="ItemsSource AutoListItems; ItemClick AutoListItemClicked"
local:MvxItemTemplate="#layout/vbmvxautoviewlistitem" />
</LinearLayout>
My item template layout is as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/LiivControl.Client.Droid"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="10dip"
android:paddingBottom="10dip"
android:paddingLeft="15dip">
<TextView
android:id="#+id/list_complex_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/list_complex_caption"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
I would like to specify the bindings for the two textview elements in my itemtemplate from code behind. I am not sure how best to go about it. I'm guess I could do something "OnViewModelSet" in the view code behind of the MvxListView. I have tried the following but for it obviously doesn't work because it can't find the control.
protected override void OnViewModelSet()
{
IVbMvxAutoListViewModel vm = base.ViewModel as IVbMvxAutoListViewModel;
TextView title = this.FindViewById<TextView>(Resource.Id.list_complex_title);
this.CreateBinding(title).For(x => x.Text).To(vm.ListItemDescriptor.TitlePropName).Apply();
TextView subTitle = this.FindViewById<TextView>(Resource.Id.list_complex_caption);
this.CreateBinding(subTitle).For(x => x.Text).To(vm.ListItemDescriptor.SubTitlePropName).Apply();
base.OnViewModelSet();
}
My other thought was to somehow intercept the oncreate for the itemtemplate view but OnCreate doesn't get called if I create a view code file for my itemtemplate layout.
To do the bindings in code it's probably best to:
implement a custom MvxListViewItem
implement a custom MvxAdapter to return the custom list view item
implement a custom MvxListView to use the custom MvxAdapter
Not tested, but the code for this is roughly:
1. implement a custom MvxListViewItem
public class CustomListItemView
: MvxListItemView
{
public MvxListItemView(Context context,
IMvxLayoutInflater layoutInflater,
object dataContext,
int templateId)
: base(context, layoutInflater, dataContext, templateId)
{
var control = this.FindViewById<TextView>(Resource.Id.list_complex_title);
var set = this.CreateBindingSet<CustomListViewItem, YourThing>();
set.Bind(control).To(vm => vm.Title);
set.Apply();
}
}
2. Create a custom MvxAdapter
In this override CreateBindableView
public class CustomAdapter
: MvxAdapter
{
public CustomAdapter(Context context)
: base(context)
{
}
protected override IMvxListItemView CreateBindableView(object dataContext, int templateId)
{
return new CustomListItemView(_context, _bindingContext.LayoutInflater, dataContext, templateId);
}
}
original: https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Droid/Views/MvxAdapter.cs#L298
3. implement a custom MvxListView to use the adapter
This should be as simple as:
public class CustomListView
: MvxListView
{
public CustomListView(Context context, IAttributeSet attrs)
: base(context, attrs, new CustomAdapter(context))
{
}
}
As long as this is in your main UI assembly, this should be useable in your axml as:
<CustomListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
local:MvxBind="ItemsSource AutoListItems; ItemClick AutoListItemClicked"
local:MvxItemTemplate="#layout/vbmvxautoviewlistitem" />
If CustomListView is not in your main UI assembly, then there are some tricks to get MvvmCross to pick it up during your Setup - see Providing Custom Android View Assemblies in https://github.com/MvvmCross/MvvmCross/wiki/Customising-using-App-and-Setup#wiki-providing-custom-views-android
The above is the best way to do this (IMO) - but if you wanted to, then you could do it in less code by just applying the bindings inside the custom adapter and by setting that adapter in OnCreate in your Activity

Android Custom ListView Rows: Text Style not being applied

Trying to create custom rows in my listview (to look like this). I've created a custom row layout & derived adapter class. The data loads and shows fine, but the text is not using any format/style specified in my custom row .xml layout file. It's all just the default size/weight, etc.
Here's the custom row layout (listview_desc.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#222222">
<TextView
android:id="#+id/name"
android:text="Name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_margin="10dip"
android:textStyle="bold"
android:textSize="20dip"
/>
<TextView
android:id="#+id/description"
android:text="Description"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_margin="10dip"
android:textSize="13dip" />
</LinearLayout>
Here's my main layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:theme="#android:style/Theme.NoTitleBar"
android:minWidth="25px"
android:minHeight="25px"
android:background="#drawable/gradient_darkbg">
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/listView1" />
</LinearLayout>
My adapter code (I'm using Xamarin, but I don't think that's the problem...)
public class ListViewFormAdapter : BaseAdapter<Form>
{
List<Form> mForms;
Activity context;
public ListViewFormAdapter(Activity context, List<Form> items)
: base()
{
this.context = context;
this.mForms = items;
}
public override long GetItemId(int position)
{
return position;
}
public override Form this[int position]
{
get { return mForms[position]; }
}
public override int Count
{
get { return mForms.Count; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = mForms[position];
View view = convertView;
if (view == null) // no view to re-use, create new
view = context.LayoutInflater.Inflate(Resource.Layout.listview_desc, null);
view.FindViewById<TextView>(Resource.Id.name).Text = item.Name;
view.FindViewById<TextView>(Resource.Id.description).Text = item.Description;
return view;
}
}
And finally the main activity where I load and use the adapter:
public class HomeScreenActivity : Activity
{
List<Form> mForms;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Create your application here
SetContentView (Resource.Layout.Home);
// Load all forms and populate the main menu
mForms = Utils.FormLoader.LoadForms("Forms");
ListView listView = FindViewById<ListView>(Resource.Id.listView1);
listView.Adapter = new ListViewFormAdapter(this, mForms);
}
}
Sorry for all the code, maybe it'll help someone in the future... thanks for any help.
I'm not sure that this is the problem, but you inflating the views incorrectly. it should be
view = inflater.inflate(R.layout.listview_desc, parent, false);
instead of
view = context.LayoutInflater.Inflate(Resource.Layout.listview_desc, null);
using the 3 parameter version of inflate
What styles aren't showing correctly? I'm not sure what you're expecting or what you're seeing from the question. If it is to do with alignment, you should bear in mind that android:layout_alignParentLeft="true" is not valid in a LinearLayout
Thanks for the response guys. Turns out it was some problem with Git/Xamarin. I committed my sources at another machine, came home, synced up and all my formatting in the row layout xml were gone. Who knows... maybe a cached version was being used or something. I actually had to use the code posted on this page to get it to work (since it was lost), so maybe someone can use this as example code. It works.

Custom view updating content of another view

I have the following situation:
in the main file I do
setContentView(R.layout.main);
which looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/layout"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/result"
android:text="Gesamtstrecke: 0.0"
/>
<test.gustav.Spielfeld
android:id="#+id/spielfeld"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</test.gustav.Spielfeld>
</LinearLayout>
the View "test.gustav.Spielfeld" refers to a class "Spielfeld" extending "View".
I now need a method that updates the content of the TextView with id "result" if a something happens in the onTouchEvent(..)-method in the Spielfeld-View.
How to do this? I've tried making a public TextView in the Main-Class but the Spielfeld class won't accept Main.this.myTextView.
I would recommend a callback approach (just like android does for all events):
Create a interface which represents the callback:
public interface OnSpielfeldUpdate {
public void onSpielfeldUpdate(Object myEventData, ...);
}
Provide a setter for the callback in SpielFeld (which will be used to send updates):
public void setOnSpielfeldUpdate(OnSpielfeldUpdate callback) {
this.callback = callback;
}
Set your callback in your activity (or somewhere else):
mySpielfeld.setOnSpielfeldUpdate(new OnSpielfeldUpdate() {
public void onSpielfeldUpdate(Object myEventData, ...) {
// perform update on result view
}
}

Categories

Resources