While writing my own adapter that extends the ArrayAdapter class, I came across the different constructors available and I noticed they all require a textViewResourceId parameter. So initially, I decided that I would feed my custom adapter class android.R.id.text1:
MyAdapter adapter = new MyAdapter(this, R.layout.myRow, android.R.id.text1);
However, later on during my development, I decided to override getView method where I would
TextView label = (TextView) myRow.findViewById(android.R.id.text1);
label.setText("Position #" + position);
Which worked fine. But then this question came to mind: if I'm doing the logic for how to display the row, is it really necessary to provide a textViewResourceId to the constructor when I initiate my custom adapter? Or is it the case that when you override getView, that parameter is no longer necessary? If my thinking is correct, what is the common practice for instantiating the adapter knowing that you will be overriding the display behavior anyways?
If you are overriding getView you do NOT need to specify a proper textViewResourceId.. You can pass in 0. The only time the ArrayAdapter tries to access that ID is within getView.. Since you are overriding getView and providing your own view textViewResourceId is never accessed..
Since the super class expects a view ID.. you still need to pass in a view ID into the super call.. however, it can just be 0 since it will never be used
ArrayAdapter Source confirming all of this
If you do not specify a proper textViewResourceId and do not override getView.. getView in the ArrayAdapter assumes your entire view is a TextView.. If your view is not a TextView.. you will end up with crashes from a ClassCastException..
you don't need to use any kind of build in adapter.
you can extend the BaseAdapter and make your own rules of what to show , what type of data to hold etc...
in fact , i almost never used the built in adapters that android has out of the box , because using the baseAdapter is very easy as it is.
Related
So, I am running up against a limitation of the Parse SDK here, and I don't know how to proceed.
I have a ListView that I am setting the adapter to from a subclass of the ParseQueryAdapter, which itself extends the BaseAdapter class.
Normally with an adapter, I pass the list along to the Adapter constructor in order to get access to the list I am dealing with, so that I can call getCount, getItem, etc. by accessing the size/index of that ArrayList.
I can't do that with the ParseQueryAdapter because it takes a ParseQueryFactory as part of the constructor, and it doesn't resolve it's items until they are fetched from the Parse app online...
Now the ParseQueryAdapter does have a List of Objects, which is exactly what I need, expect that it is Private... so even though the getItem() and getCount() methods exist, there is no way - AFAIK - to use them, as I have no access to the dataset that is fuelling my adapter...
Further to this, I would normally use getView(), which returns a position alongside my View, and everything would be snappy. Except Parse overrides that and provides a getItemView() method which resolves the Parse Object that is fetched from the cloud and passes it instead of the integer for position...
So, two questions here:
1) Is there another way in which I can access the list that is feeding an adapter ? I can hack a way to pass the resolved query into a list and pass that into the adapter later, by listening to addOnQueryLoadListener(), but that seems bad design and might take a while, handcuffing my UI.
2) Am I being naive in my understanding of how to use get getItem() and getCount() methods ?
When we use ArrayAdapter we pass something like a list to the super class. But base adapter constructor doesn't have any parameters. How does this class find the data set? I have seen in some examples they just define an array and override the functions without specifying the list as the Dataset. So how does the class understand this is the dataset? What if we define more than one list in the derived class?
Edit:
I think I should clarify my question. When we use ArrayAdapter the dataset is specified and the program knows what to iterate and calls getView for each of them. But in BaseAdapter we only define a list and override 4 functions and it works! My question is why does it work?! we didn't specify the dataset we just specify the getView body and it returns a view. I don't understand how the program finds the dataset.
As the documentation explains it well :
Common base class of common implementation for an Adapter
Means you have to do the implementation.
http://developer.android.com/reference/android/widget/BaseAdapter.html
How does this class find the data set?
It doesn't. Your subclass of BaseAdapter manages the data set.
What if we define more than one list in the derived class?
So long as you implement the abstract Adapter methods properly (getCount(), getView(), getItem(), getItemId(), ...), how you manage your own data is up to you.
Well after more considerations on the codes I think this is how base adapter works:
It loops the getView() function with 'position' parameter to be from 0 to what is returned by getCount().
we must override getCount() and send the correct index of the last item of dataSet. Each time getView() is called we can work with the views and any list we want according to the current position.
And I think the main difference between ArrayAdapter and BaseAdapter is that ArrayAdapter finds the last index of the list when we pass it to the super class but in base Adapter we should define the last index. Implementation of getView() is the same and we can use any list we want in getView. The trick was only about position parameter.
I have a ListView. Naturally I pass an adapter to it, say mDogAdapter. My ListView also implements setOnItemClickListener.
So here is my question: in the context of getting an item from the adapter, what is the difference between these two?
Dog dog =(Dog) parent.getItemAtPosition(position);
Dog dog = mDogAdapter.getItem(position);
There's no difference.
The AdapterView's getItemAtPosition() method will call getItem() on the adapter in its implementation.
It's up to you to decide which makes more sense. If it's possible that you may use a different adapter at some point, you should consider using the AdapterView's getItemAtPosition() method -- let the AdapterView deal with getting the correct data from its Adapter.
I am looking at this ListView Tutorial:
ListView Tutorial
and I was wondering how much better is to create my own ArrayAdapter, rather than just using and ArrayAdapter.
In the Tutorial it defines a "StableArrayAdapter", what exactly does this means? If I use a regular ArrayAdapter, could it be dangerous for some reason?
The two previous answers are absolutely right, but just to address more directly your question and in case someone else has the same doubt than you; a regular ArrayAdapter is not dangerous at all, the only "problem" is that it might not fulfill your needs, in which case you will have to create your own adapter, as the author of the tutorial did by creating what he called StableArrayAdapter in the end of the ListViewExampleActivity class.
Don't get lost by the name, which I guess comes from the fact that the overwritten method "hasStableIds" always returns true, it doesn't mean that the regular ArrayAdapter creates problems.
ArrayAdapter: It is merely a way to provide data to a ListView. It is also a BaseAdapter that is backed by an array of objects.
CustomAdapter: If if your ListView is a normal and simple ListView (wherein you are having one TextView per item in the list), then the use of ArrayAdapter would be apt.
But it is recommended you to create your own CustomAdapter which extends an ArrayAdapter that you can use for providing data to your ListView. This way you can easily extend your ListView to include more that one TextView or even ImageView (to show images).
CursorAdapter: Cursor Adapter is used when you have Data in a Cursor (typically when you are retrieving data from a database. The Cursor must include a column named "_id" or this class will not work.
If you are using a simple ListView, like merely a TextView per item, then just use the standard ArrayAdapter, on the other hand, if you want a custom item in the list, as in a combinations of views within each item in the ListView, then extend the ArrayAdapter and implement it to your needs.
StableArrayAdapter is merely an extended version of ArrayAdapter, but in StableArrayAdapter they have overridden the method hasStableIds() of BaseAdapter to return true.
You can check this in the following links:
StableArrayAdapter -
Override hasStableIds to return true
ArrayAdapter -
Has not Override hasStableIds but extended BaseAdapter
BaseAdapter -
Has hasStableIds but returning false
Now Question is What is the use of StableIds
This Indicates whether the item ids are stable across changes to the underlying data. If True then same id always refers to the same object. for more info
I have a ListView where I want each item to have an ID number attached to it (not the same as the position number). I was hoping this could be done by setting a tag to each View item in the ListView using setTag() when these Views are being created.
Right now I'm creating the ListView like this:
final ListView listview = (ListView) findViewById(R.id.listView1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, names);
listview.setAdapter(adapter);
The names variable in the ArrayAdapter parameters above is an ArrayList, and each string value in this list also has a unique ID that I want to link to this string somehow.
Is there any way I can get access to and modify each of the Views with a tag? One idea was to create my own extended class of ArrayAdapter and override the getView() method, but I don't really understand how it works and how I would go about doing this.
Or is there a better way to link IDs with each string like this than adding tags like I'm trying to do?
Create a ViewBinder and set the tags as the ListView is being populated with whatever you need. You can check all properties of the view to determine what tag goes where, so this should be what you're looking for.
myAdapter.setViewBinder(new MyViewBinder());
public class MyViewBinder implements ViewBinder {
#Override
public boolean setViewValue(View view, Object data, String text){
//Since it iterates through all the views of the item, change accordingly
if(view instanceof TextView){
((TextView)view).setTag("whatever you want");
}
}
}
I just used this exact same answer on another question (albeit slightly different) yesterday.
about getView , it works by using a method of recycling views. i will try to explain it in a simple way.
suppose you have tons of items that can be viewed . you don't want to really create tons of views too , since that would take a lot of memory . google thought of it and provide you the means to update only the views that need to be shown at any specific time.
so , if there is an empty space on the listview , it will be filled with a new view . if the user scrolls , the view that becomes hidden is recycled and given back to you on the getView , to be updated with the data of the one that is shown instead .
for example , if you scroll down , the upper view becomes hidden for the end user , but in fact it becomes the exact same view that is on the bottom .
in order to understand how to make the listview have the best performance and see in practice how and why it works as i've talked about , watch this video:
http://www.youtube.com/watch?v=wDBM6wVEO70
as for tags , i think you want to do something else , since the data itself (usually some sort of collection, like an arrayList) already knows where to update , because you get the position via the getView . if you want a specific view to update , you might be able to do so by using a hashmap that keeps upadting , which its key is the position in the collection , and the value is the associated view . on each time you go to getView , you need to remove the entry that belong to the view (if exists) and assign the new position with the view that you got/created .
Thanks for the answers. thisMayhem's answer would probably have been easier in the end, but on my quest to learn more I ended up making my own adapter according to this tutorial. I pass down the names and the IDs into the adapter and set the names as the text of the TextViews and the IDs as the tags.
I would rather go with the solution discussed in this thread. It is always the easiest to have all related data in same place and in this case you just create a class to hold all the information you will need for every item.