Android 使用RecyclerView实现快速索引
2017-11-03 10:53
651 查看
之前做项目的时候遇到一个需求是实现品牌的字母排序功能,网上的资料很多,但是有一部分有bug,这篇文章是我学习和解决部分bug之后的总结。今天带来的是RecyclerView的A-Z字母排序和过滤搜索功能。
首先上效果图:
重点:1、实现数据排序分类 2、实现右侧的字母导航 3、搜索
这里使用了一个中文转化为拼音的工具包,即pinyin4j-2.5.0.jar。官网地址:http://pinyin4j.sourceforge.net/
布局顶部是一个带删除按钮的文本编辑框,我们在输入框中输入字母或汉字可以自动过滤出我们想要的东西,当输入框中没有数据自动替换到原来的数据列表,然后下面一个RecyclerView用来显示数据列表,右侧是一个字母索引表,其实就是一个自定的View,当我们点击不同的字母,RecyclerView会定位到该字母索引地item。
这是主界面布局代码:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
之前出现过两个问题,一个就是,每次进入界面后EditText自动获取焦点,导致输入法自动弹出来,所以为了解决这个问题在EditText的父布局也就是我们的根布局加了两个属性:android:focusable=”true”和android:focusableInTouchMode=”true”。
还有一个问题就是侧边栏会被输入法顶上去,结解决办法是在清单配置文件的对应Activity加上android:windowSoftInputMode=”adjustPan”,防止布局被输入法顶上去。
中间的TextView是用来显示选中的字母索引。
主界面的话主要是三个方法:
初始化的方法主要是对比较器的初始化,设置监听,对数据排序和对RecyclerView的初始化。
代码:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
接下来是将数据列表的内容按拼音排序的方法,先将汉字转化成拼音,在用正则表达式分类,下边是代码:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
最后就是根据输入的内容进行数据筛选的方法:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
RecyclerView适配器的代码,都比较简单就不用多解释了,直接上代码:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
PinyinComparator是个比较器类,主要就是根据ASCII码来对数据进行比较排序:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PinyinUtils是一个将中文转化为拼音的工具类,主要提供汉字转拼音的方法和获取首字母的方法:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
最后是我们的侧边栏SideBar的代码:
首先上效果图:
重点:1、实现数据排序分类 2、实现右侧的字母导航 3、搜索
这里使用了一个中文转化为拼音的工具包,即pinyin4j-2.5.0.jar。官网地址:http://pinyin4j.sourceforge.net/
布局顶部是一个带删除按钮的文本编辑框,我们在输入框中输入字母或汉字可以自动过滤出我们想要的东西,当输入框中没有数据自动替换到原来的数据列表,然后下面一个RecyclerView用来显示数据列表,右侧是一个字母索引表,其实就是一个自定的View,当我们点击不同的字母,RecyclerView会定位到该字母索引地item。
这是主界面布局代码:
<?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" android:focusable="true" android:focusableInTouchMode="true"> <com.xp.sortrecyclerview.ClearEditText android:id="@+id/filter_edit" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="8dp" android:background="#9DDE76" android:drawableLeft="@drawable/search_bar_icon_normal" android:hint="请输入关键字" android:maxLines="1" android:textSize="15dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> <TextView android:id="@+id/dialog" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerInParent="true" android:background="#9DDE76" android:gravity="center" android:textColor="#ffffffff" android:textSize="30dp" android:visibility="invisible" /> <com.xp.sortrecyclerview.SideBar android:id="@+id/sideBar" android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true" /> </RelativeLayout> </LinearLayout>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
之前出现过两个问题,一个就是,每次进入界面后EditText自动获取焦点,导致输入法自动弹出来,所以为了解决这个问题在EditText的父布局也就是我们的根布局加了两个属性:android:focusable=”true”和android:focusableInTouchMode=”true”。
还有一个问题就是侧边栏会被输入法顶上去,结解决办法是在清单配置文件的对应Activity加上android:windowSoftInputMode=”adjustPan”,防止布局被输入法顶上去。
中间的TextView是用来显示选中的字母索引。
主界面的话主要是三个方法:
初始化的方法主要是对比较器的初始化,设置监听,对数据排序和对RecyclerView的初始化。
代码:
private void initViews() { //初始化比较器 pinyinComparator = new PinyinComparator(); sideBar = (SideBar) findViewById(R.id.sideBar); dialog = (TextView) findViewById(R.id.dialog); sideBar.setTextView(dialog); //设置右侧SideBar触摸监听 sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() { @Override public void onTouchingLetterChanged(String s) { //该字母首次出现的位置 int position = adapter.getPositionForSection(s.charAt(0)); if (position != -1) { manager.scrollToPositionWithOffset(position, 0); } } }); mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView); SourceDateList = filledData(getResources().getStringArray(R.array.date)); // 根据a-z进行排序源数据 Collections.sort(SourceDateList, pinyinComparator); //RecyclerView社置manager manager = new LinearLayoutManager(this); manager.setOrientation(LinearLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(manager); adapter = new SortAdapter(this, SourceDateList); mRecyclerView.setAdapter(adapter); //item点击事件 /*adapter.setOnItemClickListener(new SortAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, ((SortModel)adapter.getItem(position)).getName(),Toast.LENGTH_SHORT).show(); } });*/ mClearEditText = (ClearEditText) findViewById(R.id.filter_edit); //根据输入框输入值的改变来过滤搜索 mClearEditText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { //当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表 filterData(s.toString()); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
接下来是将数据列表的内容按拼音排序的方法,先将汉字转化成拼音,在用正则表达式分类,下边是代码:
private List<SortModel> filledData(String[] date) { List<SortModel> mSortList = new ArrayList<>(); for (int i = 0; i < date.length; i++) { SortModel sortModel = new SortModel(); sortModel.setName(date[i]); //汉字转换成拼音 String pinyin = PinyinUtils.getPingYin(date[i]); String sortString = pinyin.substring(0, 1).toUpperCase(); // 正则表达式,判断首字母是否是英文字母 if (sortString.matches("[A-Z]")) { sortModel.setLetters(sortString.toUpperCase()); } else { sortModel.setLetters("#"); } mSortList.add(sortModel); } return mSortList; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
最后就是根据输入的内容进行数据筛选的方法:
private void filterData(String filterStr) { List<SortModel> filterDateList = new ArrayList<>(); if (TextUtils.isEmpty(filterStr)) { filterDateList = SourceDateList; } else { filterDateList.clear(); for (SortModel sortModel : SourceDateList) { String name = sortModel.getName(); if (name.indexOf(filterStr.toString()) != -1 || PinyinUtils.getFirstSpell(name).startsWith(filterStr.toString()) //不区分大小写 || PinyinUtils.getFirstSpell(name).toLowerCase().startsWith(filterStr.toString()) || PinyinUtils.getFirstSpell(name).toUpperCase().startsWith(filterStr.toString()) ) { filterDateList.add(sortModel); } } } // 根据a-z进行排序 Collections.sort(filterDateList, pinyinComparator); adapter.updateList(filterDateList); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
RecyclerView适配器的代码,都比较简单就不用多解释了,直接上代码:
public class SortAdapter extends RecyclerView.Adapter<SortAdapter.ViewHolder> { private LayoutInflater mInflater; private List<SortModel> mData; private Context mContext; public SortAdapter(Context context, List<SortModel> data) { mInflater = LayoutInflater.from(context); mData = data; this.mContext = context; } @Override public SortAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.item, parent,false); ViewHolder viewHolder = new ViewHolder(view); viewHolder.tvTag = (TextView) view.findViewById(R.id.tag); viewHolder.tvName = (TextView) view.findViewById(R.id.name); return viewHolder; } @Override public void onBindViewHolder(final SortAdapter.ViewHolder holder, final int position) { int section = getSectionForPosition(position); //如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现 if (position == getPositionForSection(section)) { holder.tvTag.setVisibility(View.VISIBLE); holder.tvTag.setText(mData.get(position).getLetters()); } else { holder.tvTag.setVisibility(View.GONE); } if (mOnItemClickListener != null) { holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.onItemClick(holder.itemView, position); } }); } holder.tvName.setText(this.mData.get(position).getName()); holder.tvName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(mContext, mData.get(position).getName(),Toast.LENGTH_SHORT).show(); } }); } @Override public int getItemCount() { return mData.size(); } //**********************itemClick************************ public interface OnItemClickListener { void onItemClick(View view, int position); } private OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) { this.mOnItemClickListener = mOnItemClickListener; } //************************************************************** public static class ViewHolder extends RecyclerView.ViewHolder { TextView tvTag, tvName; public ViewHolder(View itemView) { super(itemView); } } /** * 提供给Activity刷新数据 * @param list */ public void updateList(List<SortModel> list){ this.mData = list; notifyDataSetChanged(); } public Object getItem(int position) { return mData.get(position); } /** * 根据ListView的当前位置获取分类的首字母的char ascii值 */ public int getSectionForPosition(int position) { return mData.get(position).getLetters().charAt(0); } /** * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置 */ public int getPositionForSection(int section) { for (int i = 0; i < getItemCount(); i++) { String sortStr = mData.get(i).getLetters(); char firstChar = sortStr.toUpperCase().charAt(0); if (firstChar == section) { return i; } } return -1; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
PinyinComparator是个比较器类,主要就是根据ASCII码来对数据进行比较排序:
public class PinyinComparator implements Comparator<SortModel> { public int compare(SortModel o1, SortModel o2) { if (o1.getLetters().equals("@") || o2.getLetters().equals("#")) { return -1; } else if (o1.getLetters().equals("#") || o2.getLetters().equals("@")) { return 1; } else { return o1.getLetters().compareTo(o2.getLetters()); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PinyinUtils是一个将中文转化为拼音的工具类,主要提供汉字转拼音的方法和获取首字母的方法:
public class PinyinUtils { /** * 获取拼音 * * @param inputString * @return */ public static String getPingYin(String inputString) { HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.LOWERCASE); format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); format.setVCharType(HanyuPinyinVCharType.WITH_V); char[] input = inputString.trim().toCharArray(); String output = ""; try { for (char curChar : input) { if (Character.toString(curChar).matches("[\\u4E00-\\u9FA5]+")) { String[] temp = PinyinHelper.toHanyuPinyinStringArray(curChar, format); output += temp[0]; } else output += Character.toString(curChar); } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } return output; } /** * 获取第一个字的拼音首字母 * @param chinese * @return */ public static String getFirstSpell(String chinese) { StringBuffer pinYinBF = new StringBuffer(); char[] arr = chinese.toCharArray(); HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); for (char curChar : arr) { if (curChar > 128) { try { String[] temp = PinyinHelper.toHanyuPinyinStringArray(curChar, defaultFormat); if (temp != null) { pinYinBF.append(temp[0].charAt(0)); } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } } else { pinYinBF.append(curChar); } } return pinYinBF.toString().replaceAll("\\W", "").trim(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
最后是我们的侧边栏SideBar的代码:
public class SideBar extends View { // 触摸事件 private OnTouchingLetterChangedListener onTouchingLetterChangedListener; public static String[] b = { "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 choose = -1; private Paint paint = new Paint(); private TextView mTextDialog; /** * 为SideBar设置显示字母的TextView * @param textDialog */ public void setTextView(TextView textDialog) { this.mTextDialog = textDialog; } public SideBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public SideBar(Context context, AttributeSet attrs) { super(context, attrs); } public SideBar(Context context) { super(context); } protected void onDraw(Canvas canvas) { super.onDraw(canvas); int height = getHeight(); int width = getWidth(); int singleHeight = height / b.length;// 获取每一个字母的高度 for (int i = 0; i < b.length; i++) { paint.setColor(Color.rgb(33, 65, 98)); // paint.setColor(Color.WHITE); paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setAntiAlias(true); paint.setTextSize(30); if (i == choose) {// 选中的状态 paint.setColor(Color.parseColor("#3399ff")); paint.setFakeBoldText(true); } // x坐标等于中间-字符串宽度的一半 float xPos = width / 2 - paint.measureText(b[i]) / 2; float yPos = singleHeight * i + singleHeight; canvas.drawText(b[i], xPos, yPos, paint); paint.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() * b.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数 switch (action) { case MotionEvent.ACTION_UP: setBackground(new ColorDrawable(0x00000000)); choose = -1;// invalidate(); if (mTextDialog != null) { mTextDialog.setVisibility(View.INVISIBLE); } break; default: setBackgroundResource(R.drawable.sidebar_background); if (oldChoose != c) { if (c >= 0 && c < b.length) { if (listener != null) { listener.onTouchingLetterChanged(b[c]); } if (mTextDialog != null) { mTextDialog.setText(b[c]); mTextDialog.setVisibility(View.VISIBLE); } choose = c; invalidate(); } } break; } return true; } /** * 触摸事件 * @param onTouchingLetterChangedListener */ public void setOnTouchingLetterChangedListener( OnTouchingLetterChangedListener onTouchingLetterChangedListener) { this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; } /** * 回调接口 */ public interface OnTouchingLetterChangedListener { void onTouchingLetterChanged(String s); } }
点击下载源码
相关文章推荐
- Android中使用RecyclerView和CardView实现瀑布流效果(StaggeredGrid)
- Android 仿联系人菜单,带字母索引,顶部挤压动画,recyclerview实现联系人页面
- Android RecyclerView使用详解及实现多选
- [置顶] Android使用RecyclerView和CardView,实现知乎日报精致布局
- Android 自定义View-实现快速索引
- Android-RecyclerView使用(三) 实现下拉刷新,上拉自动加载
- Android——RecyclerView——使用ItemDragHelper来实现酷炫拖拽效果
- Android SwipeRefreshLayout和RecyclerView 使用实现刷新数据
- android studio pulltorefresh功能实现 通过swiperefreshlayout 使用recyclerview布局
- Android09_SearchView联系人的索引快速(弹出)查询的实现
- Android使用RecyclerView实现水平滚动控件
- Android中使用RecyclerView实现下拉刷新和上拉加载
- Android使用ItemTouchHelper实现RecyclerView的item拖动位置交换
- Android 使用RecyclerView和CardView,实现知乎日报精致布局
- Android使用RecyclerView实现瀑布流
- Android RecyclerView 实现快速滑动
- Android-使用RecyclerView的ItemDecoration 实现炫酷的 吸顶效果
- (4.1.11.1)Android中使用RecyclerView和CardView实现瀑布流效果(StaggeredGrid)
- Android使用RecyclerView实现仿微信联系人列表
- Android App使用RecyclerView实现上拉和下拉刷新的方法