OK, trying to get images from my SD card to show in an activity ListView.
I'm reading lots of hints, but I'm missing an important chunk. I'd love to see a short-ish bit of source code that illustrates this, haven't found anything yet.
From my research it sounds like bindview() is called when listview.setadapter(SimpleCursorAdapter adapter) is executed. The documentation says that setImageView is called by bindview if the ViewBinder cannot handle an ImageView.
You specify the field in the FROM array, the id of the TextView in the TO array, the Cursor, and the view group with the TextView. Then set the adapter to the ListView and voila, it happens.
I think the call sequence is then:
listView.setAdapter(SimpleCursorAdapter adapter)->adapter.bindView()->adapter.setTextView
Bindview must determine the view is of type TextView, determine that it can bind it, passes theTextViewand text value from the cursor andsetTextView` does its thing.
So, that makes the call for an ImageView very similar, right?
listViw.setAdapter(SimpleCursorAdapter adapter)->adapter.bindView()->adapter.setImageView
So when bindView comes across and object of type ImageView, what does it do? Is there a default implementation like in setTextView? I'd imagine it would be hard to do that, images vary so much. Text is text, but images have formats, sizes, depths, scale, LOCATIONS, etc, etc. The string passed to the setTextView IS the text, the payload. In an image the string is (probably) the filename, only the starting point of an image.
I think I'll need to build my adapter with the database column name that holds the filename in FROM. The TO array will have just the id of the ImageView in my layout. I think I can use the default viewbinder bindview(), but will have to override setImageView() to take the expected string (filename), and build out the image loading lines to find that image name in the expected application path.
So that will be (maybe):
listView.setAdapter(MyOwnSimpleCursorAdapter myOwnAdapter)->myOwnAdapter.super.bindView()->myOwnAdapter.setImageView()
Does this sound right? If anyone can fill in the missing gaps and/or provide some working source code it would help a lot. By the way, I've written code to do this, but it isn't working. It could be a very simple bug and don't want to stop this thread if it's do-able. I think I just need a little nudge in the right direction.
I don't quite understand what is your question(and yes, you should post the failing piece of code) so here is my answer(you can also see this in the source code of the SimpleCursorAdapter):
In a SimpleCursorAdapter when the ListView requires a new View to be shown the method getView() will be called. This methods delegates the row building to 2 methods: newView()(which will create the View(inflating the xml layout you've set) and set as a tag an array of the Views with the ids from the to array) and bindView()(which will actually bind the data to the View created by newView()).
bindView() will then iterate through the arrays of Views(the Views with the ids from the to array) on which to bind the data. It will also call getString(and only the getString method, this is important) from the cursor to get the data. Next the method checks if a ViewBinder has been set on the adapter, if this is the case it will let that ViewBinder to set the data(the methods setTextView() and setImageView() will not be called if the ViewBinder successfully binds the data).
If a ViewBinder wasn't set on the adapter or the setViewValue method of the ViewBinder returns false(meaning the ViewBinder has failed) then the bindView() method will check and see which type of View it is dealing with(TextView or ImageView) and call either setViewText() or setViewImage().
The implementation of the setViewImage() will try to parse the supplied String as an int (example: an image id R.drawable.image) and if this fails it will then parse the String as an Uri and use setImageUri() on the ImageView. So an int like R.drawable.image or a String like "file://mnt/sdcard/photo100.jpg" should work with the default implementation of the SimpleCursorAdapter, otherwise override the setImageView.
Related
I'm trying to click on a item at a specific position in a grid view.
onData(instanceOf(MyClass.class))
.inAdapterView(withId(R.id.my_view))
.atPosition(R.integer.my_id)
.perform(click());
but I'm getting this java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
I'm queuing the responses using MockWebServer, even after the UI is on screen with all the list item, I'm getting this error, I'm not sure why.
Also, I want to get the content of the specific item.
Well, I think that's because you're matching class which is only one, not a specific adapter with values.
Please consider this post:
The matcher passed as argument to onData() must match the value as
returned by Adapter.getItem(). So the first version doesn't match,
because of the wrong type being used. It should be:
onData(is(instanceOf(IconRowAdapter.IconRow.class)))
What also can be a pitfall is using equalTo on different kinds of
CharSequences. String is a CharSequence, but if IconRow.getText()
returns CharSequence instead of String, then this can also be
Spannable, Editable, etc in which case equalTo wouldn't match. So if
IconRow.getText() return anything but String, make sure to convert it
into a String before comparison.
This post was taken from How to use Espresso to test item in adapter at a specific position
Your question lacks of code of tested class, so I cannot give you direct answer. I can only recommend to read StackOverflow link above.
Hope it help
You may need to "drill down" a little deeper into the view hierarchy to get to the item in the cell. Put an additional method call before the ".perform" using the id of the item in the grid cell
onChildView(withId(R.id.???)).perform(click());
Use the id of the view that the user would click on.
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 ?
Before I was extending SimpleCursorAdapter and overriding newView/getView I would specify which TextView's to populate with the to and from parameters in the constructor.
Now when I did override, the to/from parameters seem to be useless. If I don't pass a column name to "from" I can still access the column and at the same time I can't construct the adapter by passing null to both to/from (so I just pass one column not to get NullPointerException).
Should I be using to and from somehow in bindView? If so, how?
Now when I did override, the to/from parameters seem to be useless.
A SimpleCursorAdapter is designed(as its name suggest) as a simple adapter to bind a limited range of views to a Cursor's data, mapping made through the two arrays(the columns names to views with the specified ids). If you override the SimpleCursorAdapter (especially the newView() and bindView() methods that do the binding)then those columns aren't really necessary because you'll take matters into your own hands.
If I don't pass a column name to "from" I can still access the column and at the same time I can't construct the adapter by passing
null to both to/from (so I just pass one column not to get
NullPointerException).
Although you'll probably do the mapping yourself(I don't know what methods you override and how) those columns are used in other part of the adapter's code so passing null for either one of those arrays should be avoided. Anyway the real problem is that the SimpleCursorAdapter is a simple class design for basic scenarios. If you find the need to override it then you better extend its super class CursorAdapter(which comes without those arrays) and do whatever you want.
Should I be using to and from somehow in bindView? If so, how?
As those arrays represent the columns from the Cursor and the ids of the views to which to bind the data then it would make sense to use them.
these can be empty for customAdapter because you can control TextView's etc from getView()
so you can either have control from to/from parameters
or from getView()
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.
sorry for stupid question. But really interesting and incomprehensible. In this session discussed about notifyDataSetChanged() method.
From documentation for this method - "called when the data set being observed has changed, and which when read contains the new state of the data". My English bad and I do not understand all. But I right if guess that method called when I need refresh ListView with new data set?
If I'm right then I'm confused. In the past and my first program I played with contacts api of android. And run some processing in an asynctask. At this time appeared dialog with progress bar and in the background, you could see how the state of ListView changed in real time. Data for ListView row changed via BindView.
Why? So I'm in something wrong. Explain please.
As i read it, BindView is only used with cursors, which are a specific type of a data set basically. You can have alternative data sets, there is for example an ArrayListAdapter in the API which uses an ArrayList as its dataset. In case that data set changes, notifyDataSetChanged() will have to be called to notify the list view that its bounds will have to be recalculated and its views have to be redrawn (and probably some more).
If you decide to write your own and create the possibility to modify the data shown in the list view through an adapter (one could imagine adding method like addObject(SomeObject o) in your home made adapter for example), then you'd call notifyDataSetChanged() in that method.
Similarly if you have a deleteObject(SomeObject x), if the remaining data set is larger than zero you'd call notifyDataSetChanged() or when the remaining data set is empty you'd call notifyDataSetInvalidated() which in turn will to some extra stuff like setting the so called empty view in the list if you have one specified.