I am trying to generate a list from an object using an ArrayAdapter. The result Looks like:
Item A (3)
Item B (1)
Item C (0)
The number in brackets represents the amount of items that are behind that file. I want to Display an Image whenever there is a 1 in brackets - in this case only for item B.
I have an Attribute Image available that is only set true when the item has a 1.
However, when I'm creating the list, it creates everywhere the Image, except in Item C. I have created a short log to try to understand the reason and found out, that public View getView(int position, View convertView, ViewGroup parent) {
method is called up to 11 times... though the 1st 3 should be enough. when i modified my if clause that it should set imageavailable to false when an item is detected - only the 1st item had a Picture. can anyone help me out? ( i also tryed to make if(imageavailable&number==1) resulting in the same result - 1st 2 have a picture
You should be managing the adapter, not adding logic to getView. You logic for handling the adapter should be in its own method. For example:
ArrayList<Drawable> adapter = new ArrayList<Drawable>();
void constructAdapter(List<Drawable>... drawables, int sizeFilter) {
if (drawables != null) {
for (List<Drawable> l : drawables) {
if (drawable.size() == sizeFilter) {
for (int i = 0; i < sizeFilter) {
adapter.add(l.get(i));
}
}
}
}
}
And then from here you would pass the adapter list into your array adapter.
Note: I used drawable as my example, since you said images. This could be whatever you want, so long as you just change the logic to handle that particular data set.
Related
The issue is I have is that rather than re-invent the anecdotal wheel; I'd like to utilise the same layout for Spinners and also for ListViews, where the list will be based upon the same data.
I also want a similar, custom, look throughout the App, such as lists (Spinners and ListViews) having alternating row colors and colors coded according to the current core activity ().
For example. My app has Shops (a core activity) which appear as a list in a ListView (ShopName, ShopCity and ShopOrder), the layout used for this is R.layout.shoplist, as per:
My app also has Aisles (another core activity, so colors are different). The list of Aisles is restricted to a one of the Shops, thus a Spinner is incorporated, listing the available Shops in order to select the respective Aisles. The Aisle list currently looks like, without a spinner :-
I know that I can just specify the ShopLIst Listview's Items layout for the spinner in the Spinner's Adapter simply by using the ShopList Adapter as per (where sclcsr is cusror containing all shops, and selectshoplist is the Spinner):-
slcsr = dbshopmethods.getShops("", shopsorderby);
selectshoplistadapter = new AdapterShopList(this,
slcsr,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER,
getIntent(),
true,
false
);
selectshoplist.setAdapter(selectshoplistadapter);
Note! there are 3 extra parameters than with a standard adapter, The Intent is passed (an int is extracted that is used to determine which range of colors is used, the first boolean is a flag to indicate a call from a Spinner as opposed to a ListView, the second is used to indicate whether or not to show extra data)
However, the result is:-
That is the customisation is lacking in the Spinner's dropdown list.
As such the question can now be What do I need to do to set the background colours for the dropdown?
Note I have a class (ActionColorCoding) and methodology for detremining/applying the colors in the adpater's getView method, as per :-
int evenrow = ActionColorCoding.setHeadingColor(ctxt,
callerintent,
ActionColorCoding.getColorsPerGroup() - 1
) & ActionColorCoding.transparency_evenrow;
int oddrow = evenrow & ActionColorCoding.transparency_oddrow;
if (position % 2 == 0) {
view.setBackgroundColor(evenrow);
} else {
view.setBackgroundColor(oddrow);
}
So the above would be the basis of the code to be incorporated into the adapter. Custimsation of the Spinnner's Selection/selected item is not an issue as this is as per the Spinner's declaration in the activity's layout.
Note, the intention of this Question, is as a guide that may be of assistance to others for a technique that there doesn't appear to be an answer for
Spinner's actually have two layout's associated with them, the second layout being for the DropDownView and will invoke the method getDropDownView, if Overidden (whilst ListViews invoke the getView method).
One thing to note is that, if getDropDownView invoked then bindView is not invoked so you have to invoke it.
By adding the following to the ListView adapter your adapter will cater for the ListView and the Spinner :-
#Override
public View getDropDownView(int position, View convertview, ViewGroup parent) {
super.getDropDownView(position, convertview, parent);
View view = convertview;
if (fromspinner) {
int cpos = this.cursor.getPosition();
view = View.inflate(ctxt,R.layout.shoplist,null);
int evenrow = ActionColorCoding.setHeadingColor(ctxt,callerintent, ActionColorCoding.getColorsPerGroup() - 1) & ActionColorCoding.transparency_evenrow;
int oddrow = evenrow & ActionColorCoding.transparency_oddrow;
if (position % 2 == 0) {
view.setBackgroundColor(evenrow);
} else {
view.setBackgroundColor(oddrow);
}
this.cursor.moveToPosition(position);
}
bindView(view, ctxt, this.cursor);
return view;
}
Note! I don't believe that the **if (fromspinner)** construct is required but has been included as a precautionary measure.
Additional Note, callng super.getDropDownView(position, convertview, parent); appears to not be required and is thus, probably best note done.
Note! ctxt, calleritent, fromspinner and cursor are set in the Adpater's constructor as per :-
AdapterShopList(Context context,Cursor csr, int flags, Intent intent, boolean fromspinner,boolean showdetails) {
super(context, csr, 0);
ctxt = context;
callerintent = intent;
this.fromspinner = fromspinner;
this.cursor = csr;
setShopOffsets(csr);
}
The result you get is :-
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.
I am previously working on PHP and js , and recently I am working on android listview
However, I encountered a problem in creating a custom adapter for listview
public View getView(int arg0, View arg1, ViewGroup arg2) {
// TODO
if (arg1 == null) {
arg1 = myInflater.inflate(R.layout.grid, arg2, false);
}
TextView name = (TextView) arg1.findViewById(R.id.text1);
TextView desc = (TextView) arg1.findViewById(R.id.text2);
ImageView image = (ImageView) arg1.findViewById(R.id.image1);
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
name.setText(names[arg0]);
desc.setText(description[arg0]);
return arg1;
}
The problem is I have 3 array of content to pass to the listview grid, for the first two array, there are 10 element and the last one have 5 only. So , it is out of boundries for the last one. I added a condition to check whether it exceed 5 , but args0 seems not increased according to the row?
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
The first five row and some other rows also has image setted, why is that and how to fix this? thanks
In General
since you want to display Data to your list, plx create an Object that represents Data.
like you named in your comment above:
public class ListEntry {
String name = "";
String gender = "";//use enum here perhaps -.-
String photoUrl = null; //or use byte[] photo or whatever you've stored in your array before
// write getters/setters for your members
}
then you can use one array ListEntry[] (or List<ListEntry>) to access all data. this way you get around your indexOutOfBoundsException.
lookup any listadapter tutorials online, e.g. the one from Vogella
Why do more than the first five entries have an image?
Androids Adapters for Listviews implement a caching mechanism to reduce the inflating (performance/memory cost intensive) of new list-items (e.g. rows) to a minimum. therefore there are only as many rows (or little more) created as displayed by the list. since you only set images if there are any, but never remove already set images from rows, you result in some rows that replay images they shouldn't. these rows are cached from previously outscrolling rows.
therefore add something like
if (listItem.photo != null) {
image.setImageResource(images[arg0]);
} else {
image.setVisibility(View.GONE);
}
as reference for listviews and their caching mechanism see Romain Guy on ListViews
Edit Regarding usage of Listadapter
The getView(..) you posted above is inside your ListAdapter implementation, prefarrably you've extended an ArrayAdapter<T>. if so, your T should now state ListEntry and you have any line of code that states
MyArrayAdapter myAdapter = new MyArrayAdapter() or something like that.
now you have an array or List of ListEntry like List<ListEntry> myCollection = new ArrayList<ListEntry>() or ListEntry[] listEntries = new ListEntry[10] and use
myAdapter.addAll(listEntries);
to get an item of your list inside your getView(..) you can use:
ListEntry currentEntry = getItem(arg0);
and refer the single members of currentEntry to set them ;-)
What about
if (images[arg0] != null) image.setImageResource(images[arg0]);
?
I know the reason why the getView method of an Adapter is called more than once, but is there a way for knowing which of the returned view will be actually displayed on the activity?
Until now I put all the returned view linked to the same position in a list and, every time I need to modify a shown view I modify all the views corresponding to that position (one of those will be the right one...).
Surely is not the best way...
Here a piece of code of my adapter:
class MyAdapter extends BaseAdapter {
Vector<ImageView> vectorView[] = new Vector<ImageView>[25];
public MyAdapter(Activity context) {
...
}
public doSomeStuffOnAView(int position) {
// needs to know which view corresponds to the given position
// in order to avoid the following for cycle
for (ImageView iv: vector[position]) {
// do something
}
}
public View getView(int position, ...) {
ImageView childView = ...;
if (vector[position]==null) {
vector[position]=new Vector<ImageView>();
}
vector[position].add(childView);
return childView;
}
}
The method getView(...) might be called more than once for each position, but just one returned view per position will be shown on the activity. I need to know which of these. I thought it was the one returned the last time getView has been called for a position, but it is now always true.
one way but it is not standard.. you can directly check with integer variable i.e. hardcoded type. You have list of views in your xml file. jst cross check & compare with integer variable.
I'm getting some strange behavior from a listview/the getChildAt method.
I have a HashSet, iconsToUpdate, of icons that have been changed in the database. I want to iterate over the visible rows to see if any of their icons need to be updated to reflect the new icons. I don't need to test the icons not currently in view as they will be drawn properly when rendered.
My problem is that getChildAt is returning null when it seems like it shouldn't. I know that getChildAt can only return views that are currently visible, but it is returning null for some of the visible rows.
Here is my code that iterates over the visible rows:
Logger.debug("First visible index: " + f_listView.getFirstVisiblePosition());
Logger.debug("Last visible index: " + f_listView.getLastVisiblePosition());
for (int i = f_listView.getFirstVisiblePosition(); i <= f_listView.getLastVisiblePosition(); i++) {
String tag = "asdf"; // Remove when bug is fixed.
if (f_listView == null) {
Logger.debug("f_listView is null");
} else if (f_listView.getChildAt(i) == null) {
Logger.debug("Child at index " + i + " is null");
} else {
tag = (String) f_listView.getChildAt(i).getTag();
Logger.debug("Successful at index " + i + ", tag is: " + tag);
}
if (iconsToUpdate.contains(tag)) {
setIcon(i, f_aim.getInHouseIcon(tag));
}
}
Here is the log corresponding to a run of this loop:
D/...: First visible index: 3
D/...: Last visible index: 8
D/...: Successful at index 3, tag is: ...
D/...: Successful at index 4, tag is: ...
D/...: Successful at index 5, tag is: ...
D/...: Child at index 6 is null
D/...: Child at index 7 is null
D/...: Child at index 8 is null
It should be noted that the first and last visible indexes are being correctly reported, as I am viewing rows 3-8 when I run this. Rows 6, 7, 8 are being rendered properly. How are they being displayed if they are null?
Also, I do not know if this is important, but row 5 is the last visible row when I am at the top of the listview.
Any info as to why these rows are being returned as null would be greatly appreciated.
Thanks!
listView.getChildAt(i) works where 0 is the very first visible row and (n-1) is the last visible row (where n is the number of visible views you see).
The get last/first visible return the position in the dataAdapter you have. So you since you start at position 3, with what looks like 6 visible views, that's when get for positions 6-8 you get null.
In your example getChildAt(0) would return position 3. What I usually do is store the position on my views so I can lookup on my dataAdapter later if I need values.
I think your for loop should look like this:
for (int i = 0; i <= f_listView.getLastVisiblePosition() - f_listView.getFirstVisiblePosition(); i++)
Try
f_listView.getChildAt(positionOfChildYouWantGet - f_listView.getFirstVisiblePosition());
When you call listView1.getLastVisiblePosition() and listView1.getFirstVisiblePosition(), the listview returns the positions of the items that are partially visible in the listview. For example, the first item may be half visible and the last item may be half visible. In this case, even though you can see part of the item in the listview, the adapter has not yet called the getView() function for the item and therefore the item is still considered null.
In my case i have a listView and the first item is full visible but when I do getFirstVisiblePosition() the result is 1 !
It gets more weird when I scroll and make the first item half visible then my getView show that getFirstVisiblePosition() is 0.
this my code :
public View getView(final int position, View convertView, final ViewGroup parent) {
row = convertView;
final AtomPaymentHolder holder = new AtomPaymentHolder();
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
row.setTag(items.get(position));
holder.songitem = items.get(position);
final int firstPosition = MainActivity.listviewSong.getFirstVisiblePosition() - MainActivity.listviewSong.getHeaderViewsCount();
final int lastPosition = MainActivity.listviewSong.getLastVisiblePosition();
if(MusicService.indexService>= firstPosition && MusicService.indexService<= lastPosition){
MainActivity.listviewSong.getChildAt(MusicService.indexService-firstPosition).setBackgroundColor(getContext().getResources().getColor(R.color.pressed_color));
}
(when I selected the next item and scroll, it works good)
They are partially visible maybe. But when the getView() tries to recycle the views it gets FULLY visible ones other takes them as null. for example if you scroll the list and the top item is partially visible and the bottom is partially visible so these are nulls but you still see them.
I tried by ever mean in OnListItemClickListener(),but fails. At last I made some modification in my customized adapter for listview. Here in getView(),I apply clickListener on the item which i added into the list frequently. n do all required functionality there. Here is my Code, where i add Image View in the list n so apply listener on imageview.
I Think it will help those who want to change the color when specific List Item is >selected. Go For it..
In getView() of customized Adapter
//---------------------------------code------------------------------------------
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.icon_image_layout, parent, false);
ImageView imageView = (ImageView) rowView.findViewById(R.id.Icon_ImageView);
imageView.setClickable(true);
final int pos=position;
imageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
try{
if(previous_view!=null)
previous_view.setBackgroundColor(Color.WHITE);
}catch (Exception e) {
System.out.println("Exception Occurs Previous View");
}
v.setBackgroundColor(Color.RED);
MainActivity.imageView.setImageResource(MainActivity.Image_Name[pos]);
previous_view=v;
return false;
}
});
I know this is very old post. But I'm answering because people are still looking for a work around on ListView getChildAt() null pointer exception.
This is because the ArrayApdater is REMOVING and RECYCLING the views that are not visible yet on the ListView because of height. So that if you have 10 item views, and ListView can display 4 - 5 at a the time :
The Adapter REMOVE the item views at position 5 to 9, so that any attempt to adapter.getChildAt(5... to 9) will cause null pointer exception
The Adapter also RECYCLE the item view, so that any reference you made on position 3 for example will be lost when you scroll down to 5 to 9, and also any Input that you make on position 3 (EditText, Checkbox, etc.) will be recycled when you scroll down to 5 to 9 and will be reused at another position later (ex position 1, 2 or 3, etc.) with the same value
The only way I found to control this is to forget about getting the View and to have :
Attribute HashMap<Integer, ImageView> iconViews or any type you want for handling the values you want to use for each item on the list. The first type must be unique for item like item->getId() or position. Initialize it with new HashMap<>() in the Constructor;
in getViews make iconViews.put(position, iconView);
Prevent Adapter from using recycled convertView, remove condition if(convertView == null) so that adapter always inflate a brand new view instance. Because the view instance is new each time, you must set the value from HashMap each time also like if it already contains the key if(iconViews.containsKey(position)){iconView = iconViews.get(position))};. Probably in this case there is not tons of Items, so that smooth scrolling won't be a must.
And finally create public methods to get the Values outside of Adapter passing item->getId() Integer as parameter. Ex : public ImageView getIconViewAt(int position) { return iconViews.get(position); } . It will be easy then to select Item from Adapter
See more from my answer.
Try
if (f_listView.getChildCount() > 0){
f_listView.getChildAt(i)
}
Isn't it because you are saying
f_listView.getChildAt(i)
And you should be retrieving the item at that position?
f_listView.getItemAtPosition(i)