I'm trying to validate a spinner in C# in xamarin in visual studio if the user didn't select an option. similar to setError in TextView, I have seen examples in SO but all of them are for java if I'm not wrong.
This is my spinner
Spinner spinnerJobTypes = FindViewById<Spinner>(Resource.Id.spinnerJobTypes);
and this does not work for me
TextView errorText = (TextView)mySpinner.getSelectedView();
I would like to display this, if the user hasn't selected anything
else if (spinnerJobTypes.SelectedItem.ToString() == "-Select-")
{
//Display validation error
}
Any help is appreciated,
Spinner spinnerJobTypes = FindViewById<Spinner>(Resource.Id.spinnerJobTypes);
spinnerJobTypes.ItemSelected += new EventHandler<AdapterView.ItemSelectedEventArgs> (spinner_ItemSelected);
private void spinner_ItemSelected (object sender, AdapterView.ItemSelectedEventArgs e)
{
Spinner spinner = (Spinner)sender;
string toast = string.Format ("The planet is {0}", spinner.GetItemAtPosition (e.Position));
Toast.MakeText (this, toast, ToastLength.Long).Show ();
}
for reference https://developer.xamarin.com/guides/android/user_interface/spinner/
Edited:
Use this and check
errorText.SetCompoundDrawablesWithIntrinsicBounds(0, 0, Resource.Drawable.errorIcon, 0);
My Suggestion is, add an "None selected" view to the Spinner and use a bool parameter to identify whether user has selected certain item or not:
SpinnerAdapter.cs
public class SpinnerAdapter : BaseAdapter<String>
{
Context context;
List<String> list;
public SpinnerAdapter(Context c, List<String> list)
{
context = c;
this.list = list;
}
public override string this[int position] => list[position-1];
public override int Count => this.list.Count+1;
public override long GetItemId(int position)
{
return 0;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view;
if (position == 0)
{
view = LayoutInflater.From(context).Inflate(Resource.Layout.spinner_error,null);
var txtView = view.FindViewById<TextView>(Resource.Id.tvErr);
view.FindViewById<TextView>(Resource.Id.tvErr).Text = "None Selected";
//uncomment the following line if you want to show the error icon inside of spinner
//view.FindViewById<TextView>(Resource.Id.tvErr).Error = "";
}
else
{
view = convertView;
if (view == null|| view.FindViewById<TextView>(Resource.Id.tvItem)==null)
{
view = LayoutInflater.From(context).Inflate(Resource.Layout.spinner_item, null);
}
view.FindViewById<TextView>(Resource.Id.tvItem).Text = list[position-1];
}
return view;
}
}
MainActivity:
public class MainActivity : Activity
{
Spinner mSpinner;
bool validatePass=false;
TextView tvResult;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
mSpinner = FindViewById<Spinner>(Resource.Id.mSpinner);
var list = InitList();
SpinnerAdapter adapter = new SpinnerAdapter(this, list);
tvResult = FindViewById<TextView>(Resource.Id.tvResult);
mSpinner.Adapter = adapter;
mSpinner.ItemSelected += MSpinner_ItemSelected;
}
private void MSpinner_ItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
//use validatePass to indicate if user has selected something.
if (e.Position != 0)
{
validatePass = true;
}
else
{
validatePass = false;
}
tvResult.Text ="Validate Result: "+ validatePass.ToString();
}
private List<string> InitList()
{
List<string> list = new List<string>();
for (int i = 0; i < 15; i++)
{
list.Add("Item: " + i);
}
return list;
}
}
But if you really want to show the error icon inside the spinner, you can set the TextView.Error inside of GetView.
Here is my complete Demo:HelloSpinner.
Related
I have listview with 4 columns like ItemLookupCode,Quantity,Description,Price
I am adding full code i am sorting list on button click.
i have issue in following Populate() to call 4 textview
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.PriceCheckList);
List<string> list = new List<string>();
foreach (crItem item in listObject)
{
list.Add(item.ItemLookupCode);
list.Add(Convert.ToString(item.Quantity));
list.Add(item.Description);
list.Add(Convert.ToString(item.Price));
}
spacecrafts = list.ToArray();
this.InitializeViews();
this.SortData(ascending);
this.ascending = !ascending;
}
SortBtnItemLookupCode.Click += sortBtn_Click;
void sortBtn_Click(object sender, EventArgs e)
{
SortData(ascending);
this.ascending = !ascending;
}
private void InitializeViews()
{
listView = FindViewById<ListView>(Resource.Id.listView);
SortBtnItemLookupCode = FindViewById<Button>(Resource.Id.SortBtnItemLookupCode);
}
private void Populate()
{
ArrayAdapter adapter = new ArrayAdapter<string>(this, Resource.Layout.PriceCheckListTemplate, Resource.Id.lblItemLookupCode, spacecrafts);
listView.SetAdapter(adapter);
}
private void SortData(bool asc)
{
if (asc)
{
Array.Sort(spacecrafts);
}
else
{
Array.Reverse(spacecrafts);
}
Populate();
}
}
I want to assign all above 4 object to Textview but currently ArrayAdapter accept only one Textview like i have use below Resource.Id.lblItemLookupCode but How i can assign 4 object to 4 columns Textview in ArrayAdapter
Help much appreciated
Thanks in advance
Try create model class to define data for the listView's item, then customize a custom Adapter to populate the data.
Check the code:
public class MainActivity : AppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
string[] items = new string[] { "Vegetables", "Fruits", "Flower Buds", "Legumes", "Bulbs", "Tubers" };
List<CustomModel> list = new List<CustomModel>();
//add data
CustomAdapter adapter = new CustomAdapter(list);
ListView listview_ = FindViewById<ListView>(Resource.Id.listview_);
listview_.SetAdapter(adapter);
}
}
public class CustomModel
{
public string Value1 { get; set; }
...
}
public class CustomAdapter : BaseAdapter<CustomModel>
{
List<CustomModel> list = new List<CustomModel>();
public _Adapter(List<CustomModel> list)
{
this.list = list;
}
public override CustomModel this[int position]
{
get
{
return list[position];
}
}
public override int Count
{
get
{
return list.Count;
}
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var view = convertView;
view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.layout1, parent, false);
//specify value to the each textView
var textview = view.FindViewById<TextView>(Resource.Id.text_1);
textview.Text = list[position].Value1;
...
return view;
}
}
I have a custom Listview, each row contains one textviewand one checkbox. I am saving the value (or the text) of the selected row's textview in a public list named usercoin. Each time the user opens the app, the list usercoin will contain the text of the his textview selected items, and I am doing that using SQLite. The problem is I want to re-check the items which the usaer have previously selected which are available in the usercoin list. I am not able to do so.
MyActivity.cs
ListView mListView;
MyAdapter adapter;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
mListView = FindViewById<ListView>(Resource.Id.listview);
List<TableList> list = new List<TableList>();
list.Add(new TableList("Germany",false));
list.Add(new TableList("France", false));
list.Add(new TableList("Finland", false));
list.Add(new TableList("Germany", false));
list.Add(new TableList("France", false));
list.Add(new TableList("Germany", false));
list.Add(new TableList("France", false));
list.Add(new TableList("Finland", false));
adapter = new MyAdapter(this, list);
mListView.Adapter = adapter;
mListView.ItemClick += MListView_ItemClick;
}
private void MListView_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
var t = list[e.Position];
string selected = t.name;
var ll = e.View as LinearLayout;
var cb = ll.GetChildAt(2) as CheckBox;
if (cb.Checked)
{
cb.Checked = false;
adapter.changeState((int)cb.Tag, false);
}
else
{
cb.Checked = true;
adapter.changeState((int)cb.Tag, true);
}
}
class MyAdapter : BaseAdapter
{
Context mContext;
List<TableList> mitems;
public MyAdapter(Context context, List<TableList> list)
{
this.mContext = context;
this.mitems = list;
}
public override int Count
{
get
{
return mitems.Count;
}
}
public override Java.Lang.Object GetItem(int position)
{
return mitems[position];
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
DataViewHolder holder = null;
if (convertView == null)
{
convertView = LayoutInflater.From(mContext).Inflate(Resource.Layout.CoinList, null, false);
holder = new DataViewHolder();
holder.tv = convertView.FindViewById<TextView>(Resource.Id.CoinName);
holder.iv = convertView.FindViewById<ImageView>(Resource.Id.imageView1);
holder.cb = convertView.FindViewById<CheckBox>(Resource.Id.checkBox1);
convertView.Tag = holder;
}
else
{
holder = convertView.Tag as DataViewHolder;
}
holder.cb.Tag = position;
holder.tv.Text = mitems[position].Name;
holder.cb.Focusable = false;
holder.cb.Checked = mitems[position].bl;
holder.iv.SetImageResource(Resource.Drawable.dapao);
holder.cb.CheckedChange += Cb_CheckedChange;
return convertView;
}
private void Cb_CheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e)
{
var cb = sender as CheckBox;
if (e.IsChecked && !mitems[(int)cb.Tag].bl)
{
mitems[(int)cb.Tag].bl = true;
this.NotifyDataSetChanged();
}
else if (!e.IsChecked && mitems[(int)cb.Tag].bl)
{
mitems[(int)cb.Tag].bl = false;
this.NotifyDataSetChanged();
}
}
internal void changeState(int tag, bool v)
{
mitems[tag].bl = v;
this.NotifyDataSetChanged();
}
}
public class DataViewHolder : Java.Lang.Object
{
public ImageView iv { get; set; }
public TextView tv { get; set; }
public CheckBox cb { get; set; }
}
public class TableList : Java.Lang.Object
{
private string v;
public TableList(string name, bool b)
{
this.Name = name;
this.bl = b;
}
public string Name { get; set; }
public bool bl { get; set; }
}
}
}
For example, when the user run the app and select France and Germany from the listview, next time he opens the app, the usercoin list will contain France and Germany. Now the question is how can I check the checkboxes corresponding to those values in the listview. I have tried to do so by including this code in MyAdapter : BaseAdapter class:
if (Class1.usercoin.Contains(item.CoinAbr))
{
Class1.adapter[(int)holder.cb.Tag].bl = true;
this.NotifyDataSetChanged();
}
But when this code get executed, the previously checked items are checked plus some other items which the user haven't checked previously are also checked. So how can I check the previously checked items in the Listview on the app start ? Please help me to find a solution.
I have stored the data(useritems) in both DataBase and memory.
If your app is killed by system or user, you can restore the data from DataBase.
If your app isn't killed by system or user, but user jump to other activity, when he back to this activity, you can use memory to restore the data.
About DataBase, I am use SQLite.Net. I am using DBHelper to operation the DataBase.
And I have add Application class in the app.
I have update the demo. Here is gif.
When we enter value in row 1 the value entered in row 1 is appearing back in row 6 when we scroll to the row 6. Please see the below code and advice.
namespace Kites
{
public class Marks
{
// add any if you need more
public string StudentName { get; set; }
public string MarksScored { get; set; }
}
public class TEXTCHECK
{
public int POS { get; set; }
public string Value { get; set; }
}
public class MarksListViewAdapter : BaseAdapter<Marks>
{
private List<Marks> mstuduentmarks;
private List<TEXTCHECK> abc = new List<TEXTCHECK>();
private Context mcontext;
public MarksListViewAdapter (Context context, List<Marks> stud)
{
mstuduentmarks = stud;
mcontext = context;
}
public override int Count
{
get
{
return mstuduentmarks.Count;
// return mattendence.Count;
}
}
public override long GetItemId (int position)
{
return position;
}
public override Marks this[int position]
{
get
{
return mstuduentmarks [position];
// return mattendence [position];
}
}
class ViewHolder : Java.Lang.Object
{
public EditText comsevin;
public TextView namenmn;
}
public override View GetView (int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
View view = convertView;
if (view == null) // otherwise create a new one
{
view = LayoutInflater.From(mcontext).Inflate(Resource.Layout.listview_Marks, null, false);
holder = new ViewHolder();
holder.comsevin = view.FindViewById<EditText>(Resource.Id.editTextTeacherMarks);
holder.namenmn = view.FindViewById<TextView>(Resource.Id.textStudentNameTeacherMarks);
holder.namenmn.Tag = position;
view.Tag = holder;
}
else
{
holder = (ViewHolder)view.Tag;
}
holder.namenmn.Text = mstuduentmarks[position].StudentName;
int pos = (int)holder.namenmn.Tag;
holder.comsevin.TextChanged += (sender, e) =>
{
abc[pos].Value = holder.comsevin.Text;
};
//TextView txtStudent =
//txtStudent.Text = mstuduentmarks[position].StudentName;
//txtMarks.FocusChange += (object sender, View.FocusChangeEventArgs e) =>
//{
// //txtMarks.RequestFocusFromTouch ();
// mstuduentmarks[position].MarksScored = txtMarks.Text;
//};
holder.comsevin.BeforeTextChanged += (sender, e) =>
{
abc.Add(new TEXTCHECK { POS = position, Value = mstuduentmarks[position].MarksScored });
};
holder.comsevin.AfterTextChanged += (sender, e) =>
{
int a = abc[pos].POS;
mstuduentmarks[pos].MarksScored = abc[pos].Value;
};
//txtMarks.Tag = position;
//txtMarks.TextChanged += TxtMarks_TextChanged;
return view;
}
//void TxtMarks_TextChanged (object sender, Android.Text.TextChangedEventArgs e)
//{
// EditText txtMarks = (EditText)sender;
// //var position = (int)txtMarks.Tag;
//}
}
}
When we enter value in row 1 the value entered in row 1 is appearing back in row 6 when we scroll to the row 6. Please see the below code and advice.
As a rule of thumb, when experiencing lists that don't reflect the dataset (experiencing item repetition for example) in listview / recyclerview it means that you're either using dirty views which were previously used and then uncorrectly Re-Bound, or simply using wrong positions during bind
I see where you are getting it wrong:
if (view == null) // otherwise create a new one
{
view = LayoutInflater.From(mcontext).Inflate(Resource.Layout.listview_Marks, null, false);
holder = new ViewHolder();
holder.comsevin = view.FindViewById<EditText>(Resource.Id.editTextTeacherMarks);
holder.namenmn = view.FindViewById<TextView>(Resource.Id.textStudentNameTeacherMarks);
holder.namenmn.Tag = position;//<------------here!!!
view.Tag = holder;
}
TLDR Don't save positions this way.
Whats happening: this instance of your view is being reused by listView, meaning that sometimes (many times) if (view == null) will be false and this means Tag property will not be updated for row 6 (or any other calls that will use recycled Views) and you are in fact using a dirty value.
You are then trying to use the Tag property as position, but forgetting this tag is already dirty if the view was recycled
int pos = (int)holder.namenmn.Tag;
holder.comsevin.TextChanged += (sender, e) =>
{
abc[pos].Value = holder.comsevin.Text;
};
Since you have access to the position in this method call you should use it directly
take a look at this guide from Java Code geeks even though it's in Java you will be able to see a good implementation of the old ViewHolder/ListView pattern.
Hope this helps
I have an Activity inherited from ListActivity. Inside I have a SimpleListItemChecked layout. I have an Ilist called codes. I have made items checked if they contain certain word which works fine. I now want to make the items not-editable/clickable.
public class ScanHistoryActivity : ListActivity
{
protected override void OnCreate(Bundle bundle)
{
var codes = Intent.Extras.GetStringArrayList("Codes");
base.OnCreate(bundle);
codes.ToList();
ListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItemChecked, codes);
ListView lv = FindViewById<ListView>(Android.Resource.Id.List);
lv.ChoiceMode = ChoiceMode.Multiple;
foreach (var c in codes)
{
if (c.Contains("Success"))
{
int position = codes.IndexOf(c);
lv.SetItemChecked(position, true);
// Here i tried:
// lv.clickable = false; but didn't work
}
}
}
}
Tried this but not working unfortunatley..
protected override void OnCreate(Bundle bundle)
{
var codes = Intent.Extras.GetStringArrayList("Codes");
base.OnCreate(bundle);
codes.ToList();
ListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItemChecked, codes);
ListView lv = FindViewById<ListView>(Android.Resource.Id.List);
lv.ChoiceMode = ChoiceMode.Multiple;
lv.Clickable = false;
lv.Focusable = false;
foreach (var c in codes)
{
long id = lv.GetItemIdAtPosition(codes.IndexOf(c));
if (c.Contains("Success"))
{
int position = codes.IndexOf(c);
lv.SetItemChecked(position, true);
OnListItemClick(lv,lv, position, id);
}
}
}
protected override void OnListItemClick(ListView l, View v, int position, long id)
{
l.Clickable = false;
l.Focusable = false;
}
Tried this as well: Made my own adapter to try get it working better:
protected override void OnCreate(Bundle bundle)
{
var codes = Intent.Extras.GetStringArrayList("Codes");
base.OnCreate(bundle);
codes.ToList();
ListAdapter = new ScanHistoryAdapter(this, codes.ToArray());
ListView lv = FindViewById<ListView>(Android.Resource.Id.List);
lv.ChoiceMode = ChoiceMode.Multiple;
lv.Clickable = false;
lv.Focusable = false;
foreach (var c in codes)
{
long id = lv.GetItemIdAtPosition(codes.IndexOf(c));
if (c.Contains("Success"))
{
int position = codes.IndexOf(c);
lv.SetItemChecked(position, true);
View v = FindViewById(ListAdapter.GetItemId();
OnListItemClick(lv,lv, position, id);
}
}
}
protected override void OnListItemClick(ListView l, View v, int position, long id)
{
l.Clickable = false;
l.Focusable = false;
}
simply We use to pass false value to setClickable method to disable clicking in java like this way.
ListView myList = (ListView) findViewById(R.id.list_view_id);
myList.setClickable(false);
Try once !!! There may be some methods like same...
We have some method to override and make specific item unclickable like this way
#Override
public boolean isEnabled(int position) {
if(position == your_item_pos) {
return false;
}
return true;
}
I have a simple activity which textview and listview. My list view use custom adapter to display data also in the adapter i have itemClick method. I would like to change textview after clicking the list item. My problem is I don't know how to find the text view from list adapter? I'll try something like this:
TextView TeamSelect = (TextView)activity.FindViewById(Resource.Id.tvSelectPlayers);
TeamSelect.SetText("Players", TextView.BufferType.Normal);
but this give me an error:
System.NullReferenceException: Object reference not set to an instance of an object
Here is my adapter:
public HomePlayersAdapter(Activity context)
{
mInflater = LayoutInflater.From(context);
mSelectedItemsIds = new SparseBooleanArray();
this.context = context;
public override int Count //return number of items in the list
{
get { return homePlayers.Count; }
}
public override Java.Lang.Object GetItem(int position)
{
return position;
}
public Player GetObject(int position)
{
return this.homePlayers.ElementAt(position);
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
var y = (itemPos.IndexOf(position) + 1);
var item = homePlayers[position];
if (convertView == null || holder == null)
{
convertView = mInflater.Inflate(Resource.Layout.RowPlayers, null);
holder = new ViewHolder();
holder.playerName = convertView.FindViewById<TextView>(Resource.Id.tvRow); //set holder label with label list id in the view
convertView.Tag = (holder);
}
else
{
holder = (ViewHolder)convertView.Tag;
}
holder.playerName.SetText(item.firstName + " " + item.lastName, TextView.BufferType.Normal);//set data label
holder.playerName.TextSize = 30;
if (clickCount >= 15)
{
subs = 1;
Android.Widget.Toast.MakeText(context, "ok", Android.Widget.ToastLength.Short).Show();
}
else
{
subs = 0;
}
y = (itemPos.IndexOf(position) + 1);
if (itemPos.Contains(position))
{
holder.playerName.SetTextColor(mInflater.Context.Resources.GetColor(Resource.Drawable.green));
holder.playerName.SetText(y + ". " + item.firstName + " " + item.lastName, TextView.BufferType.Normal);//set data label
}
else
{ holder.playerName.SetTextColor(mInflater.Context.Resources.GetColor(Resource.Drawable.white));
}
return convertView;
}
public void itemClick(int position)
{
if (!itemPos.Contains(position))
{
clickCount++;
var selectFixtureActivity = new Intent(context, typeof(SelectPlayers));
selectFixtureActivity.PutExtra("clickCount", clickCount);
holder.playerName.SetTextColor(mInflater.Context.Resources.GetColor(Resource.Drawable.green));
itemPos.Add(position);
NotifyDataSetChanged();
insertPlayer(position);
}
else
{
clickCount--;
var selectFixtureActivity = new Intent(context, typeof(SelectPlayers));
selectFixtureActivity.PutExtra("clickCount", clickCount);
holder.playerName.SetTextColor(mInflater.Context.Resources.GetColor(Resource.Drawable.white));
deletePlayer(position);
int po = itemPos.IndexOf(position);
itemPos.RemoveAt(po);
NotifyDataSetChanged();
class ViewHolder : Java.Lang.Object
{
public TextView playerName;
}
And my activity:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.SelectPlayer);
homeListAdapter = new HomePlayersAdapter(this);
listView = FindViewById<ListView>(Resource.Id.lvSelectPlayers);
TextView TeamSelect = FindViewById<TextView>(Resource.Id.tvSelectPlayer);
listView.Adapter = homeListAdapter;
listView.ChoiceMode = ChoiceMode.Multiple;
this.listView.ChoiceMode = ChoiceMode.Multiple;
var nameHome = Intent.GetStringExtra("nameHome");
var clickCount = Intent.GetStringExtra("clickCount");
TextView homeTeam = (TextView)FindViewById(Resource.Id.tvHomeTeam);
homeTeam.SetText(nameHome, Button.BufferType.Normal);
this.listView.ItemClick += (sender, e) =>
{
homeListAdapter.itemClick(e.Position);
}
I tried do that in different way: I send clickCount variable to the activity and there in ItemClick method i tried change the text view, but clickCount var on the begining is send to the acticity but when ItemClick method finish clickCount change value to null.How can i solve this problem?
You should not access the Activity directly from the Adapter. You should set a listener on the adapter (the listener can be the Activity). This is based on the Observer pattern. It decouples the components and promotes re-usability. Then the Activity, when called, can modify its own Text View.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
ListView myListView = (ListView)findViewById(R.id.your_list);
final TextView myTextView = (TextView)findViewById(R.id.your_text);
YourAdapter adapter = new YourAdapter();
adapter.setListener(new CustomListener() { // You write this method in your adapter
#Override
public void onItemClicked(Item clickedItem) {
myListView.setText( clickedItem.toString() );
}
});
}
Or if you aren't doing anything special in the custom adapter, you can always use the built in list view listener.
myListView.setOnItemClickListener( new OnItemClick() { // This is not onClick, but onItemClick
#Override
public void onItemClicked(AdapterView<?> parent, View view, int position, long id) {
Object clickedItem = parent.getItemAtPosition(position);
myTextView.setText( clickedItem.toString() );
}
});