生活中,常常遇到这样一个Android功能,联系人按照字母排序,于是乎,爱搞事情的鄙人,也做了一个这样一个功能,以下是代码笔记。
首先,咱得导入三方包,我日,本人较懒,我看到网上很多都用sidebar ,原本以为是一个第三方包,但找来找去就是没有,那干脆照着网上说的写一个工具类吧。
SideBar.class
public class SideBar extends View {
private static final String TAG = SideBar.class.getSimpleName();
public interface OnTouchingLetterChangedListener {
void onTouchingLetterChanged(String s);
}
private OnTouchingLetterChangedListener mOnTouchingLetterChangedListener;
private String[] mLetters = null;
private Paint mPaint;
private int mTextColor;
private int mResArrayId = R.array.letter_list;
private int mChoose = -1;
private final float mDensity;
private float mY;
private float mHalfWidth, mHalfHeight;
private float mLetterHeight;
private float mAnimStep;
private int mTouchSlop;
private float mInitialDownY;
private boolean mIsBeingDragged, mStartEndAnim;
private int mActivePointerId = INVALID_POINTER;
private RectF mIsDownRect = new RectF();
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);
this.mPaint = new Paint();
this.mTextColor = Color.GRAY;
this.mPaint.setAntiAlias(true);
this.mPaint.setTextAlign(Paint.Align.CENTER);
this.mPaint.setColor(this.mTextColor);
this.mLetters = context.getResources().getStringArray(mResArrayId);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mDensity = getContext().getResources().getDisplayMetrics().density;
setPadding(0, dip2px(20), 0, dip2px(20));
}
public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener listener) {
this.mOnTouchingLetterChangedListener = listener;
}
private int getLettersSize() {
return mLetters.length;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mIsBeingDragged = false;
final float initialDownY = getMotionEventY(ev, mActivePointerId);
if (initialDownY == -1) {
return false;
}
if (!mIsDownRect.contains(ev.getX(), ev.getY())) {
return false;
}
mInitialDownY = initialDownY;
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER) {
return false;
}
final float y = getMotionEventY(ev, mActivePointerId);
if (y == -1) {
return false;
}
final float yDiff = Math.abs(y - mInitialDownY);
if (yDiff > mTouchSlop && !mIsBeingDragged) {
mIsBeingDragged = true;
}
if (mIsBeingDragged) {
mY = y;
final float moveY = y - getPaddingTop() - mLetterHeight / 1.64f;
final int characterIndex = (int) (moveY / mHalfHeight * mLetters.length);
if (mChoose != characterIndex) {
if (characterIndex >= 0 && characterIndex < mLetters.length) {
mChoose = characterIndex;
Log.d(TAG, "mChoose " + mChoose + " mLetterHeight " + mLetterHeight);
mOnTouchingLetterChangedListener.onTouchingLetterChanged(mLetters[characterIndex]);
}
}
invalidate();
}
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mOnTouchingLetterChangedListener != null) {
if (mIsBeingDragged) {
mOnTouchingLetterChangedListener.onTouchingLetterChanged(mLetters[mChoose]);
} else {
float downY = ev.getY() - getPaddingTop();
final int characterIndex = (int) (downY / mHalfHeight * mLetters.length);
if (characterIndex >= 0 && characterIndex < mLetters.length) {
mOnTouchingLetterChangedListener.onTouchingLetterChanged(mLetters[characterIndex]);
}
}
}
mStartEndAnim = mIsBeingDragged;
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
mChoose = -1;
mAnimStep = 0f;
invalidate();
return false;
}
return true;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHalfWidth = w - dip2px(16);
mHalfHeight = h - getPaddingTop() - getPaddingBottom();
float lettersLen = getLettersSize();
mLetterHeight = mHalfHeight / lettersLen;
int textSize = (int) (mHalfHeight * 0.7f / lettersLen);
this.mPaint.setTextSize(textSize);
mIsDownRect.set(w - dip2px(16 * 2), 0, w, h);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < getLettersSize(); i++) {
float letterPosY = mLetterHeight * (i + 1) + getPaddingTop();
float diff, diffY, diffX;
if (mChoose == i && i != 0 && i != getLettersSize() - 1) {
diffX = 0f;
diffY = 0f;
diff = 2.16f;
} else {
float maxPos = Math.abs((mY - letterPosY) / mHalfHeight * 7f);
diff = Math.max(1f, 2.2f - maxPos);
if (mStartEndAnim && diff != 1f) {
diff -= mAnimStep;
if (diff <= 1f) {
diff = 1f;
}
} else if (!mIsBeingDragged) {
diff = 1f;
}
diffY = maxPos * 50f * (letterPosY >= mY ? -1 : 1);
diffX = maxPos * 100f;
}
canvas.save();
canvas.scale(diff, diff, mHalfWidth * 1.20f + diffX, letterPosY + diffY);
if (diff == 1f) {
this.mPaint.setAlpha(255);
this.mPaint.setTypeface(Typeface.DEFAULT);
} else {
int alpha = (int) (255 * (1 - Math.min(0.9, diff - 1)));
if (mChoose == i)
alpha = 255;
this.mPaint.setAlpha(alpha);
this.mPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
canvas.drawText(mLetters[i], mHalfWidth, letterPosY, this.mPaint);
canvas.restore();
}
if (mChoose == -1 && mStartEndAnim && mAnimStep <= 0.6f) {
mAnimStep += 0.6f;
postInvalidateDelayed(25);
} else {
mAnimStep = 0f;
mStartEndAnim = false;
}
}
private int dip2px(int dipValue) {
return (int) (dipValue * mDensity + 0.5f);
}
private float getMotionEventY(MotionEvent ev, int activePointerId) {
final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
if (index < 0) {
return -1;
}
return MotionEventCompat.getY(ev, index);
}
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
}
}
}
以上的类直接拿来用吧,孰能生巧,慢慢就理解这是啥玩意了,哦对了,还得给他一个A~#的属性值
- 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
- Y
- Z
- \#
接下来,咱把布局给加上,我用的是recycleview,推荐用这个,炒鸡好用,下次得写个recycleview的用法了
此外还要考虑汉字转拼音
cn2spell.class
public class Cn2Spell {
public static StringBuffer sb = new StringBuffer();
/**
* 获取汉字字符串的首字母,英文字符不变
* 例如:阿飞→af
*/
public static String getPinYinHeadChar(String chines) {
sb.setLength(0);
char[] chars = chines.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < chars.length; i++) {
if (chars[i] > 128) {
try {
sb.append(PinyinHelper.toHanyuPinyinStringArray(chars[i], defaultFormat)[0].charAt(0));
} catch (Exception e) {
e.printStackTrace();
}
} else {
sb.append(chars[i]);
}
}
return sb.toString();
}
/**
* 获取汉字字符串的第一个字母
*/
public static String getPinYinFirstLetter(String str) {
sb.setLength(0);
char c = str.charAt(0);
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);
if (pinyinArray != null) {
sb.append(pinyinArray[0].charAt(0));
} else {
sb.append(c);
}
return sb.toString();
}
/**
* 获取汉字字符串的汉语拼音,英文字符不变
*/
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();
}
接下来就是修改model类了,注意:修改model类,在需要添加所以的具体model类下面,添加一下代码,(有的情况是model下面还有model,讨厌~~嘤嘤嘤)
public class model {
public static class YcTeacherBean {
private String id;
private String yc_allow_address;
private String yc_allow_class;
private String yc_allow_qrcode;
private String yc_avatar;
private int yc_delete;
private int yc_gender;
private int yc_id;
private String yc_nickname;
private String yc_phone;
private int yc_role;
private String yc_rongcloudtoken;
private String yc_status;
private String yc_token;
private String yc_user_token;
private String yc_username;
private String yc_video_name;
private String firstLetter;
private String pinYin;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getYc_allow_address() {
return yc_allow_address;
}
public void setYc_allow_address(String yc_allow_address) {
this.yc_allow_address = yc_allow_address;
}
public String getYc_allow_class() {
return yc_allow_class;
}
public void setYc_allow_class(String yc_allow_class) {
this.yc_allow_class = yc_allow_class;
}
public String getYc_allow_qrcode() {
return yc_allow_qrcode;
}
public void setYc_allow_qrcode(String yc_allow_qrcode) {
this.yc_allow_qrcode = yc_allow_qrcode;
}
public String getYc_avatar() {
return yc_avatar;
}
public void setYc_avatar(String yc_avatar) {
this.yc_avatar = yc_avatar;
}
public int getYc_delete() {
return yc_delete;
}
public void setYc_delete(int yc_delete) {
this.yc_delete = yc_delete;
}
public int getYc_gender() {
return yc_gender;
}
public void setYc_gender(int yc_gender) {
this.yc_gender = yc_gender;
}
public int getYc_id() {
return yc_id;
}
public void setYc_id(int yc_id) {
this.yc_id = yc_id;
}
public String getYc_nickname() {
return yc_nickname;
}
public void setYc_nickname(String yc_nickname) {
this.yc_nickname = yc_nickname;
}
public String getYc_phone() {
return yc_phone;
}
public void setYc_phone(String yc_phone) {
this.yc_phone = yc_phone;
}
public int getYc_role() {
return yc_role;
}
public void setYc_role(int yc_role) {
this.yc_role = yc_role;
}
public String getYc_rongcloudtoken() {
return yc_rongcloudtoken;
}
public void setYc_rongcloudtoken(String yc_rongcloudtoken) {
this.yc_rongcloudtoken = yc_rongcloudtoken;
}
public String getYc_status() {
return yc_status;
}
public void setYc_status(String yc_status) {
this.yc_status = yc_status;
}
public String getYc_token() {
return yc_token;
}
public void setYc_token(String yc_token) {
this.yc_token = yc_token;
}
public String getYc_user_token() {
return yc_user_token;
}
public void setYc_user_token(String yc_user_token) {
this.yc_user_token = yc_user_token;
}
public String getYc_username() {
return yc_username;
}
public void setYc_username(String yc_username) {
this.yc_username = yc_username;
}
public String getYc_video_name() {
return yc_video_name;
}
public void setYc_video_name(String yc_video_name) {
this.yc_video_name = yc_video_name;
}
public String getFirstLetter() {
return firstLetter;
}
public void setFirstLetter(String firstLetter) {
this.firstLetter = firstLetter;
}
public String getPinYin() {
return pinYin;
}
public void setPinYin(String pinYin) {
this.pinYin = pinYin;
}
public void initFirstLetter(){
String name=yc_username;
if (yc_video_name.equals("2")){
name=yc_nickname;
}
this.firstLetter = Cn2Spell.getPinYinFirstLetter(name);
this.pinYin = Cn2Spell.getPinYin(name);
this.firstLetter = pinYin.substring(0, 1).toUpperCase(); // 获取拼音首字母并转成大写
if (!firstLetter.matches("[A-Z]")) { // 如果不在A-Z中则默认为“#”
firstLetter = "#";
}
}
}
}
以上关键代码是这些瞎
private String firstLetter;
private String pinYin;
public String getFirstLetter() {
return firstLetter;
}
public void setFirstLetter(String firstLetter) {
this.firstLetter = firstLetter;
}
public String getPinYin() {
return pinYin;
}
public void setPinYin(String pinYin) {
this.pinYin = pinYin;
}
public void initFirstLetter(){
String name=yc_username;
if (yc_video_name.equals("2")){
name=yc_nickname;
}
this.firstLetter = Cn2Spell.getPinYinFirstLetter(name);
this.pinYin = Cn2Spell.getPinYin(name);
this.firstLetter = pinYin.substring(0, 1).toUpperCase(); // 获取拼音首字母并转成大写
if (!firstLetter.matches("[A-Z]")) { // 如果不在A-Z中则默认为“#”
firstLetter = "#";
}
}
接下来是另外一个工具类
public class PaySection extends SectionEntity implements Comparable {
public PaySection(boolean isHeader, String header) {
super(isHeader, header);
}
public PaySection(model recordChildrenModel) {
super(recordChildrenModel);
}
@Override
public int compareTo(@NonNull PaySection o) {
if ( t.getYcTeacher().getFirstLetter().equals("#") && !o.t.getYcTeacher().getFirstLetter().equals("#")) {
return 1;
} else if (! t.getYcTeacher().getFirstLetter().equals("#") && o.t.getYcTeacher().getFirstLetter().equals("#")) {
return -1;
} else {
return t.getYcTeacher().getPinYin().compareToIgnoreCase(o.t.getYcTeacher().getPinYin());
}
}
}
上边代码是针对这个model写的一个工具类,我当然能看懂,如果有幸其他人看到,不懂可以留言、
最后,激动人心的时刻到了 轮到你了 activity
public class MainActivity extends AppCompatActivity {
@BindView(R.id.record_list_sb_letter)
SideBar record_list_sb_letter;
@BindView(R.id.user_concerned)
RecyclerView recyclerView1;
private static PayAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
record_list_sb_letter.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {
@Override
public void onTouchingLetterChanged(String s) {
if (adapter == null) {
return;
}
List sectionList = adapter.getData();
for (int i = 0; i < sectionList.size(); i++) {
PaySection recordSection = adapter.getData().get(i);
if (recordSection.isHeader && recordSection.header.equalsIgnoreCase(s)) {
layoutManager.scrollToPositionWithOffset(i, 0);
return;
}
}
}
});
}
public void teaSection(List teaList){
recordSectionList = new ArrayList<>();
for (int i = 0; i < teaList.size(); i++) {
TeaModel model = teaList.get(i);
model.getYcTeacher().initFirstLetter();
recordSectionList.add(new PaySection(model));
}
Collections.sort(recordSectionList);
//增加字母
String head = null;
for (int i = 0; i < recordSectionList.size(); i++) {
if (head == null || !head.equals(recordSectionList.get(i).t.getYcTeacher().getFirstLetter())) {
head = recordSectionList.get(i).t.getYcTeacher().getFirstLetter();
recordSectionList.add(i, new PaySection(true, head));
}
}
}
public void showTopnewsData(List teaList) {
teaSection(teaList);
adapter = new PayAdapter(R.layout.my_pay_tea,R.layout.record_head_pay_item, recordSectionList);
recyclerView1.setAdapter(adapter);}
}
大概就是这么个意思吧,activity代码不全,但是关键代码都有,主要是teaSection这个方法
呼~~
来个不太美观的效果图