android adapter notifydatasetchanged not working properly - android

I have issue when image is not loaded after notifyDataSetChanged In overall i have startup class where i begin loading images from web and save them on device. And adapter uses images saved on device. So if i open my listview before i save images i see only my placeholder. And then if i return yo app then of course imageas ar loaded because theyre saved already.
creating adapter
adapter = new GridAdapter(getActivity(), grid, 6);
grid.setAdapter(adapter);
Here i save image and notifying adapter.
try {
FileOutputStream fos = context.openFileOutput(String.valueOf(id)+".jpg", Context.MODE_PRIVATE);
fos.write(imageBytes);
fos.close();
try {
GridFragment.adapter.notifyDataSetChanged();
} catch (Exception e) {
// TODO: handle exception
}
.
And in adapter i use imageloading library but simply it just sets image from device(not async etc.)
iLoader.displayImage("file:///"+mContext.getFileStreamPath
(String.valueOf(position)).toString()+".jpg", holder.imageView, DataHolder.options, this);

generally for an Adapter, notifyDataSetChanged only works if you use the add, insert, remove, and clear functions on the Adapter.
for e.g , if you have already added references to your images in the adapter (which seems probable , because the file already exists and you are writing new data to it), then if any property of your image changes (in your case, size) , notifyDataSetChanged would not do anything. it can only detect addition, removal.
(you haven't added your GridAdapter implementation, so that is all i can think of yet)

Related

Load image via URL into RecyclerAdapter

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());

Custom SimpleCursor adapter BindView calls

I wanted to know if there is a way to make the BindView work only once per item?
Are items that we scroll away from the screen getting destroyed? and when they come back we must run bindview again?
The reason is I got my adapter to Download an image and set it on the item.
when i scroll the image is getting downloaded again, even if i didnt quit the application.
the bindview is called each time an item "returns" into display so even tho i already downloaded it, it will preform the asyntask again ( the asyntask is called in the adapter)
I even tried saving BLOB on my database after each download, to know when to call the asyntast and when just to make a bitmap out of the array
if(photoBArray != null){
Bitmap bitmap = BitmapFactory.decodeByteArray(photoBArray, 0, photoBArray.length);
holder.icon.setImageBitmap(bitmap);
}else{
holder.icon.setImageResource(R.drawable.loader);
String urlString = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=100&photoreference="
+ c.getString(c.getColumnIndex(PlacesDBHelper.PHOTO_REFERENCE_COL))+"&key=API_KEY";
Log.e("Photo REFERENCE", c.getString(c.getColumnIndex(PlacesDBHelper.PHOTO_REFERENCE_COL)));
try {
URL url = new URL(urlString); // create new URL object from url string
new ImageDownloader(holder.icon, this).execute(url);
}catch (Exception e){
Log.e("Error in url", e.getMessage());
}
}
I am using chrisbanes pulltorefresh listview as well, im not sure if thats the casue for the problem
Bottom line question: how to make an item look steady and ready when he gets back into the screen and not beeign created again? ( cause even if i dont dowloand an image and only setting a bitmap, the action of setimagebitmap might be visible on a slow phone)
Thanks in advance!
The entire point of ListView is to recycle row views. They aren't destroyed, they are passed back to the adapter to be bound with new data for another row. It is expected that getView (or bindView in the case of cursor adapters) will be called again if you scroll an item off screen far enough and then scroll it back on screen.
What you need is an image caching layer between the download task and the adapter. When bindView happens, first check the cache to see if the image is there. If it is, use it. If it's not, download it and add it to the cache, then use it.

Images in ListView search results

I have a ListActivity that launches a task to hit a web service and display the results in a ListView. Each one of the results has an image ID attached to it.
I wrote a method that will get the image IDs of the rows displayed on screen (firstVisiblePosition() to lastVisiblePosition()) and launch a task to query another web service to get the images to display for those items. I call this method when the list's scroll state becomes SCROLL_STATE_IDLE. This makes it so the user can scroll and the task to get the images for the visible rows does not execute until the scrolling stops, preventing it from looking up images for off-screen rows.
My issue is that when the results initially show in the ListView, I can't find a good way to call my method to look up which image IDs to query for. Apparently, calling this method right after calling setAdapter does not work (I'm guessing because some of the ListView's work happens asynchronously). I am using multiple Adapter's (for reasons not pertinent to this post), so I need a good way of waiting for the list items to show before I call my method to get the IDs.
Any ideas?
After you've set the adapter or called notifyDatasetChanged() on the adapter, add your "load images" code to the list's post queue as a Runnable:
list.post( new Runnable() {
#Override
public void run() {
//do stuff
}
});
If I'm understanding your question right, you're having trouble loading images over the net and performance issues; if so,
I would create a simple image cache in my Adapter such as a local but global HashMap:
private HashMap&ltString, Drawable&gt imgCache = new HashMap&ltString, Drawable&gt();
then in my getView() method, I would asynchronously (using a Thread and a Handler) load the images and save loaded images in my imgCache by assigning position as the key and loaded images as Drawables.
final Handler h = new Handler() {
public void handleMessage(Message msg) {
if(msg.obj != null) {
Drawable drawable = (Drawable)msg.obj;
image.setImageDrawable(drawable);
image.postInvalidate();
image.requestLayout();
imgCache.put(cacheKey, drawable);
}
}
};
loadImage(myImageView, imageURL, h); // threaded method which loads the images from net
also, in my getView() method I would first ask imgCache to see if the image already exist before loadImage is called.
This should optimize your list and rescue you from using multiple Adapter etc.
Hope this helps,
-serkan

Android: Possible solution for updating the custom list view at random times

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);
}
}

Improve ListView efficiency when loading images from SD into the ListView

I am using a custom adapter for my ListView as per the efficient adapter sample by Romain Guy.
In the getView() method of my adapter I am assigning an ImageView a jpg image stored on SD using the following code :
File f=new File(MovieThumbs.get(position));
if(f.length() > 0) {
holder.thumb.setImageBitmap(BitmapFactory.decodeFile(MovieThumbs.get(position)));
}
When flicking through a list of some 200 items using this method the app suffers from bad stuttering as it tries dealing with the images.
Is there a more efficient solution for this?
Rather than loading the images from within the list adapter on demand how about kicking off a thread from the onCreate of your activity to load images? As each image loads you can fire a callback to the activity to display the image in the list. The callback method would be something along the lines of:
void onImageDownloadComplete(int pos, BitMap bm) {
ListView lv = getListView();
View listItem = lv.getChildAt(pos);
ImageView img = (ImageView)listItem.getChildAt(indexOfIcon);
img.setImageBitmap(bm);
}
Images need to be processed in background thread. Recycled views need to be taken into account. I try to address all these issues in my sample code, it works fine now, you may take a look Lazy load of images in ListView

Categories

Resources