看到这个效果肯定想到的是要自定义View来实现;
public class LitterSideBar extends View {
private Paint mPaint;
//被选画笔
private Paint choicePaint;
//触摸到的字母
private String mCurrentTouchLetter = "";
//定义26个字母
private String[] litterArray = {"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 int normalTextColor = Color.BLACK;
//被选画笔颜色
private int touchTextColor = Color.RED;
//字体大小
private int litterTextSize = 15;
public LitterSideBar(Context context) {
this(context, null);
}
public LitterSideBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LitterSideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化自定义属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LitterSideBar);
litterTextSize = array.getDimensionPixelSize(R.styleable.LitterSideBar_litterTextSize, (int) sp2px(litterTextSize));
normalTextColor = array.getColor(R.styleable.LitterSideBar_normalTextColor, normalTextColor);
touchTextColor = array.getColor(R.styleable.LitterSideBar_choiceTextColor, touchTextColor);
mPaint = getPaint(normalTextColor);
choicePaint = getPaint(touchTextColor);
array.recycle();
}
/**
* 初始化画笔
*
* @param color
* @return
*/
private Paint getPaint(int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(litterTextSize);
paint.setColor(color);
return paint;
}
private float sp2px(int sp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
}
上面就是一些自定义属性、画笔的一些初始化及一些常量和变量的定义;接下来需要对View的宽度和高度进行测量;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//计算宽度 字母的宽度取决于画笔的大小
int textWidth = getTextWidth("A");
//左间距+有间距+字体宽度
int width = getPaddingLeft() + getPaddingRight() + textWidth;
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
高度的话直接通过MeasureSpec.getSize(heightMeasureSpec);可以获取,宽度则需要考虑是否设置了左边距和右边距;测量完成后,调用onDraw()方法进行绘制;
@Override
protected void onDraw(Canvas canvas) {
//每个字体的高度=(屏幕的高度-上边距-下边距)/字母数组的长度
int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / litterArray.length;
for (int i = 0; i < litterArray.length; i++) {
//每个字母的中心位置
int centerY = i * itemHeight + itemHeight / 2 + getPaddingTop();
Paint.FontMetricsInt metricsInt = mPaint.getFontMetricsInt();
int dy = (metricsInt.bottom - metricsInt.top) / 2 - metricsInt.bottom;
int baseLine = centerY + dy;
int textWidth = getTextWidth(litterArray[i]);
int x = getWidth() / 2 - textWidth / 2;
//基线
//如果当前字母高亮
if (litterArray[i].equals(mCurrentTouchLetter)) {
canvas.drawText(litterArray[i], x, baseLine, choicePaint);
} else {
canvas.drawText(litterArray[i], x, baseLine, mPaint);
}
}
}
在进行绘制text文本的时候需要注意一个基线,测量、绘制完成后剩下就只有手势触摸了,在onTouchEvent方法中进行手势触摸的处理及回调;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
//计算出当前的字母 获取当前的位置 除以字母的高度,获取位置
float currentMoveY = event.getY();
int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / litterArray.length;
int currentPosition = (int) (currentMoveY / itemHeight);
if (currentPosition < 0) {
currentPosition = 0;
}
if (currentPosition > litterArray.length - 1) {
currentPosition = litterArray.length - 1;
}
String s = litterArray[currentPosition];
if (mCurrentTouchLetter == null) {
mCurrentTouchLetter = "";
}
if (mCurrentTouchLetter.equals(s)) {
return true;
}
mCurrentTouchLetter = s;
if (mListener != null) {
mListener.touch(mCurrentTouchLetter, true);
}
//重新绘制
invalidate();
break;
case MotionEvent.ACTION_UP:
if (mListener != null) {
mListener.touch(mCurrentTouchLetter, false);
}
break;
}
return true;
}
这样就处理好了,调用即可:
public class MainActivity extends AppCompatActivity {
private LitterSideBar sideBar;
private TextView currentText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sideBar = (LitterSideBar) findViewById(R.id.side_bar);
currentText= (TextView) findViewById(R.id.current_text);
currentText.setVisibility(View.GONE);
sideBar.setLetterTouchListener(new LitterSideBar.LetterTouchListener() {
@Override
public void touch(String letter,boolean isTouch) {
if(isTouch){
currentText.setVisibility(View.VISIBLE);
currentText.setText(""+letter);
}else{
currentText.setVisibility(View.GONE);
}
}
});
}
}
源码地址:
https://pan.baidu.com/s/1o9O6h9S
下面这个效果和上面那个效果的实现其实大同小异,下面这个只是在触摸滑动字母的时候有一个动画效果,实现如下:
public class SlideView extends View {
private String[] mLetters;
private Paint mPaint;
private int mChoose;
private float mDensity;
private RectF mIsDownRect = new RectF();
//是否触摸拖动
private boolean mIsBeingDragger;
//用于判断动画
private boolean mStartEndAnim;
//最小触动距离 在手机启动时就已经注入,每个手机的值会不一样
private int mTouchSlop;
private float mHalfWidth, mHalfHeight;
//每一个字母的高度
private float mLetterHeight;
private int mAnimStep;
private float mY;
private float mInitDownY;
private SlideListener listener;
public void setListener(SlideListener listener) {
this.listener = listener;
}
public SlideView(Context context) {
this(context, null);
}
public SlideView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
//初始化画笔
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.GRAY);
mPaint.setTextAlign(Paint.Align.CENTER);
//获取字母数组
mLetters = context.getResources().getStringArray(R.array.letter_list);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mDensity = getContext().getResources().getDisplayMetrics().density;
//设置边距
setPadding(0, dip2px(20), 0, dip2px(20));
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHalfHeight = h - getPaddingTop() - getPaddingBottom();
mHalfWidth = w - dip2px(16);
mLetterHeight = mHalfHeight / mLetters.length;
//字体大小
int textSize = (int) (mLetterHeight * 0.7);
mPaint.setTextSize(textSize);
mIsDownRect.set(w - dip2px(32), 0, w, h);
}
private int dip2px(int dippx) {
return (int) (dippx * mDensity + 0.5f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mLetters.length; i++) {
//通过左下顶点计算
float letterPosY = mLetterHeight * (i + 1) + getPaddingTop();
float diff, diffY, diffX;
//当前字母被选中
if (mChoose == i && i != 0 && i != mLetters.length - 1) {
//被选字母不是第一个且不是最后一个
diff = 2.2f;
diffX = 0f;
diffY = 0f;
} else {
float maxPox = Math.abs(mY - letterPosY) / mHalfHeight * 7;
diff = Math.max(1f, 2.2f - maxPox);
//没有被选中
//是否在触摸点击的范围
if (mStartEndAnim && diff != 1f) {
diff -= mAnimStep;
if (diff < 1) {
diff = 1f;
}
} else if (!mIsBeingDragger) {
//没有手指拖动恢复到原理的状态
diff = 1f;
}
diffY = maxPox * 50f * (letterPosY > mY ? -1 : 1);
diffX = maxPox * 100;
}
canvas.save();
//进行绘制字母
canvas.scale(diff, diff, mHalfWidth * 1.20f + diffX, letterPosY + diffY);
if (diff == 1f) {
mPaint.setAlpha(225);
mPaint.setTypeface(Typeface.DEFAULT);
} else {
int alpa = (int) (225 * (1 - Math.min(0.9f, diff - 1)));
if (mChoose == i) {
alpa = 225;
}
mPaint.setAlpha(alpa);
mPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
canvas.drawText(mLetters[i], mHalfWidth, letterPosY, mPaint);
canvas.restore();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN:
mIsBeingDragger = false;
float initDownY = event.getY();
if (!mIsDownRect.contains(event.getX(), event.getY())) {
return false;
}
mInitDownY = initDownY;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mStartEndAnim = false;
mIsBeingDragger = false;
mChoose = -1;
mAnimStep = 0;
invalidate();
return false;
case MotionEvent.ACTION_MOVE:
float y = event.getY();
float diff = Math.abs(y - mInitDownY);
if (diff > mTouchSlop && !mIsBeingDragger) {
mIsBeingDragger = true;
}
if (mIsBeingDragger) {
mY = y;
float moveY = y - getPaddingTop();
int charcter = (int) (moveY / mHalfHeight * mLetters.length);
if (mChoose != charcter) {
if (charcter >= 0 && charcter < mLetters.length) {
//进行回调
mChoose = charcter;
String mLetter = mLetters[mChoose];
if (listener != null) {
listener.slideListener(mLetter);
}
}
}
//进行重绘
invalidate();
}
break;
}
return true;
}
interface SlideListener {
void slideListener(String letter);
}
}
调用:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SlideView slideView = (SlideView) findViewById(R.id.slide_view);
final TextView tvChoice = (TextView) findViewById(R.id.tv_choice);
slideView.setListener(new SlideView.SlideListener() {
@Override
public void slideListener(String letter) {
tvChoice.setText(letter);
}
});
}
}