I have used this implementation of a multi select spinner I found here on StackOverflow:
package cz.destil.settleup.gui;
public class MultiSpinner extends Spinner implements
OnMultiChoiceClickListener, OnCancelListener {
private List<String> items;
private boolean[] selected;
private String defaultText;
private MultiSpinnerListener listener;
public MultiSpinner(Context context) {
super(context);
}
public MultiSpinner(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public MultiSpinner(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
#Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if (isChecked)
selected[which] = true;
else
selected[which] = false;
}
#Override
public void onCancel(DialogInterface dialog) {
// refresh text on spinner
StringBuffer spinnerBuffer = new StringBuffer();
boolean someUnselected = false;
for (int i = 0; i < items.size(); i++) {
if (selected[i] == true) {
spinnerBuffer.append(items.get(i));
spinnerBuffer.append(", ");
} else {
someUnselected = true;
}
}
String spinnerText;
if (someUnselected) {
spinnerText = spinnerBuffer.toString();
if (spinnerText.length() > 2)
spinnerText = spinnerText.substring(0, spinnerText.length() - 2);
} else {
spinnerText = defaultText;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item,
new String[] { spinnerText });
setAdapter(adapter);
listener.onItemsSelected(selected);
}
#Override
public boolean performClick() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setMultiChoiceItems(
items.toArray(new CharSequence[items.size()]), selected, this);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setOnCancelListener(this);
builder.show();
return true;
}
public void setItems(List<String> items, String allText,
MultiSpinnerListener listener) {
this.items = items;
this.defaultText = allText;
this.listener = listener;
// all selected by default
selected = new boolean[items.size()];
for (int i = 0; i < selected.length; i++)
selected[i] = true;
// all text on the spinner
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item, new String[] { allText });
setAdapter(adapter);
}
public interface MultiSpinnerListener {
public void onItemsSelected(boolean[] selected);
}
}
Now I want to change the design of the window that opens when I click on this Multispinner button, I tried to change this: android.R.layout.simple_spinner_item to my own layout but this changed only the button that open the spinner, not the items inside it.
How can I change the design on the inner multispinner items?
Thanks.
The dialog that is shown is created in the performClick() method. However, it doesn't look like you could change the layout of the list items here, as the setMultiChoiceItems(...) method does not support it.
So you will need to implement a custom view for your dialog, which you can set in the setView(...) method (example). And you will also have to implement the onClick() method by yourself then.
Update: Maybe the better way is using the setAdapter(...) method. This way you can just use your own list adapter, so you can style the items the way you want. And this method does also provide an onclick listener argument. So it may be simpler.
Update 2: I tried a bit myself, and this is what I came up with. I added a comment, where you can insert your own view:
public class MultiSpinner extends Spinner implements OnCancelListener,
OnItemClickListener {
public class MultiSpinnerListAdapter extends BaseAdapter {
#Override
public int getCount() {
return items.size();
}
#Override
public String getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO: Update the following to use your own custom view.
if (convertView == null) {
convertView = activity.getLayoutInflater().inflate(
android.R.layout.simple_list_item_multiple_choice,
parent, false);
}
CheckedTextView textView = (CheckedTextView) convertView;
textView.setText(items.get(position));
textView.setChecked(selected[position]);
return convertView;
}
}
private MultiSpinnerListAdapter adapter;
private Activity activity;
private List<String> items;
private boolean[] selected;
private String defaultText;
private MultiSpinnerListener listener;
public MultiSpinner(Context context) {
super(context);
}
public MultiSpinner(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public MultiSpinner(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
CheckedTextView textView = (CheckedTextView) view;
textView.setChecked(!textView.isChecked());
selected[position] = textView.isChecked();
}
#Override
public void onCancel(DialogInterface dialog) {
// refresh text on spinner
StringBuffer spinnerBuffer = new StringBuffer();
boolean someUnselected = false;
for (int i = 0; i < items.size(); i++) {
if (selected[i] == true) {
spinnerBuffer.append(items.get(i));
spinnerBuffer.append(", ");
} else {
someUnselected = true;
}
}
String spinnerText;
if (someUnselected) {
spinnerText = spinnerBuffer.toString();
if (spinnerText.length() > 2)
spinnerText = spinnerText
.substring(0, spinnerText.length() - 2);
} else {
spinnerText = defaultText;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item,
new String[] { spinnerText });
setAdapter(adapter);
listener.onItemsSelected(selected);
}
#Override
public boolean performClick() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setAdapter(adapter, null);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setOnCancelListener(this);
AlertDialog dialog = builder.create();
dialog.getListView().setOnItemClickListener(this);
dialog.show();
return true;
}
public void setItems(Activity activity, List<String> items, String allText,
MultiSpinnerListener listener) {
this.adapter = new MultiSpinnerListAdapter();
this.activity = activity;
this.items = items;
this.defaultText = allText;
this.listener = listener;
// all selected by default
selected = new boolean[items.size()];
for (int i = 0; i < selected.length; i++)
selected[i] = true;
// all text on the spinner
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item, new String[] { allText });
setAdapter(adapter);
}
public interface MultiSpinnerListener {
public void onItemsSelected(boolean[] selected);
}
}
Related
I want to remove the item in setOnItemLongClickListener, deletion isn't working. Can anyone see what the problem is in the code ?
Adapter
public abstract class myArrayAdapter<T> extends ArrayAdapter<T> {
protected List<T> items = new ArrayList<>();
protected int resource;
protected LayoutInflater layoutInflater;
public myArrayAdapter(Context context, int resource) {
super(context, resource);
this.resource = resource;
this.layoutInflater = LayoutInflater.from(context);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = layoutInflater.inflate(resource, null, false);
getView(position, getItem(position), view);
return view;
}
public abstract void getView(int position, T model, View view);
public void setItems(List<T> items) {
this.items = items;
notifyDataSetChanged();
}
#Override
public T getItem(int position) {
return items.get(position);
}
#Override
public int getCount() {
return items.size();
}
public List<T> getItems() {
return items;
}
#Override
public int getPosition(T item) {
return items.indexOf(item);
}
}
my activity
public class QuoteDetailActivity extends Activity {
#Inject
QuoteDetailViewModel viewModel;
#BindView(R.id.toolbar)
Toolbar toolbar;
#BindView(R.id.price_text)
TextView priceTextView;
#BindView(R.id.list_view_materials)
ListView materialsListView;
private int quoteId;
myArrayAdapter<LinkedTreeMap<String, Object>> adapter;
public static void start(Context context, int quoteId) {
Intent starter = new Intent(context, QuoteDetailActivity.class);
starter.putExtra("QUOTE_ID", quoteId);
context.startActivity(starter);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quote_detail);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(v -> finish());
quoteId = getIntent().getIntExtra("QUOTE_ID", 0);
initMaterialList();
bindToViewModel();
}
private void bindToViewModel() {
viewModel.quoteModel()
.compose(bindToLifecycle())
.subscribe(quoteModel -> {
getSupportActionBar().setTitle(String.valueOf(quoteModel.get("QuoteName")));
priceTextView.setText(String.valueOf(quoteModel.get("TotalCost")) + " + KDV");
});
viewModel.quoteMaterialModel()
.compose(bindToLifecycle())
.subscribe(materialsModel -> {
adapter.setItems(materialsModel);
});
//teklif silme
viewModel.materialDelete()
.compose(bindToLifecycle())
.subscribe(aBoolean -> {
if (aBoolean) {
finish();
}
});
attachToViewModel(viewModel);
}
#Override
protected void onResume() {
super.onResume();
viewModel.getQuoteDetail(quoteId);
}
private void initMaterialList(){
adapter = new myArrayAdapter<LinkedTreeMap<String, Object>>(this, R.layout.layout_listview_item_quote_material) {
#Override
public void getView(int position, LinkedTreeMap<String, Object> model, View view) {
TextView materialNameTextView = (TextView) view.findViewById(R.id.material_name);
TextView priceAndAmountNameTextView = (TextView) view.findViewById(R.id.price_and_amount);
TextView totalCostNameTextView = (TextView) view.findViewById(R.id.total_cost);
materialNameTextView.setText(String.valueOf(model.get("MaterialName")));
priceAndAmountNameTextView.setText("Fiy. x Mik : " + String.valueOf(model.get("Cost")) + " x " + String.valueOf(model.get("MaterialCount")));
totalCostNameTextView.setText(String.valueOf(model.get("TotalCost")));
}
};
materialsListView.setAdapter(adapter);
materialsListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, r.getDisplayMetrics());
int pxTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, r.getDisplayMetrics());
AlertDialog.Builder alertDialog = new AlertDialog.Builder(QuoteDetailActivity.this);
alertDialog.setTitle("Delete.");
alertDialog.setPositiveButton("Yes", (dialog, which) -> {
LinkedTreeMap<String, Object> selectedItem = adapter.getItem(position);
int QuoteMaterialId = ((Double) selectedItem.get("QuoteMaterialId")).intValue();
viewModel.deleteMaterial(quoteId,QuoteMaterialId);
adapter.remove(adapter.getItem(position));
adapter.notifyDataSetChanged();
});
alertDialog.setNegativeButton("No", (dialog, which) -> {
dialog.dismiss();
});
alertDialog.show();
return true; }
});
}
#Override
public void setupComponent(ActivityComponent activityComponent) {
DaggerQuoteComponent.builder()
.activityComponent(activityComponent)
.build()
.inject(this);
}
}
Deletion is not happening. Where am I making mistakes? Thanks.
try the following code:
materialsListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, r.getDisplayMetrics());
int pxTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, r.getDisplayMetrics());
AlertDialog.Builder alertDialog = new AlertDialog.Builder(QuoteDetailActivity.this);
alertDialog.setTitle("Delete.");
alertDialog.setPositiveButton("Yes", (dialog, which) -> {
LinkedTreeMap<String, Object> selectedItem = adapter.getItem(position);
int QuoteMaterialId = ((Double) selectedItem.get("QuoteMaterialId")).intValue();
viewModel.deleteMaterial(quoteId,QuoteMaterialId);
adapter.remove(selectedItem);
adapter.notifyDataSetChanged();
//adapter.remove(adapter.getItem(position));
//adapter.notifyDataSetChanged();
});
alertDialog.setNegativeButton("No", (dialog, which) -> {
dialog.dismiss();
});
alertDialog.show();
return true; }
});
You have to work with the data set, not with the adapter.
e.g: If you fill a ListView with a ArrayList<T> object, if you want to delete a row in the list you have to delete it from the ArrayList and then call the notifyDataSetChanged().
// ArrayList<T> items filled with data
// delete the item that you want
items.remove(position);
// so, communicate to the adapter that the dataset is changed
adapter.notifyDataSetChanged();
In your specific case, the item from materialsModel, then notufy it to the adapter, something like follwing:
// remove the item
// I don't know which method you must call, hope you do ;)
materialsModel.remove(position)
// then notify the adapter that the dataset is changed
adapter.notifyDataSetChanged();
Either use a different constructor with list items in MyArrayAdapter
public MyArrayAdapter(Context context, int resource, List<T> objects) {
super(context, resource, objects);
}
or override the remove method of the adapter and manually delete the items
#Override
public void remove(T object) {
items.remove(object);
}
I need to let the user to choose city in the preferences, there are many cities so I used this project to search in the list, its working great, but I need to implement it inside preferences xml, and not in a regular layout.
here is what I had before, [Main Activity class, works fine in regular layout]:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* Getting array of String to Bind in Spinner
*/
final List<String> list = Arrays.asList(getResources().getStringArray(R.array.sports_array));
/**
* Search MultiSelection Spinner (With Search/Filter Functionality)
*
* Using MultiSpinnerSearch class
*/
MultiSpinnerSearch searchSpinner = (MultiSpinnerSearch) findViewById(R.id.searchMultiSpinner);
final List<KeyPairBoolData> listArray = new ArrayList<KeyPairBoolData>();
for(int i=0; i<list.size(); i++) {
KeyPairBoolData h = new KeyPairBoolData();
h.setId(i+1);
h.setName(list.get(i));
h.setSelected(false);
listArray.add(h);
}
/***
* -1 is no by default selection
* 0 to length will select corresponding values
*/
searchSpinner.setItems(listArray, "Multi Selection With Filter", -1, new MultiSpinnerSearchListener() {
#Override
public void onItemsSelected(List<KeyPairBoolData> items) {
for(int i=0; i<items.size(); i++) {
if(items.get(i).isSelected()) {
Log.i("TAG", i + " : " + items.get(i).getName() + " : " + items.get(i).isSelected());
}
}
}
});
}
in the layout I had:
<com.androidbuts.multispinnerfilter.MultiSpinnerSearch
android:id="#+id/searchMultiSpinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:layout_marginTop="20dp" >
</com.androidbuts.multispinnerfilter.MultiSpinnerSearch>
and the original class was:
public class MultiSpinnerSearch extends Spinner implements OnCancelListener {
private List<KeyPairBoolData> items;
//private boolean[] selected;
private String defaultText;
private MultiSpinnerSearchListener listener;
MyAdapter adapter;
public MultiSpinnerSearch(Context context) {
super(context);
}
public MultiSpinnerSearch(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public MultiSpinnerSearch(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
#Override
public void onCancel(DialogInterface dialog) {
// refresh text on spinner
StringBuffer spinnerBuffer = new StringBuffer();
for (int i = 0; i < items.size(); i++) {
if (items.get(i).isSelected() == true) {
spinnerBuffer.append(items.get(i).getName());
spinnerBuffer.append(", ");
}
}
String spinnerText = "";
spinnerText = spinnerBuffer.toString();
if (spinnerText.length() > 2)
spinnerText = spinnerText.substring(0, spinnerText.length() - 2);
else
spinnerText = defaultText;
ArrayAdapter<String> adapterSpinner = new ArrayAdapter<String>(getContext(),
R.layout.textview_for_spinner,
new String[] { spinnerText });
setAdapter(adapterSpinner);
if(adapter != null)
adapter.notifyDataSetChanged();
listener.onItemsSelected(items);
}
#Override
public boolean performClick() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(defaultText);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
View view = inflater.inflate(R.layout.alert_dialog_listview_search, null);
builder.setView(view);
final ListView listView = (ListView) view.findViewById(R.id.alertSearchListView);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setFastScrollEnabled(false);
adapter = new MyAdapter(getContext(), items);
listView.setAdapter(adapter);
EditText editText = (EditText) view.findViewById(R.id.alertSearchEditText);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.getFilter().filter(s.toString());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
//builder.setMultiChoiceItems(items.toArray(new CharSequence[items.size()]), selected, this);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// items = (ArrayList<KeyPairBoolData>) adapter.arrayList;
Log.i("TAG", " ITEMS : " + items.size() );
dialog.cancel();
}
});
builder.setOnCancelListener(this);
builder.show();
return true;
}
public void setItems(List<KeyPairBoolData> items, String allText, int position,
MultiSpinnerSearchListener listener) {
this.items = items;
this.defaultText = allText;
this.listener = listener;
ArrayAdapter<String> adapterSpinner = new ArrayAdapter<String>(getContext(),
R.layout.textview_for_spinner,
new String[] { defaultText });
setAdapter(adapterSpinner);
if(position != -1)
{
items.get(position).setSelected(true);
//listener.onItemsSelected(items);
onCancel(null);
}
}
public interface MultiSpinnerSearchListener {
public void onItemsSelected(List<KeyPairBoolData> items);
}
// // Adapter Class
public class MyAdapter extends BaseAdapter implements Filterable {
List<KeyPairBoolData> arrayList;
List<KeyPairBoolData> mOriginalValues; // Original Values
LayoutInflater inflater;
public MyAdapter(Context context, List<KeyPairBoolData> arrayList) {
this.arrayList = arrayList;
inflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
private class ViewHolder {
TextView textView;
CheckBox checkBox;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.alert_dialog_listview_search_subview, null);
holder.textView = (TextView) convertView.findViewById(R.id.alertTextView);
holder.checkBox = (CheckBox) convertView.findViewById(R.id.alertCheckbox);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final KeyPairBoolData data = arrayList.get(position);
holder.textView.setText(data.getName());
holder.checkBox.setChecked(data.isSelected());
convertView.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ViewHolder temp = (ViewHolder) v.getTag();
temp.checkBox.setChecked(!temp.checkBox.isChecked());
int len = arrayList.size();
for (int i = 0; i < len; i++)
{
if (i == position)
{
data.setSelected(!data.isSelected());
Log.i("TAG", "On Click Selected : " + data.getName() + " : " + data.isSelected());
break;
}
}
}
});
holder.checkBox.setTag(holder);
return convertView;
}
#SuppressLint("DefaultLocale")
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
arrayList = (List<KeyPairBoolData>) results.values; // has the filtered values
notifyDataSetChanged(); // notifies the data with new filtered values
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
List<KeyPairBoolData> FilteredArrList = new ArrayList<KeyPairBoolData>();
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<KeyPairBoolData>(arrayList); // saves the original data in mOriginalValues
}
/********
*
* If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
* else does the Filtering and returns FilteredArrList(Filtered)
*
********/
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mOriginalValues.size();
results.values = mOriginalValues;
} else {
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mOriginalValues.size(); i++) {
Log.i("TAG", "Filter : " + mOriginalValues.get(i).getName() + " -> " + mOriginalValues.get(i).isSelected());
String data = mOriginalValues.get(i).getName();
if (data.toLowerCase().contains(constraint.toString())) {
FilteredArrList.add(mOriginalValues.get(i));
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
}
And here is what I'm trying to do, PreferencesSpinner Class (almost same as above):
public class PreferencesSpinner extends DialogPreference implements OnCancelListener {
private List<KeyPairBoolData> items;
public Spinner spinner;
//private boolean[] selected;
private String defaultText;
private PreferencesSpinnerListener listener;
MyAdapter adapter;
public PreferencesSpinner(Context context) {
super(context);
}
public PreferencesSpinner(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public PreferencesSpinner(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
#Override
public void onCancel(DialogInterface dialog) {
// refresh text on spinner
StringBuffer spinnerBuffer = new StringBuffer();
for (int i = 0; i < items.size(); i++) {
if (items.get(i).isSelected() == true) {
spinnerBuffer.append(items.get(i).getName());
spinnerBuffer.append(", ");
}
}
String spinnerText = "";
spinnerText = spinnerBuffer.toString();
if (spinnerText.length() > 2)
spinnerText = spinnerText.substring(0, spinnerText.length() - 2);
else
spinnerText = defaultText;
ArrayAdapter<String> adapterSpinner = new ArrayAdapter<String>(getContext(),
R.layout.textview_for_spinner,
new String[] { spinnerText });
spinner.setAdapter(adapterSpinner);
if(adapter != null)
adapter.notifyDataSetChanged();
listener.onItemsSelected(items);
}
public boolean performClick() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(defaultText);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
View view = inflater.inflate(R.layout.alert_dialog_listview_search, null);
builder.setView(view);
final ListView listView = (ListView) view.findViewById(R.id.alertSearchListView);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setFastScrollEnabled(false);
adapter = new MyAdapter(getContext(), items);
listView.setAdapter(adapter);
EditText editText = (EditText) view.findViewById(R.id.alertSearchEditText);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.getFilter().filter(s.toString());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
//builder.setMultiChoiceItems(items.toArray(new CharSequence[items.size()]), selected, this);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// items = (ArrayList<KeyPairBoolData>) adapter.arrayList;
Log.i("TAG", " ITEMS : " + items.size() );
dialog.cancel();
}
});
builder.setOnCancelListener(this);
builder.show();
return true;
}
public void setItems(List<KeyPairBoolData> items, String allText, int position,
PreferencesSpinnerListener listener) {
this.items = items;
this.defaultText = allText;
this.listener = listener;
ArrayAdapter<String> adapterSpinner = new ArrayAdapter<String>(getContext(),
R.layout.textview_for_spinner,
new String[] { defaultText });
spinner.setAdapter(adapterSpinner);
if(position != -1)
{
items.get(position).setSelected(true);
//listener.onItemsSelected(items);
onCancel(null);
}
}
public interface PreferencesSpinnerListener {
public void onItemsSelected(List<KeyPairBoolData> items);
}
// // Adapter Class
public class MyAdapter extends BaseAdapter implements Filterable {
List<KeyPairBoolData> arrayList;
List<KeyPairBoolData> mOriginalValues; // Original Values
LayoutInflater inflater;
public MyAdapter(Context context, List<KeyPairBoolData> arrayList) {
this.arrayList = arrayList;
inflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
private class ViewHolder {
TextView textView;
CheckBox checkBox;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.alert_dialog_listview_search_subview, null);
holder.textView = (TextView) convertView.findViewById(R.id.alertTextView);
holder.checkBox = (CheckBox) convertView.findViewById(R.id.alertCheckbox);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final KeyPairBoolData data = arrayList.get(position);
holder.textView.setText(data.getName());
holder.checkBox.setChecked(data.isSelected());
convertView.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ViewHolder temp = (ViewHolder) v.getTag();
temp.checkBox.setChecked(!temp.checkBox.isChecked());
int len = arrayList.size();
for (int i = 0; i < len; i++)
{
if (i == position)
{
data.setSelected(!data.isSelected());
Log.i("TAG", "On Click Selected : " + data.getName() + " : " + data.isSelected());
break;
}
}
}
});
holder.checkBox.setTag(holder);
return convertView;
}
#SuppressLint("DefaultLocale")
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
arrayList = (List<KeyPairBoolData>) results.values; // has the filtered values
notifyDataSetChanged(); // notifies the data with new filtered values
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
List<KeyPairBoolData> FilteredArrList = new ArrayList<KeyPairBoolData>();
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<KeyPairBoolData>(arrayList); // saves the original data in mOriginalValues
}
/********
*
* If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
* else does the Filtering and returns FilteredArrList(Filtered)
*
********/
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mOriginalValues.size();
results.values = mOriginalValues;
} else {
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mOriginalValues.size(); i++) {
Log.i("TAG", "Filter : " + mOriginalValues.get(i).getName() + " -> " + mOriginalValues.get(i).isSelected());
String data = mOriginalValues.get(i).getName();
if (data.toLowerCase().contains(constraint.toString())) {
FilteredArrList.add(mOriginalValues.get(i));
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
}
with Preferences.xml:
<com.androidbuts.multispinnerfilter.PreferencesSpinner
android:key="spinner"
android:title="spinner Title"
android:summary="Summary"/>
and of course, Preferences class:
public class Preferences extends PreferenceActivity {
public PreferencesSpinner searchSpinner;
#SuppressWarnings("deprecation")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
final List<String> list = Arrays.asList(getResources().getStringArray(R.array.sports_array));
final List<KeyPairBoolData> listArray = new ArrayList<KeyPairBoolData>();
for(int i=0; i<list.size(); i++) {
KeyPairBoolData h = new KeyPairBoolData();
h.setId(i+1);
h.setName(list.get(i));
h.setSelected(false);
listArray.add(h);
}
searchSpinner = (PreferencesSpinner) findPreference("spinner");
searchSpinner.setItems(listArray, "all text", -1, new PreferencesSpinner.PreferencesSpinnerListener() {
#Override
public void onItemsSelected(List<KeyPairBoolData> items) {
}
});
}
anyone can help me with the migration to preferences Activity?
Thanks!
Whoever need it, Here is the answer:
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
View view = inflater.inflate(R.layout.alert_dialog_listview_search, null);
builder.setView(view);
final ListView listView = (ListView) view.findViewById(R.id.alertSearchListView);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setFastScrollEnabled(false);
final List<String> list = Arrays.asList(view.getResources().getStringArray(R.array.sports_array));
final List<KeyPairBoolData> listArray = new ArrayList<KeyPairBoolData>();
for(int i=0; i<list.size(); i++) {
KeyPairBoolData h = new KeyPairBoolData();
h.setId(i+1);
h.setName(list.get(i));
h.setSelected(false);
listArray.add(h);
}
setItems(listArray, "all text", -1, new PreferencesSpinnerListener() {
#Override
public void onItemsSelected(List<KeyPairBoolData> items) {
}
});
adapter = new MyAdapter(getContext(), items);
listView.setAdapter(adapter);
EditText editText = (EditText) view.findViewById(R.id.alertSearchEditText);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.getFilter().filter(s.toString());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
//builder.setMultiChoiceItems(items.toArray(new CharSequence[items.size()]), selected, this);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// items = (ArrayList<KeyPairBoolData>) adapter.arrayList;
Log.i("TAG", " ITEMS : " + items.size() );
dialog.cancel();
}
});
builder.setOnCancelListener(this);
}
Hope that's help..
I have a custom list view, contains delete button and spinner (the spinner contain A-E characters).
And I have an issue with deleting the true row from my custom list view.
Custom list view code:
public class customListView extends BaseAdapter
{
public Activity context;
ArrayList<MyActivity.UserProperties> userPropertieses;
public String[] spinnerValues;
public LayoutInflater inflater;
public customListView(Activity context, ArrayList<MyActivity.UserProperties> userPropertieses, String[] spinnerArray)
{
super();
this.context = context;
this.userPropertieses = userPropertieses;
spinnerValues = spinnerArray;
this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() { return userPropertieses.size(); }
#Override
public Object getItem(int i) { return null; }
#Override
public long getItemId(int i) { return 0; }
class ViewHolder
{
Button btnRemove;
Spinner spinner;
}
#Override
public View getView(final int i, View view, ViewGroup viewGroup)
{
final ViewHolder holder;
if (view == null)
{
holder = new ViewHolder();
view = inflater.inflate(R.layout.custom_layout, null);
holder.spinner = (Spinner) view.findViewById(R.id.spinner);
holder.btnRemove = (Button) view.findViewById(R.id.bu_Remove);
// populate spinner
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>
(view.getContext(), android.R.layout.simple_spinner_item, spinnerValues);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
holder.spinner.setFocusable(true);
holder.spinner.requestFocus();
holder.spinner.setAdapter(dataAdapter);
view.setTag(holder);
// remove user implementation
holder.btnRemove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.i("custom list view debug", "i = " + i); // debug. verify i value is correct
((MyActivity) context).deleteUser(i);
}
});
}
else
holder = (ViewHolder) view.getTag();
return view;
}
}
And my main activity code looks like this:
public class MyActivity extends Activity
{
ListView listView;
ArrayList<UserProperties> userProperties = new ArrayList<UserProperties>();
customListView adapter;
SensorManager sensorManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
for (int i = 0; i<5; i++) {
userProperties.add(new UserProperties());
}
listView = (ListView) findViewById(R.id.listView);
String[] spinnerValues = new String[] {"A", "B", "C", "D", "E"};
adapter = new customListView(MyActivity.this, userProperties, spinnerValues);
listView.setAdapter(adapter);
}
public void deleteUser (int index)
{
Log.i("debug", "Removing item " + index); // the index is really true and the true node deleting from the ArrayList but somehow the latest delete from the UI
userProperties.remove(index);
adapter.notifyDataSetChanged();
}
}
When I click on the Remove button deleteUser method called with the right index. but although the right node deleting from userProperties ArrayList somehow after notiftDataSetChanged is still alive
and the latest node delete.
So, How can I delete the right node/row (from the ArrayList and UI...)
Thank you!
EDIT:
Just to be clear, i variable contain true index. The true node deleted from the ArrayList. but something append after I called notify method.
I prefer to stay with BaseAdapter and not implement ArrayAdapter. Thank you!
EDIT 2:
After more debugging I found out my question was wrong. the true row really deleted just spinner values somehow update their values. I cannot close the question because it already answered. Thanks.
((MyActivity) context).deleteUser(i);
This line will always delete the first value from the ListView
You can use CAB (contextual action bar)
See if the code helps you(it's basically a ListActivity with a custom adapter to hold the status of checked items(+ different background)):
public class CABSelection extends ListActivity {
private ArrayList<String> mItems = new ArrayList<String>();
private SelectionAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
for (int i = 0; i < 24; i++) {
mItems.add("Name" + i);
}
// R.layout.adapters_cabselection_row is a LinearLayout(with green
// background(#99cc00)) that wraps an ImageView and a TextView
mAdapter = new SelectionAdapter(this,
R.layout.adapters_cabselection_row, R.id.the_text, mItems);
setListAdapter(mAdapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
private int nr = 0;
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.cabselection_menu, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
StringBuilder sb = new StringBuilder();
Set<Integer> positions = mAdapter.getCurrentCheckedPosition();
for (Integer pos : positions) {
sb.append(" " + pos + ",");
}
switch (item.getItemId()) {
case R.id.edit_entry:
Toast.makeText(CABSelection.this, "Edited entries: " + sb.toString(),
Toast.LENGTH_SHORT).show();
break;
case R.id.delete_entry:
Toast.makeText(CABSelection.this, "Deleted entries : " + sb.toString(),
Toast.LENGTH_SHORT).show();
break;
case R.id.finish_it:
nr = 0;
mAdapter.clearSelection();
Toast.makeText(CABSelection.this, "Finish the CAB!",
Toast.LENGTH_SHORT).show();
mode.finish();
}
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
nr = 0;
mAdapter.clearSelection();
}
#Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
if (checked) {
nr++;
mAdapter.setNewSelection(position, checked);
} else {
nr--;
mAdapter.removeSelection(position);
}
mode.setTitle(nr + " rows selected!");
}
});
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
l.setItemChecked(position, !mAdapter.isPositionChecked(position));
}
private class SelectionAdapter extends ArrayAdapter<String> {
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
public SelectionAdapter(Context context, int resource,
int textViewResourceId, List<String> objects) {
super(context, resource, textViewResourceId, objects);
}
public void setNewSelection(int position, boolean value) {
mSelection.put(position, value);
notifyDataSetChanged();
}
public boolean isPositionChecked(int position) {
Boolean result = mSelection.get(position);
return result == null ? false : result;
}
public Set<Integer> getCurrentCheckedPosition() {
return mSelection.keySet();
}
public void removeSelection(int position) {
mSelection.remove(position);
notifyDataSetChanged();
}
public void clearSelection() {
mSelection = new HashMap<Integer, Boolean>();
notifyDataSetChanged();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);//let the adapter handle setting up the row views
v.setBackgroundColor(Color.parseColor("#99cc00")); //default color
if (mSelection.get(position) != null) {
v.setBackgroundColor(Color.RED);// this is a selected position so make it red
}
return v;
}
}
}
Another way
adapter = new MyListAdapter(this);
lv = (ListView) findViewById(android.R.id.list);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
AlertDialog.Builder adb=new AlertDialog.Builder(MyActivity.this);
adb.setTitle("Delete?");
adb.setMessage("Are you sure you want to delete " + position);
final int positionToRemove = position;
adb.setNegativeButton("Cancel", null);
adb.setPositiveButton("Ok", new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
MyDataObject.remove(positionToRemove);
adapter.notifyDataSetChanged();
}});
adb.show();
}
});
getView(final int i,
Do not make i final. You did that to use i in onClick(). But that is not possible. So remove the final. Add:
holder.btnRemove.setTag(i);
And in onClick:
int position = v.getTag();
..deleteUser(position);
Maybe you have to cast something somewhere..
Remark: You have to set the tag always. So do it just before return view;.
Please do not use an i for position.
I have set my AlertDialog to CHOICE_MODE_MULTIPLE and get the data for the list from an API. It works almost fine, however, there are 2 problems I am having troubles with now.
The first one is more serious: After I check the items then click OK, sometimes when I go back to the dialog again it unchecks all of my checked items. This happens only 10-20% the times when I test so I do not know why it happens. I tried to save the values into a global variable in my class, or a public static variable I created when the app first started, still no luck.
When I click "Cancel", the checked items still update (it's supposed not to do so).
I am having a hard time against these 2 issues, any help is very much appreciated.
EDIT: Here is my custom dialog:
public class CustomMultichoiceDialog {
Set<String> selectedString = new HashSet<String>();
private String[] items;
private AlertDialog dialog;
private Builder builder;
private Context context;
private TestAdapter adapter;
public CustomMultichoiceDialog(Context context, String[] items) {
this.context = context;
this.items = items;
builder = new AlertDialog.Builder(context);
this.adapter = new TestAdapter(context, android.R.layout.simple_list_item_checked, this.items);
builder.setAdapter(this.adapter, null);
}
public AlertDialog show() {
if (this.dialog == null) {
this.create();
}
this.adapter.notifyDataSetChanged();
this.dialog.show();
return this.dialog;
}
public String[] getSelectedItems() {
String[] result = new String[this.dialog.getListView().getCheckedItemCount()];
SparseBooleanArray checked = this.dialog.getListView().getCheckedItemPositions();
int k = 0;
for (int i = 0; i < this.items.length; i++) {
if (checked.get(i)) {
result[k++] = this.items[i];
}
}
return result;
}
public String getSelectedItemAsString() {
return Arrays.toString(this.getSelectedItems()).replace("[", "").replace("]", "")
.replace(", ", ",");
}
public AlertDialog create() {
this.dialog = builder.create();
this.dialog.getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
this.dialog.getListView().setSelector(new ColorDrawable(Color.TRANSPARENT));
return this.dialog;
}
public void setPositiveButton(final Callbacks callbacks) {
this.builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String[] selected = getSelectedItems();
callbacks.run(selected, getSelectedItemAsString());
cacheSelectedItems(selected);
}
});
}
public void setNegativeButton(final Callbacks callbacks) {
this.builder.setNegativeButton(this.context.getString(R.string.cancel),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
}
private void cacheSelectedItems(String[] selected) {
selectedString.addAll(Arrays.asList(selected));
}
public interface Callbacks {
public void run(String[] selectedItems, String selectedItemAsString);
}
class TestAdapter extends ArrayAdapter<String> {
public TestAdapter(Context context, int resource, String[] objects) {
super(context, resource, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
ViewHolder holder = (ViewHolder) view.getTag();
if (holder == null) {
holder = new ViewHolder();
holder.text = ((CheckedTextView) view.findViewById(android.R.id.text1));
holder.text.setCheckMarkDrawable(R.drawable.checkbox);
}
if(selectedString!=null){
if(selectedString.contains(holder.text.getText().toString())) {
holder.text.setSelected(true);
holder.text.setChecked(true);
}
}
return view;
}
class ViewHolder {
CheckedTextView text;
}
#Override
public long getItemId(int position) {
return position;
}
}
}
How do I create spinner which allows to choose multiple items, i.e spinner with check boxes?
I have written custom implementation of MultiSpinner. It's looking similar to normal spinner, but it has checkboxes instead of radiobuttons. Selected values are displayed on the spinner divided by comma. All values are checked by default. Try it:
package cz.destil.settleup.gui;
public class MultiSpinner extends Spinner implements
OnMultiChoiceClickListener, OnCancelListener {
private List<String> items;
private boolean[] selected;
private String defaultText;
private MultiSpinnerListener listener;
public MultiSpinner(Context context) {
super(context);
}
public MultiSpinner(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public MultiSpinner(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
#Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if (isChecked)
selected[which] = true;
else
selected[which] = false;
}
#Override
public void onCancel(DialogInterface dialog) {
// refresh text on spinner
StringBuffer spinnerBuffer = new StringBuffer();
boolean someSelected = false;
for (int i = 0; i < items.size(); i++) {
if (selected[i] == true) {
spinnerBuffer.append(items.get(i));
spinnerBuffer.append(", ");
someSelected = true;
}
}
String spinnerText;
if (someSelected) {
spinnerText = spinnerBuffer.toString();
if (spinnerText.length() > 2)
spinnerText = spinnerText.substring(0, spinnerText.length() - 2);
} else {
spinnerText = defaultText;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item,
new String[] { spinnerText });
setAdapter(adapter);
listener.onItemsSelected(selected);
}
#Override
public boolean performClick() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setMultiChoiceItems(
items.toArray(new CharSequence[items.size()]), selected, this);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setOnCancelListener(this);
builder.show();
return true;
}
public void setItems(List<String> items, String allText,
MultiSpinnerListener listener) {
this.items = items;
this.defaultText = allText;
this.listener = listener;
// all selected by default
selected = new boolean[items.size()];
for (int i = 0; i < selected.length; i++)
selected[i] = true;
// all text on the spinner
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item, new String[] { allText });
setAdapter(adapter);
}
public interface MultiSpinnerListener {
public void onItemsSelected(boolean[] selected);
}
}
You use it in XML like this:
<cz.destil.settleup.gui.MultiSpinner android:id="#+id/multi_spinner" />
And you pass data to it in Java like this:
MultiSpinner multiSpinner = (MultiSpinner) findViewById(R.id.multi_spinner);
multiSpinner.setItems(items, getString(R.string.for_all), this);
Also you need to implement the listener,which will return the same length array , with true or false to show selected to unselected..
public void onItemsSelected(boolean[] selected);
I would just like to show an alternative version of #Destil's MultiSpinner (thank you for your inspiring code) which allows to use "android:entries" in xml, just like a spinner.
It doesn't initially show a default text, like "choose one", but you can easily obtain it by setting an additional ArrayAdapter in the constructor.
MultiSpinner.java
package com.example.helloworld;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
/**
* Inspired by: http://stackoverflow.com/a/6022474/1521064
*/
public class MultiSpinner extends Spinner {
private CharSequence[] entries;
private boolean[] selected;
private MultiSpinnerListener listener;
public MultiSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiSpinner);
entries = a.getTextArray(R.styleable.MultiSpinner_android_entries);
if (entries != null) {
selected = new boolean[entries.length]; // false-filled by default
}
a.recycle();
}
private OnMultiChoiceClickListener mOnMultiChoiceClickListener = new OnMultiChoiceClickListener() {
#Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
selected[which] = isChecked;
}
};
private DialogInterface.OnClickListener mOnClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// build new spinner text & delimiter management
StringBuffer spinnerBuffer = new StringBuffer();
for (int i = 0; i < entries.length; i++) {
if (selected[i]) {
spinnerBuffer.append(entries[i]);
spinnerBuffer.append(", ");
}
}
// Remove trailing comma
if (spinnerBuffer.length() > 2) {
spinnerBuffer.setLength(spinnerBuffer.length() - 2);
}
// display new text
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item,
new String[] { spinnerBuffer.toString() });
setAdapter(adapter);
if (listener != null) {
listener.onItemsSelected(selected);
}
// hide dialog
dialog.dismiss();
}
};
#Override
public boolean performClick() {
new AlertDialog.Builder(getContext())
.setMultiChoiceItems(entries, selected, mOnMultiChoiceClickListener)
.setPositiveButton(android.R.string.ok, mOnClickListener)
.show();
return true;
}
public void setMultiSpinnerListener(MultiSpinnerListener listener) {
this.listener = listener;
}
public interface MultiSpinnerListener {
public void onItemsSelected(boolean[] selected);
}
}
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MultiSpinner">
<attr name="android:entries" />
</declare-styleable>
</resources>
layout_main_activity.xml
<com.example.helloworld.MultiSpinner
android:id="#+id/multispinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="#array/multispinner_entries" />
As far as I know Spinner doesn't have a multiple choice mode. Instead you can create an ImageButton and set a drawable down arrow in the right side and on the click event you can open a Dialog having items with the multiple checkboxes.
Thanks for the post! Great solution. I made a small change to the class (method setItems) to allow users to set already selected items instead of selecting all items to true by default.
public void setItems(
List<String> items,
List<String> itemValues,
String selectedList,
String allText,
MultiSpinnerListener listener) {
this.items = items;
this.defaultText = allText;
this.listener = listener;
String spinnerText = allText;
// Set false by default
selected = new boolean[itemValues.size()];
for (int j = 0; j < itemValues.size(); j++)
selected[j] = false;
if (selectedList != null) {
spinnerText = "";
// Extract selected items
String[] selectedItems = selectedList.trim().split(",");
// Set selected items to true
for (int i = 0; i < selectedItems.length; i++)
for (int j = 0; j < itemValues.size(); j++)
if (selectedItems[i].trim().equals(itemValues.get(j))) {
selected[j] = true;
spinnerText += (spinnerText.equals("")?"":", ") + items.get(j);
break;
}
}
// Text for the spinner
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_spinner_item, new String[] { spinnerText });
setAdapter(adapter);
}
You can check a simple library MultiSelectSpinner
You can simply do the following steps:
multiSelectSpinnerWithSearch.setItems(listArray1, new MultiSpinnerListener() {
#Override
public void onItemsSelected(List<KeyPairBoolData> items) {
for (int i = 0; i < items.size(); i++) {
if (items.get(i).isSelected()) {
Log.i(TAG, i + " : " + items.get(i).getName() + " : " + items.get(i).isSelected());
}
}
}
});
The listArray1 will be your array.
Check the full example here in How-To