很多带有列表信息的控件,如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
怎么来呢?让我们来加入数据。
/**
* 获取联系人拼音首字母字符数组
*/
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); })();