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 ?
Related
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.
Say I have a List<User>. Now I can wrap this list in an ArrayAdapter.
List<User> users = Users.getAll();
ArrayAdapter<User> = new ArrayAdapter<User>(this, android.R.layout.simple_list_item_1, users);
I then bind the adapter to a listview to display the list of Users.
Users.getAll() uses Sugar ORM to query the database and return a list of users. Items can be added to the user list from the activity that displays the user list. I am wondering how do I keep the listview updated.
Option 1
One way is to manually update the users as a I add to the database and then call adapter.notifyDataSetChanged(). This works, but it doesn't feel right because I am maintaining a "fake" list that represents what is in the database.
Option 2
I am wondering how bad is it if I just clear the items in users, update it with the results of a new database query and then call adapter.notifyDataSetChanged()?
Will all the child views be thrown away and be re-rendered? Or does it call the equals() method to see if the models bound to each child is the same and then update only what is new?
Other Info
Since I am using SugarORM, I don't think I can get access to the Cursor to do something more efficient. However if there is a better way to keep the list synced with SugarORM, I am happy to hear that as well.
In answer to your option 2: No, it doesnt call equals, because the adapter works in conjunction with the widget to re-use the views, it doens't create a new view foreach item in the list, it create a view foreach visible item and as you scroll re-uses view that left the screen.
The best option here is to create your own adapter, creating a class extending BaseAdapter and creating your own logic inside it requerying the database and notifying the change to the listview (or gridview)..
On the other hand doing what you said here:
I am wondering how bad is it if I just clear the items in users, update it with the results of a new database query and then call adapter.notifyDataSetChanged()?
isn't bad either.
Create a DAO class that extends Observable, then have your Adapter implement Observer. Now every time you add or remove a SugarRecord, do through the DAO class and whoever is register as the Observer will get notified through the following method:
#Override
public void update(Observable observable, Object o)
You can more about Observable/Observer pattern here. This is just one of the many examples and tutorials out there.
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
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.
I'm wondering a simple way to find back the object corresponding to the clicked item..
They're tons of examples on the web on how to figure out the ListView setup with the setListAdpater, but much less on how to well handle its listener.
Is "by position" the only way ?? I'm wondering a possibility to associate the objects itselves to the adapter, to not have to use their position in list (or even the displayed String!) to find back the Object referred by the clicked label..
Position is what is used always. The ListView works with using the position.
But if you want to access the ListAdapter and get a value out by providing a string, than you will have to extend ListView and implement that functionality yourself. You can overwrite the different methods that handle adding and removing and keep a HashMap where you keep the string representing the object. Then through a getObject(String key) you return the object that is in the hashmap for that key.