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 ?
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 two Fragments. One Fragment Consist of Questions While OtherFragment consist of Answers. For Example If one person click on Question(8) It means u can say that Item No 8, then it should go to next activity and highlight the Answer (8) of that Question. I am stuck. I shall be thank full to you.
Here is Some Code Which are sending Question No or list Item no to next Activity and next Activity code receiving this Question no and i saved it into Interger "a". Now what type of IF condition can work in the Next Activity?
First Activity
lst.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int index = position + 1;
Intent it = new Intent(this.getActivity(), NextActivity.class);
it.putExtra("Qlist", index);
startActivity(it);
}
Next Activity
Intent it = getActivity().getIntent();
int a = it.getIntExtra("Qlist", 0);
String result=String.valueOf(a);
Toast.makeText(getActivity(), ""+a, Toast.LENGTH_LONG).show();
ListView Adapter code of First Activity (using SQlite)
DatabaseAccess db=DatabaseAccess.getInstance(getActivity().getApplicationContext());
List<String> lst;
db.open();
lst=db.getexamq();
db.close();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(ExamQ.this.getActivity(), android.R.layout.simple_list_item_1, lst);
lv.setAdapter(adapter);
ListView Adapter code of Next Activity (using SQlite)
DatabaseAccess db=DatabaseAccess.getInstance(getActivity().getApplicationContext());
List<String> lst;
db.open();
lst=db.getexamq();
db.close();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(ExamA.this.getActivity(), android.R.layout.simple_list_item_1, lst);
lv.setAdapter(adapter);
Use the intent Bundle to move the questionId or question No to the next activity
In the adapter of the Questions ListView, set a different background color for the question with a similar questionId. You can even set the background to have a fade animation or change to normal after a certain number of milliseconds or seconds.
ALL THE CODE REQUIRED IS A LOT Write something(some code), and we can help on the ListViewAdapter in case it doesn't work
EDIT: SAMPLE FAST CUSTOM ADAPTER CODE
lv.setAdapter(new ArrayAdapter(ExamQ.this.getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, lst){
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
final String examQ = lst[position];
if (v == null) {
LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(android.R.layout.simple_list_item_1, null);
}
TextView tv = (TextView) v.findViewById(android.R.id.text1);
tv.setText(examQ);
if( examQ.equals(result)) { // Unique identifier here
v.setBackground(Color.RED); //Some other fancy color here
}
return v;
}
});
However, I just discovered you do not have a unique identifier for each question. So for the time being just use the question string and pass it to the next activity
Suppose You have a textview for each answer in the listview of second fragment. Then you can compare the question id in the getView() of adapter of listview in second fragment. Now when you find your answer just call textview.setBackgroundColor().
Edit:
After lv.setAdapter (adapter); add
View v = lv.getViewAt(a);
if(v!=null)
v.setBackgroundColor (Color.RED);
i Have Two Database
first one Contain All The Items, and the ListView Display it
and the second db contain the the Favorite item , [selected from the first database]
what i want is that when the listview display all the items
check if the item is already exist in Favoritelist then make that textview background RED for this item
i have this code that work fine
public static void ChangeMyFavoritesBG(){
for (int i = 0; i < Items.size(); i++) {
if(db.verification(Items.get(i).toString())){
try {
TextView favtextview = (TextView) listview.getChildAt(i-listview.getFirstVisiblePosition()).findViewById(R.id.item_name);
favtextview.setBackgroundResource(R.drawable.redcolor);
}catch (NullPointerException e) {
}}}}
db.verification check if item exist in favorites database
if true . then it should change the background of this item to red
this code work fine but only if i put it in button click
i need to make the code work automatically
but if i made it start automatically when the activity is loaded i get NullPointer Error
i guess because the function ChangeMyFavoritesBG(); work before the listview display items
any idea guys? and sorry for my bad english
Do this control inside the getView(int position, View convertView, ViewGroup parent) method of the Adapter used by the listView.
If your favorite is not currently visible in the ListView then getChildAt() will return null.
You are looping over all items in the list view and my guess is that it holds more items than can fit on the screen. When your favorite item is one of them then this fragment of your code
listview.getChildAt(i-listview.getFirstVisiblePosition())
will return null. And that will cause the NullPointerException when you call findViewById(R.id.item_name) on it.
Just add a check for null on the result of getChildAt(). If it is null then do nothing, if it is non-null then call the second part. This will protect against the exception when your favorite item is not on the screen, and will allow it to be colored red when your favorite is visible on the screen.
update
My apologies, I read to quickly and misunderstood your problem to be about the NullPointerException but you say that your code works fine when you call it from a button click handler but not when you call it automatically at start-up.
You are right, the ListView does not yet have any items loaded when you are still in onCreate(). You can add a delay before running you code. The following works for me:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
// initialize the ListView with data for the list items. (I'm using a string array in this
// example. You are loading it from a database table, but that is the same in principle.)
ListAdapter adapter = new ArrayAdapter<String>(this, R.layout.item_list, R.id.item_name, Items);
ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(adapter);
// ask the system to wait before setting the background color of the favorite item so that
// the ListView has time to load the items.
final int DELAY_IN_MILLISECONDS = 100;
listview.postDelayed(new Runnable() {
#Override
public void run() {
ChangeMyFavoritesBG();
}
}, DELAY_IN_MILLISECONDS);
}
As you can see in the above example, after initializing the ListView, you ask the system to wait 100 milliseconds before calling ChangeMyFavoritesBG(). Hopefully that is enough time to load the items from the database into the ListView. If it is not enough time then you can, of course, use a longer delay.
The alternative
The above should work, but to be honest I would not write it this way. The above code is very brittle because it depends on the timing of how long it takes to load the items. I recommend that you put your background coloring into a customized adapter.
Because you want the items displayed in a customized way -- you want them to have a red background when it is the favorite one -- you should use a customized adapter. Override the bindView() function to make the background red when it is the favorite one or give it a normal background when it is not the favorite.
I don't know how you currently get the items from the database into your ListView, but inheriting from SimpleCursorAdaptor would work pretty well.
public class FavoritesItemAdapter extends SimpleCursorAdapter {
public FavoritesItemAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
// read the name from the database
int nameColumnIndex = cursor.getColumnIndexOrThrow("name");
String name = cursor.getString(nameColumnIndex);
// write the name to the TextView
TextView nameText = (TextView) view.findViewById(R.id.item_name);
nameText.setText(name);
// set the background to normal or to red, depending on if it is the favorite one or not
boolean isFavorite = db_verification(name);
if (isFavorite) {
nameText.setBackgroundResource(R.drawable.redcolor);
} else {
nameText.setBackgroundResource(android.R.color.transparent);
}
}
public boolean db_verification(String name) {
// this is a stub. You must use your own code here
return name.equals("the favorite one");
}
}
You can then throw away ChangeMyFavoritesBG() and initialize your ListView with the adapter in onCreate() like this.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
Cursor cursor = readItemsFromDatabase();
String[] from = new String[] { "name_column" }; // mapping from database column name ...
int[] to = new int[] { R.id.item_name }; // ... to View ID in the item's layout.
FavoritesItemAdapter adapter = new FavoritesItemAdapter(this, R.layout.item_list, cursor, from, to, 0);
ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(adapter);
}
Good luck!
I have a list view full of items, after the users selects an item it lights up, and then it goes back to normal. Is there a way to make it so that when the user selects an item in my ListView it stays selected, and highlighted?
Apparently the "disappearing selection" is by design; it's something called "touch mode". I read through that document and still I have no idea why they thought it was a good idea. My guess is that, since Android was originally designed for small-screen devices, they expected that you would fill the screen with a list and then, when the user clicks an item, move to a new list on a different screen. Thus, the user wouldn't be aware that Android lost track of the selected item.
But this behavior is quite annoying if, for example, you want the user to select an item and then show information about that item on the same screen. If the selection disappears, how is the user supposed to know what they clicked (assuming of course that users have the attention span of a goldfish)?
One possible solution is to change all the list items into radio buttons. I don't really like that solution because it wastes screen real estate. I'd rather just use the background color to show which item is selected. I have seen one solution so far but it is not quite complete or general. So here's my solution:
1. In your XML layout file
Go to your ListView element and the following attribute: android:choiceMode="singleChoice". I'm not entirely sure what this does (by itself, it doesn't allow the user to select anything) but without this attribute, the code below doesn't work.
2. Define the following class
It is used to keep track of the selected item, and also allows you to simulate pass-by-reference in Java:
public class IntHolder {
public int value;
public IntHolder() {}
public IntHolder(int v) { value = v; }
}
3. Put the following code somewhere
I'll assume you put it in your Activity, but it could go in any class really:
static void setListItems(Context context, AdapterView listView, List listItems, final IntHolder selectedPosition)
{
setListItems(context, listView, listItems, selectedPosition,
android.R.layout.simple_list_item_1,
android.R.layout.simple_spinner_dropdown_item);
}
static void setListItems(Context context, AdapterView listView, List listItems, final IntHolder selectedPosition,
int list_item_id, int dropdown_id)
{
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> list, View lv, int position, long id) {
selectedPosition.value = position;
}
});
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context, list_item_id, listItems) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = super.getView(position, convertView, parent);
if (selectedPosition.value == position)
itemView.setBackgroundColor(0xA0FF8000); // orange
else
itemView.setBackgroundColor(Color.TRANSPARENT);
return itemView;
}
};
adapter.setDropDownViewResource(dropdown_id);
listView.setAdapter(adapter);
}
This code does two things: it attaches your list items (e.g. List<String>) to your ListView, and it overrides ArrayAdapter.getView() with some code that changes the background of the selected item.
4. Use that code to set up your list
For example:
ListView _list;
IntHolder _selectedItem = new IntHolder(-1); // nothing selected at first
#Override
protected void onCreate(Bundle savedInstanceState) {
...
_list = (ListView)findViewById(R.id.list);
List<String> items = Arrays.asList("Item 1", "Item 2", "Item 3");
setListItems(this, _list, items, _selectedItem);
}
That's all! The above assumes you want single selection. With some small modifications to getView(), you could support multi-selection too, I guess, but you should probably use checkboxes instead.
Warning: this solution needs further development. If the user uses arrow keys or buttons to select an item, that item will not be selected from the IntHolder's perspective. If the user presses the unlabeled button (what's the name of that button? "Enter"?) then the item will become "officially" selected but then you have another problem because if the user uses the arrow keys again, it will sort of look like two items are selected. Leave a comment if you figure out how to keep the "internal selection" in the IntHolder synchronized with the "keyboard selection" or whatever it's called. What is it called, anyway?
There is an attribute in ListView called listSelector:
Drawable used to indicate the currently selected item in the list.
http://developer.android.com/reference/android/widget/AbsListView.html#attr_android:listSelector
EDIT after Stan comment
To ensure that a ListView stays selected, you should
① Set the view's attribute choiceMode via xml or programmatically.
② Use an adapter that uses views which implement Checkable interface, like CheckedTextView (inside simple_list_item_single_choice layout).
File TestActivity.java
public class TestActivity extends Activity {
private static final int SINGLE_CHOICE = android.R.layout.simple_list_item_single_choice;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
String[] items = {"test 1", "test 2", "test 3"};
ListAdapter adapter = new ArrayAdapter<String>(this, SINGLE_CHOICE, items);
ListView list = (ListView) findViewById(R.id.testList);
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
list.setAdapter(adapter);
}
}
Here a simpler solution than Qwertie's:
Do not rely on given selection mechanism. Do it yourself.
View mSelectedItemView = null; //class member variable
View mTouchedItemView = null; //class member variable
ListView v = (ListView) getActivity().findViewById(R.id.listView);
// select on click
v.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter,
View clickedViewItem, int position, long id) {
if (mSelectedItemView != null)
selectedItemView.setBackgroundColor(Color.WHITE);
clickedViewItem.setBackgroundColor(Color.YELLOW);
mSelectedItemView = clickedViewItem;
}
});
// highlight on touch
v.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v instanceof ListView) {
ListView listView = (ListView) v;
// Find the child view that was touched (perform a
// hit test)
Rect rect = new Rect();
int childCount = listView.getChildCount();
int[] listViewCoords = new int[2];
v.getLocationOnScreen(listViewCoords);
int x = (int) event.getRawX() - listViewCoords[0];
int y = (int) event.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = listView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
View touchedView = child;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
touchedView
.setBackgroundColor(Color.RED);
mTouchedItemView = touchedView;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
mTouchedItemView
.setBackgroundColor(Color.WHITE);
}
}
}
}
return false;
}
});
Also this method only deals with clicks and will not work if the user uses the arrow keys.
Disclaimer: De-highlighting after touch does not work reliably.
Credits for the touching part go to ozik.dev:
Get Item from ListView only with OnTouchListener
just add this to your listview layout
android:listSelector="#drawable/selector_expandable_listview"
android:drawSelectorOnTop="true"
Use a Selector.XML File and this code:
//SetOnClickListner to catch Events
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
view.setSelected(true);
}
});
Just add this to your ListView:
android:listSelector="#color/my_color"
This answer is working try this one
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long arg3)
{
for(int a = 0; a < parent.getChildCount(); a++)
{
parent.getChildAt(a).setBackgroundColor(Color.TRANSPARENT);
}
view.setBackgroundColor(Color.GREEN);
}
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.