• Android学习系列(15)App列表之游标ListView(索引ListView)


    游标ListView,提供索引标签,使用户能够快速定位列表项。
          也可以叫索引ListView,有的人称也为Tweaked ListView,可能更形象些吧。
          一看图啥都懂了:

    1.游标(Fast scroll thumb)
          就是右边的那个拖动的方块,这个非常的简单:

    1
    2
    3
    4
    5
    <ListView
        android:id="@+id/tweaked_list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:fastScrollEnabled="true"/>

      也可以用在java后台书写:

    1
    tweakedListView.setFastScrollEnabled(true);

      在数据量有一定大的时候,滑动列表,就会出现右边的所谓的"游标"了。
          简单,这也是我为什么私下里喜欢自己写控件,但是工作中却喜欢用通用控件。
          我们看下源代码,其实就是启用FastScroller对象: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //启用FastScroller对象
    public void setFastScrollEnabled(boolean enabled) {
        mFastScrollEnabled = enabled;
        if (enabled) {
            if (mFastScroller == null) {
                mFastScroller = new FastScroller(getContext(), this);
            }
        } else {
            if (mFastScroller != null) {
                mFastScroller.stop();
                mFastScroller = null;
            }
        }
    }

    2.字母索引
         在Android学习系列(10)--App列表之拖拽ListView(上)中我们使用了一种WindowManager在ListView中添加一些自定义影像,这种方法我觉得一定是可行的。
       但是,android系统给我们提供了一个更简单的方法:使用AlphabetIndexer。
       AlphabetIndexer,实现了SectionIndexer接口,
    是adapter的一个辅助类,辅助实现在快滑时,显示索引字母。
       使用字母索引的话,必须保证数据列表是按字母顺序排序,以便AlphabetIndexerh采用二分查找法快速定位。

    1
    2
    3
    4
    5
    6
    /**
    * Cursor表示数据游标
    * sortedColumnIndex数据集合中的第几列
    * alphabet字母列表,用的最多的是"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    **/
    public AlphabetIndexer(Cursor cursor, int sortedColumnIndex, CharSequence alphabet) {}

      用到3个方法:

    1
    2
    3
    4
    //这三个方法,实现了索引数据和列表数据的对应和定位
    public int getPositionForSection(int section) {}
    public int getSectionForPosition(int position) {}
    public Object[] getSections() {}

    3.游标Cursor的实现
         Cursor接口的实现,有两种选择:
         (1).直接使用数据库查询返回的cursor
         (2).自定义实现Cursor接口的新类
         第一种方式很简单,查询一下数据库返回Cursor即可。
         这里我们以第二种方式实践,伪装一个Cursor,主要是实现3个方法:
         (1).getCount()
         (2). moveToPosition()
         (3). getString()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    /**
        * 伪装一个Cursor供AlphabetIndexer作数据索引源
        */
       private class IndexCursor implements Cursor{
            
           private ListAdapter adapter;
           private int position;
           private Map<String, String> map;
            
           public IndexCursor(ListAdapter adapter){
               this.adapter = adapter;
           }
     
           @Override
           public int getCount() {return this.adapter.getCount();}
            
           /**
            * 取得索引字母,这个方法非常重要,根据实际情况具体处理
            */
           @SuppressWarnings("unchecked")
           @Override
           public String getString(int columnIndex) {
               map = (HashMap<String, String>)adapter.getItem(position);
               return map.get(key).substring(0,1);
           }
            
           @Override
           public boolean moveToPosition(int position) {
               if(position<-1||position>getCount()){
                   return false;
               }
                
               this.position = position;
               //如果不满意位置有点向上偏的话,下面这几行代码是修复定位索引值为顶部项值的问题
               //if(position+2>getCount()){               
               //    this.position = position;
               //}else{
               //   this.position = position + 2;
               //}
               return true;
           }
            
           @Override
           public void close() {}
           @Override
           public void copyStringToBuffer(int arg0, CharArrayBuffer arg1) {}
           @Override
           public void deactivate() {}
           @Override
           public byte[] getBlob(int arg0) {return null;}
           @Override
           public int getColumnCount() {return 0;}
           @Override
           public int getColumnIndex(String columnName) {return 0;}
           @Override
           public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {return 0;}
           @Override
           public String getColumnName(int columnIndex) {return null;}
           @Override
           public String[] getColumnNames() {return null;}
           @Override
           public double getDouble(int columnIndex) {return 0;}
           @Override
           public Bundle getExtras() {return null;}
           @Override
           public float getFloat(int columnIndex) {return 0;}
           @Override
           public int getInt(int columnIndex) {return 0;}
           @Override
           public long getLong(int columnIndex) {return 0;}
           @Override
           public int getPosition() {return position;}
           @Override
           public short getShort(int columnIndex) {return 0;}
           @Override
           public boolean getWantsAllOnMoveCalls() {return false;}
           @Override
           public boolean isAfterLast() {return false;}
           @Override
           public boolean isBeforeFirst() {return false;}
           @Override
           public boolean isClosed() {return false;}
           @Override
           public boolean isFirst() {return false;}
           @Override
           public boolean isLast() {return false;}
           @Override
           public boolean isNull(int columnIndex) {return false;}
           @Override
           public boolean move(int offset) {return false;}
           @Override
           public boolean moveToFirst() {return false;}
           @Override
           public boolean moveToLast() {return false;}
           @Override
           public boolean moveToNext() {return false;}
           @Override
           public boolean moveToPrevious() {return false;}
           @Override
           public void registerContentObserver(ContentObserver observer) {}
           @Override
           public void registerDataSetObserver(DataSetObserver observer) {}
           @Override
           public boolean requery() {return false;}
           @Override
           public Bundle respond(Bundle extras) {return null;}
           @Override
           public void setNotificationUri(ContentResolver cr, Uri uri) {}
           @Override
           public void unregisterContentObserver(ContentObserver observer) {}
           @Override
           public void unregisterDataSetObserver(DataSetObserver observer) {}
            
       }

      这个类的实例就可作为AlphaIndexer的构造函数第一个参数数据游标。

    4.自定义Adapter的实现
          使用前面介绍的东西,我们来实现最终的IndexAdapter:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class IndexAdapter extends SimpleAdapter implements SectionIndexer{
         
        private AlphabetIndexer alphabetIndexer;
         
        public IndexAdapter(Context context,List<? extends Map<String, ?>> data, int resource,String[] from, int[] to) {
            super(context, data, resource, from, to);
            //设置数据游标
            //设置索引字母列表
            alphabetIndexer = new AlphabetIndexer(new IndexCursor(this), 0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        }
     
        @Override
        public Object[] getSections() {
            return alphabetIndexer.getSections();
        }
     
        @Override
        public int getPositionForSection(int section) {
            return alphabetIndexer.getPositionForSection(section);
        }
     
        @Override
        public int getSectionForPosition(int position) {
            return alphabetIndexer.getSectionForPosition(position);
        }
    }

    5.跑起来
         提供样本数据如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public List<Map<String, String>> getData(){
        List<Map<String, String>> itemList = new ArrayList<Map<String, String>>();
        String alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
         
        Map<String, String> map = null;
        for(char c:alphas.toCharArray()){
            for(int i=0; i<10; i++){               
                map = new HashMap<String, String>();
                map.put("itemText", ""+c+i);
                itemList.add(map);
            }
        }
     
        return itemList;
    }

      子项的布局文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="50dip"
        android:gravity="center_vertical"
        >
        <TextView
            android:id="@+id/tweaked_item_text"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

      使用并运行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tweake_list);
         
        tweakedListView = (ListView)findViewById(R.id.tweaked_list);
         
        //获取数据
        List<Map<String, String>> itemList = getData();
        ListAdapter adapter = new IndexAdapter(this, itemList, R.layout.tweake_list_item, new String[]{"itemText"}, new int[]{R.id.tweaked_item_text});
        tweakedListView.setAdapter(adapter);
    }

      效果如下:

    6.小结
          这种索引效果,在大数据量列表显示中非常的实用,是android开发必备常识。
          本文只是一个简单的sample,实际工作中肯定会需要进一步扩展定义:
          (1).对于复杂类型的处理,可根据Map<String,?>扩展自定义实体类,再通过adapter转换使用即可。
          (2).对于索引字母列表,可动态设置,举个例子,你的列表只有ABCD四个字母,如果索引字母列表还是设置“ABCDEFGHIJKLMNOPQRSTUVWXYZ”就不合适了,会有个索引偏位的问题。
          (3).对于复杂界面的显示,可重写adapter的getView方法自定义视图。

  • 相关阅读:
    课堂作业04 2017.10.27
    课程作业 03 动手动脑 2017.10.20
    课程作业 03 2017.10.20
    HDU 3974 Assign the task
    POJ 2155 Matrix
    POJ 2481 Cows
    HDU 3038 How Many Answers Are Wrong
    CS Academy Array Removal
    POJ_1330 Nearest Common Ancestors LCA
    CF Round 427 D. Palindromic characteristics
  • 原文地址:https://www.cnblogs.com/mingliangzhai/p/3042215.html
Copyright © 2020-2023  润新知