Bar views list and rating in android xamarin
I have a list box, each item has a text view and a score bar. Layout element:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineSpacingExtra="3dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="15sp"
android:typeface="sans"
android:layout_alignParentRight="true"/>
<RatingBar android:id="@+id/ratingbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/name"
android:numStars="5"
android:stepSize="1.0"
android:layout_alignParentLeft="true" />
</RelativeLayout>
this is the GetView method of my ListAdapter:
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
RatingBar rate;
if (view == null)
view = context.LayoutInflater.Inflate(Resource.Layout.QuestionListViewItemLayout, parent, false);
// view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItemActivated1, parent, false);
question item = this[position];
view.FindViewById<TextView>(Resource.Id.name).Text = item.text;
view.FindViewById<RatingBar>(Resource.Id.ratingbar).Rating = item.userrate;
RatingBar rb = view.FindViewById<RatingBar>(Resource.Id.ratingbar);
rb.RatingBarChange += (o, e) =>
{
item.userrate = System.Convert.ToInt32( e.Rating);
};
//view.FindViewById<TextView>(Resource.Id.SanadNumber).Text = item.sanadNumber.ToString();
return view;
}
on the line: "rb.RatingBarChange", when I change one of the rating bars in my list, some other elements change. that the delegate executes more than once every time I set a rating for one of my bars.
so when I read "item.userrate" which is edited by the rating bar there is no correct answer.
source to share
I want to have a list of Questions on my list. the question class looks like this:
public class question
{
public int id { get; set; }
public string text { get; set; }
public int questionorder { get; set; }
public int userrate { get; set; }
public question()
{
userrate = 0;
}
}
now I need a QuestionListViewItemLayout.axml file to style my list:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineSpacingExtra="3dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="15sp"
android:typeface="sans"now
android:layout_alignParentRight="true"/>
<RatingBar android:id="@+id/ratingbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/name"
android:numStars="5"
android:stepSize="1.0"
android:layout_alignParentLeft="true" />
</RelativeLayout>
I now need a ListAdapter to manage the items in my list view, but first I need to create a Wrapper class to save the views:
public class ViewWrapper : Java.Lang.Object
{
View base1;
RatingBar rate = null;
TextView label = null;
public ViewWrapper(View base1)
{
this.base1 = base1;
}
public RatingBar getRatingBar()
{
if (rate == null)
{
rate = base1.FindViewById<RatingBar>(Resource.Id.ratingbar);
}
return (rate);
}
public TextView getLabel()
{
if (label == null)
{
label = base1.FindViewById<TextView>(Resource.Id.name);
}
return (label);
}
}
this class is only created for my question class and maybe you need to create your own wrapper similar to the previous code
time to create adapter class List:
public class QuestionListAdapter : BaseAdapter<question>
{
Activity context;
List<question> list;
public QuestionListAdapter(Activity _context, List<question> _list)
: base()
{
this.context = _context;
this.list = _list;
}
public override int Count
{
get { return list.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public List<question> GetList()
{
return list;
}
public override question this[int index]
{
get { return list[index]; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
ViewWrapper wrapper;
RatingBar rate;
question item = this[position];
if (view == null)
{
view = context.LayoutInflater.Inflate(Resource.Layout.QuestionListViewItemLayout, parent, false);
wrapper = new ViewWrapper(view);
view.SetTag(Resource.Id.holder, wrapper);
rate = wrapper.getRatingBar();
rate.RatingBarChange += (o, e) =>
{
RatingBar ratingBar = o as RatingBar;
int myPosition = (int)ratingBar.GetTag(Resource.Id.holder);
question model = list[myPosition];
model.userrate = System.Convert.ToInt32(e.Rating);
};
}
else
{
wrapper = (ViewWrapper)view.GetTag(Resource.Id.holder);
rate = wrapper.getRatingBar();
}
question model1 = list[position];
wrapper.getLabel().Text = model1.text;
rate.SetTag(Resource.Id.holder, position);
rate.Rating = model1.userrate;
return view;
}
every time a view is created, I create a wrapper for it and put it in the viewport Section: [if (view == null)] and set an event to change the bar speed.
Another time when I load the views, I get the tag and set its rating.
source to share
Each time GetView starts, you create a new event handler for the RatingBarChange event that creates a closure around the current question (item) instance (effectively keeping each item instance in memory and tied to each event handler instance). All of these event handlers will run every time and update every instance of the element with the same value.
Your RatingBarChange event should have one handler that receives the selected item and applies the rating.
source to share