I made a scroll list with multiple items. When I click on one item the color of the background of that item changes.
public class MyList extends Activity {
PackageManager packMan;
public static ArrayList<ItemList> list;
private ArrayAdapter<ItemList> adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.items);
init();
create();
addClickListener();
}
public void init(){
if(list==null) {
list = new ArrayList<ItemList>();
packMan = getPackageManager();
Intent i = new Intent(Intent.ACTION_MAIN, null);
i.addCategory("com.example.example");
List<ResolveInfo> items = packMan.queryIntentActivities(i, 0);
for (ResolveInfo ri : items) {
ItemList item = new ItemList();
item.addName((String) ri.loadLabel(packMan));
item.addNamePackage(ri.activityInfo.name);
item.addIcon(ri.activityInfo.loadIcon(packMan));
list.add(item);
}
}
}
private void create(){
v = (ListView)findViewById(R.id.list);
adapter = new ArrayAdapter<ItemList>(this, R.layout.items, list) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.items, null);
ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
icon.setImageDrawable(list.get(position).getIcon());
TextView name = (TextView) convertView.findViewById(R.id.name);
name.setText(list.get(position).getName());
}
return convertView;
}
};
v.setAdapter(adapter);
}
private void addClickListener(){
v.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> av, View v, int pos, long id) {
av.getChildAt(pos).setBackgroundColor(Color.parseColor("#DDFFFF"));
}
});
}
}
I am having a problem. When I select one item others are selected automatically.
How can I resolve this problem? Thanks.
From your code it seems that on click on an item you set its background to color DDFFFF, but you never switch them back to their original color once "unselected" . You can either:
keep track of the only selected item, and switch it back to white before coloring the new one (good if you only have 1 selected item at any time)
Whiten everyone on click, and then color the new one
Keep track of the state of each item with a list of booleans (active/inactive), and then override the draw loop to color according to their state
Related
I have a validate button inside each item of a recyclerView in order to change the color to green of the current item. When a new item is added in the recyclerview , I want to set the default background (no background color).
I've tried inside the BindView(position) function so by default when a new item is added in the itemsList, the color of the current element (item 0) is green whether I clicked on validate or not and that's not what I want.
I've also tried in the onBindViewHolder function but it doesnt work.
How can I change the color of this item in this recyclerview and this color remains the same whithout considering the index changing in the List if a new item is added?
I want that each new item of this recyclerview to be in the default color (background color white or no background color) and the item +n to be in the color of the corresponding status (validated = green , reschedulded = grey)
Once the validated button has been clicked I want the item to remains in read only mode.
Here is the code :
The adapter:
private Context context;
private List<RideObject> itemList;
public TestListeAdapter(List<RideObject> itemList, Context context) {
this.itemList = itemList;
this.context = context;
}
#NotNull
#Override
public TestListeAdapter.TestListeViewHolders onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_testliste, null, false);
RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutView.setLayoutParams(lp);
return new TestListeAdapter.TestListeViewHolders(layoutView);
}
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onBindViewHolder(#NotNull TestListeAdapter.TestListeViewHolders holder, final int position) {
holder.bindView(position);
if (holder.validated){
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(context.getApplicationContext(), R.color.teal_700));
}
the viewholder Class :
class TestListeViewHolders extends RecyclerView.ViewHolder {
//MyClickListener listener;
TextView rideId;
ImageView mCheck;
ImageView mreschedulded;
CardView mCard;
TestListeViewHolders(View itemView) {
super(itemView);
mCheck = itemView.findViewById(R.id.validate);
mreschedulded= itemView.findViewById(R.id.reschedulded);
mCard = itemView.findViewById(R.id.card_view);
}
private void bindView(int pos) {
RideObject item = itemList.get(pos);
mCheck.setOnClickListener(view -> {
item.setRDVHour(timePicker.getHour());
item.setRDVMinute(timePicker.getMinute());
validated = true;
item.setValider(true);
item.confirmDriver();
//if (itemList.size() == 1){
//mCard.setCardBackgroundColor(ContextCompat.getColor(context.getApplicationContext(), R.color.teal_700));
//itemView.setBackgroundColor(ContextCompat.getColor(context.getApplicationContext(),R.color.teal_700));
});
mreschedulded.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
timePicker.setEnabled(true);
itemView.setBackgroundColor(ContextCompat.getColor(context.getApplicationContext(),R.color.grey));
}
});
}
}
In the MainActivity:
resultsTestList.add(0, mCurrentRide);
mTestListAdapter.notifyDataSetChanged()
Ive tried the code in the answer but it doesnt work . Currently, the setbackgroundcolor applies just to the first element of the list. I would want that if the first element (item0) goes to second element (item1) the item keep his color background which is not the case with the given answer code
When binding RecyclerView items, you have to think of it as that you need to change the ViewHolder completely disregarding the previous state.
The view holder should not hold key information like validated.
Whether the view holder stays in the validated state or not should depend on the item it is being bind to.
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onBindViewHolder(#NotNull TestListeAdapter.TestListeViewHolders holder, final int position) {
holder.bindView(position);
}
...
private void bindView(int pos) {
RideObject item = itemList.get(pos);
if (item.validated) {
mCard.setCardBackgroundColor(...R.color.teal_700));
else {
mCard.setCardBackgroundColor(...R.color.grey));
mCheck.setOnClickListener(view -> {
item.setRDVHour(timePicker.getHour());
item.setRDVMinute(timePicker.getMinute());
item.validated = true;
item.setValider(true);
item.confirmDriver();
});
mReschedulded.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
timePicker.setEnabled(true);
itemView.setBackgroundColor(ContextCompat.getColor(context.getApplicationContext(),R.color.grey));
}
});
}
}
I want my listView to be updated after clicking on a row (or any event, but let's focus on click).
I did something, but it updates more than one row (maybe it updates the first visible row and the one after the last visible...).
Here is the full code
Activity code
DatabaseHandler colisageBase;
ListView listView;
List<Site> sites;
String id_tournee;
SiteAdapter siteAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_site_choice);
Intent intent = getIntent();
id_tournee = intent.getStringExtra("idTourneeSelectionnee");
this.listView = findViewById(R.id.list_view_site);
this.colisageBase = new DatabaseHandler(this);
sites = colisageBase.selectAllSite(id_tournee);
siteAdapter = new SiteAdapter(SiteChoiceActivity.this, sites);
listView.setAdapter(siteAdapter);
colisageBase.closeDB();
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Site selectedSite = sites.get(position);
selectedSite.setIsBarred(true);
sites.set(position, selectedSite);
siteAdapter.notifyDataSetChanged();
//goToOperationActivity(selectedSite.SiteOut());
}
});
Adapter code
public class SiteAdapter extends ArrayAdapter<Site> {
public SiteAdapter(Context context, List<Site> sites) {
super(context, 0, sites);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.row_site,parent, false);
}
SiteViewHolder viewHolder = (SiteViewHolder) convertView.getTag();
if(viewHolder == null){
viewHolder = new SiteViewHolder();
viewHolder.heure_supposee = convertView.findViewById(R.id.heure_supposee);
viewHolder.libelle_site = convertView.findViewById(R.id.libelle_site);
viewHolder.logo_telephone = convertView.findViewById(R.id.logo_phone);
convertView.setTag(viewHolder);
}
Site site = getItem(position);
viewHolder.heure_supposee.setText(site.getHeure_supposee());
viewHolder.libelle_site.setText(site.getLibelle_site());
viewHolder.logo_telephone.setVisibility(View.INVISIBLE);
if (site.getSur_appel().equals("O")) viewHolder.logo_telephone.setVisibility(View.VISIBLE);
if (site.isBarred()) viewHolder.libelle_site.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG);
return convertView;
}
#Override
public void notifyDataSetChanged()
{
super.notifyDataSetChanged();
}
private class SiteViewHolder{
public TextView heure_supposee;
public TextView libelle_site;
public ImageView logo_telephone;
}
}
Please suggest what's wrong with the code.
The answer was given in the comments by I_A_Mok, but i have to add more details:
In the case of a cell, when you do an action in an "if" condition , you usually have to do the opposite in an "else" condition.
In my case, after my condition where I strike through text, I had to add an else condition where I don't strike through text.
if (site.isBarred()){
viewHolder.libelle_site.setPaintFlags(viewHolder.libelle_site.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}else {
viewHolder.libelle_site.setPaintFlags(viewHolder.libelle_site.getPaintFlags() & (~ Paint.STRIKE_THRU_TEXT_FLAG));
}
I want to hide the 2nd option from the list on switch on off, I know how switch works, just tell me how to hide-unhide the option from the list. I want to hide list view item by item position or something like that.
New query : Is it possible to add two different adapter and switch them on switch preference change? if yes then how to do that?
This is 100% possible.
String [] titles = {"abc","def","ghi"};
String [] descriptions = {"abc","def","ghi"};
int [] images = {R.drawable.ic_abc,R.drawable.ic_def,R.drawable.ic_ghi};
ListView lv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
lv = (ListView) findViewById(R.id.listView);
final Adapter adapter = new Adapter(getApplicationContext(), titles, descriptions, images);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
});
}
List view adapter
class Adapter extends ArrayAdapter {
int[] imageArray;
String[] titleArray;
String[] descriptionArray;
public Adapter(Context context, String[] titles1, String [] description1, int[] img1) {
super(context, R.layout.list_row, R.id.Titles, titles1);
this.imageArray = img1;
this.titleArray = titles1;
this.descriptionArray = description1;
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.list_row,parent,false);
ImageView myImage = (ImageView) row.findViewById(R.id.Icons);
TextView myTitle = (TextView) row.findViewById(R.id.Titles);
TextView myDescription = (TextView) row.findViewById(R.id.Descriptions);
myImage.setImageResource(imageArray[position]);
myTitle.setText(titleArray[position]);
myDescription.setText(descriptionArray[position]);
return row;
}
}
Switch preference
public SwitchPreference sw;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_sw);
sw = (SwitchPreference) findPreference("001");
}
on switch on/off in main activity
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
boolean sw = sharedPreferences.getBoolean("001", true);
if (sw) {
//hide list view item (only one)
} else {
//unhide list view item (only one)
}
Well, take a look, so you can call remove properly:
class Adapter extends ArrayAdapter {
List<ItemObject> data = new ArrayList();
public Adapter(Context context, String[] titles1, String [] description1, int[] img1) {
super(context, R.layout.list_row, R.id.Titles, titles1);
for(int i = 0; i < titles1.lenght; i++)
data.add(new ItemObject(titles1[i], description1[i], img1[i]);
}
static class ItemObject {
String title, description;
int image;
ItemObject(String ti, String desc, int img) {
title = ti;
description = desc;
image = img;
}
}
//plus the rest of your class
And change at getView
ItemObject row = data.get(position);
myImage.setImageResource(row.image);
myTitle.setText(row.title);
myDescription.setText(row.description);
And add this method in the adapter:
public void removeObject(int at) {
data.remove(at);
notifyDataSetChanged();
}
So you can call removeObject with a position.
I want to update a single row in my listview with different content once i press a button. I know i can use notifydatasetchanged() but that would update the whole listView.
I have read this answer and it fits perfectly for what I want to do.
I have done a listview with 5 rows and when I press the button I want to update the 4th row with a different text. I dont want to set the text programatically since this is just a dummy project just to see if refreshing a single row is possible and my real project is much more complex than just a textview.
So my question is: can i use getView() to update a single row in a listview?
Here is my code:
my Activity:
public class MainActivity extends Activity {
public ListView list1;
public listAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list1 = (ListView) findViewById(R.id.my_list);
adapter = new listAdapter(this);
list1.setAdapter(adapter);
Button button1 = (Button) findViewById(R.id.my_button);
button1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
adapter.setText("Different text");
View row2Update = list1.getChildAt(3);
list1.getAdapter().getView(3, row2Update, list1);
}
});
}
}
My adapter :
public class listAdapter extends BaseAdapter{
public Activity activity;
public String text="Normal Text";
public listAdapter(Activity activity){
this.activity = activity;
}
public void setText(String text){
this.text = text;
}
public int getCount() {
return 5;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = activity.getLayoutInflater();
LinearLayout rowView = (LinearLayout) inflater.inflate(R.layout.row_layout, null);
TextView textView = (TextView) rowView.findViewById(R.id.row_text);
textView.setText(text);
return rowView;
}
}
This is what the activity looks like:
But when I press my button nothing changes
You cannot call the getView() method of the adapter yourself. The adapter's getView() method is is only called, when
The listview is create
when the user scrolls the list view
when notifysetdatachanged() is called.
All these are done by the OS. GetView() is called for all the rows in the listview. It is not called for just a single row. So, if you want to change the rows, you have to provide the data again in a String[], ArrayList<> etc
If you want different text to appear for for a single row, onClick() of a button - you can do this
public class listAdapter extends BaseAdapter{
public Activity activity;
public ArrayList<String> text;
public listAdapter(Activity activity){
this.activity = activity;
}
public void setText(ArrayList<String> text){
this.text = text;
}
public int getCount() {
return 5;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = activity.getLayoutInflater();
LinearLayout rowView = (LinearLayout) inflater.inflate(R.layout.row_layout, null);
TextView textView = (TextView) rowView.findViewById(R.id.row_text);
textView.setText(text[position]);
return rowView;
}
}
And in your Activity :
list1 = (ListView) findViewById(R.id.my_list);
adapter = new listAdapter(this);
String[] entries={"Normal Text","Normal Text","Normal Text","Normal text","Normal text"};
ArrayList<String> text=Arrays.asList(entries);
adapter.setText(text);
list1.setAdapter(adapter);
Button button1 = (Button) findViewById(R.id.my_button);
button1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
text.set(3,"Different Text");
adapter.setText(text);
list1.setAdapter(adapter);
}
});
There is another way of doing it also as #Andy suggested in one of the comments :
listViewPeople.setOnItemClickListener(new ListView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long l) {
//IF YOU WANT TO CHANGE THE CONTENT OF THE ROW CLICKED
if(position == someNumber) {
text.set(position,"different Text");
list1.setAdapter(text);
}
}
});
Sorry for the bold text. For some reason the CTRL+K is not working for the above code.
I'm trying to create a spinner with default empty selected item, but it displays the first item from the choices of spinner. If I add null value to my string, which is the source of choices in spinner, then after opening spinner that empty row is displayed. How should I do it? Here's code I'm using:
String[] ch = {"Session1", "Session2", "Session3"};
Spinner sp = (Spinner)findViewById(R.id.spinner1);
TextView sess_name = findViewById(R.id.sessname);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,ch);
sp.setAdapter(adapter);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener({
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
int index = arg0.getSelectedItemPosition();
sess_name.setText(ch[index]);
Toast.makeText(getBaseContext(), "You have selected item : " + ch[index], Toast.LENGTH_SHORT).show();
}
Barak's solution have a problem. When you select the first item, Spinner won't call OnItemSelectedListener's onItemSelected() and refresh the empty content because the previous position and selection position both is 0.
First put a empty string at the begin of your string array:
String[] test = {" ", "one", "two", "three"};
Second build adapter, don't modify getView(), modify getDropDownView(). Set the empty View's height to 1px.
public class MyArrayAdapter extends ArrayAdapter<String> {
private static final int ITEM_HEIGHT = ViewGroup.LayoutParams.WRAP_CONTENT;
private int textViewResourceId;
public MyArrayAdapter(Context context,
int textViewResourceId,
String[] objects) {
super(context, textViewResourceId, objects);
this.textViewResourceId = textViewResourceId;
}
#Override
public View getDropDownView(int position, View convertView, #NonNull ViewGroup parent) {
TextView textView;
if (convertView == null) {
textView = (TextView) LayoutInflater.from(getContext())
.inflate(textViewResourceId, parent, false);
} else {
textView = (TextView) convertView;
}
textView.setText(getItem(position));
if (position == 0) {
ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
layoutParams.height = 1;
textView.setLayoutParams(layoutParams);
} else {
ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
layoutParams.height = ITEM_HEIGHT;
textView.setLayoutParams(layoutParams);
}
return textView;
}
}
I'm a little late to the party, but here is what I did to solve this.
If the user cancels out of selecting an initial item the spinner will retain the initial empty state. Once an initial item has been selected it works as 'normal'
Works on 2.3.3+, I have not tested on 2.2 and below
First, create an adapter class...
public class EmptyFirstItemAdapter extends ArrayAdapter<String>{
//Track the removal of the empty item
private boolean emptyRemoved = false;
/** Adjust the constructor(s) to fit your purposes. */
public EmptyFirstitemAdapter(Context context, List<String> objects) {
super(context, android.R.layout.simple_spinner_item, objects);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
#Override
public int getCount() {
//Adjust the count based on the removal of the empty item
if(emptyRemoved){
return super.getCount();
}
return super.getCount()-1;
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
if(!emptyRemoved){
// Remove the empty item the first time the dropdown is displayed.
emptyRemoved = true;
// Set to false to prevent auto-selecting the first item after removal.
setNotifyOnChange(false);
remove(getItem(0));
// Set it back to true for future changes.
setNotifyOnChange(true);
}
return super.getDropDownView(position, convertView, parent);
}
#Override
public long getItemId(int position) {
// Adjust the id after removal to keep the id's the same as pre-removal.
if(emptyRemoved){
return position +1;
}
return position;
}
}
Here is the string array I used in strings.xml
<string-array name="my_items">
<item></item>
<item>Item 1</item>
<item>Item 2</item>
</string-array>
Next, add an OnItemSelectedListener to your Spinner...
mSpinner = (Spinner) mRootView.findViewById(R.id.spinner);
String[] opts = getResources().getStringArray(R.array.my_items);
//DO NOT set the entries in XML OR use an array directly, the adapter will get an immutable List.
List<String> vals = new ArrayList<String>(Arrays.asList(opts));
final EmptyFirstitemAdapter adapter = new EmptyFirstitemAdapter(getActivity(), vals);
mSpinner.setAdapter(adapter);
mSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
//Track that we have updated after removing the empty item
private boolean mInitialized = false;
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(!mInitialized && position == 0 && id == 1){
// User selected the 1st item after the 'empty' item was initially removed,
// update the data set to compensate for the removed item.
mInitialized = true;
adapter.notifyDataSetChanged();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
// Nothing to do
}
});
It may not be a 'perfect' solution, but I hope it helps someone.
After some thinking, I believe I've come up with a method to achieve your goal. It involves creating a
custom adapter and setting/maintaining a flag to determine if an item from the spinner has been selected.
Using this method you do not need to create/use false data (your empty string).
Basically, the adapters getView method sets the text for the closed spinner. So if you override that
and set a conditional in there, you can have a blank field on startup and after you make a selection have
it appear in the closed spinner box. The only thing is you need to remember to set the flag whenever you
need to see the value in the closed spinner.
I've created a small example program (code below).
Note that I only added the single constructor I needed for my example. You can implement all the standard
ArrayAdapter constructors or only the one(s) you need.
SpinnerTest.java
public class SpinnerTestActivity extends Activity {
private String[] planets = { "Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Saturn", "Uranus", "Neptune" };
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Spinner spinner = (Spinner) findViewById(R.id.spinner);
CustomAdapter adapter = new CustomAdapter(this, // Use our custom adapter
android.R.layout.simple_spinner_item, planets);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
#Override
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
CustomAdapter.flag = true; // Set adapter flag that something
has been chosen
}
});
}
}
CustomAdapter.java
public class CustomAdapter extends ArrayAdapter {
private Context context;
private int textViewResourceId;
private String[] objects;
public static boolean flag = false;
public CustomAdapter(Context context, int textViewResourceId,
String[] objects) {
super(context, textViewResourceId, objects);
this.context = context;
this.textViewResourceId = textViewResourceId;
this.objects = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = View.inflate(context, textViewResourceId, null);
if (flag != false) {
TextView tv = (TextView) convertView;
tv.setText(objects[position]);
}
return convertView;
}
}
Here is what I use. It properly handles null (empty) selection in a generic manner. It works with any model class T, as long as class T properly implements toString(), to display the text shown in the spinner, and equals(), so that items may be selected by reference rather than by positional index.
package com.10xdev.android.components;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* A spinner where no selection is possible, and other enhancements.
* requires model class to properly implement Object.equals, with semantic comparaison (such as id comparaison)
* and a proper toString(), whose result will be displayed in the spinner
*
* #author tony.benbrahim
*/
public class EnhancedSpinner<T> extends Spinner {
private final EnhanceArraySpinnerAdapter<T> spinnerAdapter;
private final List<T> items = new ArrayList<>();
private T selected = null;
public EnhancedSpinner(final Context context, final AttributeSet attributeSet) {
super(context, attributeSet);
spinnerAdapter = new EnhanceArraySpinnerAdapter<>(context, items);
setAdapter(spinnerAdapter);
}
/**
* sets the items to be displayed
*
* #param items
*/
public void setItems(final List<T> items) {
this.items.clear();
//very iffy, but works because of type erasure
this.items.add((T) "");
this.items.addAll(items);
spinnerAdapter.notifyDataSetChanged();
updateSelected();
}
/**
* set the selected item. this may be called before or after setting items
*
* #param item the item to select, or null to clear the selection
*/
public void setSelected(final T item) {
this.selected = item;
updateSelected();
}
/**
* gets the selected item, or null if no item is selected
*
* #return
*/
#Override
public T getSelectedItem() {
return getSelectedItemPosition() != 0 ? (T) super.getSelectedItem() : null;
}
/**
* set the error message for the select
*
* #param errorMessage
*/
public void setError(final String errorMessage) {
final TextView errorText = (TextView) getSelectedView();
errorText.setError("error");
errorText.setTextColor(Color.RED);
errorText.setText(errorMessage);
}
private void updateSelected() {
if (selected == null) {
setSelection(0);
} else {
for (int i = 1; i < items.size(); ++i) {
if (selected.equals(items.get(i))) {
setSelection(i);
break;
}
}
}
}
private class EnhanceArraySpinnerAdapter<T> extends ArrayAdapter<T> {
private final LayoutInflater inflater;
public EnhanceArraySpinnerAdapter(final Context context, final List<T> objects) {
super(context, android.R.layout.simple_spinner_item, objects);
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getDropDownView(final int position, final View convertView, final ViewGroup parent) {
final TextView textView = convertView != null ? (TextView) convertView
: (TextView) inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
final Object item = getItem(position);
textView.setText(item.toString());
final ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
layoutParams.height = position == 0 ? 1 : LayoutParams.WRAP_CONTENT;
textView.setLayoutParams(layoutParams);
return textView;
}
}
}
You have to put the first element of the spinner empty, or with an string indicating that nothing is selected like the following:
String[] ch= {"","Session1", "Session2", "Session3"};
or
String[] ch= {"Nothing selected", "Session1", "Session2", "Session3"};
hope to help