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);
}
Related
I'm creating an android app, in which I'm using recyclerView and the row of recyclerView is having editText.
This is my ReadingAdapter class
public class ReadingAdapter extends RecyclerView.Adapter<ReadingAdapter.ViewHolder> implements AdapterView.OnItemSelectedListener {
Context context;
String valOpenReading, valClosReading, valConsumption;
private List<ReadingData> readingList;
static String[] arrValOpenRead, arrValClosRead, arrValConsumption;
public ReadingAdapter(Context context, List<ReadingData> readingList) {
this.context = context;
this.readingList = readingList;
arrValOpenRead = new String[readingList.size()];
arrValClosRead = new String[readingList.size()];
arrValConsumption = new String[readingList.size()];
}
#Override
public ReadingAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.reading_sheet_layout, parent, false);
return new ReadingAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(final ReadingAdapter.ViewHolder holder, final int position) {
ReadingData tempData = readingList.get(position);
holder.pdtName.setText(tempData.pdtName);
holder.keyId.setText("Key "+tempData.keyId);
holder.etClosRead.addTextChangedListener(new TextWatcher() {
boolean ignore = false;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if (ignore)
return;
ignore = true;
valOpenReading = holder.etOpenRead.getText().toString();
arrValOpenRead[position] = valOpenReading;
valClosReading = s.toString().equals("") ? "0": s.toString();
arrValClosRead[position] = valClosReading;
if (!valOpenReading.equals("")) {
if (Integer.parseInt(valClosReading) < Integer.parseInt(valOpenReading)) {
Toast.makeText(context, "Check once! closing reading should be more than opening reading!", Toast.LENGTH_LONG).show();
valConsumption = "0";
holder.consumption.setText("");
} else {
valConsumption = (Integer.parseInt(valClosReading) - Integer.parseInt(valOpenReading))+"";
arrValConsumption[position] = valConsumption;
holder.consumption.setText(valConsumption);
}
} else
Toast.makeText(context, "Please fill the opening reading!", Toast.LENGTH_SHORT).show();
ignore = false;
}
});
}
#Override
public int getItemCount() {
return readingList.size();
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView pdtName, keyId, consumption;
EditText etOpenRead, etClosRead;
public ViewHolder(View view) {
super(view);
pdtName = (TextView)view.findViewById(R.id.txt_list_pdt_supp);
keyId = (TextView)view.findViewById(R.id.key_set);
etOpenRead = (EditText)view.findViewById(R.id.open_val_set);
etClosRead = (EditText)view.findViewById(R.id.clos_val_set);
consumption = (TextView)view.findViewById(R.id.consumption_val);
}
}
}
This is my ReadingData.java
public class ReadingData {
String pdtName, keyId, openReading, closReading, consumption;
public ReadingData(String pdtName, String keyId) {
this.pdtName = pdtName;
this.keyId = keyId;
}
}
Here, if I enter value in the starting items of the recyclerView then as I scroll up the items to the bottom of the list, the last item will have that value.
Please ignore the quality of image as we can't upload above of 2MiB of snap.
Here the views are recycled as the list is scrolled. How to prevent the copying values to the other item in the list.
And that Toast is also repeated several times. How to stop this.
update:
By the suggetion of LQ Gioan through the SO question How ListView's recycling mechanism works , I got the logic how ListView actually works with recycling of views.
But I'm not sure whether the recyclerView also works same.
But here in my case, how can I implement this process. pls someone help me here.
RecyclerView reuse views, in fact it only generate the as many as views that is visible on the screen. so it's expected if you can see a value you set for other rows
The solution would be set all attributes of the view that you are changing to default or whatever the row should present from your data set
So put addTextChangedListener insode ViewHolder constructor(you can get position by calling getAdapterPosition()) for better performance and set the editText value inside onBindViewHolder method from your data set
Your Activity Code:
ListView listview = (ListView) findViewById(R.id.list_view);
listview.setItemsCanFocus(true);
Adapter adapter = new Adapter (YourActivity.this, YourArrayList);
listview .setAdapter(adapter);
Adapter class
public class Adapter extends BaseAdapter {
// Declare Variables \\
Context mContext;
LayoutInflater inflater;
Activity act;
String[] temp;
public Adapter(Context context, ArrayList<String> list) {
mContext = context;
inflater = LayoutInflater.from(mContext);
act = (Activity) context;
//-------Temp String Array-------\\
temp = new String[this.count];
for (int i = 0; i < this.count; i++) {
temp[i] = list.get(i);
}
//---------------------------\\
}
public class ViewHolder {
TextView optionTitle;
EditText optionText;
int ref;
}
#Override
public int getCount() {
return list.size;
}
#Override
public Object getItem(int position) {
return temp[position];
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(final int position, View view, ViewGroup parent) {
final ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = inflater.inflate(R.layout.lv_items_add_ques_options_mcq, null);
holder.optionTitle = (TextView) view.findViewById(R.id.add_ques_opts_count_mcq_tv);
holder.optionText = (EditText) view.findViewById(R.id.add_ques_opts_title_mcq_et);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
holder.ref = position;
holder.optionTitle.setText(getCharForNumber(position) + ":");
holder.optionText.setText(temp[position]);
holder.optionText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
}
#Override
public void afterTextChanged(Editable arg0) {
temp[holder.ref] = arg0.toString().trim();
}
});
return view;
}
public void getList() {
StaticValues.arrayListOptions = new ArrayList<String>(Arrays.asList(temp));
StaticValues.arrayListOptionsCount = new ArrayList<String>();
for (int i = 0; i < count; i++) {
StaticValues.arrayListOptionsCount.add(String.valueOf(i+1));
Log.e("err_al", StaticValues.arrayListOptions.get(i));
Log.e("err_al", StaticValues.arrayListOptionsCount.get(i));
}
}
private String getCharForNumber(int i) {
char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
if (i > 25) {
return null;
}
return Character.toString(alphabet[i]);
}}
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;
}
}
}
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);
}
}
I want to make an app which includes a listView with check boxes and a two buttons named add and delete selected. I want to delete all the item that are checked in the list view.I am unable to do that despite of my lot of efforts. Any help would be appreciated.
Here is my code
package com.example.chkbokinlistview;
public class Adapter extends ArrayAdapter<Movies> {
ArrayList<Movies> data;
Context context;
int id;
private Holder h;
public Adapter(Context context, int textViewResourceId, ArrayList<Movies> objects) {
super(context, textViewResourceId, objects);
this.data = objects;
this.context = context;
this.id = textViewResourceId;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int p = position;
View v = convertView;
LayoutInflater l = ((Activity)context).getLayoutInflater();
h = new Holder();
if (v == null) {
v = l.inflate(id, parent, false);
h.tv = (TextView) v.findViewById(R.id.textView1);
h.cb = (CheckBox) v.findViewById(R.id.checkBox1);
v.setTag(h);
}else{
h = (Holder) v.getTag();
h.cb.setChecked(true);
}
h.tv.setText(data.get(position).movieName);
h.cb.setChecked(data.get(position).deleted);
return v;
}
public void delete(){
//how to delete all the items that are checked
}
class Holder{
TextView tv;
CheckBox cb;
}
}
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
bDelete = (Button) findViewById(R.id.bDelete);
bAdd = (Button) findViewById(R.id.bAdd);
list = new ArrayList<Movies>();
a = new Adapter(this, R.layout.listitem, list);
lv.setAdapter(a);
bDelete.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
a.delete();
}
});
bAdd.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
final EditText et = new EditText(MainActivity.this);
dialog = new AlertDialog.Builder(MainActivity.this)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
list.add(new Movies(et.getText().toString(), false));
a.notifyDataSetChanged();
dialog.dismiss();
}
})
.setTitle("ADD Movie")
.setView(et)
.create();
dialog.show();
}
});
}
On checking the checkbox add that position in an ArrayList let say toBeDeleted, and when you click delete button, just remove items from your ArrayList named data according to the positions that you have in toBeDeleted and call the adapter method notifyDataSetChanged().
Add a checkedChangedListener in the getView method for your CheckBox.
h.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton arg0,
boolean arg1) {
// TODO Auto-generated method stub
if (arg1) {
list.add(position);
} else {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == position) {
list.remove(i);
break;
}
}
}
}
});
Where list is a ArrayList<Integer>,
and for deleting
private void delete() {
for(int i = 0 i<list.size;i++)
data.remove(list.get(i));
}
but before deleting you have to sort the list in decending order, in order to remove correctly, otherwise you may get an IndexOutofBoundException