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;
}
}
}
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'm in an Android project.
I want to show a AlertDialog on which the user can pick one item (about 100 items, it's dynamic). I want to add the possibility of searching also.
My real problem is that it seems I can't change the items once the Dialog is created.
The creation code:
// The List with the beaches with radio buttons, single choice!
public void showBeachesDialog(final Activity context, boolean isFromZonas)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
searchAdapter = new SearchDialogAdapater(orderedBeaches, orderedBeachesIds, context);
builder.setSingleChoiceItems(searchAdapter, -1, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
index = which;
if (!((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).isEnabled())
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
}
});
DialogOkOnClickListener listener = new DialogOkOnClickListener(context, isFromZonas);
builder.setPositiveButton(R.string.searchOK, listener);
builder.setNegativeButton(R.string.cancelar, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
// User cancelled the dialog: nothing happens
}
});
builder.setIcon(context.getResources().getDrawable(R.drawable.icon_beach));
builder.setTitle(StaticUtils.DIALOG_TITLE);
View dialogView = context.getLayoutInflater().inflate(R.layout.dialog_beaches, null);
SearchView searchView = (SearchView)dialogView.findViewById(R.id.search_beach);
searchView.setOnQueryTextListener(new SearchQueryListener(searchAdapter));
builder.setView(dialogView);
AlertDialog dialog = builder.create();
dialog.show();
dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false);
}
So, now I have the dialog with all the elements I want (the SearchDialogAdapater is fine).
The DialogOnClickListener is also working.
The search view is appearing on the dialog, and my SearchQueryListener is working perfectly, so I won't post here the code, but in Debug I can see that if I type "d" the elements without the "d" are filtered out.
Now I would like to not throw away all the code and find a way to change the items of the dialog without showing a new one...
Sorry for long question and if there is a obvious way I am missing...
Thank you all.
I solved this using a class that implements 4 interfaces so it can handles everything that I want.
The code is now like this, hope it's useful to someone who want to make its own Dialog with personalized search.
public void showBeachesDialog(final Activity context, boolean isFromZonas)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
searchAdapter = new SearchDialogAdapater
(orderedBeaches, orderedBeachesIds, context, isFromZonas);
builder.setSingleChoiceItems(searchAdapter, -1, null);
builder.setPositiveButton(R.string.searchOK, searchAdapter);
builder.setNegativeButton(R.string.cancelar, null);
builder.setIcon(context.getResources().getDrawable(R.drawable.icon_beach));
builder.setTitle(StaticUtils.DIALOG_TITLE);
View dialogView = context.getLayoutInflater().inflate
(R.layout.dialog_beaches, null);
SearchView searchView = SearchView)dialogView.findViewById
(R.id.search_beach);
searchView.setOnQueryTextListener(searchAdapter);
builder.setView(dialogView);
AlertDialog dialog = builder.create();
dialog.show();
dialog.getListView().setOnItemClickListener(searchAdapter);
searchAdapter.setDialog(dialog);
dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false);
}
The orderedBeaches and orderedBeachesIds are String[] and int[], my data to display.
Below is my adapter which uses a stack to handle the items available at any moment of the searching:
public class SearchDialogAdapater implements ListAdapter, OnQueryTextListener,
OnItemClickListener, OnClickListener {
protected Stack<String[]> stackBeaches;
protected Stack<int[]> stackBeachesIds;
protected Activity context;
protected TreeMap<String, Integer> orderedBeaches;
protected ListView listView;
protected String lastFilter = "";
public SearchDialogAdapater(String[] bs, int[] bIds, Activity cont) {
this.stackBeaches = new Stack<String[]>();
this.stackBeachesIds = new Stack<int[]>();
this.stackBeaches.push(bs);
this.stackBeachesIds.push(bIds);
this.context = cont;
}
#Override
public boolean onQueryTextChange(String newText) {
filter(newText);
this.listView.setAdapter(this);
return true;
}
public void filter(String search) {
// no longer valid the previous selected, must clean selections
selectedPosition = -1;
lastView = null;
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
// hitted "backspace"
if (search.length() < lastFilter.length()) {
if (stackBeaches.size() > 1) {
stackBeaches.pop();
stackBeachesIds.pop();
}
lastFilter = search;
return;
}
// filter
ArrayList<String> filtBs = new ArrayList<String>();
ArrayList<Integer> filtBsIds = new ArrayList<Integer>();
for (int i = 0; i < stackBeaches.peek().length; i++) {
String beach = new String(stackBeaches.peek()[i]);
if (Pattern
.compile(Pattern.quote(search), Pattern.CASE_INSENSITIVE)
.matcher(beach).find()) {
filtBs.add(beach);
filtBsIds.add(stackBeachesIds.peek()[i]);
}
}
String[] beaches = new String[filtBs.size()];
int[] ids = new int[filtBsIds.size()];
int size = filtBs.size();
for (int i = 0; i < size; i++) {
ids[i] = filtBsIds.remove(0);// head
beaches[i] = filtBs.remove(0);
}
stackBeachesIds.push(ids);
stackBeaches.push(beaches);
lastFilter = search;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
String beach = stackBeaches.peek()[position];
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.dialog_item, null);
TextView txtBeach = (TextView) convertView
.findViewById(R.id.dialBeach);
txtBeach.setText(beach);
}
if (position == selectedPosition)// the same color below
convertView.setBackgroundColor(Color.argb(128, 0, 64, 192));
return convertView;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return stackBeachesIds.peek()[position];
}
#Override
public int getCount() {
return stackBeaches.peek().length;
}
#Override
public boolean isEmpty() {
return stackBeaches.peek().length == 0;
}
private View lastView;
private int selectedPosition = -1;
#Override
public void onItemClick(AdapterView<?> adapterView, View view,
int position, long arg3) {
// some color...
view.setBackgroundColor(Color.argb(128, 133, 181, 255));
selectedPosition = position;
if (lastView != null)
lastView.setBackground(null);
lastView = view;
((SurfApplication) context.getApplication()).setBeachId(stackBeachesIds
.peek()[position]);
if (!dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled())
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
}
protected AlertDialog dialog;
public void setDialog(AlertDialog dial) {
this.dialog = dial;
this.listView = dialog.getListView();
}
#Override
public void onClick(DialogInterface dialog, int which) {
((ProgressBar) context.findViewById(R.id.pBar))
.setVisibility(ProgressBar.VISIBLE);
int beachId = stackBeachesIds.peek()[selectedPosition];
String beach = stackBeaches.peek()[selectedPosition];
// do something... Here I am creating a new Intent and starting
// the new activity within my context
}
}
Same methods are not here because they return null or do nothing at all.
Sorry for long post, but I need to answer this properly.
Thank you all and I hope someone find this useful.
Your adapter needs to implement Filterable or extend from an adapter class that does. Adapters that already implement this interface, like ArrayAdapter, should do the filtering automatically for you.
Just call Adapter.getFilter().filter(...); with the value from the search view.
I have set the custom BaseAdapter in Activity as :
llNoContactMessage = (LinearLayout) findViewById(R.id.llNoContacts);
listAdapter = new ContactsListAdapter(this, contactsList);
lvContactList = (ListView) findViewById(R.id.lvContactList);
lvContactList.setAdapter(listAdapter);
lvContactList.setOnItemClickListener(null);
And there is my Adapter class:
public class ContactsListAdapter extends BaseAdapter {
private List<ContactsInfo> contactList;
private Context context;
public ContactsListAdapter(Context context, List<ContactsInfo> contactsList) {
this.context = context;
this.contactList = contactsList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
final ContactsInfo contactsInfo = contactList.get(position);
if(view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.activity_contacts_item, parent, false);
ViewHolder vh = new ViewHolder();
vh.tvContactName = (TextView) view.findViewById(R.id.tvContactName);
vh.tvContactNumber = (TextView) view.findViewById(R.id.tvContactNumber);
vh.llRemoveContact = (LinearLayout) view.findViewById(R.id.llRemoveContact);
view.setTag(vh);
}
final ViewHolder holder = (ViewHolder) view.getTag();
holder.tvContactName.setText(contactsInfo.getContactName());
holder.tvContactNumber.setText(contactsInfo.getContactNumber());
holder.llRemoveContact.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UIUtils.showRemoveContactAlert(context, contactsInfo);
if(UIUtils.contactRemoved) {
contactList.remove(contactsInfo);
setList(getContacts());
notifyDataSetChanged();
}
}
});
return view;
}
static class ViewHolder {
TextView tvContactNumber;
TextView tvContactName;
LinearLayout llRemoveContact;
}
#Override
public int getCount() {
return contactList.size();
}
#Override
public ContactsInfo getItem(int index) {
return contactList.get(index);
}
#Override
public long getItemId(int position) {
return getItem(position).get_id();
}
public void setList(List<ContactsInfo> list) {
contactList.clear();
contactList.addAll(list);
}
private List<ContactsInfo> getContacts() {
CrisisCallingDatabase crisisCallDB = new CrisisCallingDatabase(context);
return crisisCallDB.getContactsList();
}
}
The method to delete data from database is:
public static void showRemoveContactAlert(final Context context, final ContactsInfo contact) {
AlertDialog.Builder b = new AlertDialog.Builder(context);
b.setMessage("Remove Contact");
b.setCancelable(true);
b.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
CrisisCallingDatabase ccd = new CrisisCallingDatabase(context);
if(ccd.deletContact(contact.get_id())){
Toast.makeText(context, contact.getContactName() + " removed successfully.", Toast.LENGTH_SHORT).show();
contactRemoved = true;
} else {
Toast.makeText(context, "Remove contact failed.", Toast.LENGTH_SHORT).show();
contactRemoved = false;
}
CrisisCallingDatabase.closeDBConnections();
}
});
b.setNegativeButton("No", null);
b.show();
}
But the view of the list is not removing the item from the list. However after clicking the any UI component on the list again is reloading the list i.e. the deleted item is removed. I don't know where I did mistake. Any help will be appreciated.
Move your notifyDataSetChanged() call to the end of your setList method.
Finally solved the issue, I just moved the confirmation alert code inside the On Click event like below :
holder.llRemoveContact.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
context.runOnUiThread(new Runnable() {
#Override
public void run() {
AlertDialog.Builder b = new AlertDialog.Builder(context);
b.setMessage("Remove Contact");
b.setCancelable(true);
b.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if(succefulRemoval)){
contactList.remove(contactsInfo);
setList();
Toast.makeText(context, contactsInfo.getContactName() + " removed successfully.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "Remove contact failed.", Toast.LENGTH_SHORT).show();
}
}
});
b.setNegativeButton("No", null);
b.show();
}
});
}
});
Maybe you got answer already, but just for an extra
public void setList(List<ContactsInfo> list) {
contactList.clear();
contactList.addAll(list);
contactList.notifyDataSetChanged();
}
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);
}
}
The following is my Json data from database, i want to list the interest_name in a list(only if visible is true). List must be multilevel. I parsed the json file using Gson library. But i have no idea regarding how to make a multilevel list using listview.
{"interest_id":0,"interest_name":"ROOT","visible":false,"children":
[{"interest_id":1,"interest_name":"Sports","visible":true,"children":[{"interest_id":2,"interest_name":"Archery","visible":true,"children":[]},{"interest_id":3,"interest_name":"Bow Hunting","visible":true,"children":[]}]},{"interest_id":100,"interest_name":"Contry","visible":true,"children":[{"interest_id":101,"interest_name":"Afghanistan","visible":true,"children":[]},{"interest_id":102,"interest_name":"Akrotiri","visible":true,"children":[]}]},{"interest_id":1000,"interest_name":"Education","visible":true,"children":[]},{"interest_id":1200,"interest_name":"Entertainment","visible":true,"children":[]},{"interest_id":1400,"interest_name":"Books","visible":true,"children":[]},{"interest_id":1600,"interest_name":"Services","visible":true,"children":[]},{"interest_id":1800,"interest_name":"Fitness","visible":true,"children":[]},{"interest_id":2000,"interest_name":"Fashion","visible":true,"children":[]},{"interest_id":99999,"interest_name":"Near Me","visible":false,"children":[]}]}
My code:
Home.java
Intent intent = new Intent( Home.this,InterestAddList.class);
startActivity(intent);
finish();
InterestAddList.java
public class InterestAddList extends Activity {
ListView intrestListView;
OneOnOneListAdapter adapter;
List<String> intrestList;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.intrest_add);
intrestListView = (ListView) findViewById(R.id.InterestList);
//Service Called For retrieving Data
retrieveList();
}
public void retrieveList() {
intrestList = new ArrayList<String>();
StringBuilder urlc = new StringBuilder(urlPrefix + "gai");
String url=urlc.toString();
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
String result = ServiceClient.getInstance().getResponse(url);
InterestNode ni=gson.fromJson(result, InterestNode.class);
//for(int i=0;i<ni.length;i++){
//Log.e("ni", ni.getInterestName());
//Log.e("ni", String.valueOf(ni.getInterestId()));
/* how to display interest name */
intrestList.add(ni.getInterestName());
}
adapter = new OneOnOneListAdapter(InterestAddList.this,R.layout.intrest_add_row,intrestList);
intrestListView.setAdapter(adapter);
}
private class OneOnOneListAdapter extends ArrayAdapter {
public OneOnOneListAdapter(Context context, int textViewResourceId,
List objects) {
super(context, textViewResourceId, objects);
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
final int aposition=position;
if (v == null)
{
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.intrest_add_row, null);
}
TextView intrestText =(TextView)v.findViewById(R.id.IntrestText);
intrestText.setText(intrestList.get(aposition).toString());
v.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
System.out.println("OnItem CLicked");
Toast.makeText(InterestAddList.this,"Position clicked:"+intrestList.get(aposition).toString(),Toast.LENGTH_SHORT).show();
Intent i = new Intent(InterestAddList.this,SubIntrestAddList.class);
i.putExtra("position", aposition);
startActivityForResult(i,1);
}
});
return v;
}
}}
InterestNode.java
public class InterestNode {
#SerializedName("interest_id")
int interestId;
#SerializedName("interest_name")
String interestName;
#SerializedName("visible")
boolean isVisible;
transient InterestNode parent;
#SerializedName("children")
List<InterestNode> childList = new ArrayList<InterestNode>();
public List<InterestNode> getChildren(){
return new ArrayList<InterestNode>(childList);
}
public int getInterestId() {
return interestId;
}
public String getInterestName() {
return interestName;
}
public InterestNode getParent() {
return parent;
}
public boolean isVisible() {
return isVisible;
}
public void addChild(InterestNode intNode){
childList.add(intNode);
}
public void setInterestId(int interestId) {
this.interestId = interestId;
}
public void setInterestName(String interestName) {
this.interestName = interestName;
}
public void setParent(InterestNode parent) {
this.parent = parent;
}
public void setVisible(boolean isVisible) {
this.isVisible = isVisible;
}
}
It better to use Expandable ListView to add multilevel list, instead. But you want using listview then follow below nice tutorial here step by step three level listiview is achieved.
Android Multilevel ListView Tutorial
hope it helps you!