package mcxtzhang.itemdecorationdemo.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.LinearLayoutManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import com.github.promeg.pinyinhelper.Pinyin;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import mcxtzhang.itemdecorationdemo.R;
import mcxtzhang.itemdecorationdemo.bean.BaseIndexPinyinBean;
/**
* 介绍:索引右侧边栏
* 作者:zhangxutong
* 邮箱:mcxtzhang@163.com
* CSDN:http://blog.csdn.net/zxt0601
* 时间: 16/09/04.
*/
public class IndexBar extends View {
private static final String TAG = "zxt/IndexBar";
public static String[] INDEX_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", "#"};//#在最后面(默认的数据源)
private List<String> mIndexDatas;//索引数据源
private boolean isNeedRealIndex;//是否需要根据实际的数据来生成索引数据源(例如 只有 A B C 三种tag,那么索引栏就 A B C 三项)
private int mWidth, mHeight;//View的宽高
private int mGapHeight;//每个index区域的高度
private Paint mPaint;
private int mPressedBackground;//手指按下时的背景色
//以下边变量是外部set进来的
private TextView mPressedShowTextView;//用于特写显示正在被触摸的index值
private List<? extends BaseIndexPinyinBean> mSourceDatas;//Adapter的数据源
private LinearLayoutManager mLayoutManager;
public IndexBar(Context context) {
this(context, null);
}
public IndexBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IndexBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
int textSize = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics());//默认的TextSize
mPressedBackground = Color.BLACK;//默认按下是纯黑色
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.IndexBar, defStyleAttr, 0);
int n = typedArray.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.IndexBar_textSize:
textSize = typedArray.getDimensionPixelSize(attr, textSize);
break;
case R.styleable.IndexBar_pressBackground:
mPressedBackground = typedArray.getColor(attr, mPressedBackground);
default:
break;
}
}
typedArray.recycle();
if (!isNeedRealIndex) {//不需要真实的索引数据源
mIndexDatas = Arrays.asList(INDEX_STRING);
}
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(textSize);
mPaint.setColor(Color.BLACK);
//设置index触摸监听器
setmOnIndexPressedListener(new onIndexPressedListener() {
@Override
public void onIndexPressed(int index, String text) {
if (mPressedShowTextView != null) { //显示hintTexView
mPressedShowTextView.setVisibility(View.VISIBLE);
mPressedShowTextView.setText(text);
}
//滑动Rv
if (mLayoutManager != null) {
int position = getPosByTag(text);
if (position != -1) {
mLayoutManager.scrollToPositionWithOffset(position, 0);
}
}
}
@Override
public void onMotionEventEnd() {
//隐藏hintTextView
if (mPressedShowTextView != null) {
mPressedShowTextView.setVisibility(View.GONE);
}
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
int t = getPaddingTop();//top的基准点(支持padding)
Rect indexBounds = new Rect();//存放每个绘制的index的Rect区域
String index;//每个要绘制的index内容
for (int i = 0; i < mIndexDatas.size(); i++) {
index = mIndexDatas.get(i);
mPaint.getTextBounds(index, 0, index.length(), indexBounds);//测量计算文字所在矩形,可以得到宽高
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();//获得画笔的FontMetrics,用来计算baseLine。因为drawText的y坐标,代表的是绘制的文字的baseLine的位置
int baseline = (int) ((mGapHeight - fontMetrics.bottom - fontMetrics.top) / 2);//计算出在每格index区域,竖直居中的baseLine值
canvas.drawText(index, mWidth / 2 - indexBounds.width() / 2, t + mGapHeight * i + baseline, mPaint);//调用drawText,居中显示绘制index
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setBackgroundColor(mPressedBackground);//手指按下时背景变色
//注意这里没有break,因为down时,也要计算落点 回调监听器
case MotionEvent.ACTION_MOVE:
float y = event.getY();
//通过计算判断落点在哪个区域:
int pressI = (int) ((y - getPaddingTop()) / mGapHeight);
//边界处理(在手指move时,有可能已经移出边界,防止越界)
if (pressI < 0) {
pressI = 0;
} else if (pressI >= mIndexDatas.size()) {
pressI = mIndexDatas.size() - 1;
}
//回调监听器
if (null != mOnIndexPressedListener) {
mOnIndexPressedListener.onIndexPressed(pressI, mIndexDatas.get(pressI));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
default:
setBackgroundResource(android.R.color.transparent);//手指抬起时背景恢复透明
//回调监听器
if (null != mOnIndexPressedListener) {
mOnIndexPressedListener.onMotionEventEnd();
}
break;
}
return true;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
mGapHeight = (mHeight - getPaddingTop() - getPaddingBottom()) / mIndexDatas.size();
}
/**
* 当前被按下的index的监听器
*/
public interface onIndexPressedListener {
void onIndexPressed(int index, String text);//当某个Index被按下
void onMotionEventEnd();//当触摸事件结束(UP CANCEL)
}
private onIndexPressedListener mOnIndexPressedListener;
public onIndexPressedListener getmOnIndexPressedListener() {
return mOnIndexPressedListener;
}
public void setmOnIndexPressedListener(onIndexPressedListener mOnIndexPressedListener) {
this.mOnIndexPressedListener = mOnIndexPressedListener;
}
/**
* 显示当前被按下的index的TextView
*
* @return
*/
public IndexBar setmPressedShowTextView(TextView mPressedShowTextView) {
this.mPressedShowTextView = mPressedShowTextView;
return this;
}
public IndexBar setmLayoutManager(LinearLayoutManager mLayoutManager) {
this.mLayoutManager = mLayoutManager;
return this;
}
/**
* 一定要在设置数据源{@link #setmSourceDatas(List)}之前调用
*
* @param needRealIndex
* @return
*/
public IndexBar setNeedRealIndex(boolean needRealIndex) {
isNeedRealIndex = needRealIndex;
if (mIndexDatas != null) {
mIndexDatas = new ArrayList<>();
}
return this;
}
public IndexBar setmSourceDatas(List<? extends BaseIndexPinyinBean> mSourceDatas) {
this.mSourceDatas = mSourceDatas;
initSourceDatas();//对数据源进行初始化
return this;
}
/**
* 初始化原始数据源,并取出索引数据源
*
* @return
*/
private void initSourceDatas() {
int size = mSourceDatas.size();
for (int i = 0; i < size; i++) {
BaseIndexPinyinBean indexPinyinBean = mSourceDatas.get(i);
StringBuilder pySb = new StringBuilder();
String target = indexPinyinBean.getTarget();//取出需要被拼音化的字段
//遍历target的每个char得到它的全拼音
for (int i1 = 0; i1 < target.length(); i1++) {
//利用TinyPinyin将char转成拼音
//查看源码,方法内 如果char为汉字,则返回大写拼音
//如果c不是汉字,则返回String.valueOf(c)
pySb.append(Pinyin.toPinyin(target.charAt(i1)));
}
indexPinyinBean.setPyCity(pySb.toString());//设置城市名全拼音
//以下代码设置城市拼音首字母
String tagString = pySb.toString().substring(0, 1);
if (tagString.matches("[A-Z]")) {//如果是A-Z字母开头
indexPinyinBean.setTag(tagString);
if (isNeedRealIndex) {//如果需要真实的索引数据源
if (!mIndexDatas.contains(tagString)) {//则判断是否已经将这个索引添加进去,若没有则添加
mIndexDatas.add(tagString);
}
}
} else {//特殊字母这里统一用#处理
indexPinyinBean.setTag("#");
if (isNeedRealIndex) {//如果需要真实的索引数据源
if (!mIndexDatas.contains("#")) {
mIndexDatas.add("#");
}
}
}
}
sortData();
}
/**
* 对数据源排序
*/
private void sortData() {
//对右侧栏进行排序 将 # 丢在最后
Collections.sort(mIndexDatas, new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
if (lhs.equals("#")) {
return 1;
} else if (rhs.equals("#")) {
return -1;
} else {
return lhs.compareTo(rhs);
}
}
});
//对数据源进行排序
Collections.sort(mSourceDatas, new Comparator<BaseIndexPinyinBean>() {
@Override
public int compare(BaseIndexPinyinBean lhs, BaseIndexPinyinBean rhs) {
if (lhs.getTag().equals("#")) {
return 1;
} else if (rhs.getTag().equals("#")) {
return -1;
} else {
return lhs.getPyCity().compareTo(rhs.getPyCity());
}
}
});
}
/**
* 根据传入的pos返回tag
*
* @param tag
* @return
*/
private int getPosByTag(String tag) {
if (TextUtils.isEmpty(tag)) {
return -1;
}
for (int i = 0; i < mSourceDatas.size(); i++) {
if (tag.equals(mSourceDatas.get(i).getTag())) {
return i;
}
}
return -1;
}
}