package com.doumee.keyCabinet.ui.view;
|
|
/**
|
* Created by lvzhihao on 17-5-13.
|
*/
|
|
import android.animation.ValueAnimator;
|
import android.content.Context;
|
import android.content.res.TypedArray;
|
import android.graphics.Canvas;
|
import android.graphics.Color;
|
import android.graphics.Paint;
|
import android.graphics.Path;
|
import android.graphics.RectF;
|
import android.os.Build;
|
import android.util.AttributeSet;
|
import android.view.MotionEvent;
|
import android.view.View;
|
|
import com.doumee.keyCabinet.R;
|
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 波浪侧边栏
|
* author: imilk
|
* https://github.com/Solartisan/WaveSideBar.git
|
*/
|
public class WaveSideBarView extends View {
|
private static final String TAG = "WaveSlideBarView";
|
|
// 计算波浪贝塞尔曲线的角弧长值
|
private static final double ANGLE = Math.PI * 45 / 180;
|
private static final double ANGLE_R = Math.PI * 90 / 180;
|
private OnTouchLetterChangeListener listener;
|
|
// 渲染字母表
|
private List<String> mLetters=new ArrayList<>();
|
|
// 当前选中的位置
|
private int mChoose = -1;
|
|
// 字母列表画笔
|
private Paint mLettersPaint = new Paint();
|
|
// 提示字母画笔
|
private Paint mTextPaint = new Paint();
|
// 波浪画笔
|
private Paint mWavePaint = new Paint();
|
|
private float mTextSize;
|
private float mLargeTextSize;
|
private int mTextColor;
|
private int mWaveColor;
|
private int mTextColorChoose;
|
private int mTextColorBg;
|
private int mWidth;
|
private int mHeight;
|
private int mItemHeight;
|
private int mPadding;
|
private float scanle=1.6f;//文字缩放
|
|
// 波浪路径
|
private Path mWavePath = new Path();
|
|
// 圆形路径
|
private Path mBallPath = new Path();
|
|
// 手指滑动的Y点作为中心点
|
private int mCenterY; //中心点Y
|
|
// 贝塞尔曲线的分布半径
|
private int mRadius;
|
|
// 圆形半径
|
private int mBallRadius;
|
// 用于过渡效果计算
|
ValueAnimator mRatioAnimator;
|
|
// 用于绘制贝塞尔曲线的比率
|
private float mRatio;
|
|
// 选中字体的坐标
|
private float mPosX, mPosY;
|
|
// 圆形中心点X
|
private float mBallCentreX;
|
private Map<String, Integer> letterPositionMap = new HashMap<>();
|
|
|
public WaveSideBarView(Context context) {
|
this(context, null);
|
}
|
|
public WaveSideBarView(Context context, AttributeSet attrs) {
|
this(context, attrs, 0);
|
}
|
|
public WaveSideBarView(Context context, AttributeSet attrs, int defStyle) {
|
super(context, attrs, defStyle);
|
init(context, attrs);
|
}
|
|
public void setLetterPositionMap(Map<String, Integer> letterPositionMap) {
|
this.letterPositionMap = letterPositionMap;
|
}
|
|
private void init(Context context, AttributeSet attrs) {
|
List<String> strings = Arrays.asList(context.getResources().getStringArray(R.array.waveSideBarLetters));
|
mLetters.addAll(strings);
|
|
mTextColor = Color.parseColor("#969696");
|
mWaveColor = Color.parseColor("#be69be91");
|
mTextColorBg = Color.parseColor("#ffE74F4E");
|
mTextColorChoose = context.getResources().getColor(android.R.color.white);
|
mTextSize = context.getResources().getDimensionPixelSize(R.dimen.textSize_sidebar);
|
mLargeTextSize = context.getResources().getDimensionPixelSize(R.dimen.large_textSize_sidebar);
|
mPadding = context.getResources().getDimensionPixelSize(R.dimen.textSize_sidebar_padding);
|
if (attrs != null) {
|
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.WaveSideBarView);
|
mTextColor = a.getColor(R.styleable.WaveSideBarView_sidebarTextColor, mTextColor);
|
mTextColorChoose = a.getColor(R.styleable.WaveSideBarView_sidebarChooseTextColor, mTextColorChoose);
|
mTextColorBg = a.getColor(R.styleable.WaveSideBarView_sidebarChoseBg, mTextColorBg);
|
mTextSize = a.getDimension(R.styleable.WaveSideBarView_sidebarTextSize, mTextSize);
|
mLargeTextSize = a.getFloat(R.styleable.WaveSideBarView_sidebarLargeTextSize, mLargeTextSize);
|
mWaveColor = a.getColor(R.styleable.WaveSideBarView_sidebarBackgroundColor, mWaveColor);
|
mRadius = a.getInt(R.styleable.WaveSideBarView_sidebarRadius, context.getResources().getDimensionPixelSize(R.dimen.radius_sidebar));
|
mBallRadius = a.getInt(R.styleable.WaveSideBarView_sidebarBallRadius, context.getResources().getDimensionPixelSize(R.dimen.ball_radius_sidebar));
|
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(mLargeTextSize);
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
mChoose = 0;
|
}
|
|
/**
|
*
|
* @param datas item已按照字母顺序排好序的数据
|
*/
|
public void setData(List<String> datas) {
|
|
if (datas==null||datas.size()<=0){
|
return;
|
}
|
mLetters.clear();
|
letterPositionMap.clear();
|
/*for (int i = 0; i < datas.size(); i++) {
|
String headPinyin = PinYinUtil.getPinyin(onLetterGet.letterGet(datas.get(i))).substring(0, 1);
|
if (i == 0) {
|
mLetters.add(headPinyin);
|
letterPositionMap.put(headPinyin, from + i);
|
} else {
|
if (!headPinyin.equals(mLetters.get(mLetters.size() - 1))) {
|
mLetters.add(headPinyin);
|
letterPositionMap.put(headPinyin, from + i);
|
}
|
}
|
}*/
|
for (int i = 0; i < datas.size(); i++) {
|
String headPinyin = datas.get(i);
|
if (i == 0) {
|
mLetters.add(headPinyin);
|
letterPositionMap.put(headPinyin,i);
|
} else {
|
if (!headPinyin.equals(mLetters.get(mLetters.size() - 1))) {
|
mLetters.add(headPinyin);
|
letterPositionMap.put(headPinyin, i);
|
}
|
}
|
}
|
resetItemHeight();
|
mChoose = 0;
|
invalidate();
|
}
|
|
public interface OnLetterGet<T> {
|
String letterGet(T t);
|
}
|
|
@Override
|
public boolean dispatchTouchEvent(MotionEvent event) {
|
final float y = event.getY();
|
final float x = event.getX();
|
|
int startY=mHeight/2-(mItemHeight*mLetters.size())/2;
|
final int oldChoose = mChoose;
|
int newChoose = (int) ((y-startY) / mItemHeight);
|
if (newChoose<0){
|
newChoose=0;
|
}
|
if (newChoose>mLetters.size()-1){
|
newChoose=mLetters.size()-1;
|
}
|
|
switch (event.getAction()) {
|
case MotionEvent.ACTION_DOWN:
|
if (x < mWidth - 2 * mRadius) {
|
return false;
|
}
|
startAnimator(mRatio, 1.0f);
|
mCenterY = (int) y;
|
if (oldChoose != newChoose) {
|
if (newChoose >= 0 && newChoose < mLetters.size()) {
|
mChoose = newChoose;
|
if (listener != null) {
|
listener.onLetterChange(mLetters.get(newChoose));
|
}
|
}
|
}
|
invalidate();
|
break;
|
case MotionEvent.ACTION_MOVE:
|
mCenterY = (int) y;
|
if (oldChoose != newChoose) {
|
if (newChoose >= 0 && newChoose < mLetters.size()) {
|
mChoose = newChoose;
|
if (listener != null) {
|
listener.onLetterChange(mLetters.get(newChoose));
|
}
|
}
|
}
|
invalidate();
|
break;
|
case MotionEvent.ACTION_CANCEL:
|
case MotionEvent.ACTION_UP:
|
startAnimator(mRatio, 0f);
|
break;
|
default:
|
break;
|
}
|
return true;
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
mHeight = MeasureSpec.getSize(heightMeasureSpec);
|
mWidth = getMeasuredWidth();
|
//默认字母所占高度为字体大小的2倍,当字母太多时,高度平均分配
|
resetItemHeight();
|
}
|
|
private void resetItemHeight() {
|
if (mLetters.size()<=0){
|
return;
|
}
|
mItemHeight = (mHeight - (int)(scanle*mTextSize) )/ mLetters.size();
|
if (mItemHeight>=(scanle*mTextSize)){
|
mItemHeight=(int)(scanle*mTextSize);
|
}
|
mPosX = mWidth - 1.6f * mTextSize;
|
}
|
|
@Override
|
protected void onDraw(Canvas canvas) {
|
super.onDraw(canvas);
|
|
//绘制字母列表
|
drawLetters(canvas);
|
|
//绘制波浪
|
//drawWavePath(canvas);
|
|
//绘制圆
|
//drawBallPath(canvas);
|
|
//绘制选中的字体
|
drawChooseText(canvas);
|
|
}
|
|
private void drawLetters(Canvas canvas) {
|
|
RectF rectF = new RectF();
|
rectF.left = mPosX - mTextSize;
|
rectF.right = mPosX + mTextSize;
|
rectF.top = 0;
|
rectF.bottom = mHeight;
|
|
// 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);
|
|
int startY=mHeight/2-(mItemHeight*mLetters.size())/2;
|
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 posY = startY+mItemHeight * i +mItemHeight/2+ ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
|
|
if (i == mChoose) {
|
mPosY = posY;
|
Paint paint = new Paint();
|
paint.setColor(mTextColorBg);
|
int r= (int) ((rectF.right-rectF.left)/2f);
|
canvas.drawCircle(rectF.left+r, (float) (startY+mItemHeight*i+scanle*mTextSize*0.5f+1),r-10,paint);
|
//canvas.drawRect(rectF.left ,startY+mItemHeight*i+1,rectF.right,startY+mItemHeight*(i+1)-1,paint);
|
} else {
|
canvas.drawText(mLetters.get(i), mPosX, posY, mLettersPaint);
|
}
|
}
|
|
}
|
|
private void drawChooseText(Canvas canvas) {
|
if (mChoose != -1) {
|
// 绘制右侧选中字符
|
mLettersPaint.reset();
|
mLettersPaint.setColor(mTextColorChoose);
|
mLettersPaint.setTextSize(mTextSize);
|
mLettersPaint.setTextAlign(Paint.Align.CENTER);
|
canvas.drawText(mLetters.get(mChoose), mPosX, mPosY, mLettersPaint);
|
|
// 绘制提示字符
|
if (mRatio >= 0.9f) {
|
String target = mLetters.get(mChoose);
|
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
|
float baseline = Math.abs(-fontMetrics.bottom - fontMetrics.top);
|
float x = mBallCentreX;
|
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);
|
}
|
|
private void drawBallPath(Canvas canvas) {
|
//x轴的移动路径
|
mBallCentreX = (mWidth + mBallRadius) - (2.0f * mRadius + 2.0f * mBallRadius) * mRatio;
|
|
mBallPath.reset();
|
mBallPath.addCircle(mBallCentreX, mCenterY, mBallRadius, Path.Direction.CW);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
mBallPath.op(mWavePath, Path.Op.DIFFERENCE);
|
}
|
|
mBallPath.close();
|
canvas.drawPath(mBallPath, 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();
|
invalidate();
|
}
|
});
|
mRatioAnimator.start();*/
|
}
|
|
|
public void setOnTouchLetterChangeListener(OnTouchLetterChangeListener listener) {
|
this.listener = listener;
|
}
|
|
public List<String> getLetters() {
|
return mLetters;
|
}
|
|
public void setLetters(List<String> letters) {
|
this.mLetters = letters;
|
invalidate();
|
}
|
|
public interface OnTouchLetterChangeListener {
|
void onLetterChange(String letter);
|
}
|
}
|