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
Related
I have been struggling with this issue for quite a while now, so hoping somebody will be able to lend a hand.
Essentially I have an ArrayAdapter that feeds an ArrayList of CourseCardModel objects to a Custom Swipeable View that extends AdapterView.
I can instatiate the adapter and view first time round absolutely fine, with Course Cards showing up and being able to swipe, however when I attempt to change the ArrayList of CourseCardModels associated with the ArrayAdapter, the view does not change in accordance with the new data, it just freezes.
The reason the data attached to the adapter needs to change is due to the fact that I want the user to be able to filter the Course Cards, so for example they can view cards only from Cambridge University.
Any help with this issue would be hugely appreciated, I have scoured SO for similar issues but have yet to find a solution that worked for me.
ArrayAdapter Code
public class CustomCardAdapter extends ArrayAdapter {
private TextView courseName, uniName, entryStandards, courseDuration, studyMode, qualification,
studentSatisfaction, gradProspects, t1, t2, t3, t4, t5, t6;
ArrayList<CourseCardModel> items;
View v;
LayoutInflater vi;
public CustomCardAdapter(Activity context, int resource, ArrayList<CourseCardModel> courses) {
super(context, resource, courses);
this.items = courses;
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
v = convertView;
if (v == null) {
v = vi.inflate(R.layout.course_card_inner_template, parent , false);
}
CourseCardModel c = (CourseCardModel) getItem(position);
if (c != null) {
courseName = (TextView) v.findViewById(R.id.courseCardCourseName);
uniName = (TextView) v.findViewById(R.id.courseCardUniName);
entryStandards = (TextView) v.findViewById(R.id.courseCardEntryStandards);
courseDuration = (TextView) v.findViewById(R.id.courseCardCourseDuration);
studyMode = (TextView) v.findViewById(R.id.courseCardStudyMode);
qualification = (TextView) v.findViewById(R.id.courseCardQualification);
studentSatisfaction = (TextView) v.findViewById(R.id.courseCardStudentSatisfaction);
gradProspects = (TextView) v.findViewById(R.id.courseCardGraduateProspects);
t1 = (TextView) v.findViewById(R.id.cardTV1);
t2 = (TextView) v.findViewById(R.id.cardTV2);
t3 = (TextView) v.findViewById(R.id.cardTV3);
t4 = (TextView) v.findViewById(R.id.cardTV4);
t5 = (TextView) v.findViewById(R.id.cardTV5);
t6 = (TextView) v.findViewById(R.id.cardTV6);
v.setBackgroundResource(R.drawable.newcard);
courseName.setText(c.getCourse().getCourseName());
uniName.setText(c.getCourse().getUniversity());
entryStandards.setText(c.getCourse().getEntryStandards());
courseDuration.setText(c.getCourse().getCourseDuration());
studyMode.setText(c.getCourse().getStudyMode());
qualification.setText(c.getCourse().getQualification());
gradProspects.setText(c.getCourse().getGradProspects() + " / 100");
studentSatisfaction.setText(c.getCourse().getStudentSatisfaction() + " / 5");
}
if(position ==0)
{
//float alpha = (float) 0.8;
//v.setAlpha(alpha);
courseName.setVisibility(View.VISIBLE);
}
else if (position == 1){
// Prepare the View for the animation
v.setVisibility(View.VISIBLE);
float alpha = (float) 0.8;
float alpha2 = (float) 0.3;
courseName.setAlpha(alpha2);
uniName.setAlpha(alpha2);
entryStandards.setAlpha(alpha2);
courseDuration.setAlpha(alpha2);
studyMode.setAlpha(alpha2);
qualification.setAlpha(alpha2);
studentSatisfaction.setAlpha(alpha2);
gradProspects.setAlpha(alpha2);
t1.setAlpha(alpha2);
t2.setAlpha(alpha2);
t3.setAlpha(alpha2);
t4.setAlpha(alpha2);
t5.setAlpha(alpha2);
t6.setAlpha(alpha2);
v.setAlpha(alpha);
}
else {
v.setVisibility(View.INVISIBLE);
}
return v ;
}
}
Code that attempts to alter the data
// Set up and assign card adapter
ca = new CustomCardAdapter(CardsActivity.this, android.R.layout.simple_list_item_1, generateCourseCards());
flingContainer.init(CardsActivity.this, ca);
// Update the data in the adapter
ca.clear();
ca.addAll(coursesToReturn);
ca.notifyDataSetChanged();
GenerateCourseCards() Method returns the initial list of CourseCardModel objects to populate the view.
there are situations where the update works out of the box and there are situations where it does not. for the non working cases it helped to reassign the adapter to it-s displayelement
in viewPager i used
mAdapter.notifyDataSetChanged();
mViewPager.setAdapter(mAdapter);
in gridview and listview i used
mGridView.setAdapter(mAdapter);
mList.setAdapter(mDataAdapter);
I created the following CursorAdapter which shows messages from my SQL database, everything is added well until I scroll the list, I know that the objects are recycled, but in a wrong way. Here is my CursorAdapter class:
public class ChatAdapter extends CursorAdapter {
public ChatAdapter(Context context, Cursor cursor, int flags) {
super(context, cursor, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.chat_item, parent,
false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// Find fields to populate in inflated template
TextView left = (TextView) view.findViewById(R.id.lefttext);
TextView right = (TextView) view.findViewById(R.id.righttext);
LinearLayout rightBubble = (LinearLayout) view
.findViewById(R.id.right_bubble);
LinearLayout leftBubble = (LinearLayout) view
.findViewById(R.id.left_bubble);
TextView leftDate = (TextView) view.findViewById(R.id.leftdate);
TextView rightDate = (TextView) view.findViewById(R.id.rightdate);
// Extract properties from cursor
String from = cursor.getString(cursor.getColumnIndexOrThrow("from"));
String txt = cursor.getString(cursor.getColumnIndexOrThrow("message"));
String date = cursor.getString(cursor.getColumnIndexOrThrow("t"));
String id = cursor.getString(cursor.getColumnIndexOrThrow("id"));
// Parse time
long datevalue = Long.valueOf(date) * 1000;
Date dateformat = new java.util.Date(datevalue);
String convert = new SimpleDateFormat("HH:mm").format(dateformat);
// Populate fields with extracted properties
if (from.equals("me")) {
right.setText(txt);
left.setText("");
rightBubble
.setBackgroundResource(R.drawable.balloon_outgoing_normal);
leftBubble.setBackgroundDrawable(null);
rightDate.setText(convert);
leftDate.setVisibility(View.GONE);
}
else {
left.setText(txt);
right.setText("");
leftBubble
.setBackgroundResource(R.drawable.balloon_incoming_normal);
rightBubble.setBackgroundDrawable(null);
leftDate.setText(convert);
rightDate.setVisibility(View.GONE);
}
}
}
Unfortenately, after scrolling the list, dates from the rightDate and leftDate dissapears after moving back. I think it't due the .setVisibility(View.GONE)
Any suggestions to fix this?
when the view is recycled, it is in the previous state, android did not clear the status for you.
To fix your problem, you have to set the view in question to VISIBLE when needed
Edit:
like this, add the 2 lines
if (from.equals("me")) {
// your original code
rightDate.setVisibility(View.VISIBLE); //add this
}
else {
// your original code
leftDate.setVisibility(View.VISIBLE); //add this
}
I'm parsing a web service to display in a listView backed by my own subclass of ArrayAdapter. The data is static ArrayList<Wait> in Application.java. You'll see it referenced by App.getWaits().
I use a simple refresh method for when there's new data. I've confirmed that it's being updated but it only renders if I navigate away and then return to the view.
In the past I've been able to refresh the listView by calling notifyDataSetChanged() on the adapter but right now none of these have worked for me. Thanks for taking a look... any ideas!?
//1 This is how I'd normally update the listView dynamically, but not tonight.
adapter.notifyDataSetChanged();
//2 It's the same thing really, so no good.
((WaitAdapter) list.getAdapter()).notifyDataSetChanged();
//3 Saw this as the answer to a similar question, doesn't work.
adapter.getWaits().clear();
adapter.getWaits().addAll(App.getWaits());
adapter.notifyDataSetChanged();
//4 Called in onCreate but tried a 2nd time in refresh() to manually reset adapter, doesn't work.
adapter = new WaitAdapter(getHost().getApplicationContext(), App.getWaits());
list.setAdapter(adapter);
//5 Kinda the same thing, new adapter, reset adapter... also no good.
WaitAdapter adapter = new WaitAdapter(getHost().getApplicationContext(), App.getWaits());
list.setAdapter(adapter);
//6 I read ArrayAdapter keeps its own reference to initial data object but this fails too.
adapter = null;
adapter = new WaitAdapter(getHost().getApplicationContext(), App.getWaits());
list.setAdapter(adapter);
*Update to share my WaitAdapter.java.
public class WaitAdapter extends ArrayAdapter<Wait> {
private LayoutInflater inflater;
private ArrayList<Wait> waits;
public WaitAdapter(Context context, ArrayList<Wait> data) {
super(context, R.layout.list_item_wait, data);
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
waits = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_item_wait, parent, false);
holder = new ViewHolder();
holder.checkpointName = (TextView) convertView.findViewById(R.id.checkpointName);
holder.delayAmount = (TextView) convertView.findViewById(R.id.delayAmount);
holder.timeReported = (TextView) convertView.findViewById(R.id.timeReported);
holder.dateReported = (TextView) convertView.findViewById(R.id.dateReported);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Wait wait = waits.get(position);
holder.checkpointName.setText(wait.getName());
holder.delayAmount.setText(wait.getDelayInMinutes());
holder.timeReported.setText(wait.getTimeLabel());
holder.dateReported.setText(wait.getDateLabel());
return convertView;
}
#Override
public boolean isEnabled(int position) {
return false;
}
static class ViewHolder {
TextView checkpointName;
TextView delayAmount;
TextView timeReported;
TextView dateReported;
}
}
12/14/14 Update: General implementation background.
At launch the App class starts WaitAsyncTask, which parses remote XML to fill its ArrayList waits. I'll access these waits in a few places so this way I keep them global.
WaitFragment, working with WaitAdapter, displays waits in a ListView and listens for changes to waits. User's can post waits to the web service via an AlertDialog. A successful response executes WaitAsyncTask again, updating the waits object, triggering a WaitFragment refresh().
Console logs and the web service confirm this flow of control and that waits gets updated. If I leave WaitFragment then return, it shows the updated waits. The code posted with comments #1-6 are what I've tried inside of the refresh() to update the ListView.
I use this general approach with other data and fragments in this app and their UIs refresh as intended, but none are listViews. I'm not sure I could post more source without redacting most of it but I'll share my findings once I get it working. I haven't had trouble with ListView before, but it'll be something embarrassing for sure. Thanks to everyone who took a little time :)
Just create a method in Adapter class to update/refresh the listview as follows,
public class WaitAdapter extends ArrayAdapter<Wait> {
private LayoutInflater inflater;
private ArrayList<Wait> waits;
public WaitAdapter(Context context, ArrayList<Wait> data) {
super(context, R.layout.list_item_wait, data);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
waits = data;
}
/**
* Update content
*/
public void updateListContent(ArrayList<Wait> data)
{
waits = data;
notifyDataSetChanged();
}
}
In your acivity class, just call this adapter method to update the content. Refer the below code
Note:Dont clear the array content of the adapter.
//Dont clear the arraylist of adapter
adapter.updateListContent(App.getWaits());
This may help you.
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.
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.