I have a ListView which multiple contains checkboxes for user input. I have two buttons add and remove to add and remove list items respectively.
I am using BaseAdapter for ListView and notifying the ListView using notifyDataSetChanged()
Anytime I add/remove the items. The user input in the list gets cleared. I think it's because the list is getting rebuilt everytime. Any ideas on how to keep the user input while add/remove the items in listView ?
in listview you have only one option is to use notifyDatasetChanged() with adapter. just dont clear your whole list just do add/remove and then notify adapter or use Recycclerview instead.
With recyclerview you have many options like notifyDataInserted, notifyDataRemoved, range inserted, range removed etc.
You can find a good example here
i think your programme is reading getView() function again. see the below code to avoid it, it is copy pasted but it may give you some idea-
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}
you can refer this link for more info-0
Android, how to stop reading getView() function again, if the content is already downloaded in the ListView
Related
I'm new to android programming, and I've been reading a lot about it lately. One of the features of ListView, if I understood it right, is that it recycle views and just replaces it with new data when an item is off the screen.
And just a few minutes ago, I was reading up about endless scrolling, and RecyclerView has been one of the popular choices to implement such a feature. So I looked up RecyclerView, and in this video, it is mentioned that RecyclerView recycles a view automatically to reuse it for new data (as a way to contrast its difference with ListView).
Did I misunderstand ListView about its recycling mechanism? Or if it does recycle, how do you actually implement (or how do you know you are implementing) it?
RecyclerView does recycling automatically. In order to make ListView recycle items you will need to do this modification inside of adapter class.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
//brand new
convertView = LayoutInflater.from(mContext).inflate(R.layout.days_list_item, null);
holder = new ViewHolder();
// below is variables that will be different in your case
holder.numberOfDays = (TextView) convertView.findViewById(R.id.eventDays);
holder.sinceOrUntil = (TextView) convertView.findViewById(R.id.eventType);
holder.eventTitle = (TextView) convertView.findViewById(R.id.eventTitle);
holder.daysText = (TextView) convertView.findViewById(R.id.DaysText);
convertView.setTag(holder);
}
else {
//reusing item
holder = (ViewHolder) convertView.getTag();
}
// rest of the code
}
For more details refer to this link.
I want to emulate this type of Listview in a SlideMenu. I have the SlideMenu working fine. It is a ListFragment. I want to copy this pattern like the YouTube app on Android:
I essentially have a couple of list items I need to add to the top of the list of categories. And I want a Header to separate.
I want this:
Home
Profile
Top Items
Header that says Categories
And List of Categories
I already have the Categories listed out fine on my SlideMenu. They come from an adapter that populate from a table in MySQL. But the three top items do not come from that same table (or ANY table). Is the top portion a header to a ListView? Is it its OWN ListView? or..?
Keep in mind, I want ability to sort the list (which I already have via a spinner). So Categories must be dynamic. But how to I add a couple of static items above AND make a header?
I don't really need code sample, I just want to know method to implement this.
EDIT: Here is Code in progress
This show the separator like the Channels line in the Youtube example. Need to also figure out how to add those two or three static lines up top.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
holder = new ViewHolder();
View rowView = convertView;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
int type = getItemViewType(position);
if (rowView == null) {
switch (type) {
case TYPE_ITEM:
rowView = inflater.inflate(R.layout.mastercat_layout, null,
true);
holder.textView = (TextView) rowView.findViewById(R.id.label);
holder.textView.setTypeface(tf);
holder.imageView = (ImageView) rowView.findViewById(R.id.icon);
break;
case TYPE_SEPARATOR:
rowView = inflater.inflate(R.layout.mastercat_layout_separate, null);
break;
}
rowView.setTag(holder);
} else {
holder = (ViewHolder) rowView.getTag();
}
holder.textView.setText(getItem(position));
holder.imageView.setImageResource(R.drawable.ic_launcher);
return rowView;
}
Maybe you need a couple pools of convertView in adapter?
BaseAdapter contains methods
public int getItemViewType (int position)
and
public int getViewTypeCount ()
You can override it to implement 2 pools of views - one for Headers and another one for Items of listView. Also in this case you need to change you getView method according to itemViewType, returned by getItemViewType().
I have a gallery of fairly complicated items. Each item is composed of an image and 2 buttons. When the gallery loads everything works, the buttons do what they are supposed to, and the pressed state for the buttons happens only on actual press of the buttons.
However as soon as I scroll the gallery, the buttons stop working and clicking anywhere enables the pressed state for the buttons.
I have tried embedding everything in a LinearLayout that doesn't pass on OnDown events as per this answer however, this just blocks click events.
I am aware that Gallery is not the ideal widget for complicated layouts like this, but I am wondering if there is a better workaround for this issue.
UPDATE:
I will try to explain the architecture a bit. I have a FragmentActivity which contains a ListFragment, which is made up of just a ListView.
The ListView is made up of groups of smaller elements(Bettable) along with some meta information. These groups are implemented as Gallerys. Specifically
I have extended Gallery (called OneGallery), that does several things, it makes sure that only one item is scrolled at a time, and also
transforms the gallery items as the scrolling is happening. Here is the code for that
Here is the adapter for the Gallery
And here is the code for the Bettable layout
Try to add a new wrapper layout around the child view and override the setPressed. The gallery will stop passing its state on the children and the mentioned undesired behavior that you describe will be fixed.
This is views recycling. Try to use ViewHolder pattern and set up item state for every getView call. If you want to do that you must hold view state in your complex object. For example your complex object contains TextView, ImageView and CheckBox
public View getView(int position, View convertView, ViewGroup parent) {
ComplexObject co = objects.get(position);
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
holder.checkbox = (CheckBox)convertView.findViewById(R.id.checkbox);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
holder.text.setText(co.getText());
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
holder.checkbox.setChecked(co.isChecked());
holder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
co.setChecked(isChecked);
}
});
return convertView;
}
protected class ViewHolder{
TextView text;
ImageView icon;
CheckBox checkbox;
}
Hope it will be helpful
I have a list of items. Each of them has a set of data displayed with TextViews. This data remains mostly unchanged. But I have a distance field, which I would like to update whenever I get new lock from location provider.
The question is: Should I just update my data and call notifyDataSetChanged() on my Adapter or is there a more efficient way?
Seems very expensive to reload all the lists (I have several of them in a ViewPager) just because one TextView in each list item needs to be updated.
Here is my getView() from my adapter. It might help:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.eventrow, parent, false);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.eventTitle);
holder.distance = (TextView) convertView.findViewById(R.id.eventDistance);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(((EventItem) getItem(position)).getTitle());
holder.distance.setText(String.valueOf(((EventItem) getItem(position)).getDistance()));
return convertView;
}
I also thought about directly referencing the holder.distance but it seems like a bad idea to do it outside getView().
The correct way to modify your data is to change your list item and then call notifyDataSetChanged().
The only alternative that comes to my mind is to set again the adapter on the list view which is way more expensive. There are no other ways.
So the answer is: you HAVE to go through notifyDataSetChanged().
I have a straight forward BaseAdapter for my ListView. It downloads a JSON feed and displays the data in the rows. There is a ViewHolder which contains the views and a data object called "Story". Everything works just fine.
However, after some scrolling of longer lists, I notice two things.
1) My log shows that the adapter is reloading the feed when scrolling further down. This is strange, as I put the whole JSON array into a variable, so why does it have to reload?
2) More importantly, after some scrolling back and forth, the rows contain the wrong "Story" objects. Here are the relevant parts of the getView routine:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
Story story = stories.get(position);
if (convertView == null) {
//create holder
holder = new ViewHolder();
convertView = inflator.inflate(R.layout.story_list_item, parent, false);
holder.titleView = (TextView) convertView.findViewById(R.id.story_list_title);
holder.dateView = (TextView) convertView.findViewById(R.id.story_list_date);
holder.story = story;
holder.imageView = (ImageView) convertView.findViewById(R.id.story_list_image);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// configure the view
holder.titleView.setText(story.title);
return convertView;
}
Simple enough. Now the strange thing is that I can fix the problem by eliminating the if statement if (convertView == null) (and, I presume, eliminating the row recycling as well).
But will I not run into memory problems this way? Why does the plain vanilla version not work?
Thanks for your help.
Regards,
S
You are aware that you're only assigning
holder.story = story
when convertView == null ? Consider moving holder.story = story to just after your convertView if-case and it should work a lot better. Btw, do you even need to store the "story" inside your view holder? Typically that pattern should only be used to store Views and view state information, not the data of the actual position.