I have a method that paint the View of selected item but if my ListView has a scroll ( i mean: if my screen can display 9 "lines" - positions - and the total size is bigger than 9 ) and i select one the first nine positions, it will select the position that i choose and one from the last positions ( a position that i need to scroll to see ).
Example: if i select select the position 0 and paint it, the view on position 0 and on position 11 will be painted.
If i try do to:
getListView().getChildAt(int)
And if this position is like '12', it will return null so i'm using the View that i get when onItemLongClick(AdapterView adapterView, View v, int position , long arg3) is called. It looks like that View is based on scroll because i have same View objects for scroll positions.
Here is how i paint the Views:
listView.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View v,
int position , long arg3) {
Log.d("", "");
View tmp = adapterView.getChildAt(position);
int oi = adapterView.getSelectedItemPosition();
if(!mPositions.contains(Integer.valueOf(position))){
v.setBackgroundColor(Color.CYAN);
mViews.add(v);
mPositions.add(Integer.valueOf(position));
} else {
v.setBackgroundColor(Color.TRANSPARENT);
mViews.remove(v);
mPositions.remove(Integer.valueOf(position));
}
return true;
}
});
How can i get distinct views independently if the list is bigger than my screen?
The problem here is that you really shouldn't be modifying the view directly. The whole point of a ListView is that it will create a view for an item, and then potentially use this same view to paint each and every one of the items in the list. (row recycling). It is possible here, that when you modify the background colour of the selected item's view, this view object is then actually being used to draw another row's items - looking like it's just doing it for the wrong row.
What you should do, is create a custom adapter with your own implementation of getView(). Inside this function you will do one of two things: If convertView is null, then create the View that you desire (possibly a TextView?), otherwise you will grab the view from convertView. Finally you will then set this view up however you would like based on it's position in the list. Ie: if it's text, set the value, and then set your background.
To respond to the longpress, simply change your data (mSeleted) and call notifyDataSetChanged() so that the list will redraw based on this new data.
This is a pretty simplistic overview, listviews can get as complicated as you want. There are many examples of how to create a custom listview on the web if you search.
Related
I have an ActionBarActivity with a GridView.
The GridView has 2 columns in portrait and 3 columns in landscape.
When I select items in portrait (starting my ActionMode) and then rotate the device, the selected item highlighting shifts one item to the left. For example, if I select the second item and rotate, the first item will be highlighted. If I select the first item and rotate, no items are highlighted.
The actual selection in the code is correct, just the highlighting is wrong.
I notice it does not do this if I keep the numColumns the same for portrait and landscape.
I believe this issue started occurring after I changed my activity to an ActionBarActivity so it could be a bug..
Anyone know why or how to fix it?
I had a similar scenario and ended up solving the issue be creating a custom grid item with a boolean field to keep track of whether the item is selected or not and then highlighting the item appropriately through the custom adapter. Below is a rough outline of what I did:
(1) I created a custom grid item with a boolean field, which we will call selectedStatus for simplicity's sake. I also added the corresponding methods to my grid item class to get the selected status:
public boolean getSelectedStatus ()
{
return selectedStatus;
}
public void setSelectedStatus (boolean paramSelectedStatus)
{
this.selectedStatus = paramSelectedStatus;
}
(2) I then created a custom Adapter that extends BaseAdapter to handle the custom grid object I created. In this Adapter I check the if the selected status of the grid object is true or false and highlight the item accordingly, shown below:
#Override
public View getView (final int position, View convertView, ViewGroup parent)
{
// rest of getView() code...
if (!yourGridObject.getSelectedStatus())
{
convertView.setBackgroundColor(Color.TRANSPARENT);
}
else
{
convertView.setBackgroundColor(Color.LTGRAY);
}
// rest of getView() code...
return convertView;
}
(3) Lastly, you add the onItemClickListener to set the selected status and the background color of the grid items when they are selected (clicked):
yourGridView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
YourGridObject yourGridObject = (YourGridObject) parent.getItemAtPosition(position);
if (!yourGridObject.getSelected())
{
view.setBackgroundColor(Color.LTGRAY);
yourGridObject.setSelected(true);
}
else
{
view.setBackgroundColor(Color.TRANSPARENT);
yourGridObject.setSelected(false);
}
}
});
Implementing selection this way ensures that the highlighting (selection) of the grid items will not change when the number of columns and rows swap since the selection status is contained within the grid objects themselves.
You don't need to manually handle selection of items as suggested by Willis. Android fully supports what you are asking. I will assume you are using an ArrayAdapter however this answer would apply to all adapters. Note some adapters (like CursorAdapter) won't suffer from your posted problem and don't require the following solution because it's already doing it internally.
The problem is solved in two parts. One, the adapter must enable stable Ids. Two, your adapter must actually return stable ids. You will need to extend the ArrayAdapter or which ever adapter you are using. Then ensure you have defined the following methods as shown below.
private class MyAdapter extends ArrayAdapter<YourObjects> {
#Override
public boolean hasStableIds() {
return true;
}
#Override
public long getItemId(int position) {
//Return a unique and stable id for the given position
//While unique, Returning the position number does not count as stable.
//For example:
return getItem(position).methodThatReturnsUniqueValue();
}
}
Most adapters do not enable hasStableIds. It's primarily only used when enabling a choiceMode. Which I assume you are doing here. By returning true, you are essentially telling Android to keep track of activated (highlighted) items based on their ID value instead of their position number.
Even with stable Ids enabled, you have to actually return an ID that is unique and stable across positional changes. Since most adapters do NOT enable stable IDs, they usually only return the position number as the stable id. Technically, if an item's position never changes over time then the position number "could" be used as the stable id. However, the safest way to return a stable/unique ID is to have one assigned to the class object being stored in the adapter and pull from that.
Can you explain this method's parameters? I did not understand them.
http://developer.android.com/reference/android/app/ListActivity.html#setListAdapter(android.widget.ListAdapter)
onListItemClick(ListView l, View v, int position, long id)
ListView l: defines the ListView on which the click event is happening?
View v: defines the view that was clicked within the ListView? So for a ListView, that would be the xml containing the ListView, and if its a custom ListView the xml containing the row?
int position: The position of the view in the list. Can it be retrieved using below function? getListView().getPositionForView((LinearLayout)v.getParent())
long id: The row id of the item that was clicked. Is this not same as position? Or do we provide R.id.Textview?
Its simple.
ListView l --> Is the name of the listview object on which the click is happening. (Simple)
Like
ListView l = (ListView) findViewById(R.id.list);
View v ---> There can be multiple view inside a list view. So this tells that which of the view has been clicked and takes action accordingly.
Position --> The position of the view in the list. (VIEW POSITION)
id ---> Row id of the ITEM . (ITEM ID)
A listview consists of small items/rows. While using a custom listView. Each row/item can have different functionality and layout. so for each row/item we must use different functionality..
For example in a listview at the fourth row there is a button while on all other items there is only text. So in order to put your event there you must get that particular row/item contents. the below code gives you the access to single item..
onListItemClick(ListView l, View v, int position, long id)
first parameter is parent where the click happened
second parameter is single View(child/row) of a ListView(Parent)
third parameter is the position of child view.
fourth parameter is the id of the child view.
1-first one ListView Object that one you define by :
ListView foo = (ListView) findViewbyId(R.id.foo);
2-second Row item it self (View v) one is simple that's object you click on (you can obtain the view that has been clicked), you can change color or content or what ever you want
3-position index of (View v) object in ListView start from 0
4- Row id of item (i never have been used)
I want to make a listview that as user scroll to bottom of list view other items of list fill automatically from internet.I wrote code to that in the adapter of that expandable list view (within getGroupView() method)as like this,
public View getGroupView(final int arg0, final boolean arg1, View arg2, ViewGroup arg3) {
//if(arg2==null){
//arg2=act.getLayoutInflater().inflate(R.layout.layout_exlistview_group, null);
}
//((TextView)arg2.findViewById(R.id.nameText)).setText(item.getItemName());
if(arg0>=getGroupCount()-1){//chech is near for end
/*here i run a asynctask to download data and add items to SparseArray of this class.That sparsearray is the source to the adapter to view them in listview*/
}
//return arg2;
}
So is this correct way to do this or is there any good way to do that?
From the fact that you're using getGroupView I assume you're using an ExpandableListView, not a regular ListView, which should probably be stated in your question.
Either way, the best way to do this would be to assign a OnScrollListener onto your list and then do your check there, rather than in getGroupView.
I'd recommend you put something along the lines of the following into your onScroll method:
if (firstVisibleItem + visibleItemCount > (totalItemCount - NUM_BEFORE_LOAD)) {
loadMore()
}
where NUM_BEFORE_LOAD based on your example would be 1, but you could make it anything you want to make the list load quicker than when it hits the bottom.
I have list view with 78 items, in my activity file I descripe onClick Function realization
lv.setOnItemClickListener(new ListView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Log.d("Click from activity", "win");
if(!checkedContacts.contains(clearContacts.get(arg2))){
checkedContacts.add(clearContacts.get(arg2));
arg1.setBackgroundColor(R.color.selectedItem);
} else {
checkedContacts.remove(clearContacts.get(arg2));
arg1.setBackgroundColor(Color.WHITE);
}
}
});
So when I clicked on item it change color. But if I scroll down my listView, other elements every 10 times will have new color. checkedContacts array have right values but, visualization wrong
The problem is that listviews recycle the views that are out of the screen so this way you are not saving the row status. When you get the row out of the screen it will be deleted and when it comes back again it will be refilled in your adapter again.
You may solve this, for example, using your checkedContacts var and make it accesible from your adapter so, if the position given in getViewfunction of your adapter you can check if the row has been checked and then youn can change the background color. If the user rotate the screen you may save this var in onSaveInstanceState in your activity to recover them later.
Hope it helps :)
I have two ListViews (A and B) with items of the same type (a class I created)
When I click on an item from A, it adds this object on B and if I click again it removes it.
Only that when an item is selected, I change its background using view.setBackgroundColor(myColor).
I want to be able to remove the item from list B (it works), but I want also to reset the background color. I can't figure out how to get the view of this item I'm removing.
Any ideas?
There's no guarantee that any specific ListView item will even have a view at any given time. If the item is currently off-screen, then it may not have a view. Since a specific item might not have a view, it might not make any sense to try to get the item's view.
Beyond that, because of the way ListView creates and reuses views, you'll see some odd, undesirable effects if you simply modify the views directly. As the user scrolls through the list, items that become visible will incorrectly end up with the same backgrounds as other items that have fallen outside the visible portion.
I don't know whether what follows is the best way to implement your functionality because I don't know the cost of rebuilding the list after a change. Here's the (probably naive) way I would do this:
Add another boolean member to your data object, something like isInSecondList.
Override getView() in the Adapter. In getView(), set the background to either normal or highlighted depending on the the value of the item's isInSecondList.
When an item is added or removed from the second list, update the data object to reflect the change, then call the Adapter's notifyDataSetChanged().
int position = 0;
listview.setItemChecked(position, true);
View wantedView = adapter.getView(position, null, listview);
Here is what i did
private View oldSelection;
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long arg3) {
highlightSelectdListItem(position);
}
public void highlightSelectdListItem(int position) {
clearPreviousSelection();
View newsItemView = mGridVIew.getChildAt(position);
oldSelection = newsItemView;
newsItemView.setBackgroundColor(Color.GRAY);
}
public void clearPreviousSelection() {
if (oldSelection != null) {
oldSelection.setBackgroundColor(Color.TRANSPARENT);
}
}