Apparently the notifyDataSetChanged() only updates the visible items in the listview, I have a system that changes the background color of an item when its clicked (to dkgray), and I set everything else to transparent(the default), however other items that aren't visible remain selected(dkgray) (I only want the currently selected item to be dkgray). Is there a way to force notifyDataSetChanged() to update all items.
Here's example:
//makes all item backgrounds transparent
public void resetListViewBackground(){
for (int i = 0; i < listView.getChildCount(); i++){ //parent.getChildCount()
listView.getChildAt(i).setBackgroundColor(Color.TRANSPARENT);
}
}
//reloads the listview
private void reloadListView() {
listItems.clear();
adapter.notifyDataSetChanged();
listView.invalidateViews();
ArrayList<HashMap<String, String>> notesArrayList = dbTools.getAllNotes();
for (int i = 0; i < notesArrayList.size(); i++){
String temp = "";
if (notesArrayList.get(i).get("note").length() > 51){
temp = notesArrayList.get(i).get("note").substring(0,50).toString() + "...";
} else {
temp = notesArrayList.get(i).get("note").toString();
}
listItems.add(temp);
adapter.notifyDataSetChanged();
}
}
Everywhere I call resetListViewBackground(), I call reloadListView() after.
And this is what I use to highlight the selected item.
view.setBackgroundColor(Color.DKGRAY);
Also, the most common occurrence of this problem is that every 6th item is highlighted. The listview only shows about 4 items at a time.
The getChildCount() method won't work for all list items as view recycling is done.
Astral is right as he writes in the comments.
1) Create a custom adapter.(See http://windrealm.org/tutorials/android/listview-with-checkboxes-without-listactivity.php)
2)Inside the onListItemClick listener's onItemClick() method, call your adapter's notifyDataSetChanged() method(whenever the user clicks a list item).
I modified the project that I mentioned and posted it on my Dropbox.(Just import it in Eclipse and run).
Check it out at https://www.dropbox.com/s/gchccjzpkxk8n2z/Planets_modified.zip
Related
I've an app displaying a list of presets items in a ListView.
It's possible to add, modify and remove each of these items, most often by clicking on an item (becoming selected) and then on a button to do the desired operation.
The items are then sorted and the new list is displayed, showing the result of the operation.
Rendering problems come when I keep wanting to see the initial item as selected in the new list (except in case of removing), without having to click on it, and even without operation (in case of switching between apps). So I had to use postDelay at two places in the code.
So here is my code until now, on rebuilding the list:
...
String presetId = presetsHandler.getPresetId(listIndex); // listIndex is the initial position of the selected item
presetsHandler.sortPresets();
listIndex = presetsHandler.getIndex(presetId); // New position
...
lvAdapter.setTextItems(presetsHandler.getConcatenatedDisplayPresetDataList()); // Replace all the items
listView.setAdapter(lvAdapter); // Attach the new preset list to the ListView
...
if (listIndex != LIST_INDEX_DEFAULT_VALUE) { // Need to keep initial item as selected
final long RENDERING_DELAY_MILLIS = 200;
listView.postDelayed(new Runnable() { // Delay to besure that listView is ready (after the setAdapter)
#Override
public void run() {
if (listIndex < listView.getFirstVisiblePosition() || listIndex > listView.getLastVisiblePosition()) { // Invisible
listView.setSelection(listIndex); // To see the item at position listIndex (without selecting it yet)
}
listView.postDelayed(new Runnable() { // Delay to besure that listView is ready (after the possible setSelection)
#Override
public void run() {
View view = listView.getChildAt(listIndex - listView.getFirstVisiblePosition()); // The item at position listIndex was surely visible
view.setSelected(true); // and we can make it appear as selected
}
}, RENDERING_DELAY_MILLIS);
}
}, RENDERING_DELAY_MILLIS);
}
I'd like to know if it's a good way of doing it, particularly as for the postDelayed and Runnable.
Thanks
After a moment, I found this solution, a bit stressful for the OS (10 ms between calls to the runnable), but only during the time needed to finish rendering.
The benefit of this solution is that you don't need to estimate that time lapse and it keeps the things simple.
What do you think ?
Thanks
if (listIndex != LIST_INDEX_DEFAULT_VALUE) { // Need to keep initial item as selected
final long MINIMAL_DELAY_MILLIS = 10;
final Runnable runnable = new Runnable() {
boolean isFirstTime = true;
public void run() {
if (listView.getChildCount() == 0) { // listView is not ready yet
listView.postDelayed(this, MINIMAL_DELAY_MILLIS); // Wait until ready (after setAdapter)
} else { // listView is now ready
if ((listIndex < listView.getFirstVisiblePosition()) || (listIndex > listView.getLastVisiblePosition())) { // The item at position listIndex is not visible
if (isFirstTime) {
isFirstTime = false; // Do setSelection only once !
listView.setSelection(listIndex); // To see the item at position istIndex, without selecting it yet
}
listView.postDelayed(this, MINIMAL_DELAY_MILLIS); // Wait until visible (after setSelection)
} else { // The item at position listIndex is now visible
View view = listView.getChildAt(listIndex - listView.getFirstVisiblePosition());
view.setSelected(true); // and we can make it appear as selected; There's no need to call the runnable anymore
}
}
}
};
listView.postDelayed(runnable, MINIMAL_DELAY_MILLIS); // Run it!
}
I've a recycler view on my app. I'd like to prevent multi selections on my recyclerView, because I'm getting unexpected output while trying to delete items.
holder.mView.setOnClickListener(v -> {
if (null != mListener) {
mListener.onPreviewItemClicked(position);
if (holder.isSelected) {
holder.mView.setBackgroundColor(Color.TRANSPARENT);
Log.d("positionClicked adapter",position+"");
holder.isSelected = false;
isSelected = holder.isSelected;
} else {
holder.mView.setBackgroundColor(ContextCompat.getColor(mContext, (R.color.accent_light)));
holder.isSelected = true;
positionClicked = position;
isSelected = holder.isSelected;
}
}
});
On my code, I use is Selected boolean to catch if something is clicked. When I click on an item, background of its changes. The thing is :
If I click on item1, then on item2, both items have backgrounds changed, and I'd like to have only one. And when I click on item, code below is done. But then, If i click again on item2, it goes transparent, which is good ! But the position of my item selected is wrong ( should be 1, but it's 2 because last item selected was 2). So when I delete, it will delete 2nd item and not first one...
I delete using positionClicked variable.
Any idea on how to fix this ?
You should keep if item is selected in your model, and use it in the onBindViewHolder to setup background for the ViewHolder. In OnClickListener just set true for the specific data item, false for all others and then call notifyDataSetChanged() to rebinf all items.
Currently the list when populated is starting with the view # the bottom of the list. Is there a way using listAdapters to force it to the top of the list?
Currently the orientation scrolls to the bottom on create. Is there a way to pin the screen to the top when it creates? http://imgur.com/wGTEy in this example you see that entry 1 on create is shoved upwards to make room for six... Instead I want it to populate like this. http://imgur.com/6Lg6e... entry 1 is the top of the list and 6 is pushed off to the bottom for the scroll.
If you look at the picture above you will notice it starts at the bottom of the list instead of at the top. Any Ideas?
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mStrings);
setListAdapter(mAdapter);
registerForContextMenu(getListView());
populateFields();
private void populateFields() {
if (mRowId != null) {
Cursor note = mDbHelper.fetchDaily(mRowId);
startManagingCursor(note);
String body = note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_DBODY));
mAdapter.clear();
if (!(body.trim().equals(""))){
String bodysplit[] = body.split(",");
for (int i = 0; i < bodysplit.length; i++) {
mAdapter.add(bodysplit[i].trim());
}
}
}
}
**edited to fix != string error.
You want the items later in the list to be at the top of the ListView? If so, check out this questions: Is it possible to make a ListView populate from the bottom?
You are completely changing the adapter, so the scroll position is lost in the process... You can use:
ListView listView = getListView();
int position = listView.getFirstVisiblePosition();
if (!(body.trim().equals(""))){
String bodysplit[] = body.split(",");
for (int i = 0; i < bodysplit.length; i++) {
mAdapter.add(bodysplit[i].trim());
}
}
listView.setSelection(position);
But this is not perfect as it is, if a row is added before position the index will be off. If your list contains unique values you can use ArrayAdapter#getPosition(), to find the new index.
While I still recommend using a CursorAdapter, because it handles large table data better, I want to address a point on efficiency with your ArrayAdapter code.
By using adapter.clear() and adapter.add() you are asking the ListView to redraw itself on every step... potentially dozens or hundreds of times. Instead you should work with the ArrayList directly and then ask the ListView to redraw once itself with ArrayAdapter#notifyDataSetChanged() after the loop completes.
i'm having listview with some items. i placed a Checkbox to display the checkbox with that listitem. I've write onclick method to checkall the checkbox. But, it'll select the item which was in top of the list. What's the problem? Anyone clear me? Thanks in Advance. Sample Code : -
CheckBox c = (CheckBox)findViewById(R.id.checkBox1);
if (c.isChecked())
{
c.setChecked(false);
}
else
{
c.setChecked(true);
}
private OnClickListener checkAllCheckboxes = new OnClickListener(){
public void onClick(View v) {
ListView lv = getListView();
int size = getListAdapter().getCount();
boolean check = lv.isItemChecked(0);
for(int i = 0; i <= size; i++)
lv.setItemChecked(i, !check);
}
}
OR
for(int i=0; i < listView.getChildCount(); i++){
RelativeLayout itemLayout = (RelativeLayout)listView.getChildAt(i);
CheckBox cb = (CheckBox)itemLayout.findViewById(R.id.MyListViewCheckBox);
cb.setChecked(true);
android-unable-to-check-all-the-checkboxes-in-a-custom-listview-because-of-recy
Example2
with that snippet its difficult to point out the issue.
one, the listview reuses the view, maybe that while scrolling the same view is appearing else where.
here is a tutorial http://www.marvinlabs.com/2010/10/custom-listview-ability-check-items/
check it out. or post additional code. particularly, getView in the adapter code.
It happened because at calling of Adapter view it execute at the time of your list view.
So after the you clicked on check box (true) at same number of below screen will be true. for that you have to maintain the Vector.
if(tempVector.get(position)){
holder.box.setChecked(true);
}
else{
holder.box.setChecked(false);
}
try this after your clicking event.
How would one find the position of a specific item within a ListView? (Populated by SimpleCursorAdapter).
The reason I ask: The listview is set to singleChoice mode. When the user closes and reopens the app, I'd like the user's selection to be remembered.
The way I've done it so far is when the user clicks on an item, the ID of the chosen item is saved to preferences. What I need to learn is how to reselect the item in the activity's onCreate method once it's been repopulated.
My code for saving the selected item's ID:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Cursor c = (Cursor) l.getItemAtPosition(position);
selectedItem = c.getLong(c.getColumnIndex("_id"));
}
(I've tried googling, but only seem to find how to get the position of the selected item)
Thanks!
You should try
//SimpleCursorAdapter adapter;
final int position = adapter.getCursor().getPosition();
API Docs:
public abstract int getPosition ()
Since: API Level 1
Returns the current position of the
cursor in the row set. The value is
zero-based. When the row set is first
returned the cursor will be at positon
-1, which is before the first row. After the last row is returned another
call to next() will leave the cursor
past the last entry, at a position of
count().
Returns
the current cursor position.
Update
To get an item's position based on the id used by the adapter:
private int getItemPositionByAdapterId(final long id)
{
for (int i = 0; i < adapter.getCount(); i++)
{
if (adapter.getItemId(i) == id)
return i;
}
return -1;
}
To get an item's position based on the underlying object's properties (member values)
//here i use `id`, which i assume is a member of a `MyObject` class,
//and this class is used to represent the data of the items inside your list:
private int getItemPositionByObjectId(final long id)
{
for (int i = 0; i < adapter.getCount(); i++)
{
if (((MyObject)adapter.getItem(i)).getId() == id)
return i;
}
return -1;
}
I do this straightforward in my own app:
long lastItem = prefs.getLong(getPreferenceName(), -1);
if (lastItem >= 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
if (lastItem == cursor.getLong(0)) {
spinner.setSelection(cursor.getPosition());
break;
}
cursor.moveToNext();
}
}
Spinner is populated with the cursor's contents, so I just look through them and compare with the selected item id. In your case that would be a ListView.
When you say, "...reselecting the item in the activity's onCreate method...", do you mean that when the user returns to the ListView activity, whatever item was previously chosen, is now currently at the top of the screen (assuming enough items appear in the list below it)?
If so, then from onListItemClick, you should also make an effort to save the value of position, since it tells you the position in the list of the selected item. This would allow you to not need to reverse-lookup the position from the _id.
Or is that for some reason not an option for your purposes? Do you really need to instead figure out the position from the _id?