I have a listview with one textview and a button in each row. Button background is setting using some conditions. If i have number of rows in list and scrolled, the button background get shuffling. That means on scrolling wrong background is setting as button background. How can i resolve this issue? My adapter class is as shown below:
public View getView(int position, View convertView, ViewGroup parent) {
final PhotoCondtionthreeItemView item;
Photo place = (Photo) getItem(position);
if (convertView != null) {
item = (PhotoCondtionthreeItemView) convertView;
} else {
item = new PhotoCondtionthreeItemView(context, place,
membershipSign);
}
item.setShareTag(place.getLink()+"###"+place.getServerPhotoId());
item.setBaseLayoutTag(place.getLink() + "###"
+ place.getServerPhotoId() + "###" + place.getIsGallery()
+ "###" + place.getId());
File file;
try {
file = new File(place.getLink());
if (file.exists()) {
if (cache.containsKey(place.getLink())) {
item.setThumbImg(cache.get(place.getLink()));
} else {
Bitmap bitmap = BitmapResizer.decodeFile(file, 50, 50);
item.setThumbImg(bitmap);
cache.put(place.getLink(), bitmap);
}
} else {
item.setThumbImgMissing();
}
} catch (Exception e) {
Log.e("PhotoCondThree ERROR", e.toString());
}
return item;
}
In PhotoCondtionthreeItemView class created the row for list. (not used xml for list row).
can anyone help me?
Thank you
call yourListViewadapter.notifyDataSetChanged() when data is changed
That sounds like an issue which is caused by the ListView re-using layout elements. Without seeing the code that is actually setting the image to the view it's a little hard to say for sure but maybe this blog post will help...
http://android-developers.blogspot.com/2009/05/drawable-mutations.html
Related
I have a listView that is implemented with a custom adapter I created, which includes an imageView. Throughout the list each item may or may not have an image attached(It's not necessary).
To load the image into the imageView I use Picasso library, in the getView method.
When it comes to rows that do have an image associated, my code works fine.
The problem is when the list is being displayed, rows that should not have an image are displaying one.
Here is my getView() method:
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
ReportHolder holder = null;
if(convertView==null){
LayoutInflater inflater=((Activity)context).getLayoutInflater();
convertView = inflater.inflate(layoutResourceId,null);
holder = new ReportHolder();
holder.imageReportView=(ImageView)convertView.findViewById(R.id.reportImage);
holder.reportLocation=(TextView) convertView.findViewById(R.id.report_location);
holder.reportDescription=(TextView) convertView.findViewById(R.id.report_description);
holder.reportStatus=(TextView) convertView.findViewById(R.id.report_status);
convertView.setTag(holder);
}
else
holder=(ReportHolder)convertView.getTag();
ReportData data = reportDataList.get(position);
holder.reportLocation.setText(data.address);
holder.reportDescription.setText(data.description);
holder.reportStatus.setText(data.status);
Picasso picasso = Picasso.with(this.context);
if(data.url!=null)
picasso.load("https://fourth-landing-159416.appspot.com/gcs/"+data.url+"_thumbnail").into(holder.imageReportView);
return convertView;
}
I know I'm fetching my information well because no information between rows repeats itself except the pictures. So what am I missing here?
FMI My adapter is created in a nested ASyncTask, because I´m required to fetch the information through an HTTP connection before I can insert it in the adapter:
#Override
protected void onPostExecute(final String result) {
mFeedTask = null;
mFeed.removeFooterView(mProgressView);
if(result!=null){
if(result.contains("HTTP error code: 403")){
Toast.makeText(mContext,"Token invalid. Please login again.", Toast.LENGTH_SHORT).show();
}
else if(result.equals("[]"))
Toast.makeText(mContext,"Nothing more to show.", Toast.LENGTH_SHORT).show();
else{
try {
Gson gson = new Gson();
JSONArray reports = new JSONArray(result);
LinkedList<ReportData> tmpList = new LinkedList<ReportData>();
for(int i=0; i < reports.length(); i++){
ReportData data = gson.fromJson(reports.getString(i), ReportData.class);
tmpList.add(data);
}
reportDataList.addAll(tmpList);
if(reportDataList.size()==tmpList.size()){
// First, we set the empty adapter and set up the item click listeners
adapter = new CustomAdapter(mContext,R.layout.custom_feed_row,reportDataList);
mFeed.setAdapter(adapter);
}
else {
updateTriggered=false;
adapter.notifyDataSetChanged();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
And as I have a relatively large amount of information to transfer I have to call this task multiple times. On the first time called the adapter is created and from then on notifyDataSetChanged is called to update it.
Much Help Appreciated!
I thank you in advance!
Cheers
Because listview recycling same views, so you should remove the image if this row shouldn't contain image
Do something like this in your getView:
if(data.url!=null)
picasso.load("https://fourth-landing-159416.appspot.com/gcs/"+data.url+"_thumbnail").into(holder.imageReportView);
else {
holder.imageReportView.setImageDrawable(null);;
}
I have read and tried several of the solutions to similar problems here on stack overflow, but none of them solved my problem. Here is the thing.
I have a listview which uses CustomListAdapter, each list item has a progress bar, a download button, title text and so on. When the download button is clicked a download operation is performed, and based on the result of the download(whether success or failure) the list item concerned is update(UI changes, such as if complete hide download button, update progress of the progress bar during download)
The listview displays four items at every given time
The problem is that whenever a UI change is made to an item say item 1(with index 0) the item 5 will also have the same changes, likewise if a change is made to item 3, the item 7 takes up those changes. In summary the item N+4 always imitates item N.
A look at my getView() will tell that I have checked all the known boxes.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
if (convertView == null) {
view = inflater.inflate(R.layout.item_mylibrarylist, null);
holder = new ViewHolder();
holder.name = (TextView)view.findViewById(R.id.name);
holder.name.setTypeface(MainActivity.font_bahamas);
holder.author = (TextView)view.findViewById(R.id.author);
holder.author.setTypeface(MainActivity.font_bahamas);
holder.worktype = (TextView)view.findViewById(R.id.worktype);
holder.worktype.setTypeface(MainActivity.font_bahamas);
holder.coverPic = (TextView)view.findViewById(R.id.coverPic);
holder.downloadBt = (TextView)view.findViewById(R.id.downloadBt);
holder.progressBar = (ProgressBar)view.findViewById(R.id.progressBar2);
holder.menuBt = (ImageView)view.findViewById(R.id.menuBt);
holder.position = position;
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
if(holder.position == position) {
setValuesForListItemViews(holder, position, view);
}
return view;
}
The method to set each of the list items..
private void setValuesForListItemViews(ViewHolder holder, int position, View view) {
if (!data.isEmpty()) {
// set the list item elements here
final CreativeWork creativeWork = data.get(position);
holder.name.setText(creativeWork.getName().toLowerCase());
holder.author.setText("by " + creativeWork.getOriginal_authors().toLowerCase());
holder.worktype.setText(creativeWork.getWork_type().toLowerCase());
Drawable draw = res.getDrawable(R.drawable.custom_progressbar2);
holder.progressBar.setProgressDrawable(draw);
holder.progressBar.setMax(100);
holder.progressBar.setVisibility(View.INVISIBLE);
holder.menuBt.setOnClickListener(new OnItemClickedListener(view, position, 1, creativeWork, holder.progressBar, holder.downloadBt));
holder.menuBt.setOnCreateContextMenuListener(new MContextMenuListener(creativeWork, holder.progressBar, holder.downloadBt, false));
//load image url
ImageLoader2 imgLoader12 = new ImageLoader2(activity);
imgLoader12.DisplayImage(creativeWork.getName(), R.drawable.downloads, holder.downloadBt);
ImageLoader imgLoader = new ImageLoader(activity);
imgLoader.DisplayImage(SLService.END_POINT + creativeWork.getImage_url(), R.drawable.soul_lounge, holder.coverPic);
//check if file already exist and switch off download button
DBHelper helper = new DBHelper(activity);
CreativeWork cw = helper.getCreativeWork(creativeWork);
if (cw != null) {
File file = new File(cw.getFilePath());
if (file.exists()) {
holder.menuBt.setOnCreateContextMenuListener(new MContextMenuListener(creativeWork, holder.progressBar, holder.downloadBt, true));
//check if the file download was complete
if (cw != null) {
if (cw.getFileSize() > file.length()) {
holder.progressBar.setProgressDrawable(activity.getResources().getDrawable(R.drawable.custom_progressbar3));
ImageLoader2 imgLoader2 = new ImageLoader2(activity);
imgLoader2.DisplayImage(cw.getName(), R.drawable.restart, holder.downloadBt);
holder.progressBar.setProgress((int) ((file.length() * 100) / cw.getFileSize()));
holder.progressBar.setVisibility(View.VISIBLE);
} else {
holder.downloadBt.setVisibility(View.INVISIBLE);
}
}
}
}
holder.downloadBt.setOnClickListener(new OnItemClickedListener(view, position, creativeWork, holder.progressBar, holder.downloadBt, 0));
}
}
Easiest way to solve this is adding some extra fields to your model class whose list you are passing to your adapter.
like
boolean showDownloadButton; //default is true
int progress;// default is 0
So when user clicks on download button (or any other desired event) change the boolean value of showDownloadButton to false for model object at the given position and call adapter.notifyDatasetChanged() and manage the button visibility accordingly in your adapter.
and do make sure to add both visible and gone condition for the view aswell
if(modelList.get(position).getShowDownloadButton())
{
btnDownload.setVisibility(View.VISIBLE)
}
else
{
btnDownload.setVisibility(View.GONE)
}
Nitesh's answer is right apart from that there is another way to do if you don't want to create Model class
Use setId and getId with position to identify unique row and assign that id to progressbar for the solution
I have an app whereby i am accessing a RESt webservice and presenting the request in listview (ListFrragment). It is essentially a list of articles. What i am trying to achieve is when a user clicks on an item in listview (expands article in ViewPager) is stays highlighted (indicating that the article has been read). The next time the user reloads this listview, all the item that have been read is still highlighted.
I have have created a boolean variable "read" for each article. When a user clicks on this item, the "read" variable is assigned to TRUE. Then in the custom Adapter(extends arrayAdpater), i assign the background colour (in this case green) in getView for articles whose "read" value is TRUE. This does work, but what i am finding is that other items in the listview is also randomly turning green when i start to scroll up and down the listview without actually clicking on it. I'm really stuck as i do not understand what is happening. Please can someone advise?
public void onListItemClick(ListView l, View v, int postion, long id) {
Article a = ((toReadListAdapter) getListAdapter()).getItem(postion);
// set read attribute to TRUE
a.setRead(true);
saveToReadList(toReadList);
// Open via ViewPager
Intent i = new Intent(getActivity(), ReadingList_PagerActivity.class);
startActivity(i);
Customer Adapter:
// Defining custom adapter
private class toReadListAdapter extends ArrayAdapter<Article> {
public toReadListAdapter(ArrayList<Article> listToRead) {
super(getActivity(), 0, listToRead);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.new_articlelistfragment, null);
}
Article en = getItem(position);
Log.d(TAG, "Showing articles: " + position + "/ pmid: " + en.getId());
Log.e(TAG, "isRead value: " + en.isRead());
if(en.isRead() == true){
convertView.setBackgroundColor(Color.parseColor("#C8E6C9"));
}
......
return convertView;
Inside the getView
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.new_articlelistfragment, null);
}
Article en = getItem(position);
if(en.isRead() == true){
convertView.setBackgroundColor(Color.parseColor("#C8E6C9"));
}
else{
// put the default color
convertView.setBackgroundColor( );
}
......
return convertView;
}
You are not applying the default color. Add an else statement in get view method
Friends,
I am having a problem with Expandable List - for the first time the list groups are drawn correctly, (respective textViews texts get replaced, e.g. 3 of 9) but when any of groups are clicked the textViews of another group (random) are replaced instead of beeing untouched mm.
What should be done to prevent this?
Here is the adapter class:
public class InterestListAdapter extends BaseExpandableListAdapter
{
...
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = LayoutInflater.from(_context).inflate(R.layout.activity_interest_list_group, parent, false);
}
try
{
HashMap<String, String> interest = this._listDataHeader.get(String.valueOf(groupPosition));
String interestId = interest.get("interest_id");
String interestName = interest.get("interest_name");
JSONObject interestsTree = new JSONObject(library.loadString(_context, "userInterestsTree"));
TextView listGroupName = (TextView) convertView.findViewById(R.id.interest_list_group_name);
listGroupName.setText(interestName);
if (this._listDataChild.containsKey(String.valueOf(interestId)))
{
String subinterestAll = String.valueOf(this._listDataChild.get(String.valueOf(interestId)).size());
if (interestsTree.has(interestId))
{
String subinterestChecked = interestsTree.getString(interestId);
TextView subinterestText = (TextView) convertView.findViewById(R.id.interest_list_group_selected);
subinterestText.setText(subinterestChecked + " of " + subinterestAll);
}
}
}
catch (Exception e)
{
Log.e("error", Log.getStackTraceString(e));
}
return convertView;
}
}
I'm rather sure, that you have problems with memory management. When you expand your list some cells become hidden from the screen. After then, when you scroll it, they have to be regenerated. Android OS, to save memory, doesn't generate them from scratch, but instead of that uses the same memory regions, that were used for hidden cells.
It looks like subinterestText is the problematic field. It's being set only if both these ifs are true:
if (this._listDataChild.containsKey(String.valueOf(interestId)))
and
if (interestsTree.has(interestId))
If one of them is false, subinterestText variable won't be set. So, as result, value will be usedfrom previously hidden field - in your case - 2 of 2.
To fix it - always setup all values of your cell in getGroupView method.
Tried using the following:
Populate Listview from JSON
To make a listview which uses a JsonArray containing Json Objects. For some reason, the
'public View getView(int position, View convertView, ViewGroup parent)'
code is fired more times than there are contents in the jsonarray.
I made a control test to check up on this and I found that even with just 1 Jsonobject within the jsonarray, I came up with 32 times the getView code was activated.
I am rather confused as to why this is happening, as my friends have managed to make similar codes to mine, but without the huge number of activations I am suffering from. Am I being rather slow, and this is because the individual Jsonobject has, not only the image and text in them, but about 15 other items within it? Or is ther another cause?
I would appreciate any aid towards this, I am posting the adapter code below:
public class ArticleAdapter extends BaseAdapter{
private JSONArray items;
private Context cont;
public ArticleAdapter(Context context, JSONArray array)
{
super();
this.items = array;
this.cont = context;
}
#Override
public int getCount() {
return items.length();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
WebIView sath;
TextView sati;
Log.i("Seiji", "Checking! " + position);
try
{
if(!items.isNull(position))
{
JSONObject item = items.getJSONObject(position);
if (v == null) {
v = LayoutInflater.from(cont).inflate(R.layout.saved_articles_listitem, null);
}
sath = (WebIView) v.findViewById(R.id.sathumbnail);
sati = (TextView) v.findViewById(R.id.satitle);
if(item.has("image") && sath != null)
{
JSONObject thisImage = item.getJSONObject("image");
sath.reset();
sath.setImageUrl(thisImage.getString("thumbnail"));
sath.loadImage();
}
if(sati != null)
{
sati.setText(item.getString("title"));
}
}else{
return null;
}
}
catch(Exception e)
{
Log.e("num", "Saved Art Error! " + e.toString());
}
return v;
}
}
the code which activates this class is the following:
ListView savedArtList = (ListView) sav.findViewById(R.id.savelist);
ArticleAdapter savedadapter = new ArticleAdapter(cont, flip);
ArtList.setAdapter(savedadapter);
EDIT:
Thanks to some very helpful advice I was able to figure out what was going wrong. The Listview was resizing itself every time a new row was added because I had set the views height to be 'wrap_content'. I hadnt realised that this would cause problems, but once I had set it to 'fill_parent' (or a set value in other cases), the issue disappeared and I didnt have this problem any more.
Thank you againfor the helpful advice!
getView will be called many times - per visible cell when the list view is being laid out, per visible cell when the list view is being drawn + more. This is normal behaviour and getView should be efficient. Its possible your images and/or text are making the height of each cell change as they're loaded in, meaning other cells may become visible / go off screen etc.