Im trying to efficiently load my local drawables (from my res>drawable folders) into my list items. I want this to use as little memory as possible while loading my images.
I originally had my resource name (R.drawable.list_icon) defined as an int in the model and calling setImageResource in my adapter to apply the icon.
However after reviewing the API documentation, it suggests i use setImageDrawable or setImageBitmap since they are not ran on the UI thread. I need to know if this the following code below, where i've converted the drawable to a bitmap and used setImageBitmap is a good method and if loading the local image in a AsyncTask is worth incorporating (since i've seen this method used as well).
Model
public class ProductItem {
public int listIcon;
public String listTitle;
public String listDesc;
public ProductItem(int listIcon, String listTitle,
String listDesc) {
this.listIcon = listIcon;
this.listTitle = listTitle;
this.listDesc = listDesc;
}
public int getIconID() {
return listIcon;
}
}
Adapter
public class ProductAdapter extends ArrayAdapter<ProductItem> {
Context context;
int layoutResourceID;
ProductItem data[];
ProductHolder viewHolder;
public ProductAdapter(Context context, int layoutResourceID, ProductItem[] data) {
super(context, layoutResourceID, data);
this.layoutResourceID = layoutResourceID;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(layoutResourceID, parent, false);
viewHolder = new ProductHolder();
viewHolder.productType = (TextView) convertView.findViewById(R.id.itemTitle);
viewHolder.productDesc = (TextView) convertView.findViewById(R.id.itemSubtitle);
viewHolder.productImage = (ImageView) convertView.findViewById(R.id.itemIcon);
convertView.setTag(viewHolder);
} else {
viewHolder = (ProductHolder) convertView.getTag();
}
ProductItem objectItem = data[position];
if (objectItem != null) {
viewHolder.productType.setText(objectItem.listTitle);
viewHolder.productType.setTag(objectItem.listTitle);
viewHolder.productDesc.setText(objectItem.listDesc);
viewHolder.productDesc.setTag(objectItem.listDesc);
// Im currently using the following method for obtain the resource.
InputStream is = context.getResources().openRawResource(objectItem.getIconID());
final Bitmap imageBitmap = BitmapFactory.decodeStream(is);
viewHolder.productImage.setImageBitmap(imageBitmap);
// Im using 'setImageBitmap' since it doesnt run on the UI thread, hoping that this will in return load quicker.
// However i was previously using the following.
// viewHolder.productImage.setImageResource(objectItem.getIconID());
viewHolder.productImage.setTag(objectItem.listIcon);
}
return convertView;
}
static class ProductHolder {
public TextView productType;
public TextView productDesc;
public ImageView productImage;
}
}
Usage
listData[0] = new ProductItem(R.drawable.list_icon, getString(R.string.list_title), getString(R.string.list_description));
My main question here is what is the most efficient way to load local drawables, These image icon are pretty high quality and not just simple 1 color icons, they are actual product images.
Try to use UniversalImageLoader. It works well not only for loading images from network.
Related
Hello I am new to Fresco and I am trying to load all the images in my phone to the app
when I use this URI: /storage/emulated/0/DCIM/Camera/20200206_222309.jpg
with this code:
ControllerListener listener = new BaseControllerListener();
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(xuri)
.setTapToRetryEnabled(true)
.setControllerListener(listener)
.build();
photosFragmentRecycler.setController(controller);
only the photo with the name 20200206_222309.jpg is loaded
how can I load all the images?
thanks in Advance
Your question doesn't depend on the image library used. You basically want to get a list of all camera images.
Something like this should get you the list of all URIs:
fun getMediaStoreUris(context: Context): List<Uri> {
val uris = mutableListOf<Uri>()
context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Images.Media._ID),
null,
null,
null)?.use {
val dataIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
while (it.moveToNext()) {
uris.add(
ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
it.getLong(dataIndex)))
}
}
return uris
}
Then, you can just create a simple RecyclerView adapter and ViewHolder and load images with Fresco:
public class SimpleAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
private List<Uri> mUris;
SimpleAdapter(List<Uri> uris) {
mUris = uris;
setHasStableIds(true);
}
#Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.drawee_recycler_item, parent, false);
return new SimpleViewHolder(itemView);
}
#Override
public void onBindViewHolder(SimpleViewHolder holder, int position) {
holder.mSimpleDraweeView.setImageURI(mUris.get(position));
}
#Override
public int getItemCount() {
return mUris.size();
}
#Override
public long getItemId(int position) {
return mUris.get(position).hashCode();
}
public void setData(List<Uri> uris) {
mUris = uris;
notifyDataSetChanged();
}
}
static class SimpleViewHolder extends RecyclerView.ViewHolder {
private final SimpleDraweeView mSimpleDraweeView;
SimpleViewHolder(View itemView) {
super(itemView);
mSimpleDraweeView = itemView.findViewById(R.id.drawee_view);
}
}
and use the adapter:
final SimpleAdapter adapter = new SimpleAdapter(getMediaStoreUris(context));
recyclerView.setAdapter(adapter);
Fresco has a full sample in their Showcase sample application here: https://github.com/facebook/fresco/blob/9fde53f963d07495118c61ea2d9a3fce575afeb7/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/drawee/DraweeRecyclerViewFragment.java
This is going to work if you allow permission to access local storage, there's a guide here or to test it, by simply going to "App Info" for your app and manually allowing it.
This is a very simple solution and in practice, you probably want to implement pagination etc. for the media store. But this should guide you in the right direction.
I have a parsed XML which I have read data from there. I have manually declared some data and the other data will be saved by the user.
I have declared with glide but it is not getting the right icon or when there is no internet the icons are hidden.
The declared the xml they have the icons at the drawable and when I parse they are readable and I can show.
What I want is there any option to take the favicon and save to drawable or to an internal storage or cache so I can read even if I dont have internet.
The items which are declared manually and parsed for them take the icon from drawable but for the others take the favicon from url.
Here is what I have tried so far.
The Pojo.class
public class Bookmark implements Parcelable, Comparable {
String name, id, nativeUrl, searchUrl;
long db_id;
int icon;
int viewType;
// Constructor, getters, setters & other default functions are omitted for simplicity.
}
The Adapter.class
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
ArrayList<Bookmark> arrayList;
BookmarkDB bookmarkDB;
public static final int ITEM_TYPE_ONE = 0;
public static final int ITEM_TYPE_TWO = 1;
boolean connected = false;
String BASE_URL = "https://besticon-demo.herokuapp.com/icon?url=";
public MyAdapter(Context context, ArrayList<Bookmark> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
private boolean switchOnOff;
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = null;
if (viewType == ITEM_TYPE_ONE) {
view = LayoutInflater.from(context).inflate(R.layout.grid_item, parent, false);
return new ViewHolder(view);
} else if (viewType == ITEM_TYPE_TWO) {
view = LayoutInflater.from(context).inflate(R.layout.add_bookmark, parent, false);
return new ButtonViewHolder(view);
} else {
return null;
}
}
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, final int position) {
final int itemType = getItemViewType(position);
if (itemType == ITEM_TYPE_ONE) {
final ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.tvName.setText(arrayList.get(position).getName());
RequestOptions requestOptions = new RequestOptions();
bookmarkDB = new BookmarkDB(context);
viewHolder.tvIcon.setImageResource(arrayList.get(position).getIcon());
Glide.with(context)
.load(BASE_URL+arrayList.get(position).getSearchUrl() + "&size=32;")
.apply(requestOptions)
.into(viewHolder.tvIcon);
viewHolder.tvIcon.setImageResource(arrayList.get(position).getIcon());
viewHolder.tvId.setText(arrayList.get(position).getId());
viewHolder.tvSearchUrl.setText(arrayList.get(position).getSearchUrl());
} else if (itemType == ITEM_TYPE_TWO) {
ButtonViewHolder buttonViewHolder = (ButtonViewHolder) holder;
buttonViewHolder.imgButton.setImageResource(arrayList.get(position).getIcon());
}
}
#Override
public int getItemViewType(int position) {
// Based on you list you will return the ViewType
if (arrayList.get(position).getViewType() == 0) return ITEM_TYPE_ONE;
else return ITEM_TYPE_TWO;
}
}
Ass you can see at the Adapter.class
// This get the icon from drawable and it sets to parsed array.
viewHolder.tvIcon.setImageResource(arrayList.get(position).getIcon());
// This gets the icon from URL and sets it to the parsed array but only when the user has internet.
Glide.with(context)
.load(BASE_URL+arrayList.get(position).getSearchUrl() + &size=32;")
.apply(requestOptions)
.into(viewHolder.tvIcon);
This is the declared XML which I have parsed, the file it is declared under res/xml/bookmarks.xml
<Bookmarks>
<Bookmark name="Bing" hidden="" icon="bing" id="0" nativeUrl="" searchUrl="https://www.bing.com" />
<Bookmark name="Google" hidden="" icon="google" id="1" nativeUrl="" searchUrl="https://www.google.com" />
<Bookmark name="Youtube" hidden="" icon="youtube" id="2" nativeUrl="" searchUrl="http://m.youtube.com" />
</Bookmarks>
So if I have understood correctly, you have two problems.
The default icon is not showing (i.e. hidden) when there is no internet.
You want to show the images once loaded even if there is no internet.
Both of these two can be achieved using Glide with minimal change in your code. Just change the image loading portion of your code using Glide like the following.
String imageUrl = BASE_URL + arrayList.get(position).getSearchUrl() + "&size=32";
Glide.with(context)
.load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.placeholder)
.into(viewHolder.tvIcon);
DiskCacheStrategy.ALL manages the caching of your images and should load the images even if the device is offline. Setting the placeholder using the .placeholder(R.drawable.placeholder) can be used, when the images are not loaded for internet problem and hence you want to show a default image instead.
If you want to read more about the caching strategies, this link can help as well.
Update 1
Based on your comment, I think you should have an if statement here. When the drawable is available locally, then you will use the setImageResource function, else use Glide to load the image.
Update 2
You do not have to save the images locally if you are using Glide with DiskStrategy.ALL. It will cache the images and will show automatically when the internet connection is not available. I would like to suggest you to remove the manual setup.
Update 3
If you want to use some of the icons from your own drawable, then you might consider putting something like this.
if (theIconIsAvailableInDrawable) showThatFromDrawable();
else useGlideToShowTheIconFromUrlUsingDiskCacheStrategy();
Hope you get the idea.
I can download image and display it in ListView. But the problem I am facing is,
When I am loading the images they all get loaded in the first row of the list. It shows being loaded one by one in the first row. While the other rows hold the default image. Its look weird. What to do. The code below:
private class simpsync extends AsyncTask<String, Integer , Bitmap>{
private final WeakReference imageViewReference;
simpsync(ImageView iv){
//imageView=iv;
imageViewReference=new WeakReference(iv);
}
#Override
protected Bitmap doInBackground(String... param) {
Bitmap bmp=CommonFunctions.overlay(CommonFunctions.loadUrlBitmap(param[0]));
return bmp;
}
protected void onPostExecute(Bitmap bitmap) {
//imageView.setImageBitmap(result);
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = (ImageView) imageViewReference.get();
if (imageView != null) {
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
}
This code is the getView function of class BaseAdapter
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ViewHolder holder;
if(convertView==null){
/****** Inflate tabitem.xml file for each row ( Defined below ) *******/
//list_book_detail_entry
if(requestType==SearchAndIndex.SEARCH_IN_SEPAERATE)
{
vi = inflater.inflate(R.layout.list_book_detail_buy, parent, false);
//vi = inflater.inflate(R.layout.list_book_detail_buy, null);
}
else{
vi = inflater.inflate(R.layout.list_book_detail_entry, parent, false);
//vi = inflater.inflate(R.layout.list_book_detail_entry, parent, false);
}
/****** View Holder Object to contain tabitem.xml file elements ******/
holder = new ViewHolder();
holder.bookTitle=(TextView)vi.findViewById(R.id.BookTitle);
holder.writer = (TextView) vi.findViewById(R.id.WriterName);
holder.imageUrl=(ImageView)vi.findViewById(R.id.ImageUrl);
holder.isbn=(TextView)vi.findViewById(R.id.BookISBN);
holder.serialNumber=(TextView)vi.findViewById(R.id.BookSerialNumber);
holder.availabilityView=(ImageView)vi.findViewById(R.id.AvailabilityView);
holder.publisher=(TextView)vi.findViewById(R.id.Publisher);
holder.publishingDate=(TextView)vi.findViewById(R.id.PublishingDate);
/************ Set holder with LayoutInflater ************/
vi.setTag( holder );
}
else
holder=(ViewHolder)vi.getTag();
if(data.size()<=0)
{
holder.bookTitle.setText("--");
holder.writer.setText("--");
holder.publisher.setText("--");
holder.publisher.setText("----+--+--");
}
else
{
tempValues=null;
tempValues = ( BookDetailsStruct ) data.get( position );
holder.writer.setText( tempValues.Writer );
holder.publisher.setText(tempValues.Publisher);
holder.publishingDate.setText(tempValues.getIssueDetail(0).publishingDate);
simpsync sp=new simpsync(holder.imageUrl);
if(requestType==SearchAndIndex.SEARCH_IN_SEPAERATE)
{
if(tempValues.getIssueDetail(0)!=null)
{
String toAdd;
if(tempValues.getIssueDetail(0).serialNumber==-1)
toAdd="";
else
toAdd=" [ 巻"+tempValues.getIssueDetail(0).serialNumber+" ]";
holder.bookTitle.setText( tempValues.BookName+toAdd);
sp.execute(tempValues.getIssueDetail(0).smallImageUrl);
}
}
else{
if(tempValues.largetNumberIndex!=-1)
{
String toAdd;
if(tempValues.getIssueDetail(tempValues.largetNumberIndex).serialNumber==-1)
toAdd="";
else
toAdd=" ("+tempValues.getIssueCount()+"巻)";
holder.bookTitle.setText( tempValues.BookName+toAdd);
sp.execute(tempValues.getIssueDetail(tempValues.largetNumberIndex).smallImageUrl);
}
else{
holder.bookTitle.setText( tempValues.BookName);
sp.execute(tempValues.getIssueDetail(0).smallImageUrl);
}
}
vi.setOnClickListener(new OnItemClickListener( position ));
}
return vi;
}
if you have any further question, please let me know
The "problem" is the recycle behavior of ListView. You are not respecting it enough. When you scroll down and a View disappears on the top, it will be reused at the bottom (thats why you use the ViewHolder pattern, thats good). But you also start a asynchronous task and give it the ImageView and to hold onto it. Since the whole view of the row (and with that the imageview) gets recycled, it wont be eligible for garbage collection, thus the asynctask has a valid ImageView to display the image once its finished.
To correct your code, I suggest you simply adapt what is written on the android developer page, it nearly is copy-past-ready code for you to use:
Load Bitmaps into a GridView Implementation
You can also use 3rd party libraries, because other smart people have also faced this problem and came up with good solutions:
Glide
Picasso
They both have a very (very) simple Api to get things done and they are both highly efficient and tunable, with already good settings by default.
You can use Picasso for loading images into ListView
#Override
public void getView(int position, View convertView, ViewGroup parent) {
SquaredImageView view = (SquaredImageView) convertView;
if (view == null) {
view = new SquaredImageView(context);
}
String url = getItem(position);
Picasso.with(context).load(url).into(view);
}
OR
#Override
public void getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ImageView imgView = v.findViewById(R.id.someImageView);
String url = getItem(position);
Picasso.with(context).load(url ).into(imgView);
}
Many common pitfalls of image loading on Android are handled automatically by Picasso:
Handling ImageView recycling and download cancelation in an adapter.
Complex image transformations with minimal memory use.
Automatic memory and disk caching.
In one of my android activity, I've a ListView lvFeedsList.
Each row element in the listView will contain 3 textViews - RSSFeedName, publishDate & feedLength
The contents of the feeds is retrived from a HTTPRsponse.
I'm fetching this response in an AsyncTask.
So, in the doInBackground(), I've send the HTTPRequest & received & parsed the response & prepared the ArrayList containing 3 above mentioned information.
Then inside the doInBackground() only, I'm creating the customized ArrayAdapter for forming the 3 TextViews in row element.
My intetions are to set this adapter on ListView in onPostExecute().
But, when I run the application, the ListView does not display anything.
I tried to debug & it seems like getView() in the ArrayAdapter class is not getting called. (But I'm not sure if this is the reason).
Here is the code, sorry for the length...it seemed necessary.
Activity Code:
public class GenericFeedsActivity extends Activity{
private ListView lvFeedsList;
private ArrayList<FeedsClass> feedList;
protected void onCreate(Bundle savedInstanceState) {
...
lvFeedsList = (ListView) findViewById(R.id.lvFeedsList);
lvFeedsList.setOnItemClickListener(this);
lvFeedsList.setEnabled(false);
...
new AsyncResponseHandler(this).execute();
}
class AsyncResponseHandler extends AsyncTask {
Context context;
FeedListAdapter adapter;
public AsyncResponseHandler(Context c) {
this.context = c;
}
#Override
protected Object doInBackground(Object... params) {
...
/*
* Sending HTTPRequest to a URL & getting list of feeds
* Saving this list of feeds in a ArrayList -feedList, containing elements of type FeedsClass (declared above)
* Below line parses the HTTPResponse XML & stores various information in feedList.
*/
feedList = utils.parseRssResponseXML(in); // Working fine, geeting elements
adapter = new FeedListAdapter(
GenericFeedsActivity.this, feedList);
in.close();
return null;
}
protected void onPostExecute(Object e) {
// Setting Arrayadapter
lvFeedsList.setAdapter(adapter);
lvFeedsList.setEnabled(true);
}
}
}
Adapter Code:
public class FeedListAdapter extends ArrayAdapter {
private Context context;
private ArrayList<FeedsClass> feedList;
public FeedListAdapter(Context c, ArrayList<FeedsClass> data) {
super(c, R.layout.rowlayout);
this.context = c;
this.feedList = data;
}
class ViewHolder {
TextView tvFeedName;
TextView tvFeedPubDate;
TextView tvFeedLength;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if (row == null) {
LayoutInflater inflator = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflator.inflate(R.layout.rowlayout, null);
holder = new ViewHolder();
holder.tvFeedName = (TextView) row.findViewById(R.id.tvFeedName);
holder.tvFeedPubDate = (TextView) row.findViewById(R.id.tvFeedPubDate);
holder.tvFeedLength = (TextView) row.findViewById(R.id.tvFeedLength);
row.setTag(holder);
} else {
holder = (ViewHolder) row.getTag();
}
// Getting values of feedName, publishDate & feedLength
String feedName = feedList.get(position).getTitle();
String feedDate = feedList.get(position).getPublishDate();
String feedLength = feedList.get(position).getStreamLength();
holder.tvFeedName.setText(feedName);
holder.tvFeedPubDate.setText(feedDate);
holder.tvFeedLength.setText(feedLength);
}
return row;
}
}
The issue is that you are subclassing ArrayAdapter. This doesn't work because ArrayAdapter internally thinks you do not have any elements in your data; it doesn't just magically know to look in the lvFeedsList variable because the data set it uses is internal.
Instead, in your constructor make sure to call this constructor instead:
Adapter code:
public FeedListAdapter(Context c, ArrayList<FeedsClass> data) {
super(c, R.layout.rowlayout, data); // add 'data'
this.context = c;
this.feedList = data;
}
Which will make everything work correctly.
adapter.notifyDataSetChanged()
could help at the end on of AsyncResponseHandler.onPostExecute(). If not - check whether ArrayList which hold data for adapter is empty or not.
I've been evaluating NOSTRA's Universal-Image-Loader library to asynchronously download images and show them in ListView. So far it works fine except for one problem.
Sometimes Bitmaps from memory cache get attached to wrong ImageViews when the list is being scrolled. After scrolling is stopped, correct images are attached. This situation is quite rare and I couldn't find a 100% way to reproduce it. I shot a video last time it happened.
Here is the ArticleAdapter code, both the UIL config and the bindView() method can be found there.
public class ArticleAdapter extends CursorAdapter {
private LayoutInflater inflater;
private ViewHolder holder;
public ArticleAdapter(Context context, Cursor cursor, boolean autoRequery) {
super(context, cursor, autoRequery);
imageLoader = ImageLoader.getInstance();
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.download_progress_thumb)
.cacheInMemory()
.cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.build();
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(context)
.threadPriority(Thread.NORM_PRIORITY - 2)
.threadPoolSize(4)
.discCache(new UnlimitedDiscCache(Utils.getCacheDirectory(context)))
.defaultDisplayImageOptions(options)
.build();
imageLoader.init(configuration);
titleIndex = cursor.getColumnIndex(Articles.TITLE);
descriptionIndex = cursor.getColumnIndex(Articles.DESCRIPTION);
isUnreadIndex = cursor.getColumnIndex(Articles.IS_UNREAD);
isNewIndex = cursor.getColumnIndex(Articles.IS_NEW);
urlIndex = cursor.getColumnIndex(Articles.URL);
hostIndex = cursor.getColumnIndex(Articles.HOST);
timeIndex = cursor.getColumnIndex(Articles.PUBLISH_TIME);
bkgUnreadArticle = context.getResources().getColor(R.color.list_bkg_unread_article);
bkgReadArticle = context.getResources().getColor(R.color.list_bkg_read_article);
textUnreadTitle = context.getResources().getColor(R.color.list_text_unread_title);
textReadTitle = context.getResources().getColor(R.color.list_text_read_title);
inflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
String date = Utils.format(cursor.getLong(timeIndex), Utils.DATE);
holder = (ViewHolder) view.getTag();
holder.titleView.setText(cursor.getString(titleIndex));
holder.descriptionView.setText(date);
int isNew = cursor.getInt(isNewIndex);
if (isNew == 1)
holder.isNewView.setVisibility(View.VISIBLE);
else
holder.isNewView.setVisibility(View.INVISIBLE);
int isUnread = cursor.getInt(isUnreadIndex);
if (isUnread == 1){
holder.titleView.setTextColor(textUnreadTitle);
holder.rowLayout.setBackgroundColor(bkgUnreadArticle);
} else {
holder.titleView.setTextColor(textReadTitle);
holder.rowLayout.setBackgroundColor(bkgReadArticle);
}
String url = cursor.getString(urlIndex);
String host = cursor.getString(hostIndex);
if (host.equalsIgnoreCase(Consts.HOST_LENTA) || host.equalsIgnoreCase(Consts.HOST_REALTY)) {
holder.thumbView.setVisibility(View.VISIBLE);
imageLoader.displayImage(Utils.makeImageUrl(url, Utils.THUMBNAIL), holder.thumbView);
} else
holder.thumbView.setVisibility(View.GONE);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
holder.thumbView = (ImageView) v.findViewById(R.id.list_thumb);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
}
I would really appreciate any help on this matter.
For ListViews, GridViews and other lists which are used view re-using in its adapters you should call .resetViewBeforeLoading() in DisplayImageOptions to prevent this effect.
Also documentation says:
Init ImageLoader with configuration only once
Do you do it only once? Adapter's constructor isn't good place for it.
UPD: Sorry, my answer isn't useful. .resetViewBeforeLoading() doesn't help because you use .showStubImage(...). So you should have correct UIL work but you don't. And it's very strange.
I had this problem on a regular basis, even though I was only initiating the ImageLoader once, I wasn't doing it only when I needed it (in the adaptor), after I changed the init() part in Application class it worked brilliantly. I haven't even had to use restartViewOnLoading() or setStubImage(). Here's the code if necessary.
import android.content.Context;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
public class Application extends android.app.Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
DisplayImageOptions imgOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.showImageOnLoading(R.drawable.default_picture)
.build();
ImageLoaderConfiguration imgConfig = new ImageLoaderConfiguration.Builder(mContext)
.defaultDisplayImageOptions(imgOptions)
.build();
ImageLoader.getInstance().init(imgConfig);
}
public static Context getAppContext(){
return mContext;
}
}
EDIT: You can check this conversation here for a deeper understanding of the issue.
Basically there are 3 solutions
1) Set android:layout_width and android:layout_height parameters for ImageViews in dips ('wrap_content' and 'match_parent' are not acceptable)
2) Call ImageLoader after ImageView was drawn (in imageView.post(...):
imageView.post(new Runnable() {
#Override
public void run() {
imageLoader.displayImage(imageUri, imageView);
}
});
3) Pass ImageViewAware (instead of ImageView) which doesn't consider actual view size:
Intead of:
imageLoader.displayImage(imageUri, imageView);
do following:
ImageAware imageAware = new ImageViewAware(imageView, false)
imageLoader.displayImage(imageUri, imageAware);
Just see how to set Holders because I think you have written faulty logic inside your Adapter thats why it is repeating views.
There is also Custom Cursor Adapter with Holder and Get View & BindView discussion.
Add this line in your code ::
holder.thumbView.setTag(Utils.makeImageUrl(url, Utils.THUMBNAIL).get(position));
imageLoader.displayImage(Utils.makeImageUrl(url, Utils.THUMBNAIL), view_holder.image);
I have same problem and fixed it. It is not because Universal-Image-Loader library. It is because you use holder in wrong logic to load image.
Try to replace
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
holder.thumbView = (ImageView) v.findViewById(R.id.list_thumb);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
With
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
ImageView thumbView = (ImageView) v.findViewById(R.id.list_thumb);
imageLoader.displayImage("Your image URL", thumbView);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
And remember to remove imageloader in your bindView function