失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android 使用RecyclerView实现(仿微信)的联系人A-Z字母排序和过滤搜索功能

Android 使用RecyclerView实现(仿微信)的联系人A-Z字母排序和过滤搜索功能

时间:2020-03-21 02:30:58

相关推荐

Android 使用RecyclerView实现(仿微信)的联系人A-Z字母排序和过滤搜索功能

之前做项目的时候遇到一个需求是实现品牌的字母排序功能,网上的资料很多,但是有一部分有bug,这篇文章是我学习和解决部分bug之后的总结。今天带来的是RecyclerView的A-Z字母排序和过滤搜索功能。

首先上效果图:

重点:1、实现数据排序分类 2、实现右侧的字母导航 3、搜索

这里使用了一个中文转化为拼音的工具包,即pinyin4j-2.5.0.jar。官网地址:/

布局顶部是一个带删除按钮的文本编辑框,我们在输入框中输入字母或汉字可以自动过滤出我们想要的东西,当输入框中没有数据自动替换到原来的数据列表,然后下面一个RecyclerView用来显示数据列表,右侧是一个字母索引表,其实就是一个自定的View,当我们点击不同的字母,RecyclerView会定位到该字母索引地item。

这是主界面布局代码:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/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>

之前出现过两个问题,一个就是,每次进入界面后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() {@Overridepublic 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社置managermanager = new LinearLayoutManager(this);manager.setOrientation(LinearLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(manager);adapter = new SortAdapter(this, SourceDateList);mRecyclerView.setAdapter(adapter);//item点击事件/*adapter.setOnItemClickListener(new SortAdapter.OnItemClickListener() {@Overridepublic 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() {@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {//当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表filterData(s.toString());}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count,int after) {}@Overridepublic void afterTextChanged(Editable s) {}});}

接下来是将数据列表的内容按拼音排序的方法,先将汉字转化成拼音,在用正则表达式分类,下边是代码:

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;}

最后就是根据输入的内容进行数据筛选的方法:

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);}

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;}@Overridepublic 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;}@Overridepublic 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() {@Overridepublic void onClick(View v) {mOnItemClickListener.onItemClick(holder.itemView, position);}});}holder.tvName.setText(this.mData.get(position).getName());holder.tvName.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Toast.makeText(mContext, mData.get(position).getName(),Toast.LENGTH_SHORT).show();}});}@Overridepublic 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;}}

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());}}}

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];} elseoutput += 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();}}

最后是我们的侧边栏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();}}@Overridepublic 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);}}

以上就是主要代码了,如果需要的话可以下载整个项目源码,欢迎star。

点击下载源码

这里是一个升级版本,新增顶部悬浮,侧边栏使用wavesidebar,实现方式更加优雅。

《RecyclerView字母排序,顶部悬浮,过滤搜索最优雅的实现方式》

/SilenceOO/article/details/77839683

如果觉得《Android 使用RecyclerView实现(仿微信)的联系人A-Z字母排序和过滤搜索功能》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。