Android实现侧边栏按字母索引

用户需求

很多带有列表信息的控件,如ListView,RecyclerView,当需要显示大量信息的时候,
右侧都有一个竖直排列的字母表,手指滑动即可实现按字母定位,比如你手机里的系统联系人界面。

概念设计

  • 右侧的字母栏,实现View.OnTouchListener接口,当手指触摸滑动时,
    获取相对于触发这个事件的视图(这里即指字母栏)的左上点的坐标的纵坐标Y,是不是有点绕口。
  • 用当前的Y除以字母栏的高度height,得到一个浮点数index,这个浮点数其实就是在字母栏左上角为原点的坐标系下,你手指触摸位置所占的高度比例。
  • index乘以27,转换为整数,就得到了当前触摸位置的字母序号,我们再从一个字母字符串数组里,按序号匹配字母就好了。

是不是很疑惑为什么是27,而不是26?明明字母只有26个嘛!哈哈,如果你细心观察,你就会发现还有一个通配符#

详细设计

假如我们现在要显示的信息是联系人列表,那么我们的第一步,是把联系人姓名(汉子)转化为拼音,这个网上有很多教程,不是本文的重点,先不讲,假设我们已经写好了这个方法String.getPinYin().

初始化

LinearLayout alphabetLayout是一个带有字母的纵向线性布局,你可以用图片来做,也可以自己绘制,甚至可以放27个TextView,方法很多。

public class AlphabetSearch implements View.OnTouchListener{
    LinearLayout alphabetLayout;//显示字母的线性布局
    ListView listView; //包含信息的列表
    TextView current; //屏幕居中显示当前选中的字母,手指离开时隐藏
    List firstKey; //联系人数据list的首字母集合

    String alpabet[]={"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R",
                       "S","T","U","V","W","X","Y","Z","#"};
    float height;//alphabetLayout的高度

    public AlphabetSearch(LinearLayout alphabetLayout, ListView listView, TextView current){
        this.alphabetLayout = alphabetLayout;
        this.listView = listView;
        this.current = current;

获取字母栏高度

注意这里直接调用alphabetLayout.getHeight()是会报错的,这是很多新手会烦的错误,原因在于对Androdi视图绘制机制不了解,我们要等视图绘制完了,再获取高度。

        //这是个view事件的观察者。它的初始化就是调用View.getViewTreeObserver()。
        ViewTreeObserver vto = this.alphabetLayout.getViewTreeObserver();
        /**注册一个回调函数,当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时调用这个回调函数。
         *参数 listener  将要被添加的回调函数
         *异常 IllegalStateException  如果isAlive() 返回false
         */
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                height = AlphabetSearch.this.alphabetLayout.getHeight();
                AlphabetSearch.this.alphabetLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });
        alphabetLayout.setOnTouchListener(this);
    }

处理数据

至此构造方法写完了,但我们的List firstKey怎么来呢?让我们来加入数据。

/**
     * 获取联系人拼音首字母字符数组
     */
    public List getFirstKey(Listlist){
        List firstKey = new ArrayList<>();
        for (int i=0;i0,1);
            if(key.matches("[A-Z]")){
                firstKey.add(key);
            }else {
                firstKey.add("#");
            }
        }
        return firstKey;
    }

第一次需要加入联系人数据,也为了以后的数据更新,我们再写一个供外部调用更新数据的方法。

 public void whenListChange(Listlist){
        this.firstKey = getFirstKey(list);
    }

处理事件

接下来,处理监听事件

/**
     * 触摸事件
     * @param v
     * @param event
     * @return
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        current.setVisibility(View.VISIBLE);
        float Y = (event.getY()/height)*27;
        int index = (int)Y;
        if(index > -1 && index < 28) {//防止越界
            String key = alpabet[index];
            current.setText(key);
            if(firstKey.contains(key)){
                int position = firstKey.indexOf(key);
                listView.setSelectionFromTop(position,0);
            }
        }

        //手指抬起或者离开UI边界
        if (event.getAction() == MotionEvent.ACTION_UP||event.getAction()==MotionEvent.ACTION_OUTSIDE) {
            current.setVisibility(View.GONE);
        }
        return true;
    }
}

以上代码块,组在一起是一个完整的类,可以直接拿去用。

完成。

(function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();

你可能感兴趣的:(Android)