I have an API which returns JSON and I would like to load an Image from a URL which is provided by this API. The Image should be passed into Adapter for a Recycling View.
Right now all Items which contain an Imgae_URL are getting skipped by my Adapter and I dont really understand why.
if (json_img_url.isNotEmpty()) {
Executors.newSingleThreadExecutor().execute({
val conn = URL(json_img_url).openConnection()
conn.connect()
val iStream:InputStream = conn.getInputStream()
val img_bitmap:Bitmap? = BitmapFactory.decodeStream(iStream)
newItems.add(Item(....img_bitmap))
})
....
itemArrayAdapter.addItems(newItems)
URL :"https://s3.us-east-2.amazonaws.com/c...."
The URls used are valid and Images on the S3 Bucket are all public.
The If statment returns true (I checked with Log.d) but the Item does not appear on the Phone, I dont recive an error and the app does not crash its just like the Item was never there...
I know there are librarys like Picasso or Glide but even with them I could not make it work and to be honest I would like to accomplish this task without a having to install an extra package, it just feels wrong.
Unlike ListView, there is no way to add or remove items directly through the RecyclerView adapter. You need to make changes to the data source directly and notify the adapter of any changes.There are many method available to use when notifying the adapter of different changes:
notifyItemChanged(int pos) : Notify that item at position has changed.
notifyItemInserted(int pos): Notify that item reflected at position has been newly inserted.
notifyItemRemoved(int pos): Notify that items previously located at position has been removed from the data set.
notifyDataSetChanged(): Notify that the dataset has changed. Use only as last resort.
Every time we want to add or remove items from the RecyclerView, we will need to explicitly inform to the adapter of the event. Unlike the ListView adapter, a RecyclerView adapter should not rely on notifyDataSetChanged() since the more granular actions should be used. See the API documentation for more details.
Also, if you are intending to update an existing list, make sure to get the current count of items before making any changes. For instance, a getItemCount() on the adapter should be called to record the first index that will be changed.
// record this value before making any changes to the existing list
int curSize = itemArrayAdapter.getItemCount();
// update the existing list
newItems.add(Item(....img_bitmap));
// curSize should represent the first element that got added
// newItems.size() represents the itemCount
itemArrayAdapter.notifyItemRangeInserted(curSize, newItems.size());
Related
I'm new to all of this and was making good progress but I have hit a brick wall with working out how to do the next thing. I am using Kotlin and have a Fragment with an associated Recyclerview Adapter. I would like to set OnClick (On or Off) against items in a row depending upon a value in the Fragment which could change at any time.
My adapter works fine to show and update the array of data and also to implement OnClick.
I have tried sending a data element via a constructor which changed in the fragment but always showed as the initial setting in the adapter. The same with trying to call a method.
Many other questions touch on the issue but only show snippets of code, and it seems that I'm not advanced enough to get them working successfully in my code.
Could anyone please provide a pointer to a working set of Kotlin code that includes parsing a variable from fragment to adapter - perhaps in Git or a tutorial. I'm sure that if I can study a working program I can move forward. Thank you.
It would have been better if you had included your Adapter and Fragment code in the question, that would have helped us in understanding how you have setup everything and what data model are you passing to adapter.
But looking at your question, one solution that comes to my mind is to add an enabled boolean in your data model that is displayed in the ViewHolder. Using this you can set view.clickable = model.enabled. Now whenever your "value in the Fragment" changes you can update this list and let the adapter rebind items.
Note that the above solution is when you want to selectively enable/disable clicks on individual items. If you want to do this for all items at once, it's better to create a variable in adapter that you can change from the Fragment, and inside the clickListener you can check the value of that adapter variable. If it's false, just return out of the click listener. Something like,
view.setOnClickListener {
if(adapterValue) {
// handle Click
}
}
If this approach doesn't help, I would ask you to add more context in your question and show what you have done so far.
I want to update one item calling asynchronous call to server API inside onBindViewHolder. I send item at position + position into the callback. Response call arrives in 20 seconds into callback funcion. Into the callback function is returned response and position. Then I call notifyItemChanged(pos, response). And inside payload onBindViewHolder I update item on that position and its View. Problem is that if async request takes 20 seconds and user will scroll quickly throught RecyclerView it will update wrong item View because that view at position could be unbound or could be bound to other item from list.
Is there any way how to track which item at which position is unbound to cancel request? I have to update this one info after adapter is created, because if I will do that before, user will not see any recyclerView item for almost 2 minutes till my list is ready. (only this one call is taking so long).
I wouldn't pass the position as context. I would pass an identifier. Could be an internal integer as id or something. Then you can easily determine which id's are currently visible on the screen and see if that list contains the id of the calback.
I am not sure about your question, but you could work onto your items directly in your adapter.
Then you do:
Adapter adapter = new BaseAdapter(newDataset);
recyclerView.setAdapter(adapter);
recyclerView.notifyDataSetChanged();
Otherwise, if your really work on views rather than dataset, you can do this:
in your adapter:
create a View array
create a custom function View getViewAtPos(int pos)
check if your view is visible in your current scrollview (here)
I have implemented a firebase list adapter to load a list of items (List A). My data structure is setup such that within List A, each item also contains a reference id to some information located somewhere else (isNice) in the database. Likewise:
ListA
- ObjA
- title : "hi"
- id : "ObjAid"
isNice
- ObjAid : "true"
I'm currently using another database operation to look up the id in the "isNice" child, passing the "ObjAid", the list position, and then a resultReceiver to get the result back. My problem is, when the resultReceiver get a resultData (the value "true"), I have no idea how modify the data within firebase list at the specific position.
My past experience is to use load my data into my own ArrayList and create a custom adapter for the listView, in that case, I could easily update the populated view as extra information is loaded. I would like to avoid setting up my own ArrayList to store the data for the time being, favoring the simplicity of the FirebaseListAdapter. All tips are appreciated, thx :)
After some trial and error, I've instead gone with the RecyclerView + FirebaseRecyclerViewAdapter approach. Using recycler is better for the long run too, in my use case the list could get to 3K+. Using RecyclerView.findViewHolderForAdapterPosition() I can reference the specific itemView after I get the data from the result receiver likewise:
// Reference the Item's view at the specific position
myViewHolder itemViewHolder = recyclerView.findViewHolderForAdapterPosition(position);
// Reference and set the views and here......
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.
I am having a situation where I want to update my Custom List View using BaseAdapter whenever my Database is updated. I have tried calling invalidate() on this Custom List but it didn't work, similarly I even tried having a timer to update my list after sometime, that didn't work either. Please let me know of possible solution.
Update:
This is how I am making my custom list view
li= (ListView)findViewById(R.id.id_lv_row);
ColorDrawable divcolor = new ColorDrawable(Color.DKGRAY);
registerForContextMenu(li);
li.setDivider(divcolor);
li.setDividerHeight(2);
li.setAdapter(new FriendsPositionAdapter(this));
BaseAdapter.notifyDataSetChanged() should do the trick as long as the data behind the adapter actually changed. That's all you need to do to refresh the list.
Invalidate is for repainting views only, you have to tell to the List adapter (BaseAdapter) that dataset has changed.
When the data changes, asign the new dataset to the adapter, and later call notifyDataSetChanged()...
in order to make functional notifyDataSetChanged() the adapter data must be changed. Remember that the original data that change is not reflected automatically to the adapter.
//here i retrieve the new list, named "beans"
lista = (BeanList) result.getDataObject();
Vector<Bean>beans = list.getBeanList();
((BeanListAdapter)listAdapter).syncData(beans);
((BeanListAdapter)listAdapter).notifyDataSetChanged();
//now the syncData method
public void syncData( List<PINPropiedad> newData ){
for(Object o : newData){
add(o);
}
}