I have a DialogFragment and in it's layout I have a EditText and a ListView. The Listview basically shows the list of contacts (Initially this list has 0 items). When the edittext's value is updated I populate the list with contacts that have the text typed in the EditText.
On the EditText I have used a addTextChangedListener to update the list with desired contacts as the user types in a name or email address of the contact.
The weird problem I am facing is that the list (or maybe the layout) gets updated only when I press the back button to hide the keyboard after typing. As long as the soft keyboard is showing the the list does not get updated (Except for the very first time when items are added to the empty list).
Following is some of the code for better understanding.
CustomDialogFragment.java
(in onCreateView):
// Set list adapter for contacts list
contactsList = (ListView) shareView.findViewById(R.id.contactList);
emailContactAdapter = new EmailContactAdapter(getActivity(), emailContacts, shareFragment);
contactsList.setAdapter(emailContactAdapter);
// Implement Phone-book contact share
sendToInput = (EditText) shareView.findViewById(R.id.contact_name);
sendToInput.addTextChangedListener(onContactNameSearch);
in onContactNameSearch (TextWatcher):
public TextWatcher onContactNameSearch = new TextWatcher() {
private generics commonMethods = new generics();
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
emailContacts.clear();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
Log.d("DEBUG::REACH", "After Text Changed called");
String textValue = s.toString();
// Show or hide the share apps list based on user input
// and whether or not the list is already showing or not
if (textValue.equals("")) {
Log.d("DEBUG::REACH", "TEXT value is empty");
showAppList();
emailContacts.clear();
} else {
Log.d("DEBUG::REACH", "TEXT has value");
// Hide app list if visible
if (isAppListShowing()) hideAppList();
// Get the email contacts list based on the user query
emailContacts.addAll(commonMethods.getEmailContacts(appContext, textValue));
}
adapter.notifyDataSetChanged();
}
My assumption is that the list adapter's list is correctly updated but due to some reason the layout does not reflect the new changes until the soft keyboard is hidden.
Questions:
Has anyone faced a similar issue before (Could not find any resources while googling :/) ?
Why does this happen ?
Is there anything related to this in the official docs ?
What's the best way to resolve this ?
PS: The code in the afterTextChanged method was previously in the onTextChanged method and I was facing the same issue.
UPDATE (Added screenshots for better understanding)
The following is when the dialog fragment is shown and no text has been typed in the edittext.
Now when I type in "A" and the list populates.
I add another few letters but the list does not update. I added letters "mit" so now the query becomes "Amit" but no change in the list.
Now when I press the hardware back button on the device to hide the keyboard. The keyboard is hidden and the list is updated.
(Please do not mind the overlapping contact names and emails, still have to fix the layout :P)
UPDATE2 (Adding EmailContactAdapter code)
Following is the EmailContactAdapter class
public class EmailContactAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<EmailContact> contacts;
private ProductShareFragment fragment;
private LayoutInflater inflater;
/**
* Constructor
*/
public EmailContactAdapter(Activity activity, ArrayList<EmailContact> contacts, ProductShareFragment fragment) {
this.activity = activity;
this.contacts = contacts;
this.fragment = fragment;
}
#Override
public int getCount() {
return contacts.size();
}
#Override
public Object getItem(int position) {
return contacts.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null) {
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (convertView == null) {
convertView = inflater.inflate(R.layout.email_contact_list_row, null);
}
EmailContact contact = contacts.get(position);
ImageView contactImage = (ImageView) convertView.findViewById(R.id.email_contact_image);
TextView contactName = (TextView) convertView.findViewById(R.id.email_contact_name);
TextView contactEmail = (TextView) convertView.findViewById(R.id.email_contact_email);
// contactImage.setImageBitmap(contact.getImage());
contactName.setText(contact.getName());
contactEmail.setText(contact.getEmail());
return convertView;
}
}
You are trying to change visible list with changing emailContacts, but adapter still contains old list data.
Solution: after each new text in EditText create new adapter (is a VERY bad way), or create method in EmailContactAdapter to replace items - in your case contacts field.
In the below code, you are populating the list with the result of commonMethods.getEmailContacts
// Get the email contacts list based on the user query
emailContacts.addAll(commonMethods.getEmailContacts(appContext, textValue));
Surely you first need to do emailContacts.Clear() otherwise the list is not going to change?
Try delete all emailContacts.clear();. Then add emailContacts.clear(); just before emailContacts.addAll(commonMethods.getEmailContacts(appContext, textValue));. You are appending your list for every letter your typed.
Related
In my app, the user has a list (a recyclerview) of his favorite movies, and for each movie in his favorites he can add a note and in that list I have this recycling problem where when he opens the dialog to add a note he adds the note and the note icon in the recyclerview reflects on that change, it's red in color when a note is added, but then when I click on another item in the list and come back to the item where I just added the note, the note icon goes back to grey and no note is seen in the dialog (the textview where the note should be is empty). The note only shows up when I manage to call my FavoriteFragment's OnResume where a refresh method gets called, it calls CursorAdapter changeCursor.
Also on click of an item in the list, the view gets expanded to reveal the Add/Edit note action, like so:
#Override
public void onBindViewHolder(final FavoriteHolder holder, final int position) {
// Passing the binding operation to cursor loader
final boolean isExpanded = position == mExpandedPosition;
holder.releasesActions.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
if (isExpanded) {
mPreviousExpandedPosition = position;
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(mPreviousExpandedPosition);
mCurrentPosition = position;
holder.initReleaseActions((Cursor) mCursorAdapter.getItem(mCurrentPosition));
notifyItemChanged(position);
// fixes recycling issue
// refresh only last position
// holder.initReleaseActions();
// mCursorAdapter.getItem(mPreviousExpandedPosition);
}
});
mCursorAdapter.getCursor().moveToPosition(position);
mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor());
}
The initReleaseActions method is the one which should update the note actions, to reflect changes on the note icon.
Here's its implementation in the FavoriteHolder:
public void initReleaseActions(Cursor cursor) {
// Setting our components
// User saved release actions
mHeartIcon.setImageResource(R.drawable.ic_favorite_red);
if (cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COL_NOTE_ADDED)) == 1) {
mNoteIcon.setImageResource(R.drawable.ic_note_red);
mNoteText.setText("Edit note");
}
}
My question is why won't this method to its job? I don't understand, in my mind it should work, do I always have to refresh the entire cursor if changes happen?
Oh and here's my implementation of CursorAdapater:
public FavoriteReleasesAdapter(Context context, String sortType, TextView nothingFoundTxt) {
mContext = context;
mDatabaseHelper = DatabaseHelper.getDatabaseHelper(mContext);
mCursor = getUserCursor(sortType);
mInflater = LayoutInflater.from(mContext);
mNothingFoundTxt = nothingFoundTxt;
mAlarmReceiver = new AlarmReceiver();
mIsCoversHidden = SharedPrefManager.read(SharedPrefManager.KEY_PREF_HIDE_COVERS, false);
mCursorAdapter = new CursorAdapter(mContext, mCursor, 0) {
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.favorite_release_item, parent, false);
FavoriteHolder holder = new FavoriteHolder(view);
holder.setFavoriteReleaseAdapter(FavoriteReleasesAdapter.this);
holder.setIsCoversHidden(mIsCoversHidden);
holder.setDatabaseHelper(mDatabaseHelper);
holder.setAlarmReceiver(mAlarmReceiver);
holder.setContext(mContext);
view.setTag(holder);
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
FavoriteHolder holder = (FavoriteHolder) view.getTag();
holder.setFavoriteDetails(cursor);
}
};
}
// this is the update method that gets called on FavoriteFragment onResume
public void updateCursor(String sortType) {
mCursor = getUserCursor(sortType);
mCursorAdapter.changeCursor(mCursor);
notifyDataSetChanged();
toggleEmptyTxt();
// notifyItemRemoved(mCursor.getPosition());
}
Do I always have to refresh the entire cursor if changes happen?
Yes.
Cursor is like a snapshot of the database when the query was executed. If you change anything on the database, you must update your cursor as well (run a new query and replace the old cursor by a new one). If you don't want to do that, you can convert your cursor to an ArrayList<YourModelClass> and this way, you can dinamically update specific positions.
Another point about your code:
if (cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COL_NOTE_ADDED)) == 1) {
mNoteIcon.setImageResource(R.drawable.ic_note_red);
mNoteText.setText("Edit note");
}
You only set the red icon but never set the grey icon. However, RecyclerView re-uses a view. So, it may try to re-use a view with red-icon in a position where the movies does not have notes (grey icon). So, correct approach would be:
if (cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COL_NOTE_ADDED)) == 1) {
mNoteIcon.setImageResource(R.drawable.ic_note_red);
mNoteText.setText("Edit note");
} else {
// Re-apply default text and default icon
}
I have one listview in my application,it contains two rows one for task and another one for alarm,date,severity. Initially first row of the list item only displayed for all list item and the second one is invisible. When i click the list item the second row displayed for that item as well as click another list item at that time the above list item closed that second row. Its working fine for me...My problem is if i open one list item and then swipe the listview at then i click the another list item at that time the above one cannot be closed because the above list item instance will be chnaged.please any one help me how to solve this problem...
int lastselectedPosition == -1
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long id) {
TextView textviewDate=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
selectedtaskDate=textviewDate.getText().toString().trim();
if (lastselectedPosition == -1) {
Log.i(TAG,"Loopif:"+lastselectedPosition);
TextView twTaskTime = (TextView) view
.findViewById(R.id.taskTimeidDaytoDay);
TextView twSeverity = (TextView) view
.findViewById(R.id.severityidDaytoDay);
TextView twAlarm = (TextView) view
.findViewById(R.id.alarmidDaytoDay);
twAlarm.setVisibility(view.VISIBLE);
twSeverity.setVisibility(view.VISIBLE);
twTaskTime.setVisibility(view.VISIBLE);
lastselectedPosition = position;
lastSelectedItem = arg0.getChildAt(position);
} else {
// Log.i(TAG,"LoopElse:"+lastselectedPosition);
lastSelectedItem.findViewById(R.id.taskTimeidDaytoDay)
.setVisibility(lastSelectedItem.GONE);
lastSelectedItem.findViewById(R.id.severityidDaytoDay)
.setVisibility(lastSelectedItem.GONE);
lastSelectedItem.findViewById(R.id.alarmidDaytoDay).setVisibility(
lastSelectedItem.GONE);
if (lastselectedPosition != position) {
view.findViewById(R.id.taskTimeidDaytoDay).setVisibility(
view.VISIBLE);
view.findViewById(R.id.severityidDaytoDay).setVisibility(
view.VISIBLE);
view.findViewById(R.id.alarmidDaytoDay).setVisibility(
view.VISIBLE);
lastselectedPosition = position;
lastSelectedItem = arg0.getChildAt(position);
} else {
lastselectedPosition = -1;
lastSelectedItem = null;
}
}
GetView():
#Override
public View getView(int position, View view, ViewGroup parent) {
Log.i("XXXX", "Inside getView");
final DaytoDayTaskGetterSetter objDaytoDaygetset=getItem(position);
TextView textviewTask;
TextView txtviewAlarm ,txtviewTaskTime ,txtviewSeverity;
Log.i(TAG,"InsideGetView:"+position);
LayoutInflater inflater=(LayoutInflater)context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if(view==null)
{
view=inflater.inflate(R.layout.daytodaylistlayout,null);
}
Log.i("XXXX", "before first test");
textviewTask=(TextView)view.findViewById(R.id.tasknameidDaytoDay);
txtviewAlarm=(TextView)view.findViewById(R.id.alarmidDaytoDay);
txtviewSeverity=(TextView)view.findViewById(R.id.severityidDaytoDay);
txtviewTaskTime=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
return view;
}
In first i click the "gdfgdtet" list item it show another row and then i click the second list item "dfgsdgsd" at that time the above list item "gdfgdtet" closed the second row.This is a normal output.Suppose if i open the "gdfgdtet" list item and then swipe the listview at that time both of "gdfgdtet" "dfgsdgsd" will be opened and crashed...because the above one list item reference changed when i am swiping please how to solve this problem...
I'll try to provide you a good answer that explains why you are having this problems, but the general idea is that you have to see this video - http://www.youtube.com/watch?v=wDBM6wVEO70
please take my words kindly - you don't seems to understand what ListView + BaseAdapter recycling mechanism is all about, and I strongly recommend you see the full video I linked you to, and read more about that.
in general, the specific problem in your code is that you are holding reference to listview item (lastSelectedItem), then trying to use it latter assuming it's still representing same list item. that's wrong. in that stage (after scrolling) the view already been recycled to represent another item in the list (based on the adapter implementation).
listView's number of childs is not the size of adapter.getCount()!!!!!!!!
listViews's number of childs = number of visible list items on screen + 1 + headers + footers
let's say you have the 5 first items visible on screen, then you are scrolling down. when you see the 7 item you actually see the same view instance that used to show the first list item and been recycled.
getView will call in this stage with convertView != null and position in the adapter to let you reuse the item by putting new values such different text/image to the same instance
this mechanism provides ability to display list of "infinite" number of items in the list, and holding in memory only a few number of views. imagine that you have list of 5000 items in the list, and each one of them have different view instance - you would get outOfMemory exception in a sec!
complete explanation about that would be hard to write in stackoverflow answer's context.
it just too long trying to explain one of the most important and complex UI components in android, but this links would be a good start:
http://www.youtube.com/watch?v=wDBM6wVEO70
How ListView's recycling mechanism works
http://mobile.cs.fsu.edu/the-nuance-of-android-listview-recycling-for-n00bs/
if you are interstead in "quick" fix for your specific problem, the solution would be:
hold in the data structure represents your list item additional field indicating if it in "close" or "open state. when item been clicked - change the data accordinly and call notifyDatasetChanged(). inside the getView() check if item is open or close and populate it accordinly
by the way - it's not only "quick fix" solution, but also the right thing to do anyway
You should pay attention to Tal Kanel's answer and consider this one to be an extension to it. His advice will help you in the long run.
Add a boolean field to DaytoDayTaskGetterSetter class:
public class DaytoDayTaskGetterSetter {
....
....
boolean open;
public DaytoDayTaskGetterSetter (.., .., boolean o) {
....
....
open = o;
}
....
....
public boolean shouldOpen() {
return open;
}
public void setOpen(boolean o) {
open = o;
}
}
In your getView(), check if the object has its open value set:
DaytoDayTaskGetterSetter obj = (DaytoDayTaskGetterSetter) getItem(position);
if (obj.shouldOpen()) {
// Set visibility to true for the items
} else {
// Set visibility to false for the items
}
On list item click, traverse the list and set open for all list items to false. Use the position to retrieve DaytoDayTaskGetterSetter and set its open to true:
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
obj.setOpen(false);
}
DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
yourAdapter.getItem(position);
clickedItem.setOpen(true);
yourAdapter.notifyDataSetChanged();
}
Edit 1:
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
yourAdapter.getItem(position);
if (clickedItem.shouldOpen()) {
clickedItem.setOpen(false);
} else {
for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
obj.setOpen(false);
}
clickedItem.setOpen(true);
}
yourAdapter.notifyDataSetChanged();
}
I have a ListView that contains items with checkboxes that should behave sometimes like a CHOICE_MODE_MULTIPLE and sometimes like a CHOICE_MODE_SINGLE. What I mean is for certain items in the list, when selected certain other items needs to be deselected whilst other can remain selected.
So when item A is checked I can find in my data the item B that needs to be unchecked but how do I get the UI to refresh to show this as I (I believe) cannot find the actual View that represents B but just it's data?
It sounds like you're off to a good start. You're right that you should be manipulating the underlying data source for item B when A is clicked.
Two tips that may help you:
Your getView() method in the Adapter should be looking at your data source and changing convertView based on what it finds. You cannot find the actual View that represents B because in a ListView, the Views are recycled and get reused as different data needs to be displayed. Basically, when an item is scrolled off the list, the View that was used gets passed to the getView() function as convertView, ready to handle the next element's data. For this reason, you should probably never directly change a View in a ListView based on user input, but rather the underlying data.
You can call notifyDataSetChanged() from within your adapter to signal that somewhere the underlying data has been changed and getView() should be called again for the elements currently displayed in your list.
If you're still having trouble, feel free to post some code that illustrates the specific problem that you're having. It's much easier to provide concrete advice when the problem is better defined. Hope this helps!
you can use singleChoice alartDialog, i have used like:
private int i = 0; // this is global
private final CharSequence[] items = {"Breakfast", "Lunch", "Dinner"}; // this is global
Button settings = (Button)view.findViewById(R.id.settings);
settings.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
//Title of Popup
builder.setTitle("Settings");
builder.setSingleChoiceItems(items, i,
new DialogInterface.OnClickListener() {
// When you click the radio button
public void onClick(DialogInterface dialog, int item){
i=item;
}
});
builder.setPositiveButton("Confirm",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (i == 0) {
//it means 1st item is checked, so do your code
}
if (i == 1) {
//it means 2nd item is checked, so do your code
} /// for more item do if statement
}
});
//When you click Cancel, Leaves PopUp.
builder.setNegativeButton("Cancel", null);
builder.create().show();
}
});
i have initialized i=0, so that for the very first time when user click on settings button, the first item is selected. and after then when user select other item, i have saved the i value so that next time when user click settings button, i can show user his/her previously selected item is selected.
I come across and solve this question today.
public class ItemChooceActivity extends Activity implements OnItemClickListener {
private int chosenOne = -1;
class Madapter extends BaseAdapter {
.....
.....
#Override
public View getView(final int position, View convertView,
ViewGroup parent) {
// TODO Auto-generated method stub
if (chosenOne != position) {
set the view in A style
} else {
set the view in B style
}
return convertView;
}
}
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long arg3) {
,,,,
chosenOne = position;
adapter.notifyDataSetChanged();
,,,
}
}
I am developing an app in which I need a ListView whose rows have a TextView, 2 CheckBox and a Spinner.
However, I am experiencing issues with onItemSelected() of the Spinner, as it gets called each time it is displayed for each row. In this method I am updating database records with the selected option, but as Android calls it automatically, every time the items get reset because Android calls it with position 0 and this is the value updated in the database.
I have read a lot of links about the issue with onItemSelected() and some hacks, but all of them are to use without a ListView. Any points here?
I have tried to track in a List which positions are actually displayed to make it work but it does not. I think it is because of the recycling in Android that causes the troubleshooting method get called for Spinners already shown!
So the point is: How can I differenciate a real call to onItemSelected() because of a user selection from the Android call when displaying the Spinner?
Here is the code of my adapter that extends SimpleCursorAdapter.
Thank you so much in advance.
public ParticipationAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
mActivity = (Activity)context;
ParticipationComment.ParticipationCommentManager commentManager = new ParticipationComment.ParticipationCommentManager(mActivity);
mParticipationCommentsCursor = commentManager.get();
mActivity.startManagingCursor(mParticipationCommentsCursor);
commentManager.detach();
mPositionsOfCursorIds = getPositionsOfCursorIds(mParticipationCommentsCursor);
mSpinnerPositionsDisplayed = new ArrayList<Integer>();
}
#Override
public View getView(final int participationPosition, View convertView, ViewGroup parent) {
final Cursor participationsCursor = getCursor();
mActivity.startManagingCursor(participationsCursor);
participationsCursor.moveToPosition(participationPosition);
View participationRow;
if (convertView == null) {
participationRow = LayoutInflater.from(mActivity).inflate(R.layout.participation_row_student, null);
} else {
mSpinnerPositionsDisplayed.remove((Integer)convertView.getTag());
participationRow = convertView;
}
participationRow.setTag(participationPosition);
Spinner commentSpinner = (Spinner)participationRow.findViewById(R.id.participation_comment_id_spinner);
SimpleCursorAdapter commentSpinnerAdapter = new SimpleCursorAdapter(
mActivity,
android.R.layout.simple_spinner_item,
mParticipationCommentsCursor,
new String[] {DatabaseManager.NAME},
new int[] {android.R.id.text1}
);
commentSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
commentSpinner.setAdapter(commentSpinnerAdapter);
long participationCommentId = participationsCursor.getLong(participationsCursor.getColumnIndex(DatabaseManager.PARTICIPATION_COMMENT_ID));
if (participationCommentId != 0) {
commentSpinner.setSelection(mPositionsOfCursorIds.get(participationCommentId));
}
commentSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
participationsCursor.moveToPosition(participationPosition);
if (!mSpinnerPositionsDisplayed.contains(participationPosition)) {
// Android calls this method the first time a Spinner is displayed,
// to differentiate from a real user click we check if the current Spinner's position
// in the ListView is being shown
mSpinnerPositionsDisplayed.add(participationPosition);
} else {
ParticipationComment participationComment = new ParticipationComment((Cursor)parent.getItemAtPosition(position));
Participation.ParticipationManager participationManager = new Participation.ParticipationManager(mActivity);
Participation participation = new Participation(participationsCursor);
participation.setConnectionProfileParticipationCommentId(participationComment.getConnectionProfileId());
participation.setParticipationCommentId(participationComment.getIdOpenErp());
participation.setChanged(true);
participationManager.update(participation);
participationManager.detach();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
// Not used
}
});
TextView studentName = (TextView)participationRow.findViewById(R.id.participation_student_name);
studentName.setText(participationsCursor.getString(participationsCursor.getColumnIndex(DatabaseManager.NAME)));
CheckBox expectedPresent = (CheckBox)participationRow.findViewById(R.id.participation_expected_present_value);
expectedPresent.setChecked(participationsCursor.getInt(participationsCursor.getColumnIndex(DatabaseManager.EXPECTED_PRESENT)) == 1);
CheckBox present = (CheckBox)participationRow.findViewById(R.id.participation_present_value);
present.setChecked(participationsCursor.getInt(participationsCursor.getColumnIndex(DatabaseManager.PRESENT)) == 1);
return participationRow;
}
A better way is to use a AlertDialog Variant.. like this.. and create a button which initially has the first selection as its Text and its changed based on the AlertDialog choice..
What about using a small flag to discard first call of ItemSelected ?
I have list of checkboxes in list binded by Custom simpleCursorAdapter.
In my custom simpleCursorAdapter, I've overridden newView and bindView with my modifications.
I've managed somehow to do multichoice.
The wierd thing is, after I delete any item from my list, the first item's checkbox is being checked all of a sudden. How does that happen? How can I solve it?
My SimpleCursorAdapter class:
public class MyListCursorAdapter extends SimpleCursorAdapter
{
private Context context;
private int layout;
public MyCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to)
{
super(context, layout, c, from, to);
this.context = context;
this.layout = layout;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
CheckBox chkBoxBtn = (CheckBox) v.findViewById (R.id.deleteTwittChkBox);
if (chkBoxBtn != null)
{
chkBoxBtn.setChecked(false);
}
return v;
}
#Override
public void bindView(View v, Context context, Cursor c)
{
--binding view to my textsview in my items
//now it's the importat part:
CheckBox chkBoxBtn = (CheckBox) v.findViewById(R.id.deleteTwittChkBox);
if (chkBoxBtn != null)
{
chkBoxBtn.setId(Integer.valueOf(c.getString(c
.getColumnIndex(MyUsers.User._ID))));
chkBoxBtn.setOnClickListener(new OnItemClickListener(chkBoxBtn, v));
chkBoxBtn.setChecked(false);
}
}
//i couldnt find another way of doing this, but this is how i set listeners to my checkboxses
static ArrayList<String> checkedItemsList = new ArrayList<String>();
private class OnItemClickListener implements OnClickListener
{
private int mPosition;
private CheckBox chkBox;
OnItemClickListener(CheckBox mChkBox, View v)
{
chkBox = mChkBox;
chkBox.setChecked(false);
}
#Override
public void onClick(View v)
{
if (chkBox.isChecked())
{
checkedItemsList.add(String.valueOf(chkBox.getId()));
}
else
{
checkedItemsList.remove(String.valueOf(chkBox.getId()));
}
}
}
}
Here is the code part from the ListActivity class which describes the button that deletes the checked box items:
OnClickListener btListener = new OnClickListener()
{
public void onClick(View view)
{
// long[] items = listView.getCheckItemIds();
int x = 0;
Uri myUri = Uri
.parse("content://com.idan.datastorageprovider/users");
String where = "_id" + "=?";
//here i am tatking all checkboxes which ive added from the adapter class
ArrayList<String> checkedItemsList = MySimpleCursorAdapter.checkedItemsList;
for (String itemID : checkedItemsList)
{
getContentResolver()
.delete(myUri, where, new String[] { itemID});
checkedItemsList.remove(itemID);
}
}
};
I doubt that SimpleCursorAdapter is the right class to extend here.
Is the "checked" state connected to the data XML in any way? No? So you need your own custom adapter!
Basically all adapters have to implement a way to generate a view from a given element (more precisely an element position!). This will be called at any time where the list wants to display an element. Now, the trick it uses is to re-use formerly created list view elements that cannot be seen on screen any more! Thus: when you scroll your list down and an element disappears at the top, EXACTLY this view object will be re-used for the next appearing item.
So, when this method is called with a given "old" view that should be re-used, all contained elements will have to be set according the elements data. If a checkbox is part of this game, you will have to have a storage for the checked state! It is not sufficient to have a checkbox as there will be less checkbox objects as there are list elements!
SimpleCursorAdapters are there to - yeah - represent SIMPLE things. An XML describing data (images and text, as the documentation states). Because of this simplicity all you have to do here is provide a method to create NEW element view objects - you are not intercepting the re-use process AT ALL! It basically only knows how to put the data into an existing view object - but it is lacking the knowledge of how to handle checked/unchecked boxes!
Your solution: write your own BaseAdapter extension and do what has to be done: implement "getView" (and some other methods like getItem, getItemId and getCount). It's not hard at all!
This API Demo uses a BaseAdapter and the mExpanded state here is basically identical to your checkbox states!
Good luck!
You might need to call notifyDataSetChanged when you modify the data.
The problem is probably that you're calling setChecked from within the onItemClickListener. One hacky way around this is to do the following before and after you call setChecked from within your listener:
chkBox.setClickable(false);
chkBox.setChecked(false);
checkBox.setClickable(true);
This will prevent your onItemClickListener from getting called when you manually call setChecked.