自定义联系人快速索引栏
Posted z8z87878
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义联系人快速索引栏相关的知识,希望对你有一定的参考价值。
——每天做一点,温故而知新
看看效果吧
额,我这个为了简单,简单的用了下toast,所以有三秒,有点长.没录到它消失的.大于两M.这不是重点,有兴趣的可以下载代码自己改.来看看这个索引栏栏怎么画的吧.相对于我们前面QQ消息拖动小球http://blog.csdn.net/z8z87878/article/details/51760032这个简单多了,我直接贴代码,看着代码说
/**
* Created by Root on 2016/6/28.
*/
public class IndexView extends View
public static String indexStr[]="*","#","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";
private int mHeight; //控件高度
private int mAvgHeight; //字母的平均高度
private Paint mPaint; //画笔
private int mWidth; //控件宽度
private Rect bounds; //字母所在的矩形
private int mTextHeight; //字母高度
private float mTextWidth; //字母宽度
private int touchIndex = -1; //触摸位置
public IndexView(Context context)
this(context,null);
public IndexView(Context context, AttributeSet attrs)
this(context, attrs,0);
public IndexView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true); //抗锯齿
mPaint.setDither(true); //防抖动
mPaint.setColor(Color.GRAY);
mPaint.setTextAlign(Paint.Align.CENTER); //字坐标为字底部中间
mPaint.setTypeface(Typeface.DEFAULT_BOLD); //粗体
mPaint.setTextSize(context.getResources().getDisplayMetrics().density * 13);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) //测量完成后会调用
super.onSizeChanged(w, h, oldw, oldh);
mHeight = getMeasuredHeight();//控件高度
mWidth = getMeasuredWidth(); //控件宽度
mAvgHeight = mHeight / indexStr.length ; //字母平均分到的高度
mTextWidth = mPaint.measureText(indexStr[5]);
Rect bounds = new Rect();
mPaint.getTextBounds(indexStr[5],0,indexStr[5].length(),bounds);//
mTextHeight = bounds.height();//假设字母都一样高吧,不要每个都去测量,也差不了多少
invalidate();
@Override
protected void onDraw(Canvas canvas)
for (int i = 0; i < indexStr.length; i++)
mPaint.setColor( touchIndex == i ? Color.RED : Color.GRAY); //触摸的画红色,默认灰色
//这里画的x,y是相对于视图的坐标系的.上次自定义的QQ消息拖动小球也是,不过它的视图是手机屏幕,所以看上去相对于手机坐标
canvas.drawText(indexStr[i],mWidth / 2.0f,mAvgHeight/2.0f + mTextHeight / 2.0f+ mAvgHeight * i,mPaint);
//参数1:字母 参数2:横坐标 参数3:纵坐标 参数4:画笔
private int tempIndex = -1;
@Override
public boolean onTouchEvent(MotionEvent event)
switch (event.getAction())
case MotionEvent.ACTION_DOWN:
tempIndex = (int) (event.getY() / mAvgHeight); //event.getY()视图坐标系,event.getRawY()手机坐标系
if (tempIndex != touchIndex)//有变化才回调
touchIndex = tempIndex;
if (mOnIndexChangeListener != null) //索引栏选中改变接口回调
mOnIndexChangeListener.onChange(indexStr[touchIndex]);
break;
case MotionEvent.ACTION_MOVE:
tempIndex = (int) (event.getY() / mAvgHeight);
if(tempIndex != touchIndex)
touchIndex = tempIndex;
if (mOnIndexChangeListener != null) //索引栏选中改变接口回调
mOnIndexChangeListener.onChange(indexStr[touchIndex]);
break;
case MotionEvent.ACTION_UP:
if (mOnIndexChangeListener != null)
mOnIndexChangeListener.onStop(); //停止滑动了.通知listView,防止跟listView滑动冲突
break;
invalidate(); //重绘调用onDraw()
return true;
public void setTouchIndex(int index)
touchIndex = index;
tempIndex = touchIndex;
invalidate();
public interface OnIndexChangeListener
void onChange(String str);
void onStop();
public OnIndexChangeListener mOnIndexChangeListener;
public void setOnIndexChangeListener(OnIndexChangeListener listener)
mOnIndexChangeListener = listener;
注释还行是不是O(∩_∩)O~,说下流程吧,构造函数我们初始化一些参数,然后接着就是onSizeChange了,这里我们可以拿到我们想要的各种宽高度,是这样的view是要先测量,布局,然后再onDraw画的,所以我们可以准确的画出我们要画的东西.画的时候,我们主要就是注意画的字母的位置,然后画的坐标是相对于我们的控件的坐标系的!!所以注意不要乱给坐标值,超出范围就看不到了.确定字母坐标位置,这个画个图分析下就是把高度分成多少个字母份,然后每个字母的位置,静下心分析下很快就出来了吧,所以这个控件还是很好画的.我们来看看布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ListView
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<com.it.indexview.view.IndexView
android:id="@+id/index"
android:layout_width="30dp"
android:layout_height="match_parent"
android:background="#aaf76d03"/>
</LinearLayout>
然后来看看这样不加listView是什么样吧
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
// initData();
initListener();
private void initView()
mIndexView = (IndexView) findViewById(R.id.index);
mListView = (ListView) findViewById(R.id.list);
mToast = new Toast(this);
mToast.setGravity(Gravity.CENTER, 0, 0);
mToast.setDuration(Toast.LENGTH_SHORT);
View view = View.inflate(this, R.layout.toast_layout, null);
mTextView = (TextView) view.findViewById(R.id.text_toast);
mToast.setView(view); //自定义toast
private boolean isIndexViewScroll = false;
private void initListener()
mIndexView.setOnIndexChangeListener(new IndexView.OnIndexChangeListener()
@Override
public void onChange(String str)
isIndexViewScroll = true;
showToast(str);
@Override
public void onStop()
isIndexViewScroll = false;
);
private void showToast(String str)
mTextView.setText(str);
mToast.show();
嗯,要加listView之前,我们的联系人应该排序啊,我们的手机联系人,我知道的应该是数字排前面,还有根据拼音来的吧,这是我准备的数据
private static String name[] = "哪一天", "女人", "南下", "噢噢噢", "欧阳", "藕断丝连",
"哦吧", "怕怕", "魄力", "彭大头", "胖大海", "剖析", "钱多多", "恰恰恰", "雀巢", "企鹅", "权志龙",
"日日日", "热热热", "容祖儿", "荣耀", "人生", "嘎嘎嘎", "龚大华", "化骨绵掌",
"哈哈", "赫连霸", "嗨嗨嗨", "将军", "间不容发", "夹克", "邵亮", "少年", "扫把", "时间", "深圳", "沈万三",
"团灭发动机", "坦荡荡", "潭水深千尺", "统一", "段子手", "独孤求败", "滴滴", "都敏俊", "都儿子", "鹅鹅鹅", "儿子",
"胡一刀", "胡大海", "胡圆圆", "福贝勒", "福尔康", "高晓松", "郜大头", "劣化", "嘞嘞嘞", "莫莫莫", "头衔", "无名之辈", "吴三岁", "五元",
"吴丽", "喂喂喂", "行行行", "咸鱼", "夏东海", "洗刷刷", "唐振浩", "1233123", "21312433212", "阿毛", "爱国", "阿达", "奥特曼", "步惊云",
"不哭死神",
"布衣", "卜算子",
"陈友谅", "程大宝", "趁早", "辰小二", "西门吹雪", "显卡", "赵无极", "赵日天",
"曾小强", "朱元璋", "中神通", "钟灵儿", "钟无艳", "炸了", "木子", "穆桂英",
"莫大头", "马自达", "码头", "能子", "农牧", "煎饼", "接口", "卡卡卡", "孔乙己", "孔夫子",
"空山鸟语", "流言蜚语", "刘尧儿子", "刘邦", "累累累", "渣渣渣";
我们给ListView这些数据前,我们应该对他们进行排序,这里介绍个包可以把汉语转换成拼音pinyin4j-2.5.6.jar
一般用法
/**
* Created by Root on 2016/6/28.
*/
public class PinYinUtil
public static String getPinying(String hanyu)
StringBuilder sb = new StringBuilder();
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); //拼音格式
format.setCaseType(HanyuPinyinCaseType.UPPERCASE); //大写
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); //不要声调
char[] chars = hanyu.toCharArray();
for (int i = 0; i < chars.length; i++)
char ch = chars[i];
if (Character.isSpaceChar(ch)) //如果是空格,跳过
continue;
if (ch >= -127 && ch < 128) //如果不是汉字,是数字,键盘上的字符不进行汉字转拼音
sb.append(ch);
else
String s ="";
try //多音字返回多个字母,这里只取第一个音,有一些生僻字字识别不了,抛异常
s = PinyinHelper.toHanyuPinyinStringArray(ch, format)[0];
sb.append(s);
catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination)
badHanyuPinyinOutputFormatCombination.printStackTrace();
sb.append(s);
return sb.toString();
这样我们就可以把汉字转化成拼音了,这就可以进行比较了.我们初始化数据
private void initData()
mPersonList = new ArrayList<Person>();
for (int i = 0; i < name.length; i++)
mPersonList.add(new Person(name[i]));
Collections.sort(mPersonList); //排序,person要实现Comparable<T>接口
// for (Person p : mPersonList)
// Log.d("MainActivity", p.toString());
//
mListView.setAdapter(new PerAdapt(this,mPersonList));
selectIndex(-1);
peison类实现Comparable接口
/**
* Created by Root on 2016/6/28.
*/
public class Person implements Comparable<Person>
public String name;
public String piny;
public Person(String name)
this.name = name;
this.piny = PinYinUtil.getPinying(name);
@Override
public String toString()
return "Person" +
"name='" + name + '\\'' +
", piny='" + piny + '\\'' +
'';
@Override
public int compareTo(Person another) //根据字符串比较
return this.piny.compareTo(another.piny);
这样我们的ListView就有序的展示数据了,接下来就是最后的监听了,listview滑动的时候要监听位置,索引栏滑动也要监听位置变化做出相应操作/这也是体力话.贴下代码吧.
private int selectIndex(int index) //返回索引栏字母对应的下标
char ch;
if (index < 0)
ch= mPersonList.get(mListView.getFirstVisiblePosition()).piny.charAt(0); //第一个可见条目对应的拼音首字母
else
ch = mPersonList.get(index).piny.charAt(0);
if (ch >= 48 && ch <= 57) //0~9对应的ascll码值
mIndexView.setTouchIndex(1);
return 1;
else //前两个,第一个我也不知道是撒,虽然手机联系人那有....第二个是数字,所以跳过从第三个开始循环
for (int i = 2; i < IndexView.indexStr.length; i++)
if(IndexView.indexStr[i].equals(String.valueOf(ch)))
mIndexView.setTouchIndex(i);
return i;
return 0;
private boolean isIndexViewScroll = false;
private void initListener()
mIndexView.setOnIndexChangeListener(new IndexView.OnIndexChangeListener()
@Override
public void onChange(String str)
isIndexViewScroll = true;//索引栏在滑动,通知listview不要影响我
showToast(str);
if ("#".equals(str))
mListView.smoothScrollToPosition(0); //数字都是在最上面吧
return;
for (int i = 0; i < mPersonList.size(); i++)
char c = mPersonList.get(i).piny.charAt(0);
if (str.equals(String.valueOf(c)))
mListView.setSelection(i); //滑到第一个找到的姓氏
break;
@Override
public void onStop()
isIndexViewScroll = false;
);
mListView.setOnScrollListener(new AbsListView.OnScrollListener()
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
int tempIndex = -1;
int index = -1;
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
if (!isIndexViewScroll) //索引栏不滑我才处理索引栏
tempIndex = selectIndex(firstVisibleItem);
if (tempIndex != index) //防止重复显示
index = tempIndex;
showToast(IndexView.indexStr[index]);
);
嗯,就到这里吧,有兴趣向下完整代码看的话,资源审核通过后,我会在评论那里提供链接
以上是关于自定义联系人快速索引栏的主要内容,如果未能解决你的问题,请参考以下文章