1.效果图
2.build.gradle 加入
compile 'com.belerweb:pinyin4j:2.5.1'// 名称解析
3.7个java类
3.1 SideBarActivity.java
public class SideBarActivity extends AppCompatActivity {
private static final String TAG = "SideBarActivity";
private EditText editText;
private ListView listView;
private SideBar sideBar;
private MyAdapter adapter;
private List list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_side_bar);
editText = (EditText) this.findViewById(R.id.friend_search);
listView = (ListView) this.findViewById(R.id.listView);
sideBar = (SideBar) this.findViewById(R.id.sidebar);
initView();
initData();
}
private void initView() {
/**
* sideBar触摸事件
*/
sideBar.setOnStrSelectCallBack(new SideBar.ISideBarSelectCallBack() {
@Override
public void onSelectStr(int index, String selectStr) {
if (list == null) {
return;
}
for (int i = 0; i < list.size(); i++) {
if (selectStr.equalsIgnoreCase(list.get(i).getFirstPinYin())) {
listView.setSelection(i); //选择到首字母出现的位置
return;
}
}
}
});
/**
* 输入框监听事件
*/
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
searchData(charSequence.toString());
}
@Override
public void afterTextChanged(Editable editable) {
}
});
/**
* listView点击事件
*/
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> adapterView, View view, int position, long l) {
String search = editText.getText().toString().trim();
//未搜索点击事件
if (TextUtils.isEmpty(search)) {
Log.e(TAG, "onItemClick: " + list.get(position).getName());
} else {
//搜索后点击事件
Log.e(TAG, "onItemClick: " + mSortList.get(position).getName());
}
}
});
}
private List mSortList;
private void searchData(String filterStr) {
mSortList = new ArrayList<>();
//未输入显示原来的数据
if (Tools.isEmpty(filterStr)) {
mSortList = list;
} else {
//遍历搜索后的list 添加至新的list中
for (DataBean sortModel : list) {
String name = sortModel.getName();
if (name.toUpperCase().indexOf(filterStr.toString().toUpperCase()) != -1 || Tool.getPinYin(name).
toUpperCase().startsWith(filterStr.toString().toUpperCase())) {
mSortList.add(sortModel);
}
}
}
// 根据a-z进行再次排序
Collections.sort(mSortList, new PinYinComparator());
//刷新adapter
adapter.updateListView(mSortList);
}
private void initData() {
DataBean data1 = new DataBean("李艺彤");
DataBean data2 = new DataBean("鞠婧祎");
DataBean data3 = new DataBean("刘然");
DataBean data4 = new DataBean("邱欣怡");
DataBean data5 = new DataBean("莫寒");
DataBean data6 = new DataBean("费沁源");
DataBean data7 = new DataBean("张语格");
DataBean data8 = new DataBean("宋欣冉");
DataBean data9 = new DataBean("曾艳芬");
DataBean data10 = new DataBean("龚诗琪");
list.add(data1);
list.add(data2);
list.add(data3);
list.add(data4);
list.add(data5);
list.add(data6);
list.add(data7);
list.add(data8);
list.add(data9);
list.add(data10);
//A-Z字母索引排序
Collections.sort(list, new PinYinComparator());
adapter = new MyAdapter(this, list);
listView.setAdapter(adapter);
}
}
3.2 MyAdapter.java
public class MyAdapter extends BaseAdapter {
private List list;
private LayoutInflater inflater;
public MyAdapter(Context context, List dataBeen) {
this.list = dataBeen;
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return list.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View view, ViewGroup viewGroup) {
ViewHodler viewHolder;
if (view == null) {
viewHolder = new ViewHodler();
//关联布局
view = inflater.inflate(R.layout.list_item, null);
//找id (注意通过view点否则null)
viewHolder.index = (TextView) view.findViewById(R.id.item_index);
viewHolder.name = (TextView) view.findViewById(R.id.item_name);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHodler) view.getTag();
}
viewHolder.name.setText(list.get(position).getName());
//如果当前位置等于该分类首字母的位置 ,则认为是第一次出现
if (position == getPositionForSection(list.get(position).getFirstPinYin())) {
//只保留首字母
viewHolder.index.setVisibility(View.VISIBLE);
//转大写
viewHolder.index.setText(list.get(position).getFirstPinYin().toUpperCase());
} else {
//其他隐藏并站位
viewHolder.index.setVisibility(View.GONE);
}
return view;
}
class ViewHodler {
//索引与名称
TextView index, name;
// LinearLayout linearLayout;
}
/**
* 获取catalog首次出现位置
*/
private int getPositionForSection(String catalog) {
for (int i = 0; i < getCount(); i++) {
String sortStr = list.get(i).getFirstPinYin();
if (catalog.equalsIgnoreCase(sortStr)) {
return i;
}
}
return -1;
}
/**
* 当ListView数据发生变化时,调用此方法来更新ListView
*
* @param list
*/
public void updateListView(List list) {
this.list = list;
notifyDataSetChanged();
}
}
3.3 Tool.java
public class Tool {
/**
* 获取汉字字符串的汉语拼音,英文字符不变
*/
public static String getPinYin(String chines) {
sb.setLength(0);
char[] nameChar = chines.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < nameChar.length; i++) {
if (nameChar[i] > 128) {
try {
sb.append(PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0]);
} catch (Exception e) {
e.printStackTrace();
}
} else {
sb.append(nameChar[i]);
}
}
return sb.toString();
}
}
3.4 SideBar.java
/**
* 26个字母索引
* 可以找其他效果的sideBar,百度很多
*/
public class SideBar extends AppCompatTextView {
private String[] letters = new String[]{"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", "#"};
/**
* 普通情况下字体大小
*/
float singleTextH;
private Paint textPaint;
private Paint bigTextPaint;
private Paint scaleTextPaint;
private Canvas canvas;
private int itemH;
private int w;
private int h;
/**
* 缩放离原始的宽度
*/
private float scaleWidth;
/**
* 滑动的Y
*/
private float eventY = 0;
/**
* 缩放的倍数
*/
private int scaleSize = 1;
/**
* 缩放个数item,即开口大小
*/
private int scaleItemCount = 6;
private ISideBarSelectCallBack callBack;
public SideBar(Context context) {
this(context, null);
}
public SideBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
if (attrs != null) {
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);
scaleSize = ta.getInteger(R.styleable.SideBar_scaleSize, 1);
scaleItemCount = ta.getInteger(R.styleable.SideBar_scaleItemCount, 6);
scaleWidth = ta.getDimensionPixelSize(R.styleable.SideBar_scaleWidth, dp(100));
ta.recycle();
}
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(getCurrentTextColor());
textPaint.setTextSize(getTextSize());
textPaint.setTextAlign(Paint.Align.CENTER);
bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bigTextPaint.setColor(getCurrentTextColor());
bigTextPaint.setTextSize(getTextSize() * (scaleSize + 3));
bigTextPaint.setTextAlign(Paint.Align.CENTER);
scaleTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scaleTextPaint.setColor(getCurrentTextColor());
scaleTextPaint.setTextSize(getTextSize() * (scaleSize + 1));
scaleTextPaint.setTextAlign(Paint.Align.CENTER);
}
/**
* 字母数据源
*/
public void setDataResource(String[] data) {
letters = data;
invalidate();
}
/**
* 回调
*/
public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {
this.callBack = callBack;
}
/**
* 设置字体缩放比例
*
* @param scale
*/
public void setScaleSize(int scale) {
scaleSize = scale;
invalidate();
}
/**
* 设置缩放字体的个数,即开口大小
*
* @param scaleItemCount
*/
public void setScaleItemCount(int scaleItemCount) {
this.scaleItemCount = scaleItemCount;
invalidate();
}
private int dp(int px) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (px * scale + 0.5f);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {
eventY = event.getY();
invalidate();
return true;
} else {
eventY = 0;
invalidate();
break;
}
case MotionEvent.ACTION_CANCEL:
eventY = 0;
invalidate();
return true;
case MotionEvent.ACTION_UP:
if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {
eventY = 0;
invalidate();
return true;
} else
break;
}
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
this.canvas = canvas;
DrawView(eventY);
}
private void DrawView(float y) {
int currentSelectIndex = -1;
if (y != 0) {
for (int i = 0; i < letters.length; i++) {
float currentItemY = itemH * i;
float nextItemY = itemH * (i + 1);
if (y >= currentItemY && y < nextItemY) {
currentSelectIndex = i;
if (callBack != null) {
callBack.onSelectStr(currentSelectIndex, letters[i]);
}
//画大的字母
Paint.FontMetrics fontMetrics = bigTextPaint.getFontMetrics();
float bigTextSize = fontMetrics.descent - fontMetrics.ascent;
canvas.drawText(letters[i], w - getPaddingRight() - scaleWidth - bigTextSize, singleTextH + itemH
* i, bigTextPaint);
}
}
}
drawLetters(y, currentSelectIndex);
}
private void drawLetters(float y, int index) {
//第一次进来没有缩放情况,默认画原图
if (index == -1) {
w = getMeasuredWidth();
h = getMeasuredHeight();
itemH = h / letters.length;
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
singleTextH = fontMetrics.descent - fontMetrics.ascent;
for (int i = 0; i < letters.length; i++) {
canvas.drawText(letters[i], w - getPaddingRight(), singleTextH + itemH * i, textPaint);
}
//触摸的时候画缩放图
} else {
//遍历所有字母
for (int i = 0; i < letters.length; i++) {
//要画的字母的起始Y坐标
float currentItemToDrawY = singleTextH + itemH * i;
float centerItemToDrawY;
if (index < i)
centerItemToDrawY = singleTextH + itemH * (index + scaleItemCount);
else
centerItemToDrawY = singleTextH + itemH * (index - scaleItemCount);
float delta = 1 - Math.abs((y - currentItemToDrawY) / (centerItemToDrawY - currentItemToDrawY));
float maxRightX = w - getPaddingRight();
//如果大于0,表明在y坐标上方
scaleTextPaint.setTextSize(getTextSize() + getTextSize() * delta);
float drawX = maxRightX - scaleWidth * delta;
//超出边界直接画在边界上
if (drawX > maxRightX){
canvas.drawText(letters[i], maxRightX, singleTextH + itemH * i, textPaint);
} else{
canvas.drawText(letters[i], drawX, singleTextH + itemH * i, scaleTextPaint);
}
}
}
}
public interface ISideBarSelectCallBack {
void onSelectStr(int index, String selectStr);
}
}
3.5 数据类 DataBean.java
public class DataBean implements IFirstPinYin {
/**
* 名称
*/
private String name;
private String pinyin;// 姓名对应的拼音
private String firstPinYin;//拼音首字母
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
public void setFirstPinYin(String firstPinYin) {
this.firstPinYin = firstPinYin;
}
public String getPinyin() {
if (TextUtils.isEmpty(pinyin)) {
pinyin = Tool.getPinYin(name); // 根据姓名获取拼音
}
return pinyin;
}
public String getFirstPinYin() {
if (TextUtils.isEmpty(pinyin)) {
pinyin = Tool.getPinYin(name); // 根据姓名获取拼音
}
firstPinYin = pinyin.substring(0, 1).toUpperCase(); // 获取拼音首字母并转成大写
if (!firstPinYin.matches("[A-Z]")) { // 如果不在A-Z中则默认为“#”
firstPinYin = "#";
}
return firstPinYin;
}
public DataBean(String name) {
this.name = name;
}
}
3.6 IFirstPinYin.java
/**
* 首字母拼音类
*/
public interface IFirstPinYin {
//姓名对应的拼音
String getPinyin();
//拼音首字母
String getFirstPinYin();
}
3.7 排序 PinYinComparator.java
/**
* 首字母拼音排序
*/
public class PinYinComparator implements Comparator<IFirstPinYin> {
@Override
public int compare(IFirstPinYin o1, IFirstPinYin o2) {
if (o1.getFirstPinYin().equals("#") && !o2.getFirstPinYin().equals("#")) {
return 1;
} else if (!o1.getFirstPinYin().equals("#") && o2.getFirstPinYin().equals("#")) {
return -1;
} else {
return o1.getPinyin().compareToIgnoreCase(o2.getPinyin());
}
}
}
4.2个布局一个shape
4.1 activity_side_bar.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:background="@drawable/view_radius"
android:gravity="center"
android:orientation="horizontal"
android:padding="5dp">
<EditText
android:id="@+id/friend_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:hint="搜索关键词"
android:textSize="15sp"/>
LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:divider="#00000000"/>
<SideBar
android:id="@+id/sidebar"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:paddingRight="10dp"
android:textSize="12sp" />
RelativeLayout>
LinearLayout>
4.2 list_item.xml
<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:id="@+id/item_index"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#99999999"
android:padding="5dp"
android:text="A"
android:textSize="16sp" />
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="-"
android:textSize="16sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="15dp"
android:background="#99999999"/>
LinearLayout>
4.3 view_radius.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp" />
<solid android:color="#666"/>
shape>
5.最后是SideBar的style 在values下新建attrs.xml
<resources>
<declare-styleable name="SideBar">
<attr name="scaleSize" format="integer" />
<attr name="scaleItemCount" format="integer" />
<attr name="scaleWidth" format="dimension" />
declare-styleable>
resources>