今天带来的是 RecyclerView 的 A-Z 字母排序和过滤搜索功能。
效果图
01.jpg 02.jpg前期准备
- 在 app/build.gradle 添加如下依赖
implementation files('libs\\pinyin4j-2.5.0.jar')
//TitleBar
implementation 'com.github.getActivity:TitleBar:8.6'
//Butter Knife
implementation 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
-
中文转化为拼音的工具包 pinyin4j-2.5.0.jar ,官网地址:http://pinyin4j.sourceforge.net/。
-
array.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="waveSideBarLetters">
<item>\u2606</item>
<item>\u0023</item>
<item>A</item>
<item>B</item>
<item>C</item>
<item>D</item>
<item>E</item>
<item>F</item>
<item>G</item>
<item>H</item>
<item>I</item>
<item>J</item>
<item>K</item>
<item>L</item>
<item>M</item>
<item>N</item>
<item>O</item>
<item>P</item>
<item>Q</item>
<item>R</item>
<item>S</item>
<item>T</item>
<item>U</item>
<item>V</item>
<item>W</item>
<item>X</item>
<item>Y</item>
<item>Z</item>
</array>
<string-array name="date">
<item>裘豆豆</item>
<item>B李莎</item>
<item>jb</item>
<item>Jobs</item>
<item>动力火车</item>
<item>伍佰</item>
<item>#蔡依林</item>
<item>$797835344$</item>
<item>Jack</item>
<item>9527</item>
<item>戚薇</item>
<item>齐期浩二</item>
<item>齐天大圣</item>
<item>品冠</item>
<item>吴克群</item>
<item>贲素琴</item>
<item>缪丝</item>
<item>成龙</item>
<item>王力宏</item>
<item>汪峰</item>
<item>王菲</item>
<item>那英</item>
<item>张伟</item>
<item>~夏先生</item>
<item>阿aaa</item>
<item>阿李珊</item>
<item>陈奕迅</item>
<item>周杰伦</item>
<item>曾一鸣</item>
<item>哈林</item>
<item>高进</item>
<item>高雷</item>
<item>阮金天</item>
<item>龚琳娜</item>
<item>苏醒</item>
<item>苏永康</item>
<item>陶喆</item>
<item>沙宝亮</item>
<item>宋冬野</item>
<item>宋伟</item>
<item>袁成杰</item>
<item>张学友</item>
<item>李德华</item>
<item>郑源</item>
<item>白山水</item>
<item>白亮</item>
<item>陈龙</item>
<item>陈丽丽</item>
</string-array>
</resources>
- attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="waveSideBar">
<attr name="textColor" format="color|reference"/>
<attr name="backgroundColor" format="color|reference"/>
<attr name="chooseTextColor" format="color|reference"/>
<attr name="textSize" format="dimension"/>
<attr name="hintTextSize" format="dimension"/>
<attr name="radius" format="dimension"/>
<attr name="circleRadius" format="dimension"/>
</declare-styleable>
</resources>
代码
1. 自定义搜索框
public class ClearEditText extends EditText implements
View.OnFocusChangeListener, TextWatcher {
private Drawable mClearDrawable;
public ClearEditText(Context context) {
this(context, null);
}
public ClearEditText(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.editTextStyle);
}
public ClearEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mClearDrawable = getCompoundDrawables()[2];
if (mClearDrawable == null) {
mClearDrawable = getResources()
.getDrawable(R.mipmap.ic_delete);
}
mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight());
setClearIconVisible(false);
setOnFocusChangeListener(this);
addTextChangedListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (getCompoundDrawables()[2] != null) {
if (event.getAction() == MotionEvent.ACTION_UP) {
boolean touchable = event.getX() > (getWidth()
- getPaddingRight() - mClearDrawable.getIntrinsicWidth())
&& (event.getX() < ((getWidth() - getPaddingRight())));
if (touchable) {
this.setText("");
}
}
}
return super.onTouchEvent(event);
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
setClearIconVisible(getText().length() > 0);
} else {
setClearIconVisible(false);
}
}
protected void setClearIconVisible(boolean visible) {
Drawable right = visible ? mClearDrawable : null;
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], right, getCompoundDrawables()[3]);
}
@Override
public void onTextChanged(CharSequence s, int start, int count,
int after) {
setClearIconVisible(s.length() > 0);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
public void setShakeAnimation(){
this.setAnimation(shakeAnimation(5));
}
public static Animation shakeAnimation(int counts){
Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0);
translateAnimation.setInterpolator(new CycleInterpolator(counts));
translateAnimation.setDuration(1000);
return translateAnimation;
}
}
2. 自定义波浪侧边栏
/**
* Created on 2022/1/21 10:41
* 波浪侧边栏
* @author Gong Youqiang
*/
public class WaveSideBar extends View {
private static final String TAG = "WaveSideBar";
// 计算波浪贝塞尔曲线的角弧长值
private static final double ANGLE = Math.PI * 45 / 180;
private static final double ANGLE_R = Math.PI * 90 / 180;
private OnTouchLetterChangeListener mListener;
// 渲染字母表
private List<String> mLetters;
// 当前选中的位置
private int mChoosePosition = -1;
private int mOldPosition;
private int mNewPosition;
// 字母列表画笔
private Paint mLettersPaint = new Paint();
// 提示字母画笔
private Paint mTextPaint = new Paint();
// 波浪画笔
private Paint mWavePaint = new Paint();
private int mTextSize;
private int mHintTextSize;
private int mTextColor;
private int mWaveColor;
private int mTextColorChoose;
private int mWidth;
private int mHeight;
private int mItemHeight;
private int mPadding;
// 波浪路径
private Path mWavePath = new Path();
// 圆形路径
private Path mCirclePath = new Path();
// 手指滑动的Y点作为中心点
private int mCenterY; //中心点Y
// 贝塞尔曲线的分布半径
private int mRadius;
// 圆形半径
private int mCircleRadius;
// 用于过渡效果计算
private ValueAnimator mRatioAnimator;
// 用于绘制贝塞尔曲线的比率
private float mRatio;
// 选中字体的坐标
private float mPointX, mPointY;
// 圆形中心点X
private float mCircleCenterX;
public WaveSideBar(Context context) {
this(context, null);
}
public WaveSideBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WaveSideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mLetters = Arrays.asList(context.getResources().getStringArray(R.array.waveSideBarLetters));
mTextColor = Color.parseColor("#969696");
mWaveColor = Color.parseColor("#bef9b81b");
mTextColorChoose = ContextCompat.getColor(context, android.R.color.white);
mTextSize = context.getResources().getDimensionPixelSize(R.dimen.textSize);
mHintTextSize = context.getResources().getDimensionPixelSize(R.dimen.hintTextSize);
mPadding = context.getResources().getDimensionPixelSize(R.dimen.padding);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.waveSideBar);
mTextColor = a.getColor(R.styleable.waveSideBar_textColor, mTextColor);
mTextColorChoose = a.getColor(R.styleable.waveSideBar_chooseTextColor, mTextColorChoose);
mTextSize = a.getDimensionPixelSize(R.styleable.waveSideBar_textSize, mTextSize);
mHintTextSize = a.getDimensionPixelSize(R.styleable.waveSideBar_hintTextSize, mHintTextSize);
mWaveColor = a.getColor(R.styleable.waveSideBar_backgroundColor, mWaveColor);
mRadius = a.getDimensionPixelSize(R.styleable.waveSideBar_radius, context.getResources().getDimensionPixelSize(R.dimen.radius));
mCircleRadius = a.getDimensionPixelSize(R.styleable.waveSideBar_circleRadius, context.getResources().getDimensionPixelSize(R.dimen.circleRadius));
a.recycle();
}
mWavePaint = new Paint();
mWavePaint.setAntiAlias(true);
mWavePaint.setStyle(Paint.Style.FILL);
mWavePaint.setColor(mWaveColor);
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(mTextColorChoose);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setTextSize(mHintTextSize);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final float y = event.getY();
final float x = event.getX();
mOldPosition = mChoosePosition;
mNewPosition = (int) (y / mHeight * mLetters.size());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//限定触摸范围
if (x < mWidth - 1.5 * mRadius) {
return false;
}
mCenterY = (int) y;
startAnimator(1.0f);
break;
case MotionEvent.ACTION_MOVE:
mCenterY = (int) y;
if (mOldPosition != mNewPosition) {
if (mNewPosition >= 0 && mNewPosition < mLetters.size()) {
mChoosePosition = mNewPosition;
if (mListener != null) {
mListener.onLetterChange(mLetters.get(mNewPosition));
}
}
}
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
startAnimator(0f);
mChoosePosition = -1;
break;
default:
break;
}
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
mWidth = getMeasuredWidth();
mItemHeight = (mHeight - mPadding) / mLetters.size();
mPointX = mWidth - 1.6f * mTextSize;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制字母列表
drawLetters(canvas);
//绘制波浪
drawWavePath(canvas);
//绘制圆
drawCirclePath(canvas);
//绘制选中的字体
drawChooseText(canvas);
}
/**
* 绘制字母列表
*
* @param canvas
*/
private void drawLetters(Canvas canvas) {
RectF rectF = new RectF();
rectF.left = mPointX - mTextSize;
rectF.right = mPointX + mTextSize;
rectF.top = mTextSize / 2;
rectF.bottom = mHeight - mTextSize / 2;
mLettersPaint.reset();
mLettersPaint.setStyle(Paint.Style.FILL);
mLettersPaint.setColor(Color.parseColor("#F9F9F9"));
mLettersPaint.setAntiAlias(true);
canvas.drawRoundRect(rectF, mTextSize, mTextSize, mLettersPaint);
mLettersPaint.reset();
mLettersPaint.setStyle(Paint.Style.STROKE);
mLettersPaint.setColor(mTextColor);
mLettersPaint.setAntiAlias(true);
canvas.drawRoundRect(rectF, mTextSize, mTextSize, mLettersPaint);
for (int i = 0; i < mLetters.size(); i++) {
mLettersPaint.reset();
mLettersPaint.setColor(mTextColor);
mLettersPaint.setAntiAlias(true);
mLettersPaint.setTextSize(mTextSize);
mLettersPaint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = mLettersPaint.getFontMetrics();
float baseline = Math.abs(-fontMetrics.bottom - fontMetrics.top);
float pointY = mItemHeight * i + baseline / 2 + mPadding;
if (i == mChoosePosition) {
mPointY = pointY;
} else {
canvas.drawText(mLetters.get(i), mPointX, pointY, mLettersPaint);
}
}
}
/**
* 绘制选中的字母
*
* @param canvas
*/
private void drawChooseText(Canvas canvas) {
if (mChoosePosition != -1) {
// 绘制右侧选中字符
mLettersPaint.reset();
mLettersPaint.setColor(mTextColorChoose);
mLettersPaint.setTextSize(mTextSize);
mLettersPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(mLetters.get(mChoosePosition), mPointX, mPointY, mLettersPaint);
// 绘制提示字符
if (mRatio >= 0.9f) {
String target = mLetters.get(mChoosePosition);
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float baseline = Math.abs(-fontMetrics.bottom - fontMetrics.top);
float x = mCircleCenterX;
float y = mCenterY + baseline / 2;
canvas.drawText(target, x, y, mTextPaint);
}
}
}
/**
* 绘制波浪
*
* @param canvas
*/
private void drawWavePath(Canvas canvas) {
mWavePath.reset();
// 移动到起始点
mWavePath.moveTo(mWidth, mCenterY - 3 * mRadius);
//计算上部控制点的Y轴位置
int controlTopY = mCenterY - 2 * mRadius;
//计算上部结束点的坐标
int endTopX = (int) (mWidth - mRadius * Math.cos(ANGLE) * mRatio);
int endTopY = (int) (controlTopY + mRadius * Math.sin(ANGLE));
mWavePath.quadTo(mWidth, controlTopY, endTopX, endTopY);
//计算中心控制点的坐标
int controlCenterX = (int) (mWidth - 1.8f * mRadius * Math.sin(ANGLE_R) * mRatio);
int controlCenterY = mCenterY;
//计算下部结束点的坐标
int controlBottomY = mCenterY + 2 * mRadius;
int endBottomX = endTopX;
int endBottomY = (int) (controlBottomY - mRadius * Math.cos(ANGLE));
mWavePath.quadTo(controlCenterX, controlCenterY, endBottomX, endBottomY);
mWavePath.quadTo(mWidth, controlBottomY, mWidth, controlBottomY + mRadius);
mWavePath.close();
canvas.drawPath(mWavePath, mWavePaint);
}
/**
* 绘制左边提示的圆
*
* @param canvas
*/
private void drawCirclePath(Canvas canvas) {
//x轴的移动路径
mCircleCenterX = (mWidth + mCircleRadius) - (2.0f * mRadius + 2.0f * mCircleRadius) * mRatio;
mCirclePath.reset();
mCirclePath.addCircle(mCircleCenterX, mCenterY, mCircleRadius, Path.Direction.CW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mCirclePath.op(mWavePath, Path.Op.DIFFERENCE);
}
mCirclePath.close();
canvas.drawPath(mCirclePath, mWavePaint);
}
private void startAnimator(float value) {
if (mRatioAnimator == null) {
mRatioAnimator = new ValueAnimator();
}
mRatioAnimator.cancel();
mRatioAnimator.setFloatValues(value);
mRatioAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator value) {
mRatio = (float) value.getAnimatedValue();
//球弹到位的时候,并且点击的位置变了,即点击的时候显示当前选择位置
if (mRatio == 1f && mOldPosition != mNewPosition) {
if (mNewPosition >= 0 && mNewPosition < mLetters.size()) {
mChoosePosition = mNewPosition;
if (mListener != null) {
mListener.onLetterChange(mLetters.get(mNewPosition));
}
}
}
invalidate();
}
});
mRatioAnimator.start();
}
public void setOnTouchLetterChangeListener(OnTouchLetterChangeListener listener) {
this.mListener = listener;
}
public List<String> getLetters() {
return mLetters;
}
public void setLetters(List<String> letters) {
this.mLetters = letters;
invalidate();
}
public interface OnTouchLetterChangeListener {
void onLetterChange(String letter);
}
}
- 适配器
/**
* Created on 2022/1/21 9:39
*
* @author Gong Youqiang
*/
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;
}
@Override
public SortAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_name, parent,false);
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.tvName = (TextView) view.findViewById(R.id.tvName);
return viewHolder;
}
@Override
public void onBindViewHolder(final SortAdapter.ViewHolder holder, final int position) {
if (mOnItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(holder.itemView, position);
}
});
}
holder.tvName.setText(this.mData.get(position).getName());
holder.tvName.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext, mData.get(position).getName(),Toast.LENGTH_SHORT).show();
}
});
}
@Override
public 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 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;
}
}
- Bean 类
/**
* Created on 2022/1/21 9:38
*
* @author Gong Youqiang
*/
public class SortModel {
private String name;
private String letters;//显示拼音的首字母
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLetters() {
return letters;
}
public void setLetters(String letters) {
this.letters = letters;
}
}
- 字母排序
/**
* Created on 2022/1/21 9:41
*
* @author Gong Youqiang
*/
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());
}
}
}
- 拼音工具类
/**
* Created on 2022/1/21 9:43
*
* @author Gong Youqiang
*/
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];
} else
output += 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();
}
/**
* 汉字转换位汉语拼音首字母,英文字符不变,特殊字符丢失 支持多音字,生成方式如(长沙市长:cssc,zssz,zssc,cssz)
*
* @param chines
* 汉字
* @return 拼音
*/
public static String converterToFirstSpell(String chines) {
StringBuffer pinyinName = new StringBuffer();
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 {
// 取得当前汉字的所有全拼
String[] str = PinyinHelper.toHanyuPinyinStringArray(
nameChar[i], defaultFormat);
if (str != null) {
for (int j = 0; j < str.length; j++) {
// 取首字母
pinyinName.append(str[j].charAt(0));
if (j != str.length - 1) {
pinyinName.append(",");
}
}
}
// else {
// pinyinName.append(nameChar[i]);
// }
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
pinyinName.append(nameChar[i]);
}
pinyinName.append(" ");
}
// return pinyinName.toString();
return parseTheChineseByObject(discountTheChinese(pinyinName.toString()));
}
/**
* 汉字转换位汉语全拼,英文字符不变,特殊字符丢失
* 支持多音字,生成方式如(重当参:zhongdangcen,zhongdangcan,chongdangcen
* ,chongdangshen,zhongdangshen,chongdangcan)
*
* @param chines
* 汉字
* @return 拼音
*/
public static String converterToSpell(String chines) {
StringBuffer pinyinName = new StringBuffer();
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 {
// 取得当前汉字的所有全拼
String[] str = PinyinHelper.toHanyuPinyinStringArray(
nameChar[i], defaultFormat);
if (str != null) {
for (int j = 0; j < str.length; j++) {
pinyinName.append(str[j]);
if (j != str.length - 1) {
pinyinName.append(",");
}
}
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
pinyinName.append(nameChar[i]);
}
pinyinName.append(" ");
}
// return pinyinName.toString();
return parseTheChineseByObject(discountTheChinese(pinyinName.toString()));
}
/**
* 去除多音字重复数据
*
* @param theStr
* @return
*/
private static List<Map<String, Integer>> discountTheChinese(String theStr) {
// 去除重复拼音后的拼音列表
List<Map<String, Integer>> mapList = new ArrayList<>();
// 用于处理每个字的多音字,去掉重复
Map<String, Integer> onlyOne;
String[] firsts = theStr.split(" ");
// 读出每个汉字的拼音
for (String str : firsts) {
onlyOne = new Hashtable<>();
String[] china = str.split(",");
// 多音字处理
for (String s : china) {
Integer count = onlyOne.get(s);
if (count == null) {
onlyOne.put(s, new Integer(1));
} else {
onlyOne.remove(s);
count++;
onlyOne.put(s, count);
}
}
mapList.add(onlyOne);
}
return mapList;
}
/**
* 解析并组合拼音,对象合并方案(推荐使用)
*
* @return
*/
private static String parseTheChineseByObject(
List<Map<String, Integer>> list) {
Map<String, Integer> first = null; // 用于统计每一次,集合组合数据
// 遍历每一组集合
for (int i = 0; i < list.size(); i++) {
// 每一组集合与上一次组合的Map
Map<String, Integer> temp = new Hashtable<>();
// 第一次循环,first为空
if (first != null) {
// 取出上次组合与此次集合的字符,并保存
for (String s : first.keySet()) {
for (String s1 : list.get(i).keySet()) {
String str = s + s1;
temp.put(str, 1);
}
}
// 清理上一次组合数据
if (temp != null && temp.size() > 0) {
first.clear();
}
} else {
for (String s : list.get(i).keySet()) {
String str = s;
temp.put(str, 1);
}
}
// 保存组合数据以便下次循环使用
if (temp != null && temp.size() > 0) {
first = temp;
}
}
String returnStr = "";
if (first != null) {
// 遍历取出组合字符串
for (String str : first.keySet()) {
returnStr += (str + ",");
}
}
if (returnStr.length() > 0) {
returnStr = returnStr.substring(0, returnStr.length() - 1);
}
return returnStr;
}
}
- TitleItemDecoration.java
/**
* Created on 2022/1/21 9:42
*
* @author Gong Youqiang
*/
public class TitleItemDecoration extends RecyclerView.ItemDecoration {
private static final String TAG = "TitleItemDecoration";
private List<SortModel> mData;
private Paint mPaint;
private Rect mBounds;
private int mTitleHeight;
private static int TITLE_BG_COLOR = Color.parseColor("#FFDFDFDF");
private static int TITLE_TEXT_COLOR = Color.parseColor("#FF000000");
private static int mTitleTextSize;
public TitleItemDecoration(Context context, List<SortModel> data) {
super();
mData = data;
mPaint = new Paint();
mBounds = new Rect();
mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, context.getResources().getDisplayMetrics());
mTitleTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, context.getResources().getDisplayMetrics());
mPaint.setTextSize(mTitleTextSize);
mPaint.setAntiAlias(true);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
int position = params.getViewLayoutPosition();
if (position > -1) {
if (position == 0) {//等于0的时候绘制title
drawTitle(c, left, right, child, params, position);
} else {
if (null != mData.get(position).getLetters() && !mData.get(position)
.getLetters().equals(mData.get(position - 1).getLetters())) {
//字母不为空,并且不等于前一个,也要title
drawTitle(c, left, right, child, params, position);
}
}
}
}
}
/**
* 绘制Title区域背景和文字的方法
*最先调用,绘制最下层的title
* @param c
* @param left
* @param right
* @param child
* @param params
* @param position
*/
private void drawTitle(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) {
mPaint.setColor(TITLE_BG_COLOR);
c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint);
mPaint.setColor(TITLE_TEXT_COLOR);
mPaint.getTextBounds(mData.get(position).getLetters(), 0, mData.get(position).getLetters().length(), mBounds);
c.drawText(mData.get(position).getLetters(),
child.getPaddingLeft(),
child.getTop() - params.topMargin - (mTitleHeight / 2 - mBounds.height() / 2), mPaint);
}
/**
* 最后调用,绘制最上层的title
* @param c
* @param parent
* @param state
*/
@Override
public void onDrawOver(Canvas c, final RecyclerView parent, RecyclerView.State state) {
int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
if (position == -1) return;//在搜索到没有的索引的时候position可能等于-1,所以在这里判断一下
String tag = mData.get(position).getLetters();
View child = parent.findViewHolderForLayoutPosition(position).itemView;
//Canvas是否位移过的标志
boolean flag = false;
if ((position + 1) < mData.size()) {
//当前第一个可见的Item的字母索引,不等于其后一个item的字母索引,说明悬浮的View要切换了
if (null != tag && !tag.equals(mData.get(position + 1).getLetters())) {
//当第一个可见的item在屏幕中剩下的高度小于title的高度时,开始悬浮Title的动画
if (child.getHeight() + child.getTop() < mTitleHeight) {
c.save();
flag = true;
/**
* 下边的索引把上边的索引顶上去的效果
*/
c.translate(0, child.getHeight() + child.getTop() - mTitleHeight);
/**
* 头部折叠起来的视效(下边的索引慢慢遮住上边的索引)
*/
/*c.clipRect(parent.getPaddingLeft(),
parent.getPaddingTop(),
parent.getRight() - parent.getPaddingRight(),
parent.getPaddingTop() + child.getHeight() + child.getTop());*/
}
}
}
mPaint.setColor(TITLE_BG_COLOR);
c.drawRect(parent.getPaddingLeft(),
parent.getPaddingTop(),
parent.getRight() - parent.getPaddingRight(),
parent.getPaddingTop() + mTitleHeight, mPaint);
mPaint.setColor(TITLE_TEXT_COLOR);
mPaint.getTextBounds(tag, 0, tag.length(), mBounds);
c.drawText(tag, child.getPaddingLeft(),
parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2),
mPaint);
if (flag)
c.restore();//恢复画布到之前保存的状态
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
if (position > -1) {
//等于0的时候绘制title
if (position == 0) {
outRect.set(0, mTitleHeight, 0, 0);
} else {
if (null != mData.get(position).getLetters() &&
!mData.get(position).getLetters().equals(mData.get(position - 1).getLetters())) {
//字母不为空,并且不等于前一个,绘制title
outRect.set(0, mTitleHeight, 0, 0);
} else {
outRect.set(0, 0, 0, 0);
}
}
}
}
}
- 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".MainActivity">
<com.hjq.bar.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:background="@color/purple_500"
android:layout_height="?android:attr/actionBarSize"
app:title="联系人"
app:titleStyle="bold"
app:titleSize="18sp"
app:backButton="false"
app:titleColor="@color/white"/>
<com.hkt.demo.weight.ClearEditText
android:id="@+id/filter_edit"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:background="@drawable/search_bg"
android:drawableLeft="@mipmap/ic_search"
android:hint="请输入关键字"
android:maxLines="1"
android:textSize="15dp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_contact"
android:layout_marginTop="12dp"
android:layout_marginLeft="12dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.hkt.demo.weight.WaveSideBar
android:id="@+id/sideBar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:chooseTextColor="@android:color/white"
app:textColor="@color/text_E8"
app:backgroundColor="#bef9b81b"
app:textSize="10sp"
app:hintTextSize="32sp"
app:radius="20dp"
app:circleRadius="24dp"/>
</FrameLayout>
</LinearLayout>
- MainActivity.java
public class MainActivity extends AppCompatActivity {
@BindView(R.id.rv_contact)
RecyclerView mRecyclerView;
@BindView(R.id.sideBar)
WaveSideBar mSideBar;
@BindView(R.id.filter_edit)
ClearEditText mClearEditText;
private SortAdapter mAdapter;
private List<SortModel> mDateList;
private PinyinComparator mComparator;
private TitleItemDecoration mDecoration;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initView();
}
private void initView() {
mComparator = new PinyinComparator();
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
mDateList = filledData(getResources().getStringArray(R.array.date));
// 根据a-z进行排序源数据
Collections.sort(mDateList, mComparator);
mDecoration = new TitleItemDecoration(this, mDateList);
//设置右侧SideBar触摸监听
mSideBar.setOnTouchLetterChangeListener(new WaveSideBar.OnTouchLetterChangeListener() {
@Override
public void onLetterChange(String letter) {
//该字母首次出现的位置
int position = mAdapter.getPositionForSection(letter.charAt(0));
if (position != -1) {
manager.scrollToPositionWithOffset(position, 0);
}
}
});
mAdapter = new SortAdapter(this,mDateList);
mRecyclerView.setLayoutManager(manager);
mRecyclerView.addItemDecoration(mDecoration);
mRecyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this, DividerItemDecoration.VERTICAL));
mRecyclerView.setAdapter(mAdapter);
//根据输入框输入值的改变来过滤搜索
mClearEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
filterData(s.toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
/**
* 为RecyclerView填充数据
*
* @param date
* @return
*/
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;
}
/**
* 根据输入框中的值来过滤数据并更新RecyclerView
*
* @param filterStr
*/
private void filterData(String filterStr) {
List<SortModel> filterDateList = new ArrayList<>();
if (TextUtils.isEmpty(filterStr)) {
filterDateList = filledData(getResources().getStringArray(R.array.date));
} else {
filterDateList.clear();
for (SortModel sortModel : mDateList) {
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, mComparator);
mDateList.clear();
mDateList.addAll(filterDateList);
mAdapter.notifyDataSetChanged();
}
}
网友评论