So now in my code I check first and I only create the view from scratch if it's null. Otherwise, I only modify the existing view to change the background color to signify that it has been selected. I also store the two Drawables that are the backgrounds so they don't have to be retrieved and assigned memory for each object. This allows the ListView to refresh quickly as it doesn't have to reacquire the underlying objects and rebuild the entire view based on the object's properties each time the selection is changed. This seems like a simple thing, but maybe someone else that is struggling with ListView performance will happen across my post.
Side projects, and non-profit work. Always in search of the indefinable hack.
Showing posts with label ListView. Show all posts
Showing posts with label ListView. Show all posts
Saturday, June 15, 2013
Efficient Custom ListView Adapter Selections
A customer requested I create a ListView of their business objects using custom graphics and text based on the status of the object. I had no problem creating it but they complained that it took too long to refresh when making selections. After puzzling over the code for awhile trying to determine how to speed it up, I realized that the convertView paramter passed in to the GetView method that I was overriding in my custom listview adapter was actually the existing view of the object, and if it wasn't null I didn't have to recreate it.
So now in my code I check first and I only create the view from scratch if it's null. Otherwise, I only modify the existing view to change the background color to signify that it has been selected. I also store the two Drawables that are the backgrounds so they don't have to be retrieved and assigned memory for each object. This allows the ListView to refresh quickly as it doesn't have to reacquire the underlying objects and rebuild the entire view based on the object's properties each time the selection is changed. This seems like a simple thing, but maybe someone else that is struggling with ListView performance will happen across my post.
So now in my code I check first and I only create the view from scratch if it's null. Otherwise, I only modify the existing view to change the background color to signify that it has been selected. I also store the two Drawables that are the backgrounds so they don't have to be retrieved and assigned memory for each object. This allows the ListView to refresh quickly as it doesn't have to reacquire the underlying objects and rebuild the entire view based on the object's properties each time the selection is changed. This seems like a simple thing, but maybe someone else that is struggling with ListView performance will happen across my post.
Labels:
Android,
BaseAdapter,
C#,
convertView,
GetView,
ListView,
ListViewAdapter,
Mono for Android,
Selection,
Xamarin,
Xamarin.Android
Tuesday, May 14, 2013
Defining ContextMenus for ListView Items in Xamarin Android
While working on my Android app I had need to display a list of items with position-dependent context menus. It took me awhile to develop a clean solution so I'd like to share my work with anyone who would like dynamic menus available on their ListViews that display over items using a long-press.
Inside of my Activity class I have declared three important variables:
What each variable tracks should be self explanatory from the declaration, but please comment if you have questions on anything in my posts. The reason it's important to declare these within scope of the Activity class and not within a method is because we lose focus of what ListView item is being interacted with in-between the step of creating a menu and when that menu is used. There might be a way to extract that information from the second step, but none was obvious to me and I found the approach of using private variables an easy solution.
In addition to those three variables I've also created constant values to track the int-based menu options:
These allow me to refer to actions in a clear way in the code. In the OnCreate method of my Activity there are two important steps needed to use context menus:
Now that I've associated my ListView with a private variable and have registered it for ContextMenus I only have to define two additional methods for everything to work. The first is the method that is triggered when an item is selected because of the RegisterForContextMenu call made in OnCreate, and it creates the menu that is displayed:
Okay, so now my menu will show up and it will look different based on what text the item in the ListView contains, and what item number it is assigned, which is the 0-based index in the listview. You can see in my menu code above that I have added the ability to View to any-and-all items, but only the first item in the list can be edited, and only items containing my special text can be deleted.
The last step is to specify what actions occur when the menu is used. We do this in the OnContextItemSelected method:
This is where the information captured as selectedItemText and selectedItemNumber from OnCreateContextMenu comes in handy. Since this method only has access to the item that was clicked, and that IMenuItem contains no information about the ListView item we have to refer to our Activity-scope variables selectedItemText and selectedItemNumber.
As always, feel free to post questions or comments, even if it's to suggest a better way of doing this!
Inside of my Activity class I have declared three important variables:
private ListView itemList; private int selectedItemNumber; private string selectedItemText;
What each variable tracks should be self explanatory from the declaration, but please comment if you have questions on anything in my posts. The reason it's important to declare these within scope of the Activity class and not within a method is because we lose focus of what ListView item is being interacted with in-between the step of creating a menu and when that menu is used. There might be a way to extract that information from the second step, but none was obvious to me and I found the approach of using private variables an easy solution.
In addition to those three variables I've also created constant values to track the int-based menu options:
private const int EDIT_ITEM = 0; private const int VIEW_ITEM = 1; private const int DELETE_ITEM = 2;
These allow me to refer to actions in a clear way in the code. In the OnCreate method of my Activity there are two important steps needed to use context menus:
protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); SetContentView (Resource.Layout.SendTickets); //Two important steps to for my context menus itemList = (ListView) FindViewById(Resource.Id.myListViewName); RegisterForContextMenu(itemList); }
Now that I've associated my ListView with a private variable and have registered it for ContextMenus I only have to define two additional methods for everything to work. The first is the method that is triggered when an item is selected because of the RegisterForContextMenu call made in OnCreate, and it creates the menu that is displayed:
public override void OnCreateContextMenu(IContextMenu menu, View v, IContextMenuContextMenuInfo info) { AdapterViewAdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) info; selectedItemText = ((TextView) menuInfo.TargetView).Text; selectedItemId = menuInfo.Id; //My listview is set to have a "No Data" item on the first row if it's empty if (!selectedTicketText.Equals("No Data")) { menu.SetHeaderTitle("My Menu Header"); //All items can be viewed menu.Add(0, VIEW_ITEM, 0, "View Item"); //But only the first item in the list can be edited if (selectedItemId== 0) menu.Add(0, EDIT_ITEM, 0, "Edit Item"); if (!selectedTicketText.Contains ("Words that mean it can be deleted")) menu.Add (0, DELETE_ITEM, 0, "Delete Item"); } else //This won't create any menu at all { menu.SetHeaderTitle("No Options"); } }
Okay, so now my menu will show up and it will look different based on what text the item in the ListView contains, and what item number it is assigned, which is the 0-based index in the listview. You can see in my menu code above that I have added the ability to View to any-and-all items, but only the first item in the list can be edited, and only items containing my special text can be deleted.
The last step is to specify what actions occur when the menu is used. We do this in the OnContextItemSelected method:
public override bool OnContextItemSelected(IMenuItem item) { if (item.ItemId.Equals (VIEW_ITEM)) { MethodThatViewsMyItem(selectedItemText); } else if (item.ItemId.Equals(EDIT_ITEM)) { MethodThatEditsMyItem(selectedItemNumber); } else if (item.ItemId.Equals(DELETE_ITEM)) { MethodThatDeletesMyItem(selectedItemNumber); } return base.OnOptionsItemSelected(item); }
This is where the information captured as selectedItemText and selectedItemNumber from OnCreateContextMenu comes in handy. Since this method only has access to the item that was clicked, and that IMenuItem contains no information about the ListView item we have to refer to our Activity-scope variables selectedItemText and selectedItemNumber.
As always, feel free to post questions or comments, even if it's to suggest a better way of doing this!
Labels:
Android,
C#,
ContextMenu,
ListView,
Menus,
Mono for Android,
MonoDroid,
Xamarin.Android
Location:
Utah, USA
Subscribe to:
Posts (Atom)