37

I've looked at a lot of tutorials for making a ListView have the alphabetical letters on the side (like the Contacts list), but they all seem to using a ListActivity class and/or data from a database while I'm just using a ListView (no special Activity) and an ArrayList of data. Does anyone know how I can implement that alphabetical-scroll feature in the Contacts list for my own ListView?

Edited Again

I followed this tutorial, which I thought would finally make it work, but I'm still getting a forced close.

class AlphabeticalAdapter extends ArrayAdapter<String> implements SectionIndexer
{
    private HashMap<String, Integer> alphaIndexer;
    private String[] sections;

    public AlphabeticalAdapter(Context c, int resource, List<String> data)
    {
        super(c, resource, data);
        for (int i = 0; i < data.size(); i++)
        {
            String s = data.get(i).substring(0, 1).toUpperCase();
            alphaIndexer.put(s, i);
        }

        Set<String> sectionLetters = alphaIndexer.keySet();
        ArrayList<String> sectionList = new ArrayList<String>(sectionLetters);
        Collections.sort(sectionList);
        sections = new String[sectionList.size()];
        sectionList.toArray(sections);

    }

    public int getPositionForSection(int section)
    {   
        return alphaIndexer.get(sections[section]);
    }

    public int getSectionForPosition(int position)
    {
        return 1;
    }

    public Object[] getSections()
    {
        return sections;
    }
}

In the LogCat, it says java.lang.RuntimeException: Unable to resume activity {(package of another app I made) android.app.SuperNotCalledExcpetion. I thought that was really weird because I don't reference that other app at all in the one I'm working with. I tried uninstalling that other app, still got the forced close, but I could see what the LogCat said because it didn't update.

Final Edit

Here is the working code. Sorry for posting it something like 9 months overdue.

class AlphabeticalAdapter extends ArrayAdapter<String> implements SectionIndexer
{
    private HashMap<String, Integer> alphaIndexer;
    private String[] sections;

    public AlphabeticalAdapter(Context c, int resource, List<String> data)
    {
        super(c, resource, data);
        alphaIndexer = new HashMap<String, Integer>();
        for (int i = 0; i < data.size(); i++)
        {
            String s = data.get(i).substring(0, 1).toUpperCase();
            if (!alphaIndexer.containsKey(s))
                alphaIndexer.put(s, i);
        }

        Set<String> sectionLetters = alphaIndexer.keySet();
        ArrayList<String> sectionList = new ArrayList<String>(sectionLetters);
        Collections.sort(sectionList);
        sections = new String[sectionList.size()];
        for (int i = 0; i < sectionList.size(); i++)
            sections[i] = sectionList.get(i);   
    }

    public int getPositionForSection(int section)
    {   
        return alphaIndexer.get(sections[section]);
    }

    public int getSectionForPosition(int position)
    {
        for ( int i = sections.length - 1; i >= 0; i-- ) {
            if ( position >= alphaIndexer.get( sections[ i ] ) ) {
                return i;
            }
        }
        return 0;
    }

    public Object[] getSections()
    {
        return sections;
    }
}

Edit : getSectionForPosition is important if you want your scroller appear in the right place while you are scrolling. Returning only 1 (or 0) tells that you are just scrolling in the first (or second) section which will result in scroller's wrong position (mostly goes out of screen). Added code returns the appropriate section so scroller can know where it actually is.

Pat Needham
  • 5,698
  • 7
  • 43
  • 63

2 Answers2

21

I forgot to instantiate alphaIndexer. Works perfectly now.

class AlphabeticalAdapter extends ArrayAdapter<String> implements SectionIndexer
{
    private HashMap<String, Integer> alphaIndexer;
    private String[] sections;

    public AlphabeticalAdapter(Context c, int resource, List<String> data)
    {
        super(c, resource, data);
        alphaIndexer = new HashMap<String, Integer>();
        for (int i = 0; i < data.size(); i++)
        {
            String s = data.get(i).substring(0, 1).toUpperCase();
            if (!alphaIndexer.containsKey(s))
                alphaIndexer.put(s, i);
        }

        Set<String> sectionLetters = alphaIndexer.keySet();
        ArrayList<String> sectionList = new ArrayList<String>(sectionLetters);
        Collections.sort(sectionList);
        sections = new String[sectionList.size()];
        for (int i = 0; i < sectionList.size(); i++)
            sections[i] = sectionList.get(i);   
    }

    public int getPositionForSection(int section)
    {   
        return alphaIndexer.get(sections[section]);
    }

    public int getSectionForPosition(int position)
    {
        return 1;
    }

    public Object[] getSections()
    {
        return sections;
    }
}
Muhannad A.Alhariri
  • 3,702
  • 4
  • 30
  • 46
Pat Needham
  • 5,698
  • 7
  • 43
  • 63
3

If I understand you correctly, you want to set fastScrollEnabled=true and that will give you the little thumb scroller on the right. If you want to add a floating letter like the one in contacts, there is a great example in the APIDemos project in List9.java (http://www.devdaily.com/java/jwarehouse/android-examples/platforms/android-2/samples/ApiDemos/src/com/example/android/apis/view/List9.java.shtml)

Sashi Kolli
  • 751
  • 5
  • 6
  • I think they are asking for actually seeing A, B, C, etc, similar to the iPhone contacts list UI. A custom scrollbar widget type thing. – i_am_jorf Aug 20 '11 at 02:27
  • There's a section and a visible dialog during scroll. And fastScrollEnabled is set to true on ListView. – Harald Wilhelm Aug 20 '11 at 06:15