前面的两篇讲了快速搜索的侧边#A-Z、侧滑的实现。本篇将会实现模糊搜索的效果。实现模糊搜索之前我们还是先实现以下侧栏点击字母的定位效果。当我们点击侧栏字母的时候希望能定位到拼音的首字母是我们所点击的字母,这一点本来想在前两篇说但是一想还是跟模糊搜索一块将吧,都是实现定位到某个位置的。这里我们会用到一个关于拼音的工具
上面的效果图我们可以看到如果我们的列表里面首字母含有某个字母的时候,含有相同的首字母只会在第一条数据上面显示含有的字母标志,其余的则不会显示。这个效果的实现很简单,我们做布局的时候其实都是同一个布局样式
<?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="wrap_content"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="16dp"
android:background="#B51421"
android:textColor="#FFFFFF"
android:id="@+id/tv_first_alphabet"
android:visibility="gone"
android:text="A"/>
<lyx.robert.quicksearch.view.SwipeLayout
android:id="@+id/swp_slip"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"
android:id="@+id/tv_contact_name"
android:background="#FFFFFF"
android:padding="10dp"
/>
<include layout="@layout/layout_slip"></include>
</lyx.robert.quicksearch.view.SwipeLayout>
</LinearLayout>
上面的布局是关于显示的内容的,我们会发现我们的每个条目是有两个TextView其中一个是显示字母的另一个才是显示的内容。而我们看到的却是只有第一个条目显示字母,这是因为我们会判断,如果当前字母与上一个字母的首字母不相同的话当前条目的字母显示,否则就要隐藏,这样我们的效果就出来了,判断的代码如下
String currentAlphabet=contactBean.getPinyin().charAt(0)+"";
if(position>0){
String lastAlphabet = list.get(position-1).getPinyin().charAt(0)+"";
//获取上一个item的首字母
if(currentAlphabet.equals(lastAlphabet)){
//首字母相同,需要隐藏当前item的字母的TextView
holder.tv_first_alphabet.setVisibility(View.GONE);
}else {
//不相同就要显示当前的首字母
holder.tv_first_alphabet.setVisibility(View.VISIBLE);
holder.tv_first_alphabet.setText(currentAlphabet);
}
}else {
holder.tv_first_alphabet.setVisibility(View.VISIBLE);
holder.tv_first_alphabet.setText(currentAlphabet);
}
下面来说一下我们的模糊搜索,所谓的模糊搜索不过就是当我们不知道全名称的时候可以输入一部分来进行搜索。比如张先生,我们可以通过全拼ZHANGXIANSHENG来搜索,也可以通过简拼ZXS来搜索,不过此搜索不是特别精确,因为如果你的列表里面含有的联系人含有ZXS也能搜索出来,我们也可以输入汉字张先生来搜索。我们先来说一下全拼搜索,当然模糊搜索也是离不了拼音工具的
package lyx.robert.quicksearch.utils;
import android.text.TextUtils;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
public class PinYinUtil {
/**
* 获取汉字的拼音,会销毁一定的资源,所以不应该被频繁调用
* @param chinese
*/
public static String getPinyin(String chinese){
if(TextUtils.isEmpty(chinese)) return null;
//用来设置转化的拼音的大小写,或者声调
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//设置转化的拼音是大写字母
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//设置转化的拼音不带声调
//1.由于只能对单个汉字转化,所以需要将字符串转化为字符数组,然后对每个字符转化,最后拼接起来
char[] charArray = chinese.toCharArray();
String pinyin = "";
for (int i = 0; i < charArray.length; i++) {
//2.过滤空格
if(Character.isWhitespace(charArray[i]))continue;
//3.需要判断是否是汉字
//汉字占2个字节,一个字节范围是-128~127,那么汉字肯定大于127
if(charArray[i]>127){
//可能是汉字
try {
//由于多音字的存在,比如单 dan shan,
String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(charArray[i],format);
if(pinyinArr!=null){
pinyin += pinyinArr[0];//此处即使有多音字,那么也只能取第一个拼音
}else {
//说明没有找到对应的拼音,汉字有问题,或者可能不是汉字,则忽略
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
//说明转化失败,不是汉字,比如O(∩_∩)O~,那么则忽略
}
}else {
//肯定不是汉字,应该是键盘上能够直接输入的字符,这些字符能够排序,但不能获取拼音
//所以可以直接拼接
pinyin += charArray[i];
}
}
return pinyin;
}
}
根据上面的工具我们可以获取列表里面的每个联系人的拼音的全拼,全拼很简单。那么简拼又是如何实现的呢?简拼就是获取每个名字的每个字的首字母,我们可以根据每个名字的长度,循环取出名字里面的每个字的拼音,然后获取拼音的第一个字母即可
public PinYinStyle parsePinYinStyle(String content) {
PinYinStyle pinYinStyle = new PinYinStyle();
if (content != null && content.length() > 0) {
//其中包含的中文字符
String[] enStrs = new String[content.length()];
for (int i=0;i<content.length();i++){
enStrs[i] = PinYinUtil.getPinyin(String.valueOf(content.charAt(i)));
}
for (int i = 0, length = enStrs.length; i < length; i++) {
if (enStrs[i].length() > 0) {
//拼接简拼
pinYinStyle.briefnessSpell += enStrs[i].charAt(0);
}
}
}
return pinYinStyle;
}
剩下的就是关于悬浮提示的实现了。悬浮提示就是根据我们点击的某个字母然后将首字母以我们点击的字母开头的依次显示出来。
for (int i = 0; i < contactList.size(); i++) {
String firstAlphabet = contactList.get(i).getPinyin().toString().trim().charAt(0)+"";
if(letter.equals(firstAlphabet)){
//说明找到了,那么应该讲当前的item放到屏幕顶端
tv_notice.setText(letter);
if(!alphabetList.contains(String.valueOf(contactList.get(i).getName().trim().charAt(0)))){
alphabetList.add(String.valueOf(contactList.get(i).getName().trim().charAt(0)));
}
}
MainActivity.class
package lyx.robert.quicksearch.activities;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.nineoldandroids.view.ViewPropertyAnimator;
import lyx.robert.quicksearch.Bean.ContactBean;
import lyx.robert.quicksearch.adapter.ContactAdapter;
import lyx.robert.quicksearch.utils.PinYinUtil;
import lyx.robert.quicksearch.view.SideLetterBar;
import lyx.robert.quicksearch.R;
import lyx.robert.quicksearch.Bean.PinYinStyle;
import lyx.robert.quicksearch.utils.SwipeManager;
import lyx.robert.quicksearch.adapter.AlphabetAdp;
import lyx.robert.quicksearch.view.ClearEditText;
public class MainActivity extends Activity {
private SideLetterBar sideLetterBar;
private ListView lv_contact;
private ListView lv_alphabet;
private TextView tv_alphabet;
private TextView tv_notice;
private ClearEditText et_clear;
private List<String>alphabetList;
RelativeLayout rel_notice;
ContactAdapter adapter ;
private String[] data = new String[] {
"15129372345","15129372334","15129372335","15129372343","15129372347","151293723423",
//A
"安先生", "敖先生", "艾先生", "爱先生",
//B
"巴先生", "白先生", "鲍先生","包先生", "班先生", "毕先生","边先生", "卞先生", "薄先生",
//C
"蔡先生", "岑先生", "曹先生","陈先生", "程先生", "褚先生","昌先生", "车先生", "常先生",
//D
"戴先生", "狄先生", "窦先生","董先生", "杜先生","杜先生","杜先生", "丁先生","邓先生", "段先生", "党先生",
//E
"鄂先生",
//F
"费先生", "范先生","樊先生", "方先生", "房先生","丰先生", "封先生", "冯先生","法先生",
//G
"盖先生", "甘先生", "高先生"," 葛先生", "耿先生", "古先生","顾先生", "关先生", "郭先生",
//H
"海先生", "郝先生", "韩先生","何先生", "贺先生", "胡先生","扈先生", "黄先生", "华先生",
//J
"姬先生", "季先生", "纪先生","金先生", "焦先生", "姜先生","贾先生", "郏先生", "靳先生",
//K
"寇先生", "孔先生", "康先生","柯先生", "况先生", "亢先生","夔先生", "蒯先生", "隗先生",
//L
"李先生", "郎先生", "鲁先生","柳先生", "雷先生", "刘先生","林先生", "蓝先生", "吕先生",
//M
"马先生", "满先生", "苗先生","穆先生", "毛先生", "麻先生","孟先生", "梅先生", "莫先生",
//N
"那先生", "能先生", "倪先生","年先生", "宁先生", "聂先生","牛先生", "农先生", "聂先生",
//O
"欧先生", "欧阳先生",
//P
"潘先生", "庞先生", "裴先生","彭先生", "皮先生", "濮先生","蓬先生", "逄先生", "浦先生",
//Q
"戚先生", "齐先生", "祁先生","乔先生", "屈先生", "钱先生","秦先生", "邱先生", "裘先生",
//R
"冉先生", "饶先生", "任先生","阮先生", "芮先生", "戎先生","容先生", "荣先生", "融先生",
//S
"宋先生", "舒先生", "苏先生","孙先生", "索先生", "沈先生","邵先生", "施先生", "石先生",
//T
"邰先生", "谭先生", "陶先生","唐先生", "汤先生", "田先生","佟先生", "屠先生", "滕先生",
//W
"万先生", "邬先生", "乌先生","吴先生", "伍先生", "武先生","王先生", "韦先生", "魏先生",
//X
"奚先生", "席先生", "习先生","夏先生", "萧先生", "熊先生","项先生", "徐先生", "许先生",
//Y
"燕先生", "鄢先生", "颜先生","闫先生", "阎先生", "晏先生","姚先生", "杨先生", "叶先生",
//Z
"翟先生", "张先生", "章先生","赵先生", "甄先生", "曾先生","周先生", "郑先生", "祝先生",
};
private ArrayList<ContactBean> contactList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contactList = dataList();
//2.对数据进行排序
initView();
initEvent();
initData();
}
private void initView() {
sideLetterBar = (SideLetterBar) findViewById(R.id.sideLetterBar);
lv_contact = (ListView) findViewById(R.id.lv_contact);
lv_alphabet = (ListView) findViewById(R.id.lv_alphabet);
tv_notice = (TextView) findViewById(R.id.tv_notice);
rel_notice = (RelativeLayout) findViewById(R.id.rel_notice);
et_clear = (ClearEditText) findViewById(R.id.et_clear);
tv_alphabet = (TextView) findViewById(R.id.tv_alphabet);
rel_notice.post(new Runnable() {
@Override
public void run() {
tv_notice.getHeight();
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) rel_notice.getLayoutParams();
params.height = tv_notice.getHeight()*5;
params.width = tv_notice.getWidth();
rel_notice.setLayoutParams(params);
}
});
}
private void initEvent() {
sideLetterBar.setOnTouchLetterListener(new SideLetterBar.OnTouchLetterListener() {
@Override
public void onTouchLetter(String letter) {
alphabetList.clear();
ViewPropertyAnimator.animate(rel_notice).alpha(1f).setDuration(0).start();
//根据当前触摸的字母,去集合中找那个item的首字母和letter一样,然后将对应的item放到屏幕顶端
for (int i = 0; i < contactList.size(); i++) {
String firstAlphabet = contactList.get(i).getPinyin().charAt(0)+"";
if(letter.equals(firstAlphabet)){
lv_contact.setSelection(i);
rel_notice.setVisibility(View.VISIBLE);
break;
}
if(letter.equals("#")){
lv_contact.setSelection(0);
rel_notice.setVisibility(View.GONE);
}
}
for (int i = 0; i < contactList.size(); i++) {
String firstAlphabet = contactList.get(i).getPinyin().toString().trim().charAt(0)+"";
if(letter.equals(firstAlphabet)){
//说明找到了,那么应该讲当前的item放到屏幕顶端
tv_notice.setText(letter);
if(!alphabetList.contains(String.valueOf(contactList.get(i).getName().trim().charAt(0)))){
alphabetList.add(String.valueOf(contactList.get(i).getName().trim().charAt(0)));
}
}
}
showCurrentWord(letter);
//显示当前触摸的字母
AlphabetAdp alphabetAdp = new AlphabetAdp(MainActivity.this,alphabetList);
lv_alphabet.setAdapter(alphabetAdp);
alphabetAdp.notifyDataSetChanged();
}
});
lv_contact.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if(scrollState== AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL){
//如果垂直滑动,则需要关闭已经打开的layout
SwipeManager.getInstance().closeCurrentLayout();
}
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
int pos = lv_contact.getFirstVisiblePosition();
if (contactList.size()>0){
tv_alphabet.setVisibility(View.VISIBLE);
String text = contactList.get(pos).getPinyin().charAt(0)+"";
Pattern p = Pattern.compile("[0-9]*");
Matcher m1 = p.matcher(text);
if(m1.matches()){
tv_alphabet.setText("#");
}else {
tv_alphabet.setText(text);
}
}else {
tv_alphabet.setVisibility(View.GONE);
}
}
});
et_clear.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
fuzzySearch(s.toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
lv_alphabet.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String alphabet = alphabetList.get(position).trim();
setIsVisiable();
for (int i = 0;i<contactList.size();i++){
if (alphabet.equals(String.valueOf(contactList.get(i).getName().trim().charAt(0)))){
int pos = i%lv_contact.getChildCount();
int childCount = lv_contact.getChildCount();
if(position==0&&pos-position==1||childCount-pos==1){
lv_contact.setSelection(i);
}else {
lv_contact.setSelection(i-1);
}
break;
}
}
}
});
}
private void initData() {
//3.设置Adapter
adapter = new ContactAdapter(this,contactList);
lv_contact.setAdapter(adapter);
alphabetList = new ArrayList<>();
}
protected void showCurrentWord(String letter) {
tv_notice.setText(letter);
setIsVisiable();
}
private Handler handler = new Handler();
private void setIsVisiable(){
handler.removeCallbacksAndMessages(null);
handler.postDelayed(new Runnable() {
@Override
public void run() {
ViewPropertyAnimator.animate(rel_notice).alpha(0f).setDuration(1000).start();
}
}, 4000);
}
private ArrayList <ContactBean> dataList() {
// 虚拟数据
ArrayList <ContactBean> mSortList = new ArrayList<ContactBean>();
for(int i=0;i<data.length;i++){
ContactBean bean = new ContactBean(data[i]);
bean.pinYinStyle = parsePinYinStyle(data[i]);
mSortList.add(bean);
}
Collections.sort(mSortList);
return mSortList;
}
private void fuzzySearch(String str) {
ArrayList<ContactBean> filterDateList = new ArrayList<ContactBean>();
// 虚拟数据
if (TextUtils.isEmpty(str)){
sideLetterBar.setVisibility(View.VISIBLE);
filterDateList = dataList();
}else {
filterDateList.clear();
sideLetterBar.setVisibility(View.GONE);
for(ContactBean contactBean : dataList()){
String name = contactBean.getName();
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
Matcher m = p.matcher(str);
if(m.matches()){
str = PinYinUtil.getPinyin(str);
}
if(PinYinUtil.getPinyin(name).contains(str.toUpperCase())|| contactBean.pinYinStyle.briefnessSpell.toUpperCase().contains(str.toUpperCase())
|| contactBean.pinYinStyle.completeSpell.toUpperCase().contains(str.toUpperCase())){
filterDateList.add(contactBean);
}
}
}
contactList = filterDateList;
adapter = new ContactAdapter(this,filterDateList);
lv_contact.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
public PinYinStyle parsePinYinStyle(String content) {
PinYinStyle pinYinStyle = new PinYinStyle();
if (content != null && content.length() > 0) {
//其中包含的中文字符
String[] enStrs = new String[content.length()];
for (int i=0;i<content.length();i++){
enStrs[i] = PinYinUtil.getPinyin(String.valueOf(content.charAt(i)));
}
for (int i = 0, length = enStrs.length; i < length; i++) {
if (enStrs[i].length() > 0) {
//拼接简拼
pinYinStyle.briefnessSpell += enStrs[i].charAt(0);
}
}
}
return pinYinStyle;
}
}
点击前往GitHub下载源码
帅哥/美女,如果对您有帮助在GitHub上面点下star呗哦!再csdn上面给个好评也行啊!也不枉费我忙活了这些时间了。谢谢!