在开发app的过程中,如果用到通讯录或者类似的列表,需要快速在其中定位,可以根据列表项的拼音首字母来定位,这时候就需要用到右侧字母索引了。必如现在的微信通讯录界面就是如此。在实现这种功能的过程中,还是挺复杂的,很难我觉得。在网上各种查找资料,困难重重,好在最后终于捯饬出来了,伤不起。。。。特此记录一下写的过程。
1、创建自定的view,用作右侧列表索引。
public class RulerWidget 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" }; public static int INDEX_LENGTH = indexStr.length; OnTouchingLetterChangedListener onTouchingLetterChangedListener; Paint mPaint = new Paint(); boolean showBkg = false; int choose = -1; public RulerWidget(Context context) { super(context); } public RulerWidget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public RulerWidget(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // if(showBkg){ canvas.drawColor(Color.parseColor("#40000000")); // } int height = getHeight(); int width = getWidth(); int singleHeight = height / indexStr.length; for(int i=0;i<indexStr.length;i++){ mPaint.setColor(Color.WHITE); mPaint.setTextSize(24); mPaint.setTypeface(Typeface.DEFAULT_BOLD); mPaint.setAntiAlias(true); if(i == choose){ mPaint.setColor(Color.parseColor("#3399ff")); mPaint.setFakeBoldText(true); } float xPos = width/2 - mPaint.measureText(indexStr[i])/2; float yPos = singleHeight * i + singleHeight; canvas.drawText(indexStr[i], xPos, yPos, mPaint); mPaint.reset(); } } @Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); final float y = event.getY(); final int oldChoose = choose; final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; final int c = (int) (y/getHeight()*indexStr.length); switch (action) { case MotionEvent.ACTION_DOWN: showBkg = true; if(oldChoose != c && listener != null){ if(c > 0 && c< indexStr.length){ listener.onTouchingLetterChanged(indexStr[c]); choose = c; invalidate(); } } break; case MotionEvent.ACTION_MOVE: if(oldChoose != c && listener != null){ if(c > 0 && c< indexStr.length){ listener.onTouchingLetterChanged(indexStr[c]); choose = c; invalidate(); } } break; case MotionEvent.ACTION_UP: // showBkg = false; choose = -1; invalidate(); break; } return true; } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } public void setOnTouchingLetterChangedListener( OnTouchingLetterChangedListener onTouchingLetterChangedListener) { this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; } }
对应的回调接口定义:
/** * @date 2014-9-3 * @Description: ruler触摸回调 */ public interface OnTouchingLetterChangedListener{ public void onTouchingLetterChanged(String s); }
2、创建fragment片段对应的布局文件:
<?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="vertical" > <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" > <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:fadingEdge="none" android:scrollbars="none" > </ListView> <TextView android:id="@+id/tv" android:layout_width="60dp" android:layout_height="60dp" android:gravity="center" android:background="#f0606060" android:layout_gravity="center" android:text="A" android:textColor="#ffffff" android:textSize="30sp" /> <com.hy.ticket.view.RulerWidget android:id="@+id/sidrbar" android:layout_width="30.0dip" android:layout_height="fill_parent" android:layout_gravity="right|center" /> </FrameLayout> </LinearLayout>
3、引入pinyin4j.jar包(版本建议使用最新),写一个工具类,包含获取中文字符串拼音首字母,获取中文拼音等方法。
public class StringHelper { public static String getPingYin(String src) { char[] t = src.toCharArray(); String[] strs = new String[t.length]; HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.LOWERCASE); format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); format.setVCharType(HanyuPinyinVCharType.WITH_V); String str = ""; try{ for(int i=0; i<t.length; i++) { if(java.lang.Character.toString(t[i]).matches( "[\\u4E00-\\u9FA5]+")) { strs = PinyinHelper.toHanyuPinyinStringArray(t[i], format); str += strs[0]; }else{ str += java.lang.Character.toString(t[i]); } } //长沙,长春多音字处理 if(str.equals("zhangsha")) { str = "changsha"; } if(str.equals("zhangchun")) { str = "changchun"; } return str; }catch(Exception e) { e.printStackTrace(); } return str; } public static String getHeaderChar(String src) { String convert = ""; char word = src.charAt(0); String[] arr = PinyinHelper.toHanyuPinyinStringArray(word); if(arr != null) { convert += arr[0].charAt(0); }else{ convert += word; } return convert.toUpperCase(); } public static String getPinYinHeaderChar(String src) { String convert = ""; for(int i=0; i<src.length(); i++) { char word = src.charAt(i); String[] arr = PinyinHelper.toHanyuPinyinStringArray(word); if(arr != null) { convert += arr[0].charAt(0); }else{ convert += word; } } return convert.toUpperCase(); } }
4、建立一个工具类,用于根据首字母排序,获取大些字母及对应的中文字符串(如A 阿尔法 B 北京 C 长沙 长春 常州等):
public class SortUtil { public static List<Station> sortList(String[] srcNames, List<Station> list) { List<Station> newList = new ArrayList<Station>(); for(int i=0; i<srcNames.length; i++) { if(srcNames[i].length() != 1) { for(int j=0; j<list.size(); j++) { if(srcNames[i].equals(list.get(j).getPinYinName())) { Station s = new Station(list.get(j).getName(), list.get(j).getPinYinName()); newList.add(s); } } }else{ newList.add(new Station(srcNames[i])); } } return newList; } public static String[] sortIndex(List<Station> stations) { TreeSet<String> set = new TreeSet<String>(); // 获取初始化数据源中的首字母,添加到set中 for (Station station : stations) { set.add(StringHelper.getPinYinHeaderChar(station.getName()).substring( 0, 1)); } // 新数组的长度为原数据加上set的大小 String[] names = new String[stations.size() + set.size()]; int i = 0; for (String string : set) { names[i] = string; i++; } String[] pinYinNames = new String[stations.size()]; for (int j = 0; j < stations.size(); j++) { stations.get(j).setPinYinName( StringHelper .getPingYin(stations.get(j).getName().toString())); pinYinNames[j] = StringHelper.getPingYin(stations.get(j).getName() .toString()); } // 将原数据拷贝到新数据中 System.arraycopy(pinYinNames, 0, names, set.size(), pinYinNames.length); // 自动按照首字母排序 Arrays.sort(names, String.CASE_INSENSITIVE_ORDER); return names; } }
listViewAdapter,填充ListView的适配器:
public class ListViewAdapter extends BaseAdapter { private Context context; private List<Station> stations; private ViewHolder viewHolder; public ListViewAdapter(Context context, List<Station> stations) { this.context = context; this.stations = stations; } @Override public int getCount() { return stations.size(); } @Override public Object getItem(int position) { return stations.get(position); } @Override public long getItemId(int position) { return position; } @Override public boolean isEnabled(int position) { if (stations.get(position).getName().length() == 1)// 如果是字母索引 return false;// 表示不能点击 return super.isEnabled(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { String item = stations.get(position).getName(); viewHolder = new ViewHolder(); if(item.length() == 1) { convertView = LayoutInflater.from(context).inflate(R.layout.index, null); TextView indexTV = (TextView) convertView.findViewById(R.id.indexTV); indexTV.setText(stations.get(position).getName()); viewHolder.indexTV = indexTV; }else{ convertView = LayoutInflater.from(context).inflate(R.layout.item, null); TextView itemTV = (TextView) convertView.findViewById(R.id.itemTV); itemTV.setText(stations.get(position).getName()); viewHolder.itemTV = itemTV; } return convertView; } //内部类 private class ViewHolder { private TextView itemTV; private TextView indexTV; } }
5、最后一步,建立布局文件对应的fragment片段(或Activity,这里使用的是fragment):
public class ListStation extends Fragment { private Map<String, Integer> selector; private LinearLayout layoutIndex; private ListView listView; private TextView textView; private ListViewAdapter adapter; private String[] sections = { "#", "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 List<Station> stations; private List<Station> newStations; private int height; private boolean flag = false; private LinearLayout layout; private RulerWidget ruler; private Handler handler; private OverlayThread overlayThread; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.station_list, container, false); listView = (ListView) view.findViewById(R.id.listView); textView = (TextView) view.findViewById(R.id.tv); ruler = (RulerWidget) view.findViewById(R.id.sidrbar); ruler.setOnTouchingLetterChangedListener(new LetterListViewListener()); initOverlay(); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onCreate(savedInstanceState); stations = StationService.getAllStation(); String[] allNames = SortUtil.sortIndex(stations); newStations = SortUtil.sortList(allNames, stations); //这个map是建立大写字母对应位于listView位置的索引 selector = new HashMap<String, Integer>(); for(int i=0; i<allNames.length; i++) { if(allNames[i].length() == 1) { selector.put(allNames[i], i); } } adapter = new ListViewAdapter(getActivity(), newStations); listView.setAdapter(adapter); handler = new Handler(); overlayThread = new OverlayThread(); } //初始化汉语拼音首字母弹出提示框 private void initOverlay() { LayoutInflater inflater = LayoutInflater.from(getActivity()); // overlay = (TextView) inflater.inflate(R.layout.overlay, null); textView.setVisibility(View.INVISIBLE); WindowManager.LayoutParams lp = new WindowManager.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); WindowManager windowManager = (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE); // windowManager.addView(textView, lp); } private class LetterListViewListener implements OnTouchingLetterChangedListener{ @Override public void onTouchingLetterChanged(final String s) { if(selector.get(s) != null) { int position = selector.get(s); listView.setSelection(position); textView.setText(s); textView.setVisibility(View.VISIBLE); handler.removeCallbacks(overlayThread); //延迟一秒后执行,让overlay为不可见 handler.postDelayed(overlayThread, 1500); } } } //设置overlay不可见 private class OverlayThread implements Runnable { @Override public void run() { textView.setVisibility(View.GONE); } } /*class GetDataAsyTask extends AsyncTask { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Object doInBackground(Object... params) { stations = StationService.getAllStation(); String[] allNames = SortUtil.sortIndex(stations); newStations = SortUtil.sortList(allNames, stations); if(newStations != null) { newStations.clear(); newStations = null; } if(stations != null) { stations.clear(); stations = null; } if(allNames != null) { allNames = null; } return null; } @Override protected void onPostExecute(Object result) { // super.onPostExecute(result); if(newStations == null) { // textView.setVisibility(View.VISIBLE); return; } handleSuccessData(); } } private Integer getPosition(final int j) { Integer pos = null; int i = j; while (pos == null && i <= RulerWidget.indexStr.length - 1) { pos = selector.get(RulerWidget.indexStr[i]); i++; } if (pos == null) { pos = newStations.size() - 1; } return pos; } private void handleSuccessData() { listView.setVisibility(View.VISIBLE); ruler.setVisibility(View.VISIBLE); adapter = new ListViewAdapter(getActivity(), newStations); ruler.setOnRulerTouch(new OnRulerTouch() { @Override public void onUP() { } @Override public void onOthers() { } @Override public void onMove(int position) { textView.setText(RulerWidget.indexStr[position]); listView.setSelection(getPosition(position)); } @Override public void onDown(int position) { textView.setVisibility(View.VISIBLE); textView.setText(RulerWidget.indexStr[position]); listView.setSelection(getPosition(position)); } }); listView.setAdapter(adapter); adapter.notifyDataSetChanged(); } */ }
这样,带拼音索引的listView建立完成。。
相关推荐
一、效果:我们看到很多软件的通讯录在右侧都有一个字母索引功能,像微信,小米通讯录,QQ,还有美团选择地区等等。这里我截了一张美团选择城市的图片来看看; 我们今天就来实现图片中右侧模块的索引功能,包括触摸...
Android代码 实现带字母索引效果的listview列表, 类似Android 通讯录中使用的检索联系人得效果.滑动listview时 旁边会出现一个字母索引条,手指滑动索引条 屏幕中间会出现当前索引字母,同时listview内容会被过滤...
在Android中,我们可以使用`SlidingPaneLayout`或`DrawerLayout`作为基础,然后进行定制化开发,添加触摸事件监听,实现滑动动画,以及根据当前选中的字母更新联系人列表。 接着,我们需要使用`RecyclerView`来展示...
在Android应用开发中,创建一个类似手机联系人应用的界面是一项常见的任务,其中包括对数据进行字母排序并实现字母索引定位的功能。这个功能允许用户快速浏览和定位到以特定字母开头的数据项,提高用户体验。本篇...
3. **创建索引**:将所有条目的首字母拼音收集到一个新的List中,并去重,形成A-Z的字母索引列表。 4. **实现Adapter**:创建一个自定义的ArrayAdapter或CursorAdapter,扩展其功能以支持拼音检索。在getView()方法...
在ListView的右侧添加一个垂直的字母索引栏,通常称为侧滑栏或滑动索引。当用户触摸字母时,它会高亮并滚动ListView到相应的分组。这个栏可以自定义实现,也可以使用第三方库如`android-letters-view`。 7. **优化...
这篇教程“android 通讯录(A-Z)带可模糊查询搜索框”着重讲解如何创建一个能够进行字母索引排序和模糊查询的通讯录界面。在这个过程中,开发者会接触到Android的UI设计、数据处理以及搜索功能的实现。 首先,UI...
记得在我刚开始接触到美团网的时候就对美团网这个城市定位、选择城市功能很感兴趣,觉得它做得很...(3)下面是一个ListView用来显示数据列表,右侧是一个字母索引表,当我们点击不同的字母,ListView会定位到该字母地方
要实现右侧的字母索引,我们首先需要处理的是将联系人姓名转换为拼音。这里提到了“pinyin4j”包,这是一个Java库,用于汉字转拼音。在项目中引入pinyin4j后,我们可以对每个联系人的姓名进行处理,获取其首字母,...
在Android开发中,实现类似微信联系人索引列表的功能是一项常见的需求,主要是为了方便用户快速定位到特定的联系人。这个功能通常包括一个字母栏,显示A到Z的字母,以及一个按照字母顺序排列的列表。以下是实现这个...
然后进入正题:自定义View实现右侧索引导航栏IndexBar,对数据源的排序字段按照拼音排序,最后将RecyclerView和IndexBar联动起来,触摸IndexBar上相应字母,RecyclerView滚动到相应位置。(在屏幕中间显示的其实就是...
通讯录应用通常会按照联系人姓名的首字母进行分组,用户可以通过点击屏幕右侧的字母栏快速跳转到对应的字母部分。这种设计被称为“快速索引”或“字母滚动条”。 在Android中,实现这一功能主要涉及以下知识点: 1...
创建一个比较器类,重写`compare()`方法,比较两个对象(通常是联系人实体类)的首字母拼音对应的ASCII值。如果首字母是字母,则根据ASCII值排序;如果不是字母,将其排在字母之后。这样,整个列表就会按照拼音的首...
在这个案例中,我们需要创建一个自定义的ListView或RecyclerView,以显示联系人列表,并在右侧添加一个A-Z的字母导航栏。这可以通过继承这些视图并重写相关方法来实现,例如`onDraw()`来绘制额外的字母导航栏。 2. ...
标题“类似于联系人列表的那种选择国家和地区”所描述的就是这样一个界面,它具有字母索引功能,用户可以通过点击右侧的字母快速定位到所需国家或地区。 首先,我们需要创建一个数据结构来存储国家和地区的相关信息...
它通常出现在通讯录界面的右侧,显示A到Z的字母,用户只需点击相应字母,就能跳转到该字母开头的联系人列表。这一功能对于拥有大量联系人的用户来说非常实用,因为它极大地减少了手动滑动屏幕寻找特定联系人的时间。...
列表根据首字符的拼音字母来排序,且可以通过侧边栏的字母索引来进行定位。 实现这样一个效果并不难,只要自定义一个索引View,然后引入一个可以对汉字进行拼音解析的jar包——pinyin4j-2.5.0即可 首先,先来定义侧...
本项目旨在实现一个功能完善的Android城市列表,该列表按照字母顺序排列,并且右侧带有字母索引,点击字母可快速跳转至相应字母开头的城市。以下是关于这个功能的详细实现步骤和涉及的关键知识点。 首先,我们需要...
接着,我们创建`SideBar`类,这是一个自定义的View,用于在ListView右侧显示字母索引。`SideBar`包含一个触摸监听器接口`OnTouchingLetterChangedListener`,当用户点击字母时,会触发回调方法`...