├── SearchSidebar ├── .gitignore ├── libs │ └── pinyin4j-2.5.0.jar ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── icon_search.png │ │ │ │ └── icon_delete_gray.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable │ │ │ │ ├── default_useravatar.png │ │ │ │ ├── shape_slide_grey.xml │ │ │ │ ├── btn_list_item_bg.xml │ │ │ │ ├── shape_border_grey.xml │ │ │ │ └── bg_sidebar.xml │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ └── arrays.xml │ │ │ ├── menu │ │ │ │ └── menu_main.xml │ │ │ └── layout │ │ │ │ ├── mainactivity.xml │ │ │ │ ├── listviewsortbarfilter_item.xml │ │ │ │ └── listviewsortbarfilter.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── lujianchao │ │ │ └── SearchSideBar │ │ │ ├── MainActivity.java │ │ │ └── SearchSidebar │ │ │ ├── SearchEditText.java │ │ │ ├── SortAdapter.java │ │ │ ├── PinyinUtils.java │ │ │ ├── SideBar.java │ │ │ └── SearchSideBar.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── adan │ │ └── contactsdome │ │ └── ApplicationTest.java ├── proguard-rules.pro ├── build.gradle ├── app.iml ├── applist.iml └── SearchSidebar.iml ├── asprojects.zip ├── SearchSidebar.gif ├── .gitattributes ├── .gitignore └── README.md /SearchSidebar/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /asprojects.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/asprojects.zip -------------------------------------------------------------------------------- /SearchSidebar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar.gif -------------------------------------------------------------------------------- /SearchSidebar/libs/pinyin4j-2.5.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/libs/pinyin4j-2.5.0.jar -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/mipmap-hdpi/icon_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/src/main/res/mipmap-hdpi/icon_search.png -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/drawable/default_useravatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/src/main/res/drawable/default_useravatar.png -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/mipmap-hdpi/icon_delete_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itgowo/SearchSidebar/HEAD/SearchSidebar/src/main/res/mipmap-hdpi/icon_delete_gray.png -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 联系人 3 | Settings 4 | 5 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/drawable/shape_slide_grey.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #F55813 4 | #5A5A5A 5 | #FFFFFF 6 | #000000 7 | 8 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/drawable/btn_list_item_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/drawable/shape_border_grey.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/drawable/bg_sidebar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /SearchSidebar/src/androidTest/java/com/adan/contactsdome/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.adan.contactsdome; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/layout/mainactivity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /SearchSidebar/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Administrator\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /SearchSidebar/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.adan.contactsdome" 9 | minSdkVersion 10 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | compile 'com.android.support:appcompat-v7:23.1.1' 25 | compile files('libs/pinyin4j-2.5.0.jar') 26 | compile 'com.google.code.gson:gson:2.7' 27 | compile 'com.github.bumptech.glide:glide:3.7.0' 28 | } 29 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/java/com/lujianchao/SearchSideBar/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.SearchSideBar; 2 | 3 | import android.app.Activity; 4 | import android.content.ContentResolver; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | 8 | 9 | import com.lujianchao.SearchSideBar.SearchSidebar.SearchSideBar; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class MainActivity extends Activity { 15 | 16 | private SearchSideBar mSortBarFilter; 17 | 18 | private List mMembers = new ArrayList<>(); 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.mainactivity); 24 | mSortBarFilter = (SearchSideBar) findViewById(R.id.lv_contact); 25 | String[] mStrings = getResources().getStringArray(R.array.contacts); 26 | Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" 27 | + getResources().getResourcePackageName(R.mipmap.ic_launcher) + "/" 28 | + getResources().getResourceTypeName(R.mipmap.ic_launcher) + "/" 29 | + getResources().getResourceEntryName(R.mipmap.ic_launcher)); 30 | for (int i = 0; i < mStrings.length; i++) { 31 | SearchSideBar.ContactSortModel mMember = new SearchSideBar.ContactSortModel(); 32 | mMember.setID("aaaaaaa" + i); 33 | mMember.setName(mStrings[i]); 34 | mMember.setLogo(uri.toString()); 35 | mMembers.add(mMember); 36 | } 37 | mSortBarFilter.setMembers(mMembers, new SearchSideBar.onItemClickListener() { 38 | @Override 39 | public void onItem(int position, SearchSideBar.ContactSortModel mModel) { 40 | System.out.println("position = [" + position + "], mModel = [" + mModel + "]"); 41 | } 42 | }); 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/layout/listviewsortbarfilter_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 19 | 20 | 24 | 25 | 34 | 35 | 45 | 46 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SearchSidebar 2 | 3 | ## 一:关键词: 4 | ~~~~ 5 | 联系人列表 导航bar 搜索 6 | ~~~~ 7 | 8 | QQ:1264957104 9 | 10 | 11 | 12 | ## 二:说明 13 | 导航bar有很多了,我看了别人的有一些不满足需求,于是改了改: 14 | 1.增加了对数字和特殊符号的处理,按#处理了。 15 | 2.针对导航字母数量不同做了处理。当数量少时,间距较大方便点击,数量越多间距越小。 16 | 3.做成了自定义View方式,充分解耦,使用方便。 17 | 18 | 如果直接导入会出错,把布局文件listviewsortbarfilter.xml里自定义view重新写一下路径和名字就可以了,clean项目,一般没问题了。 19 | 核心代码方法: mSortBarFilter.setMembers(mMembers, new SearchSideBar.onItemClickListener() 20 | 21 | ## 示例 22 | ![ image](https://github.com/hnsugar/SearchSidebar/blob/master/SearchSidebar.gif) 23 | 24 | 25 | 26 | ## 布局 27 | 28 | 32 | 33 | ## 代码 34 | 35 | 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.mainactivity); 39 | mSortBarFilter = (SearchSideBar) findViewById(R.id.lv_contact); 40 | String[] mStrings = getResources().getStringArray(R.array.contacts); 41 | Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" 42 | + getResources().getResourcePackageName(R.mipmap.ic_launcher) + "/" 43 | + getResources().getResourceTypeName(R.mipmap.ic_launcher) + "/" 44 | + getResources().getResourceEntryName(R.mipmap.ic_launcher)); 45 | for (int i = 0; i < mStrings.length; i++) { 46 | SearchSideBar.ContactSortModel mMember = new SearchSideBar.ContactSortModel(); 47 | mMember.setID("aaaaaaa" + i); 48 | mMember.setName(mStrings[i]); 49 | mMember.setLogo(uri.toString()); 50 | mMembers.add(mMember); 51 | } 52 | mSortBarFilter.setMembers(mMembers, new SearchSideBar.onItemClickListener() { 53 | @Override 54 | public void onItem(int position, SearchSideBar.ContactSortModel mModel) { 55 | System.out.println("position = [" + position + "], mModel = [" + mModel + "]"); 56 | } 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | / 6 | ! 7 | */*- 8 | aaa 9 | 111 10 | 222 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 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/res/layout/listviewsortbarfilter.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | 23 | 26 | 27 | 35 | 36 | 49 | 50 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/java/com/lujianchao/SearchSideBar/SearchSidebar/SearchEditText.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.SearchSideBar.SearchSidebar; 2 | 3 | /** 4 | * Created by Lu JianChao on 2016/6/29. 5 | * https://github.com/hnsugar 6 | */ 7 | 8 | import android.content.Context; 9 | import android.graphics.Rect; 10 | import android.graphics.drawable.Drawable; 11 | import android.text.Editable; 12 | import android.text.TextWatcher; 13 | import android.util.AttributeSet; 14 | import android.view.MotionEvent; 15 | import android.widget.EditText; 16 | 17 | import com.lujianchao.SearchSideBar.R; 18 | 19 | 20 | /** 21 | * @description: 自定义带有删除功能的EditText 22 | */ 23 | public class SearchEditText extends EditText { 24 | private final static String TAG = "EditTextWithDel"; 25 | private Drawable imgLeft, imgAble; 26 | private Context mContext; 27 | 28 | public SearchEditText(Context context) { 29 | super(context); 30 | mContext = context; 31 | init(); 32 | } 33 | 34 | public SearchEditText(Context context, AttributeSet attrs, int defStyle) { 35 | super(context, attrs, defStyle); 36 | mContext = context; 37 | init(); 38 | } 39 | 40 | public SearchEditText(Context context, AttributeSet attrs) { 41 | super(context, attrs); 42 | mContext = context; 43 | init(); 44 | } 45 | 46 | private void init() { 47 | imgLeft = mContext.getResources().getDrawable(R.mipmap.icon_search); 48 | imgAble = mContext.getResources().getDrawable(R.mipmap.icon_delete_gray); 49 | addTextChangedListener(new TextWatcher() { 50 | @Override 51 | public void onTextChanged(CharSequence s, int start, int before, int count) { 52 | } 53 | 54 | @Override 55 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 56 | } 57 | 58 | @Override 59 | public void afterTextChanged(Editable s) { 60 | setDrawable(); 61 | } 62 | }); 63 | setDrawable(); 64 | } 65 | 66 | // 设置删除图片 67 | private void setDrawable() { 68 | if (length() < 1) { 69 | setCompoundDrawablesWithIntrinsicBounds(imgLeft, null, null, null); 70 | } else { 71 | setCompoundDrawablesWithIntrinsicBounds(imgLeft, null, imgAble, null); 72 | } 73 | } 74 | 75 | // 处理删除事件 76 | @Override 77 | public boolean onTouchEvent(MotionEvent event) { 78 | if (imgAble != null && event.getAction() == MotionEvent.ACTION_UP) { 79 | int eventX = (int) event.getRawX(); 80 | int eventY = (int) event.getRawY(); 81 | Rect rect = new Rect(); 82 | getGlobalVisibleRect(rect); 83 | rect.left = rect.right - 90; 84 | if (rect.contains(eventX, eventY)) 85 | setText(""); 86 | } 87 | return super.onTouchEvent(event); 88 | } 89 | 90 | @Override 91 | protected void finalize() throws Throwable { 92 | super.finalize(); 93 | } 94 | } -------------------------------------------------------------------------------- /SearchSidebar/src/main/java/com/lujianchao/SearchSideBar/SearchSidebar/SortAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.SearchSideBar.SearchSidebar; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.ImageView; 10 | import android.widget.SectionIndexer; 11 | import android.widget.TextView; 12 | 13 | 14 | import com.lujianchao.SearchSideBar.R; 15 | 16 | import java.util.List; 17 | 18 | public class SortAdapter extends BaseAdapter implements SectionIndexer { 19 | private List list = null; 20 | private Context mContext; 21 | 22 | public SortAdapter(Context mContext, List list) { 23 | this.mContext = mContext; 24 | this.list = list; 25 | } 26 | 27 | /** 28 | * 当ListView数据发生变化时,调用此方法来更新ListView 29 | * 30 | * @param list 31 | */ 32 | public void updateListView(List list) { 33 | this.list = list; 34 | notifyDataSetChanged(); 35 | } 36 | 37 | public int getCount() { 38 | return this.list.size(); 39 | } 40 | 41 | public Object getItem(int position) { 42 | return list.get(position); 43 | } 44 | 45 | public long getItemId(int position) { 46 | return position; 47 | } 48 | 49 | public View getView(final int position, View view, ViewGroup arg2) { 50 | ViewHolder viewHolder = null; 51 | final SearchSideBar.ContactSortModel mContactSortModel = list.get(position); 52 | if (view == null) { 53 | view = LayoutInflater.from(mContext).inflate(R.layout.listviewsortbarfilter_item, null); 54 | viewHolder = new ViewHolder(view); 55 | view.setTag(viewHolder); 56 | } else { 57 | viewHolder = (ViewHolder) view.getTag(); 58 | } 59 | 60 | int section = getSectionForPosition(position); 61 | if (position == getPositionForSection(section)) { 62 | viewHolder.tvLetter.setVisibility(View.VISIBLE); 63 | viewHolder.tvLetter.setText(mContactSortModel.getSortLetters()); 64 | } else { 65 | viewHolder.tvLetter.setVisibility(View.GONE); 66 | } 67 | 68 | viewHolder.contactsName.setText(this.list.get(position).getName()); 69 | viewHolder.contactsItemIcon.setImageURI(Uri.parse(list.get(position).getLogo())); 70 | // Glide.with(mContext).load(list.get(position).getLogo()).into(viewHolder.contactsItemIcon); 71 | return view; 72 | 73 | } 74 | 75 | 76 | class ViewHolder { 77 | TextView tvLetter; 78 | private ImageView contactsItemIcon; 79 | private TextView contactsName; 80 | 81 | ViewHolder(View mView) { 82 | tvLetter = (TextView) mView.findViewById(R.id.tv_catagory); 83 | contactsItemIcon = (ImageView) mView.findViewById(R.id.contacts_item_icon); 84 | contactsName = (TextView) mView.findViewById(R.id.contacts_name); 85 | } 86 | } 87 | 88 | public int getSectionForPosition(int position) { 89 | return list.get(position).getSortLetters().charAt(0); 90 | } 91 | 92 | public int getPositionForSection(int section) { 93 | for (int i = 0; i < getCount(); i++) { 94 | String sortStr = list.get(i).getSortLetters(); 95 | char firstChar = sortStr.toUpperCase().charAt(0); 96 | if (firstChar == section) { 97 | return i; 98 | } 99 | } 100 | return -1; 101 | } 102 | 103 | @Override 104 | public Object[] getSections() { 105 | return null; 106 | } 107 | } -------------------------------------------------------------------------------- /SearchSidebar/src/main/java/com/lujianchao/SearchSideBar/SearchSidebar/PinyinUtils.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.SearchSideBar.SearchSidebar; 2 | 3 | import net.sourceforge.pinyin4j.PinyinHelper; 4 | import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; 5 | import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; 6 | import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; 7 | import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType; 8 | import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; 9 | 10 | /** 11 | * @author: 肖丽娟 12 | * @description: 13 | * @projectName: SelectCityDome 14 | * @date: 2016-03-01 15 | * @time: 15:45 16 | */ 17 | public class PinyinUtils { 18 | /** 19 | * 获得汉语拼音首字母 20 | * 21 | * @param chines 汉字 22 | * @return 23 | */ 24 | public static String getAlpha(String chines) { 25 | String pinyinName = ""; 26 | char[] nameChar = chines.toCharArray(); 27 | HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); 28 | defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE); 29 | defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); 30 | for (int i = 0; i < nameChar.length; i++) { 31 | if (nameChar[i] > 128) { 32 | try { 33 | pinyinName += PinyinHelper.toHanyuPinyinStringArray( 34 | nameChar[i], defaultFormat)[0].charAt(0); 35 | } catch (BadHanyuPinyinOutputFormatCombination e) { 36 | e.printStackTrace(); 37 | } 38 | } else { 39 | pinyinName += nameChar[i]; 40 | } 41 | } 42 | return pinyinName; 43 | } 44 | 45 | /** 46 | * 将字符串中的中文转化为拼音,英文字符不变 47 | * 48 | * @param inputString 汉字 49 | * @return 50 | */ 51 | public static String getPingYin(String inputString) { 52 | HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); 53 | format.setCaseType(HanyuPinyinCaseType.LOWERCASE); 54 | format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); 55 | format.setVCharType(HanyuPinyinVCharType.WITH_V); 56 | String output = ""; 57 | if (inputString != null && inputString.length() > 0 && !"null".equals(inputString)) { 58 | char[] input = inputString.trim().toCharArray(); 59 | try { 60 | for (int i = 0; i < input.length; i++) { 61 | if (Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) { 62 | String[] temp = PinyinHelper.toHanyuPinyinStringArray(input[i], format); 63 | output += temp[0]; 64 | } else 65 | output += Character.toString(input[i]); 66 | } 67 | } catch (BadHanyuPinyinOutputFormatCombination e) { 68 | e.printStackTrace(); 69 | } 70 | } else { 71 | return "*"; 72 | } 73 | return output; 74 | } 75 | 76 | /** 77 | * 汉字转换位汉语拼音首字母,英文字符不变 78 | * 79 | * @param chines 汉字 80 | * @return 拼音 81 | */ 82 | public static String converterToFirstSpell(String chines) { 83 | String pinyinName = ""; 84 | char[] nameChar = chines.toCharArray(); 85 | HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); 86 | defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE); 87 | defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); 88 | for (int i = 0; i < nameChar.length; i++) { 89 | if (nameChar[i] > 128) { 90 | try { 91 | pinyinName += PinyinHelper.toHanyuPinyinStringArray( 92 | nameChar[i], defaultFormat)[0].charAt(0); 93 | } catch (BadHanyuPinyinOutputFormatCombination e) { 94 | e.printStackTrace(); 95 | } 96 | } else { 97 | pinyinName += nameChar[i]; 98 | } 99 | } 100 | return pinyinName; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/java/com/lujianchao/SearchSideBar/SearchSidebar/SideBar.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.SearchSideBar.SearchSidebar; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Typeface; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.widget.TextView; 12 | 13 | 14 | import com.lujianchao.SearchSideBar.R; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | /** 21 | * ListView右侧的字母索引View 22 | */ 23 | public class SideBar extends View { 24 | 25 | public static String[] INDEX_STRING = {"#", "A", "B", "C", "D", "E", "F", "G", "H", "I", 26 | "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", 27 | "W", "X", "Y", "Z"}; 28 | 29 | private OnTouchingLetterChangedListener onTouchingLetterChangedListener; 30 | private List letterList; 31 | private int choose = -1; 32 | private Paint paint = new Paint(); 33 | private TextView mTextDialog; 34 | 35 | public SideBar(Context context) { 36 | this(context, null); 37 | } 38 | 39 | public SideBar(Context context, AttributeSet attrs) { 40 | this(context, attrs, 0); 41 | } 42 | 43 | public SideBar(Context context, AttributeSet attrs, int defStyle) { 44 | super(context, attrs, defStyle); 45 | init(); 46 | } 47 | 48 | private void init() { 49 | setBackgroundColor(Color.parseColor("#00FFFFFF")); 50 | letterList = Arrays.asList(INDEX_STRING); 51 | } 52 | 53 | protected void onDraw(Canvas canvas) { 54 | super.onDraw(canvas); 55 | int height = getHeight();// 获取对应高度 56 | int width = getWidth();// 获取对应宽度 57 | int singleHeight = height / letterList.size();// 获取每一个字母的高度 58 | for (int i = 0; i < letterList.size(); i++) { 59 | paint.setColor(Color.parseColor("#606060")); 60 | paint.setTypeface(Typeface.DEFAULT_BOLD); 61 | paint.setAntiAlias(true); 62 | paint.setTextSize(30); 63 | // 选中的状态 64 | if (i == choose) { 65 | paint.setColor(Color.parseColor("#4F41FD")); 66 | paint.setFakeBoldText(true); 67 | } 68 | // x坐标等于中间-字符串宽度的一半. 69 | float xPos = width / 2 - paint.measureText(letterList.get(i)) / 2; 70 | float yPos = singleHeight * i + singleHeight / 2; 71 | canvas.drawText(letterList.get(i), xPos, yPos, paint); 72 | paint.reset();// 重置画笔 73 | } 74 | } 75 | 76 | @Override 77 | public boolean dispatchTouchEvent(MotionEvent event) { 78 | final int action = event.getAction(); 79 | final float y = event.getY();// 点击y坐标 80 | final int oldChoose = choose; 81 | final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; 82 | final int c = (int) (y / getHeight() * letterList.size());// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数. 83 | 84 | switch (action) { 85 | case MotionEvent.ACTION_UP: 86 | setBackgroundColor(Color.parseColor("#00FFFFFF")); 87 | choose = -1; 88 | invalidate(); 89 | if (mTextDialog != null) { 90 | mTextDialog.setVisibility(View.GONE); 91 | } 92 | break; 93 | default: 94 | setBackgroundResource(R.drawable.bg_sidebar); 95 | if (oldChoose != c) { 96 | if (c >= 0 && c < letterList.size()) { 97 | if (listener != null) { 98 | listener.onTouchingLetterChanged(letterList.get(c)); 99 | } 100 | if (mTextDialog != null) { 101 | mTextDialog.setText(letterList.get(c)); 102 | mTextDialog.setVisibility(View.VISIBLE); 103 | } 104 | choose = c; 105 | invalidate(); 106 | } 107 | } 108 | break; 109 | } 110 | return true; 111 | } 112 | 113 | public void setIndexText(ArrayList indexStrings) { 114 | this.letterList = indexStrings; 115 | if (indexStrings.size() > 20) { 116 | } else if (indexStrings.size() > 15) { 117 | getLayoutParams().height = indexStrings.size() * 80; 118 | 119 | } else if (indexStrings.size() > 10) { 120 | getLayoutParams().height = indexStrings.size() * 120; 121 | 122 | } else { 123 | getLayoutParams().height = indexStrings.size() * 180; 124 | } 125 | invalidate(); 126 | } 127 | 128 | /** 129 | * 为SideBar设置显示当前按下的字母的TextView 130 | * 131 | * @param mTextDialog 132 | */ 133 | public void setTextView(TextView mTextDialog) { 134 | this.mTextDialog = mTextDialog; 135 | } 136 | 137 | /** 138 | * 向外公开的方法 139 | * 140 | * @param onTouchingLetterChangedListener 141 | */ 142 | public void setOnTouchingLetterChangedListener( 143 | OnTouchingLetterChangedListener onTouchingLetterChangedListener) { 144 | this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; 145 | } 146 | 147 | /** 148 | * 接口 149 | */ 150 | public interface OnTouchingLetterChangedListener { 151 | void onTouchingLetterChanged(String s); 152 | } 153 | } -------------------------------------------------------------------------------- /SearchSidebar/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | -------------------------------------------------------------------------------- /SearchSidebar/src/main/java/com/lujianchao/SearchSideBar/SearchSidebar/SearchSideBar.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.SearchSideBar.SearchSidebar; 2 | 3 | import android.content.Context; 4 | import android.text.Editable; 5 | import android.text.TextUtils; 6 | import android.text.TextWatcher; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | import android.widget.AdapterView; 10 | import android.widget.LinearLayout; 11 | import android.widget.ListView; 12 | import android.widget.TextView; 13 | 14 | import com.lujianchao.SearchSideBar.R; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.Comparator; 19 | import java.util.List; 20 | 21 | /** 22 | * Created by Lu JianChao on 2016/6/29. 23 | * https://github.com/hnsugar 24 | */ 25 | public class SearchSideBar extends LinearLayout { 26 | private ListView sortListView; 27 | private SideBar sideBar; 28 | private Context mContext; 29 | private TextView dialog; 30 | private SortAdapter adapter; 31 | private SearchEditText mEtSearchName; 32 | private List SourceDateList; 33 | private onItemClickListener mOnItemClickListener; 34 | 35 | public SearchSideBar(Context context, AttributeSet attrs) { 36 | super(context, attrs); 37 | mContext = context; 38 | addView(View.inflate(context, R.layout.listviewsortbarfilter, null)); 39 | } 40 | 41 | public void setMembers(List mMembers, onItemClickListener mListener) { 42 | this.SourceDateList = mMembers; 43 | this.mOnItemClickListener = mListener; 44 | initViews(); 45 | } 46 | 47 | private void initViews() { 48 | mEtSearchName = (SearchEditText) findViewById(R.id.et_search); 49 | sideBar = (SideBar) findViewById(R.id.sidrbar); 50 | dialog = (TextView) findViewById(R.id.dialog); 51 | sortListView = (ListView) findViewById(R.id.sortListView); 52 | initDatas(); 53 | initEvents(); 54 | setAdapter(); 55 | } 56 | 57 | private void setAdapter() { 58 | 59 | filledData(SourceDateList); 60 | Collections.sort(SourceDateList, new PinyinComparator()); 61 | adapter = new SortAdapter(mContext, SourceDateList); 62 | sortListView.setAdapter(adapter); 63 | } 64 | 65 | private void initEvents() { 66 | //设置右侧触摸监听 67 | sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() { 68 | @Override 69 | public void onTouchingLetterChanged(String s) { 70 | //该字母首次出现的位置 71 | int position = adapter.getPositionForSection(s.charAt(0)); 72 | if (position != -1) { 73 | sortListView.setSelection(position); 74 | } 75 | } 76 | }); 77 | 78 | //ListView的点击事件 79 | sortListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 80 | 81 | @Override 82 | public void onItemClick(AdapterView parent, View view, int position, long id) { 83 | if (mOnItemClickListener != null) { 84 | mOnItemClickListener.onItem(position, (ContactSortModel) adapter.getItem(position)); 85 | } 86 | } 87 | }); 88 | 89 | //根据输入框输入值的改变来过滤搜索 90 | mEtSearchName.addTextChangedListener(new TextWatcher() { 91 | @Override 92 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 93 | 94 | } 95 | 96 | @Override 97 | public void onTextChanged(CharSequence s, int start, int before, int count) { 98 | //当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表 99 | filterData(s.toString()); 100 | } 101 | 102 | @Override 103 | public void afterTextChanged(Editable s) { 104 | 105 | } 106 | }); 107 | } 108 | 109 | private void initDatas() { 110 | sideBar.setTextView(dialog); 111 | } 112 | 113 | /** 114 | * 根据输入框中的值来过滤数据并更新ListView 115 | * 116 | * @param filterStr 117 | */ 118 | private void filterData(String filterStr) { 119 | List mSortList = new ArrayList<>(); 120 | if (TextUtils.isEmpty(filterStr)) { 121 | mSortList = SourceDateList; 122 | } else { 123 | mSortList.clear(); 124 | for (ContactSortModel sortModel : SourceDateList) { 125 | String name = sortModel.getName(); 126 | if (name.toUpperCase().indexOf(filterStr.toString().toUpperCase()) != -1 || PinyinUtils.getPingYin(name).toUpperCase().startsWith(filterStr.toString().toUpperCase())) { 127 | mSortList.add(sortModel); 128 | } 129 | } 130 | } 131 | // 根据a-z进行排序 132 | Collections.sort(mSortList, new PinyinComparator()); 133 | adapter.updateListView(mSortList); 134 | } 135 | 136 | /** 137 | * 计算出首字母 138 | * @param mMembers 139 | * @return 140 | */ 141 | private List filledData(List mMembers) { 142 | List mSortList = mMembers; 143 | ArrayList indexString = new ArrayList<>(); 144 | 145 | for (int i = 0; i < mSortList.size(); i++) { 146 | String pinyin = PinyinUtils.getPingYin(mSortList.get(i).getName()); 147 | String sortString = pinyin.substring(0, 1).toUpperCase(); 148 | if (sortString.matches("[A-Z]")) { 149 | mSortList.get(i).setSortLetters(sortString.toUpperCase()); 150 | if (!indexString.contains(sortString)) { 151 | indexString.add(sortString); 152 | } 153 | } else { 154 | if (!indexString.contains("#")) { 155 | indexString.add("#"); 156 | } 157 | } 158 | 159 | } 160 | Collections.sort(indexString); 161 | sideBar.setIndexText(indexString); 162 | return mSortList; 163 | } 164 | 165 | 166 | /** 167 | * 用来对ListView中的数据根据A-Z进行排序,前面两个if判断主要是将不是以汉字开头的数据放在后面 168 | */ 169 | public class PinyinComparator implements Comparator { 170 | 171 | public int compare(ContactSortModel o1, ContactSortModel o2) { 172 | //这里主要是用来对ListView里面的数据根据ABCDEFG...来排序 173 | if (o1.getSortLetters().equals("@") 174 | || o2.getSortLetters().equals("#")) { 175 | return 1; 176 | } else if (o1.getSortLetters().equals("#") 177 | || o2.getSortLetters().equals("@")) { 178 | return -1; 179 | } else { 180 | return o1.getSortLetters().compareTo(o2.getSortLetters()); 181 | } 182 | } 183 | } 184 | 185 | 186 | public static class ContactSortModel { 187 | 188 | private String name;//显示的数据 189 | private String ID; 190 | private String Logo; 191 | private String sortLetters = "#";//显示数据拼音的首字母 192 | private String tag; 193 | 194 | public String getTag() { 195 | return tag; 196 | } 197 | 198 | public void setTag(String mTag) { 199 | tag = mTag; 200 | } 201 | 202 | public String getName() { 203 | return name; 204 | } 205 | 206 | public void setName(String name) { 207 | this.name = name; 208 | } 209 | 210 | public String getSortLetters() { 211 | return sortLetters; 212 | } 213 | 214 | public void setSortLetters(String sortLetters) { 215 | this.sortLetters = sortLetters; 216 | } 217 | 218 | public String getID() { 219 | return ID; 220 | } 221 | 222 | public void setID(String mID) { 223 | ID = mID; 224 | } 225 | 226 | public String getLogo() { 227 | return Logo; 228 | } 229 | 230 | public void setLogo(String mLogo) { 231 | Logo = mLogo; 232 | } 233 | 234 | @Override 235 | public String toString() { 236 | return "ContactSortModel{" + 237 | "name='" + name + '\'' + 238 | ", ID='" + ID + '\'' + 239 | ", Logo='" + Logo + '\'' + 240 | ", sortLetters='" + sortLetters + '\'' + 241 | '}'; 242 | } 243 | } 244 | 245 | public interface onItemClickListener { 246 | public void onItem(int position, ContactSortModel mModel); 247 | } 248 | 249 | } 250 | 251 | -------------------------------------------------------------------------------- /SearchSidebar/applist.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /SearchSidebar/SearchSidebar.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | --------------------------------------------------------------------------------