SectionIndexer - Using with ArrayAdapter and CustomObject? - android

I am following a great coding example over here: This SO question. It is regarding implementing a SectionIndexer interface to an array adapter.
However, how would you do the same thing if your ArrayAdapter is passing an ArrayList< MyObject > not an ArrayList< String >?
For example, this is where my code is different then his code. He has:
class AlphabeticalAdapter extends ArrayAdapter<String> implements SectionIndexer {
private HashMap<String, Integer> alphaIndexer;
private String[] sections;
public AlphabeticalAdapter(Context c, int resource, List<String> data) {
alphaIndexer = new HashMap<String, Integer>();
for (int i = 0; i < data.size(); i++) {
String s = data.get(i).substring(0, 1).toUpperCase();
alphaIndexer.put(s, i);
}
// other stuff
}
I am having problems adapting that for loop to my situation. I can't measure the size like he does. Where he has the above, my adapter begins with.
public class CustomAdapter extends ArrayAdapter<Items> implements
SectionIndexer {
public ItemAdapter(Context context, Items[] objects) {
Where he is passing one ArrayList, I have to pass in three, but to make that happen, had to wrap in a custom object class. One of the ArrayLists that I want to sort is one of three fields in the class called "name". It is a string obviously.
I want to scroll through that alphabetically with SectionIndex based on that name field. How do I change the example code from the other question to work in this scenario?
Where he has "data.size()", I need something like "name.size()" - I think?

Where he is passing one ArrayList, I have to pass in three, but to
make that happen, had to wrap in a custom object class. One of the
ArrayLists that I want to sort is one of three fields in the class
called "name".
You don't have three ArrayLists, you have an ArrayList of custom objects that were built from three ArrayLists(so the size is the size of the List that you pass to the adapter). From this point of view the only change in your code is to use the name from that custom object Items to build the sections:
for (int i = 0; i < data.size(); i++) {
String s = data.get(i).name.substring(0, 1).toUpperCase();
if (!alphaIndexer.containsKey(s)) {
alphaIndexer.put(s, i);
}
}
// ...
There aren't other changes. Also you may need to sort the List of Items that you pass to the adapter using:
Collections.sort(mData);
where your Items class must implement the Comparable<Items> interface:
class Items implements Comparable<Items> {
String name;
// ... rest of the code
#Override
public int compareTo(Items another) {
// I assume that you want to sort the data after the name field of the Items class
return name.compareToIgnoreCase(another.name);
}
}

Related

Storing a HashMap with OrmLite

I have a class ShoppingList with the following Map:
#DatabaseField(dataType = DataType.SERIALIZABLE)
Map<Category, List<Product>> products;
Then, on a Fragment, I have a ListView that should show all Categories. Therefore, I set the Adapter for it with the following code:
aisles.setAdapter(new ShoppingListEditAdapter(getActivity(), new ArrayList<Category>(mShoppingList.getMap().keySet())));
By default it should be empty. If I click a button, a Dialog opens, and there I input the text for the new category or aisle that I want to create. Thus, I run the following code:
Category category = new Category();
category.setName(editText.getText().toString());
mShoppingList.getMap().put(category, new ArrayList<Product>());
try {
helper.getShoppingListDao().update(mShoppingList);
((BaseAdapter) aisles.getAdapter()).notifyDataSetChanged();
} catch (SQLException e) {
e.printStackTrace();
}
This should modify the ShoppingList I have for this Fragment, adding a Category to the Map. However, even if I call notifyDatasetChanged() for my Adapter or restart the application, the ListView doesn't get populated with the data. Both Category and Product are Serializable. Why doesn't it get populated?
In keeping with what I've posted as a comment, here is a simple adapter that will accept a Map and display the keys of the map as the values of the list. I've not tested it in accordance with the actual item id's, but for displaying a map that will be mutated, it seems to work (i.e. if the map itself is changed and notifyDataSetChanged() is called, it will work). I've counted on ArrayAdapter to do the heavy lifting and just overrode the methods I wanted.
The obvious downside is I'm not sure how expensive it is to call keySet().toArray() but I imagine it's a linear time operation, beyond the extra memory usage. But basically I don't know a great way to get around this issue, as it relies on taking a Set into some sort of ordered collection.
public class MapAdapter<K, V> extends ArrayAdapter<K> {
private Map<K, V> items;
public MapAdapter(Context context, int resource, Map<K,V> items) {
super(context, resource);
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public K getItem(int position) {
return ((K []) items.keySet().toArray())[position];
}
}

why references in adapter constructor are not updated?

Consider folowing code:
public class MediaItemAdapter extends BaseAdapter {
final List<Row> rows;
public MediaItemAdapter(Activity activity, ArrayList<HashMap<String, String>> data) {
Log.d("mediaadapter", "listcreation: " + data.size());
rows = new ArrayList<Row>();// member variable
int i=0;
for (HashMap<String, String> addvert:data) {
rows.add(new MeadiaRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE), addvert));
Log.d("mediaadapter", addvert.get("title"));
if (i % 20 == 0) {
rows.add(new SourseRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE)));
}
i++;
}
}
It is constructor of my adapter. After each 20 MediaRows, I wish to add one sourse row. It has ArrayList data. The problem that data is always empty. I build my adapter according to this tutorial. In a tutorial thingth works fine. But in tutorial initial dataset is predefined. In my cade initial data set is empty and populates in working time. However, after I call notifyDataSetChanged the data remains empty (even though the dataset is not).
How can I get updated values for data?
What is nesessary to do - is to move
int i=0;
for (HashMap<String, String> addvert:data) {
rows.add(new MeadiaRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE), addvert));
Log.d("mediaadapter", addvert.get("title"));
if (i % 20 == 0) {
rows.add(new SourseRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE)));
}
i++;
}
from adapter to overriden notifyDataSetChanged in adapter class

List View with Section Headings

I've seen quite a few examples of how to add alphabetical section headers to list views online. Example:
I implemented the functionality from
this website
. However, I have a list of approximately 8000 items. When trying to load this page it takes about 8 seconds which is obviously way too slow. With just a normal AlphabetIndexer it takes about 1.5 seconds (still slow, but much better).
Does anyone have any ideas on how to speed this up? If not, are there any other examples that are quicker than this?
Thanks!
What exactly do you mean by trying to load the page? How long does it take if you just load the items and don't do any indexing? 8000 items is not that many to iterate over. It could however be a lot of items to load from disk, or the internet. You might want to consider showing a loading screen and reading in the data for your rows in the background.
The code you showed looks particularly complicated for what you're trying to do. Below is a solution I've used. You can google for SectionIndexer. In my code itemManager is basically just an abstraction on a list, placeholders are null values, everything else is the data structure containing the information for the rows. Some code is omitted:
//based on http://twistbyte.com/tutorial/android-listview-with-fast-scroll-and-section-index
private class ContactListAdapter extends BaseAdapter implements SectionIndexer {
final HashMap<String, Integer> alphaIndexer = new HashMap<String, Integer>();
final HashMap<Integer, String> positionIndexer = new HashMap<Integer, String>();
String[] sections;
public ContactListAdapter() {
setupHeaders();
}
public void setupHeaders(){
itemManager.clearPlaceholders();
for (int i = 0; i < itemManager.size(); i++) {
String name = itemManager.get(i).displayName();
String firstLetter = name.substring(0, 1);
if (!alphaIndexer.containsKey(firstLetter)) {
itemManager.putPlaceholder(i);
alphaIndexer.put(firstLetter, i);
positionIndexer.put(i, firstLetter);
++i;
}
}
final Set<String> sectionLetters = alphaIndexer.keySet();
final ArrayList<String> sectionList = new ArrayList<String>(sectionLetters);
Collections.sort(sectionList);
sections = new String[sectionList.size()];
sectionList.toArray(sections);
}
#Override
public int getItemViewType(int position) {
return itemManager.isPlaceholder(position) ? ViewType.HEADER.ordinal() : ViewType.CONTACT.ordinal();
}
#Override
public int getViewTypeCount() {
return ViewType.values().length;
}
#Override
public int getPositionForSection(int section) {
return alphaIndexer.get(sections[section]);
}
#Override
public int getSectionForPosition(int position) {
return 1;
}
#Override
public Object[] getSections() {
return sections;
}
ListView adapters have a class you can override called getViewType(int position) and getViewTypeCount().
Those you would override to allow Android to know how many different type of views your adapter uses.
In your case override getViewTypeCount() to return 2 since you will have two types of views. One your standard and second your header view.
Than you will need to know at what position you will have your header views shown. Once you do you can easily return your views on the getView(...) function.

How to apply sorting on list of item in android

I am working on an Android project. I have a list of items, and one row has three columns.
Now I need to sort the list based on these columns.
Column headers are : number, name, summary .
Now, if I click on number,than the list should be sorted by number and if I click on name, than the list should be sorted by name.
Now I am not getting which layout, or event I should use for this header in Android.
Please suggest me.
you can use comparator , in ur adapter of list
public class CustomerByNameComparator implements Comparator<Customer>
{
public int compare(Customer o1, Customer o2) {
return o1.getName().compareToIgnoreCase( o2.getName() );
}
}
hope it help u......
EDIT:
You have to read this similar question, you will be able to sort your data:
How to sort an ArrayList using multiple sorting criteria?
After sorting data, you have simply to call notifyDataSetChanged on your adapter:
yourAdapter.notifyDataSetChanged();
More info about sorting:
http://en.allexperts.com/q/Java-1046/Sort-2-attributes-collection.htm
First, create a Comparator class for each
different aspect of the Employee class that you might need.
For example:
public class EmployeeAgeComparator
implements Comparator
{
public int compare(Object o1, Object o2) {
int age1 = ((Employee)o1).getAge();
int age2 = ((Employee)o2).getAge();
if (age1 < age2) return -1;
if (age2 < age1) return 1;
return 0;
}
}
Once you've got EmployeeIdComparator, EmployeeNameComparator and all the rest, then use this code:
Comparator mc;
mc = new EmployeeIdComparator();
Collections.sort(list, mc);

Re-index/Refresh a SectionIndexer

Is there any way to re-index a SectionIndexer after new items are added to a ListView?
I found this solution, but the overlay is position in the top left corner after the SectionIndexer is refreshed.
Anyone have any ideas?
Once the FastScroller (its in AbsListView class that ListView extends from) obtains your sections by calling SectionIndexer#getSections(), it never re-obtains them unless you enable/disable fast-scrolling like mentioned in the link you mentioned. To get the value to be displayed on screen, FastScroller calls the section's toString method.
One potential solution is to have a custom SectionIndexer that have the following characteristics:
The sections array is of fixed length (max length of the expected number of sections. For example, if the sections represent English alphabet it will be 26)
Have a custom object to represent sections, rather than using strings
Overwrite the toString method of your custom section object to display what you want based on the current 'section values'.
-
e.g. In your custom SectionIndexer
private int mLastPosition;
public int getPositionForSection(int sectionIndex) {
if (sectionIndex < 0) sectionIndex = 0;
// myCurrentSectionLength is the number of sections you want to have after
// re-indexing the items in your ListView
// NOTE: myCurrentSectionLength must be less than getSections().length
if (sectionIndex >= myCurrentSectionLength) sectionIndex = myCurrentSectionLength - 1;
int position = 0;
// --- your logic to find the position goes in here
// --- e.g. see the AlphabeticIndexer source in Android repo for an example
mLastPosition = position;
return mLastPosition;
}
public Object[] getSections() {
// Assume you only have at most 3 section for this example
return new MySection[]{new MySection(), new MySection(), new MySection()};
}
// inner class within your CustomSectionIndexer
public class MySection {
MySection() {}
public String toString() {
// Get the value to displayed based on mLastPosition and the list item within that position
return "some value";
}
}
I found that the best way to do this is to call setContentView(R.layout.whatever) and then re-populate the ListView with your new adapter / new data items. This will redraw the ListView with your new items and the FastScroll Overlay will appear in the correct place.
I found notifyDataSetInvalidated working fine, here's the idea:
public class MyAdapter extends XXXAdapter implements SectionIndexer {
...
public void updateDataAndIndex(List data, Map index) {
// update sections
// update date set
notifyDataSetInvalidated();
}
}
update your data set and index (sections) somehow, and then notifyDataSetInvalidated, the index will refresh.
You can force reloading sections list to ListView by listView.setAdapter(yourAdapter)

Categories

Resources