Custom ListView Items repeated - android

I am trying to add dynamic textview to listview items.textviews can be of 1-2 or more than that depending on data I have succeed in adding the textview but problem is textviews are repeated on scroll.
I am creating new object of textview each time in loop.I am aware of the problem that android tries to reuse existing view but i have to add new view every time.
Here is my code in custom adapter:
public class ViewHolder {
TextView text1;
LinearLayout linearLayout;
TextView t;
TextView t1;
}
getView method
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.two_item_icon_text, null);
holder = new ViewHolder();
holder.text1 = (TextView) convertView.findViewById(R.id.text1);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.linearLayout = (LinearLayout) convertView.findViewById(R.id.lin_lay_dynamic);
holder.text1.setText("" + DATA1[position]);
String tmp, dateparsed;
dateparsed = DATA1[position].substring(0, DATA1[position].indexOf(":"));
for (int x = 0; x < calendareventholder1.size(); x++) {
objHolder = (CalendarEventHolder) calendareventholder1.get(x);
if (objHolder.opendate.equals(displaydate[current])) {
tmp = objHolder.dtstarttime.toString().substring(0, objHolder.dtstarttime.toString().indexOf(":"));
if (Integer.parseInt(tmp) >= Integer.parseInt(dateparsed) && Integer.parseInt(tmp) < Integer.parseInt(dateparsed) + 1) {
holder.t = new TextView(convertView.getContext());
holder.t.setText(":-d ");
holder.t.setOnClickListener(this);
if (Common.isChildSessionAlerted(String.valueOf(objHolder.id), getApplicationContext(), object1)) {
holder.t.setText(holder.t.getText() + objHolder.dtstarttime + " " + objHolder.dtendtime + " :-a");
} else {
holder.t.setText(holder.t.getText() + objHolder.dtstarttime + " " + objHolder.dtendtime);
}
holder.t.setTag(objHolder.id);
holder.t.setTextSize(Common.getPreferenceInt(getApplicationContext(), Common.PREF_FONT_SIZE, 10));
holder.t.setTextColor(Color.BLACK);
holder.t.setText(getSmiledText(ScheduleActivity.this,
holder.t.getText().toString()));
holder.linearLayout.addView(holder.t);
holder.t1 = new TextView(convertView.getContext());
holder.t1.setOnClickListener(this);
holder.t1.setText(objHolder.title);
holder.t1.setTag(objHolder.id);
holder.t1.setTextSize(Common.getPreferenceInt(getApplicationContext(), Common.PREF_FONT_SIZE, 10));
holder.t1.setTextColor(Color.BLACK);
holder.linearLayout.addView(holder.t1);
}
}
}
return convertView;
}

Its a little hard to really understand what is in holder since it isn't super descriptive, but if I am reading your question right, the reason you are getting repeated items is because you are just adding Views and never deleting what was there before. ListView recycles views, meaning what was there previously stays there. As it scrolls it actually keeps reusing views rather than creating new ones. I won't go into detail because there are a bunch of question on here with similar problems. But at the top you should do
getView() {
/*previous stuff, and holder.linearLayout must have been set!*/
if(holder.linearLayout.getChildCount() > 0)
holder.linearLayout.removeAllViews();
This way any views already there will be removed. One last thing, you should also check out this. Immensely helpful in understanding why you have to do what I posted.

Related

Picasso Only Loading 5 Images until re-using ones in cache

I'm fairly new to android programming. I've run into some issues loading images (bitmaps) into a ListView. It works flawlessly until I have around 5 images loaded, then it reuses the first image I loaded (assuming from cache). I've tried a lot of solutions and bitmap samplings but, still no luck. Here is my code:
#Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
view = getLayoutInflater().inflate(R.layout.listview_item, parent, false);
Contact currentContact = Contacts.get(position);
TextView name = (TextView) view.findViewById(R.id.contactName);
name.setText(currentContact.getName());
TextView phone = (TextView) view.findViewById(R.id.phoneNumber);
phone.setText(currentContact.getPhone());
TextView email = (TextView) view.findViewById(R.id.email);
email.setText(currentContact.getEmail());
TextView address = (TextView) view.findViewById(R.id.cAddress);
address.setText(currentContact.getAddress());
Context mContext = getApplicationContext();
File file = mContext.getDir("imageDir", Context.MODE_PRIVATE);
String path = "file:" + file.getPath() + "/" + currentContact.getImage() + ".png";
ImageView contactListImage = (ImageView) view.findViewById(R.id.contactListImage);
Picasso.with(getApplicationContext())
.load(path)
.centerInside()
.fit()
.error(R.drawable.user_2)
.into(contactListImage)
}
return view;
}
I did try using
recycleMemoryCache()
Thank in you advance!
You're not using the recycled view correctly.
The view parameter you have there is either null, which means you have to inflate a new one, or not null, which means you should re-use it - assign new values to all the fields etc.
You simply need to move the closing curly-brace above return view; to just after your view inflation:
if (view == null) {
view = getLayoutInflater().inflate(R.layout.listview_item, parent, false);
}
As an aside, I'd look up the viewholder pattern - or just start using RecyclerView, as that enforces it (and is the way of the future for lists on Android).

setImageResource() while using view holder pattern with listview

I have a custom base adapter for my listview which I use view holder pattern. I have to change the source of several images dynamically. But when I change one of the images, after a couple of scroll whole images are changing. If I just remove the view holder pattern everything is just fine.
I searched some questions like: Facing critical issue: Toggle image source inside Listview using ViewHolder Pattern
But I couldn't find a proper way to do both.
Here is my getview() function.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// if (convertView == null) {
holder = new ViewHolder();
convertView = this.inflater.inflate(R.layout.list_row2, parent, false);
// For every single item placed on XML
// Holder must be updated here
holder.codeName = (TextView) convertView.findViewById(R.id.codeName);
holder.level = (TextView) convertView.findViewById(R.id.level);
holder.pt1 = (TextView) convertView.findViewById(R.id.pt1Value);
holder.pt2 = (TextView) convertView.findViewById(R.id.pt2Value);
holder.tt3 = (TextView) convertView.findViewById(R.id.tt3Value);
holder.tt4 = (TextView) convertView.findViewById(R.id.tt4Value);
holder.lastUpdate = (TextView) convertView.findViewById(R.id.lastUpdate);
holder.status = (ImageView) convertView.findViewById(R.id.status);
convertView.setTag(holder);
// }
// else {
// holder = (ViewHolder) convertView.getTag();
// }
// Formatting level
SingleSystem temp = sys.get(position);
temp.seq1 = temp.seq1.replace("," , ".");
Float tempLevel = Float.parseFloat(temp.seq1);
Integer level = Math.round(tempLevel);
//Check if there is an alert on system
if(temp.seq13.contains("1") || temp.seq14.contains("1") ||
temp.seq16.contains("1") || temp.seq30.contains("1") || temp.seq31.contains("1") || temp.seq32.contains("1") ||
temp.seq45.contains("1") )
{
holder.status.setImageResource(R.drawable.process_warning); //If an alert, change running picture
}
//Set background color according to level
if(level<=15){
holder.level.setBackgroundColor(Color.rgb(255,17,0));
holder.status.setImageResource(R.drawable.offline);
}
else if(level>15 && level <30){
holder.level.setBackgroundColor(Color.rgb(255,128,0));
}
else{
holder.level.setBackgroundColor(Color.rgb(66,213,4));
}
// Check Time Out
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
try {
Date serverTime = dateFormat.parse(temp.serverTime);
Date communicationTime = dateFormat.parse(temp.communicationDate);
long diff = serverTime.getTime() - communicationTime.getTime();
diff = diff/1000;
diff = diff/60;
Integer cOut = Integer.parseInt(temp.communicationTimeOut);
if(diff > cOut){
holder.status.setImageResource(R.drawable.offline);
}
}
catch (Exception e){
e.printStackTrace();
}
return convertView;
}
As I said if I remove the if(convertView == null) part, everything is okey. For now I just have 16 rows but it may increase. Will it be big performance issue in the future ? How can I change my code to use view holder pattern ?
Thanks for your answer.
The views are reused, ie. they have all values/colors/drawables from when they were displayed before. You need to reset the image's drawable when reusing the view:
else {
holder = (ViewHolder) convertView.getTag();
holder.status.setImageDrawable(null);
}

Android ListView: load items only on scroll

I have a custom adapter for a list view. It has 3 fields: a thumbnail and two text fields.
Once the layout is inflated, the thumbnails are downloaded from an external server.
How do i change the functionality so that the images are downloaded only when the user scrolls down.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Inflate the layout
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_single, parent, false);
ImageView image = (ImageView) rowView.findViewById(R.id.picture);
TextView firstline = (TextView) rowView.findViewById(R.id.firstline);
TextView secondline = (TextView) rowView.findViewById(R.id.secondline);
// Get information about the report
AnimalLocationLog current = objects.get(position);
// Get all the important information
String species = current.getSpecies();
String sanitizedSpecies = MiscHelpers.sanitizeString(species);
int reportId = objects.get(position).getTrackerId();
// Construct the URL to the image
String imageLocation = websiteUrl + sanitizedSpecies + separator + reportId + extension;
// Display user report
downloader.download(imageLocation, image);
firstline.setText(species);
secondline.setText("Spotted " + current.getDateTime().toString("yyyy-MM-dd H:m"));
return rowView;
}
Add an OnScrollListener to your ListView with ListView.setOnScrollListener() and do the download in the ScrollListener.onScroll() method. You can use the ListView.getLastVisiblePosition() to figure out what list items are visible and which aren't.

getView() is not working as i supposed

I am going to develop a APP where i am using customize alert dialog. Data have come from database to this alert-dialog.
menu_to_showdialogCursor = dh.rawQuery("SELECT _id ,item_name,Item_cost FROM order_customer WHERE item_name LIKE ? AND Item_cost LIKE ?", new String[]{"%","%"});
OrderListAdapter orderAdapter = new OrderListAdapter(MainScreen.this,menu_to_showdialogCursor );
orderList.setAdapter(orderAdapter);
ABove line is calling my Adapter class.Now i ma going to show my getView() in adapter class
public View getView(int position, View convertView, ViewGroup parent) {
OrderViewHolder orderViewHolder = null;
if (convertView == null) {
orderViewHolder = new OrderViewHolder();
convertView = inflater.inflate(R.layout.order_list_row, null);
orderViewHolder.setTvTitle((TextView) convertView
.findViewById(R.id.orderTitle));
orderViewHolder.setTvPrice((TextView) convertView
.findViewById(R.id.orderPrice));
orderViewHolder.setIvDelete((ImageButton) convertView
.findViewById(R.id.deleteOrder));
convertView.setTag(orderViewHolder);
} else {
orderViewHolder = (OrderViewHolder) convertView.getTag();
}
if (position != 0)
{
System.out.println(" value of position :"+position);
List lit = new ArrayList();
List litp = new ArrayList();
// System.out.println(" value of postion of cursor : ");
OrderViewHolder odr_obj = null;
if (oStarterCursor.moveToFirst()) {
do{
odr_obj = new OrderViewHolder();
title = oStarterCursor.getString(oStarterCursor.getColumnIndex("item_name"));
System.out.println("value of title :"+title);
lit.add(title);
price = oStarterCursor.getString(oStarterCursor.getColumnIndex("Item_cost"));
System.out.println("value of price :"+price);
litp.add(price);
}while(oStarterCursor.moveToNext());
}
// _id = oStarterCursor.getInt(oStarterCursor.getColumnIndex("_id"));
if (title != null) {
title = title.trim();
Iterator it = lit.iterator();
while (it.hasNext()) {
int i =0;
//System.out.println(" data iterator "+it.next().toString());
String test =it.next().toString();
System.out.println(" iterator vavue :"+test);
orderViewHolder.getTvTitle().setText(test);
}
orderViewHolder.getTvTitle().setTextColor(R.color.black);
orderViewHolder.getTvTitle().setTextSize(12);
orderViewHolder.getTvTitle().setTypeface(Typeface.DEFAULT);
orderViewHolder.getTvTitle().setGravity(
Gravity.CENTER_VERTICAL);
}
if (price != null) {
price = price.trim();
orderViewHolder.getTvPrice().setText(price + ".00");
orderViewHolder.getTvTitle().setTextColor(R.color.black);
orderViewHolder.getTvTitle().setTextSize(12);
orderViewHolder.getTvTitle().setTypeface(Typeface.DEFAULT);
orderViewHolder.getTvTitle().setGravity(
Gravity.CENTER_VERTICAL);
}
//_id = oStarterCursor.getInt(oStarterCursor.getColumnIndex("_id"));
//convertView.setTag(R.id.orderTitle, _id);
if (orderViewHolder.getIvDelete() != null) {
//orderViewHolder.getIvDelete().setTag(R.id.orderTitle, _id);
}
}
return convertView;}
Problem is that in my data base there is two row as in below image..
But in my alert dialog only showing one row twice. I did not get where is logical mistake.For conveniences i am going to show my screen-shot
So Where is problem i can't get please some body help me take out of this problem.
I think it's your line :
if (position != 0)
The first line of an adapter is 0 so it will not set the data for the first line of data.
I believe its because you are using getView wrong, getView does not just get called once it gets called many times. you are looping through your cursor each time getView is called so you are only displaying the last item in the cursor after you loop through your 2 lists.
if you are getting data from a cursor you should be using SimpleCursorAdapter and then override bindView and just use the cursor variable in the method each time its called
Edit
basically along the lines of this
oStarterCursor.moveToPosition(position);
orderViewHolder.getTvTitle().setText(oStarterCursor.getString(oStarterCursor.getColumnIndex("item_name")));
is all you need to get the data really

Image in listview via database+assets

I have a database which has a name of an animal, and in an other column a sound of the animal.
The listview works fine, and now I would like to extend it with an image out of the assets.
For this I've read about how to get the assets data and use it.
An example I've tried worked fine for me.
Now I want this "assets" code (at least I think I want this) in the extended BaseAdapter class.
Unfortunately I'm doing something wrong as I can't use the getAssets() in the BaseAdapter.
The problem starts in the try-catch block: " getAssets " doesn't get recognized
Which way would I think of solving this?
Creating another class in which this assets code can run in an extended "Activity"?
Or are there better ways of showing an image via database info in a listview?
Thank you for your support in my quest to get familiar with Android / Java.
public View getView(int position, View convertView, ViewGroup parent) {
// get view reference
View view = convertView;
// if null
if(view == null) {
// inflate new layout
view = mInflater.inflate(R.layout.layout_list_item, null);
// create a holder
ViewHolder holder = new ViewHolder();
// find controls
holder.txtName = (TextView)view.findViewById(R.id.txtName);
holder.txtPicture = (ImageView)view.findViewById(R.id.txtPicture);
// set data structure to view
view.setTag(holder);
}
// get selected user info
UserInfo userInfo = mListUserInfo.get(position);
// if not null
if(userInfo != null) {
// query data structure
ViewHolder holder = (ViewHolder)view.getTag();
// set data to display
holder.txtName.setText(userInfo.getName() + ", " + userInfo.getPicture() );
try {
// get input stream
InputStream ips = getAssets().open( userInfo.getPicture() + ".jpg");
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
// set image to ImageView
holder.txtPicture.setImageDrawable( d );
}
catch(IOException ex) {
Log.e("Imageloading", "Could not load '" + ex.getMessage()+ "'!");
}
}
// return view
return view;
}
I've just edited the code.
To solve the getAssets() thing I've done the following:
holder.txtPicture.setImageDrawable( getSomePicture(null, userInfo.getPicture() + ".jpg") );
public Drawable getSomePicture(Context myContext, String WhichPicture) throws IOException {
// get input stream
InputStream ips = myContext.getAssets().open( WhichPicture );
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
return d;
}
This still is not the solution, researching some more....
Found an interesting source for Lazy Loading
to call getAssets() function in non-activity class you need reference to Context. In your updated code you called function 'getSomePicture()' and passed null to myContext parameter. that means your code will fail because you have myContext.getAssets() later in your method code.
try to do this in your getView method:
Context context = getContext();
holder.txtPicture.setImageDrawable( getSomePicture(context, userInfo.getPicture() + ".jpg") );
You won't have access to the getAssets() method because that is a method that only the Context (and it's subclass, Activity) have. What I find is easiest here is to make your BaseAdapter subclasses private inner classes of the Activity in which it is to be used. That way, you'll have access to the getAssets() method.
Here is what this might look like:
public class YourExampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
}
private class YourListAdapter extends BaseAdapter {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// get view reference
View view = convertView;
// if null
if(view == null) {
// inflate new layout
view = mInflater.inflate(R.layout.layout_list_item, null);
// create a holder
ViewHolder holder = new ViewHolder();
// find controls
holder.txtName = (TextView)view.findViewById(R.id.txtName);
holder.txtPicture = (ImageView)view.findViewById(R.id.txtPicture);
// set data structure to view
view.setTag(holder);
}
// get selected user info
UserInfo userInfo = mListUserInfo.get(position);
// if not null
if(userInfo != null) {
// query data structure
ViewHolder holder = (ViewHolder)view.getTag();
// set data to display
holder.txtName.setText(userInfo.getName() + ", " + userInfo.getPicture() );
try {
// get input stream
InputStream ips = getAssets().open( userInfo.getPicture() + ".jpg");
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
// set image to ImageView
holder.txtPicture.setImageDrawable( d );
}
catch(IOException ex) {
Log.e("Imageloading", "Could not load '" + ex.getMessage()+ "'!");
}
}
// return view
return view;
}
// Override other key methods of BaseAdapter here
}
}
Alternatively, you could take in a Context object in the constructor when the BaseAdapter subclass is instantiated, and keep a reference to that with which to call getAssets() on. Please post a comment if you need an example of this.

Categories

Resources