效果图如下:
首先看看布局文件,自定义的控件中包含一个 ListView,用于显示具体的数据内容:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginTop="20dp" android:background="#fffafafa" android:orientation="vertical" > <com.common.myletterview.LetterFilterListView android:id="@+id/letterView" android:layout_width="wrap_content" android:layout_height="fill_parent" > <ListView android:id="@+id/listView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:cacheColorHint="#00000000" android:divider="@null"/> </com.common.myletterview.LetterFilterListView> </LinearLayout>
加载完 xml 中的 View 后,在自定义控件中添加靠右显示的一列字母控件、点击那列字母后的显示字母控件,这两个子控件均继承自 View,均采用自绘。
右边一列字母显示控件的绘制如下:
/** * 初始化. */ private void init() { mLetters = new char[] { ‘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‘, ‘#‘ }; mPaint = new Paint(); mPaint.setColor(Color.parseColor("#949494")); mPaint.setTypeface(Typeface.DEFAULT_BOLD); mPaint.setTextSize(22); mPaint.setAntiAlias(true); mPaint.setTextAlign(Paint.Align.CENTER); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float height = getHeight(); mSingleHeight = height / mLetters.length; mWidthCenter = getMeasuredWidth() / (float) 2; for (int i = 0; i < mLetters.length; i++) { canvas.drawText(String.valueOf(mLetters[i]), mWidthCenter, mSingleHeight + (i * mSingleHeight), mPaint); } }
自定义实体实现每项内容与内容首字母的捆绑:
public class Letter implements Comparable<Letter> { private int id; private String name;//名字 private String firstLetter;//名字首字母 public Letter() { super(); } public Letter(int id, String name, String firstLetter) { super(); this.id = id; this.name = name; this.firstLetter = firstLetter; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } @Override public int compareTo(Letter another) { if (this.getFirstLetter().equals("@") || another.getFirstLetter().equals("#")) { return -1; } else if (this.getFirstLetter().equals("#") || another.getFirstLetter().equals("@")) { return 1; } else { return this.getFirstLetter().compareTo(another.getFirstLetter()); } } }
适配器实现 SectionIndexer 接口,接口中的两个方法实现是关键,具体如下:
/** * 根据ListView的当前位置获取分类的首字母的Char ascii值 */ public int getSectionForPosition(int position) { return mList.get(position).getFirstLetter().charAt(0); } /** * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置 */ public int getPositionForSection(int section) { for (int i = 0; i < getCount(); i++) { String sortStr = mList.get(i).getFirstLetter(); char firstChar = sortStr.toUpperCase().charAt(0); if (firstChar == section) { return i; } } return -1; }
适配器每个 Item 包括字母行跟内容行,如果当前位置等于该分类首字母的Char的位置 ,则显示字母行跟内容行,否则只显示出内容行,具体代码如下:
//根据position获取分类的首字母的Char ascii值 int section = getSectionForPosition(position); //如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现 if(position == getPositionForSection(section)){ viewHolder.tvLetter.setVisibility(View.VISIBLE); viewHolder.tvLetter.setText(letter.getFirstLetter()); }else{ viewHolder.tvLetter.setVisibility(View.GONE); }
在点击右边字母列表的时候,根据点击的位置重新计算点击了那个字母,从而作相应的定位,并显示选中的字母:
public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); int index = 0;//点击的位置在 mLetters 中的索引 int i = (int) event.getY(); int div = (int) mSingleHeight; /** 重新计算出索引 */ if (div != 0) { index = i / div; } if (index >= mLetters.length) { index = mLetters.length - 1; } else if (index < 0) { index = 0; } switch (event.getAction()) { case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: mLetterSelectedView.setViewY(mSingleHeight + (index * mSingleHeight));//设置选中字母显示的y轴位置 mLetterSelectedView.setSelectedLetter(mLetters[index]);//设置选中的字母 if (mLetterSelectedView.getVisibility() == View.GONE)//若控件为隐藏状态,则显示 mLetterSelectedView.setVisibility(View.VISIBLE); /** 显示1s后消失*/ mLetterSelectedView.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub mLetterSelectedView.setVisibility(View.GONE); } }, 1000); if (mListView.getAdapter() != null) { ListAdapter listAdapter = (ListAdapter) mListView.getAdapter(); if (mSectionIndexter == null) { mSectionIndexter = (SectionIndexer) listAdapter; } int position = mSectionIndexter.getPositionForSection(mLetters[index]); if (position == -1) {//列表中没有首字母为选中字母的的项 return true; } mListView.setSelection(position); } } return true; }
仅仅看此博客可能很难理解具体的实现,大家可以结合完整的代码查看,完整代码:自定义快速查找字母控件
时间: 2024-10-24 21:53:03