[Tut]Alphabetic FastScroll ListView - similar to Contacts

Tutorials with advanced 'difficulty' and more Lines of Code.

[Tut]Alphabetic FastScroll ListView - similar to Contacts

Postby qlimax » Fri Jan 08, 2010 12:10 am

Hi everybody,
I've seen that many people asked about how to make a fast-scroll listview with alphabet letters.
I've searched with google about a Fastscrolling list similar to the contact listview... but with no results (in terms of code or tutorials).

So I decided to make this short tutorial.
That's my own implementation, so it can be surely -in a way or in another- be optimized
but let's face it:

Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.    android:orientation="vertical"
  4.    android:layout_width="fill_parent"
  5.    android:layout_height="fill_parent"
  6.    >
  7.  
  8. <ListView
  9. android:id="@+id/myListView"
  10. android:layout_width="fill_parent"
  11. android:layout_height="fill_parent">
  12. </ListView>
  13.  
  14. </LinearLayout>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4


Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package ch.egsolutions.tutorial;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Random;
  8. import java.util.Set;
  9.  
  10. import android.app.Activity;
  11. import android.content.Context;
  12. import android.os.Bundle;
  13. import android.util.Log;
  14. import android.widget.ArrayAdapter;
  15. import android.widget.ListView;
  16. import android.widget.SectionIndexer;
  17.  
  18. public class AlphabetIndexer extends Activity {
  19.         ListView myListView;
  20.         ArrayList<String> elements;
  21.  
  22.         @Override
  23.         public void onCreate(Bundle savedInstanceState) {
  24.                 super.onCreate(savedInstanceState);
  25.                 setContentView(R.layout.main);
  26.  
  27.                 // elements
  28.                 String s = "QWERTZUIOPASDFGHJKLYXCVBNM";
  29.                 Random r = new Random();
  30.                 elements = new ArrayList<String>();
  31.                 for (int i = 0; i < 300; i++) {
  32.  
  33.                         elements.add(s.substring(r.nextInt(s.length())));
  34.  
  35.                 }
  36.                 Collections.sort(elements); // Must be sorted!
  37.  
  38.                 // listview
  39.                 myListView = (ListView) findViewById(R.id.myListView);
  40.                 myListView.setFastScrollEnabled(true);
  41.                 MyIndexerAdapter<String> adapter = new MyIndexerAdapter<String>(
  42.                                 getApplicationContext(), android.R.layout.simple_list_item_1,
  43.                                 elements);
  44.                 myListView.setAdapter(adapter);
  45.  
  46.         }
  47.  
  48.         class MyIndexerAdapter<T> extends ArrayAdapter<T> implements SectionIndexer {
  49.  
  50.                 ArrayList<String> myElements;
  51.                 HashMap<String, Integer> alphaIndexer;
  52.  
  53.                 String[] sections;
  54.  
  55.                 public MyIndexerAdapter(Context context, int textViewResourceId,
  56.                                 List<T> objects) {
  57.                         super(context, textViewResourceId, objects);
  58.                         myElements = (ArrayList<String>) objects;
  59.                         // here is the tricky stuff
  60.                         alphaIndexer = new HashMap<String, Integer>();
  61.                         // in this hashmap we will store here the positions for
  62.                         // the sections
  63.  
  64.                         int size = elements.size();
  65.                         for (int i = size - 1; i >= 0; i--) {
  66.                                 String element = elements.get(i);
  67.                                 alphaIndexer.put(element.substring(0, 1), i);
  68.                         //We store the first letter of the word, and its index.
  69.                         //The Hashmap will replace the value for identical keys are putted in
  70.                         }
  71.  
  72.                         // now we have an hashmap containing for each first-letter
  73.                         // sections(key), the index(value) in where this sections begins
  74.  
  75.                         // we have now to build the sections(letters to be displayed)
  76.                         // array .it must contains the keys, and must (I do so...) be
  77.                         // ordered alphabetically
  78.  
  79.                         Set<String> keys = alphaIndexer.keySet(); // set of letters ...sets
  80.                         // cannot be sorted...
  81.  
  82.                         Iterator<String> it = keys.iterator();
  83.                         ArrayList<String> keyList = new ArrayList<String>(); // list can be
  84.                         // sorted
  85.  
  86.                         while (it.hasNext()) {
  87.                                 String key = it.next();
  88.                                 keyList.add(key);
  89.                         }
  90.  
  91.                         Collections.sort(keyList);
  92.  
  93.                         sections = new String[keyList.size()]; // simple conversion to an
  94.                         // array of object
  95.                         keyList.toArray(sections);
  96.  
  97.                         // ooOO00K !
  98.  
  99.                 }
  100.  
  101.                 @Override
  102.                 public int getPositionForSection(int section) {
  103.                         // Log.v("getPositionForSection", ""+section);
  104.                         String letter = sections[section];
  105.  
  106.                         return alphaIndexer.get(letter);
  107.                 }
  108.  
  109.                 @Override
  110.                 public int getSectionForPosition(int position) {
  111.  
  112.                         // you will notice it will be never called (right?)
  113.                         Log.v("getSectionForPosition", "called");
  114.                         return 0;
  115.                 }
  116.  
  117.                 @Override
  118.                 public Object[] getSections() {
  119.  
  120.                         return sections; // to string will be called each object, to display
  121.                         // the letter
  122.                 }
  123.  
  124.         }
  125. }
Parsed in 0.044 seconds, using GeSHi 1.0.8.4


Note: if your values are stored in a sorted cursor, there is an helper class called AlphabetIndexer that,
once instanced, will provide you methods to call (as return value) in the getsections/getsectionforposition/getpositionforsection
I wrote a tutorial on this topic tutusing_alphabetindexer_for_fastscrolling_listview-t10282.html



here is a short video
http://www.youtube.com/watch?v=t9oi04RMqsc

enjoy !
Last edited by qlimax on Wed Jan 13, 2010 8:41 pm, edited 3 times in total.
¯`·.¸¸.><((((º>¯`·.¸¸. ><((((º>
User avatar
qlimax
Master Developer
Master Developer
 
Posts: 271
Joined: Mon Aug 31, 2009 10:54 am
Location: Swiss

Top

Postby aminaz » Fri Jan 08, 2010 9:45 am

Thnks qlimax
Thanks a lot.
:))) u saved my day thk uuuuuuuuuuuuuuuuuuuuu
Last edited by aminaz on Fri Jan 08, 2010 6:54 pm, edited 1 time in total.
aminaz
Junior Developer
Junior Developer
 
Posts: 19
Joined: Thu Jan 07, 2010 5:07 pm

Postby michael1221988 » Fri Jan 08, 2010 6:47 pm

Thanks man! This works like a charm.
michael1221988
Junior Developer
Junior Developer
 
Posts: 12
Joined: Fri Jan 08, 2010 4:39 am

Postby aminaz » Fri Jan 08, 2010 6:55 pm

I move the definition of the classe adapter to another file but i obtain force close (the variable "elements", i declarte it as static variable in the main class)
Is it necessar that i declare the adapter class like the code u mentionnate?
i want to make another adopter because i want to load from my date base a cursor(name, celle phone and a photo of the contact ...) is it posible?


thnks a lot
aminaz
Junior Developer
Junior Developer
 
Posts: 19
Joined: Thu Jan 07, 2010 5:07 pm

Postby qlimax » Sat Jan 09, 2010 2:24 pm

aminaz wrote:I move the definition of the classe adapter to another file but i obtain force close (the variable "elements", i declarte it as static variable in the main class)
Is it necessar that i declare the adapter class like the code u mentionnate?
i want to make another adopter because i want to load from my date base a cursor(name, celle phone and a photo of the contact ...) is it posible?


thnks a lot


Of course you can declare it into another .java file,

just use myElements instead of elements
(in the tutorial the myElements object is not used, but it is just the same obj as elements)

Note: if your source elements are stored in a Cursor, take a look to this class
http://developer.android.com/reference/ ... dexer.html
can be useful...


BYE
¯`·.¸¸.><((((º>¯`·.¸¸. ><((((º>
User avatar
qlimax
Master Developer
Master Developer
 
Posts: 271
Joined: Mon Aug 31, 2009 10:54 am
Location: Swiss

Postby Droid123 » Sat Jan 09, 2010 7:01 pm

Could you please explain what this code does and how it works? Or maybe post some screenshots?
Droid123
Experienced Developer
Experienced Developer
 
Posts: 77
Joined: Sat Nov 21, 2009 1:21 pm

Top

Postby qlimax » Sat Jan 09, 2010 9:15 pm

Droid123 wrote:Could you please explain what this code does and how it works? Or maybe post some screenshots?

I uploaded a video :wink:
http://www.youtube.com/watch?v=t9oi04RMqsc

bye
¯`·.¸¸.><((((º>¯`·.¸¸. ><((((º>
User avatar
qlimax
Master Developer
Master Developer
 
Posts: 271
Joined: Mon Aug 31, 2009 10:54 am
Location: Swiss

Postby Zeba Momin » Tue Jan 12, 2010 8:03 am

Hi qlimax..thanks for this lovely tutorial..works like a charm :)

Can u plzzz also tell us how do we use AlphabetIndexer???
A small example will be of great help :)
Thank you
Zeba Momin
Developer
Developer
 
Posts: 49
Joined: Thu Sep 17, 2009 6:53 am

Postby achie1266 » Wed Jan 13, 2010 1:13 am

thanks again qlimax.

saved my time here. it works like a charm.

now I need to start working on alphabet indexing.
achie
User avatar
achie1266
Master Developer
Master Developer
 
Posts: 223
Joined: Mon Nov 09, 2009 10:56 pm
Location: Denver

Postby qlimax » Wed Jan 13, 2010 8:42 pm

@zeba momin & achie1266
I wrote a tutorial on how to use the AlphabetIndexer helper class
http://www.anddev.org/tutusing_alphabet ... 10282.html

BYE :wink:
¯`·.¸¸.><((((º>¯`·.¸¸. ><((((º>
User avatar
qlimax
Master Developer
Master Developer
 
Posts: 271
Joined: Mon Aug 31, 2009 10:54 am
Location: Swiss

section aren't update on update adapter

Postby ionelt » Thu Jan 28, 2010 9:02 am

I have some problem when I update the adapter.
The problem is that the “sections” aren't updated.
Thanks.
ionelt
Once Poster
Once Poster
 
Posts: 1
Joined: Thu Jan 28, 2010 8:48 am

Postby ivan_bur » Wed Apr 14, 2010 4:54 pm

Hi qlimax, do you know why alphabetical indexing not work when list items count less than 30 ?
ivan_bur
Once Poster
Once Poster
 
Posts: 1
Joined: Wed Apr 14, 2010 4:48 pm

Re: [Tut]Alphabetic FastScroll ListView - similar to Contact

Postby Mr. Ambiguous » Fri Jun 25, 2010 8:27 pm

I'm trying to adapt this FastScroll adapter to work with my ListView items that display more than just one line of text but it's giving some really strange behavior. It must have something with swapping out the ArrayAdapter<?> for BaseAdapter. The class I'm adapting, Contact, simply holds two strings Name and Number.

onCreate is pretty much a copy of the original:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. @Override
  2.         protected void onCreate(Bundle savedInstanceState) {
  3.                 super.onCreate(savedInstanceState);
  4.                 setContentView(R.layout.address_book);
  5.                
  6.                 // elements
  7.                 String s = "QWERTZUIOPASDFGHJKLYXCVBNM";
  8.                 Random r = new Random();
  9.                 ArrayList<Contact>elements = new ArrayList<Contact>();
  10.                 for (int i = 0; i < 50; i++) {
  11.  
  12.                         elements.add(new Contact(s.substring(r.nextInt(s.length())), String.valueOf(r.nextLong())));
  13.  
  14.                 }
  15.                
  16.                 ListView myListView = (ListView) findViewById(R.id.contacts);
  17.                 myListView.setFastScrollEnabled(true);
  18.                 IndexerAdapter ia = new IndexerAdapter(elements);
  19.                 myListView.setAdapter(ia);             
  20.         }
Parsed in 0.033 seconds, using GeSHi 1.0.8.4


But IndexerAdapter must be where the problem is:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         class IndexerAdapter extends BaseAdapter implements SectionIndexer {
  2.  
  3.                 ArrayList<Contact> mContacts;
  4.                 HashMap<String, Integer> alphaIndexer;
  5.  
  6.                 String[] sections;
  7.                
  8.                 public IndexerAdapter(ArrayList<Contact> contacts) {
  9.                         alphaIndexer = new HashMap<String, Integer>();
  10.                         mContacts = contacts;
  11.                        
  12.                         // elements must be sorted
  13.                         Collections.sort(mContacts);
  14.                        
  15.                         // Put the first letters of each element into a map
  16.                         for (int i = mContacts.size() - 1; i >= 0; i--) {
  17.                                 String element = mContacts.get(i).getName();
  18.                                 alphaIndexer.put(element.substring(0, 1), i);
  19.                         }
  20.                        
  21.                         // now we have an hashmap containing for each first-letter
  22.                         // sections(key), the index(value) in where this sections begins
  23.  
  24.                         // we have now to build the sections(letters to be displayed)
  25.                         // array .it must contains the keys, and must (I do so...) be
  26.                         // ordered alphabetically
  27.                        
  28.                         Set<String> keys = alphaIndexer.keySet(); // set of letters
  29.                        
  30.                         // Copy into a List for sorting
  31.                         Iterator<String> it = keys.iterator();
  32.                         ArrayList<String> keyList = new ArrayList<String>();
  33.                         while (it.hasNext()) {
  34.                                 String key = it.next();
  35.                                 keyList.add(key);
  36.                         }
  37.                         Collections.sort(keyList);
  38.                        
  39.                         // Convert to array
  40.                         sections = new String[keyList.size()];
  41.                         keyList.toArray(sections);
  42.                 }
  43.  
  44.                 @Override
  45.                 public int getPositionForSection(int section) {
  46.                         String letter = sections[section];
  47.  
  48.                         return alphaIndexer.get(letter);
  49.                 }
  50.  
  51.                 @Override
  52.                 public int getSectionForPosition(int position) {
  53.                         return 0;
  54.                 }
  55.  
  56.                 @Override
  57.                 public Object[] getSections() {
  58.                         return sections;
  59.                 }
  60.  
  61.                 @Override
  62.                 public int getCount() {
  63.                         return mContacts.size();
  64.                 }
  65.  
  66.                 @Override
  67.                 public Object getItem(int position) {
  68.                         return mContacts.get(position);
  69.                 }
  70.  
  71.                 @Override
  72.                 public long getItemId(int position) {
  73.                         return position;
  74.                 }
  75.  
  76.                 @Override
  77.                 public View getView(int position, View convertView, ViewGroup parent) {
  78.                         View view;
  79.                 if(convertView == null){
  80.                         LayoutInflater li = getLayoutInflater();
  81.                         view = li.inflate(R.layout.groups_item, null);
  82.  
  83.                         TextView label = (TextView)view.findViewById(R.id.groups_item_title);
  84.                         label.setText(mContacts.get(position).getName());
  85.  
  86.                         label = (TextView)view.findViewById(R.id.groups_item_subtitle);
  87.                         label.setText(mContacts.get(position).getNumber());
  88.                 }
  89.                 else
  90.                 {
  91.                         view = convertView;
  92.                 }
  93.                 return view;
  94.                 }
  95.                
  96.         }
Parsed in 0.043 seconds, using GeSHi 1.0.8.4


And the contact class:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         public class Contact implements Comparable<Contact> {
  2.                 private String mName;
  3.                 private String mNumber;
  4.                
  5.                 public Contact(String name, String number) {
  6.                         mName = name;
  7.                         mNumber = number;
  8.                 }
  9.                
  10.                 public String getName() { return mName; }
  11.                 public String getNumber() { return mNumber; }
  12.                 public String toString() { return mName; }
  13.  
  14.                 @Override
  15.                 public int compareTo(Contact another) {
  16.                         int diff = mName.compareToIgnoreCase(another.mName);
  17.                         if (diff < 0)
  18.                             return -1;
  19.                         else if (diff > 0)
  20.                             return 1;
  21.                         else
  22.                             return 0;
  23.                 }
  24.  
  25.         }
Parsed in 0.037 seconds, using GeSHi 1.0.8.4


The strange behavior that's happening is that the ListView repeats itself after the first visible elements and scrolling the list causes items off-screen to randomly move around.
Mr. Ambiguous
Freshman
Freshman
 
Posts: 2
Joined: Fri Jun 25, 2010 8:10 pm

Re: [Tut]Alphabetic FastScroll ListView - similar to Contact

Postby Mr. Ambiguous » Fri Jun 25, 2010 8:53 pm

Whoa I just realized that I've been using this same BaseAdapter code for other lists (Without the SectionIndexer) but I only once tested them with more than a few elements going off screen, and when I did make the large list, I just repeatedly added the same two items over and over so I never would have noticed that things were getting out of order whenever I scrolled the list quickly. So I guess the question becomes, what's wrong with my code for extending BaseAdapter?
Mr. Ambiguous
Freshman
Freshman
 
Posts: 2
Joined: Fri Jun 25, 2010 8:10 pm

Re: [Tut]Alphabetic FastScroll ListView - similar to Contact

Postby raj_genx » Sun Sep 26, 2010 7:40 am

Thanks a lot, qlimax .

One more query, how did you recorded video for youtube ? Plz... ;)

Thanks,
Kautilya
raj_genx
Once Poster
Once Poster
 
Posts: 1
Joined: Sun Sep 26, 2010 7:35 am

Top
Next

Return to Advanced Tutorials

Who is online

Users browsing this forum: No registered users and 3 guests