基于VIEW,参考别人代码实现,十分简单。
标尺刻度使用默认使用1,2,3,4,5
自定义刻度标签传入参数String数组,选中值使用数组索引值,设置默认值也可以索引。
根据数组长度划分刻度,根据传入的默认值选中刻度
@SuppressLint("ClickableViewAccessibility")
public class NewWheelView extends View {
public interface OnValueChangeListener {
void onValueChange(int value);
}
private int mTextSize = 16;
private float mDensity;
//默认选中值
private int mValue = 7;
//总项数
private int mMaxValue = 10;
//标尺间隔,可由控件宽度除以mMaxNum算出
private int mLineDivider = 60;
//每页最大显示数
private int mMaxNum = 5;
//控件的宽高
private int mWidth, mHeight;
//滑动相关参数
private int mLastX, mMove;
private int mMinVelocity;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
//回调
private OnValueChangeListener mListener;
//是滞带背景
private boolean isHasBg = false;
//显示的标签
private List<String> mLabels;
@SuppressWarnings("deprecation")
public NewWheelView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(getContext());
mDensity = getContext().getResources().getDisplayMetrics().density;
mMinVelocity = ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();
if (isHasBg) {
setBackgroundDrawable(createBackground());
}
}
/**
* 默认显示的界面的元素个数
* @param num
*/
public void setMaxShow(int num){
mMaxNum = num;
}
/**
*
* @param labels 标尺显示项的内容
* @param index 默认选中的内容的索引
*/
public void setLabels(List<String> labels, int index){
mLabels = labels;
mMaxValue = mLabels.size()-1;
mValue = index;
}
public void setFontSize(int fontsize){
mTextSize = fontsize;
}
//创建背景
private GradientDrawable createBackground() {
float strokeWidth = 4 * mDensity; // 边框宽度
float roundRadius = 6 * mDensity; // 圆角半径
int strokeColor = Color.parseColor("#FF666666");// 边框颜色
setPadding((int)strokeWidth, (int)strokeWidth, (int)strokeWidth, 0);
int colors[] = { 0xFF999999, 0xFFFFFFFF, 0xFF999999 };// 分别为开始颜色,中间夜色,结束颜色
GradientDrawable bgDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors);// 创建drawable
bgDrawable.setCornerRadius(roundRadius);
bgDrawable.setStroke((int)strokeWidth, strokeColor);
return bgDrawable;
}
/**
* 设置用于接收结果的监听器
*
* @param listener
*/
public void setValueChangeListener(OnValueChangeListener listener) {
mListener = listener;
}
/**
* 获取当前刻度值
*
* @return
*/
public float getValue() {
return mValue;
}
//载入时设置默认值
public void setValue(int val){
mValue = val;
postInvalidate();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mWidth = getWidth();
mHeight = getHeight();
super.onLayout(changed, left, top, right, bottom);
mLineDivider = (int) ((mWidth/mMaxNum)/mDensity);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawScaleLine(canvas);
drawMiddleLine(canvas);
}
/**
* 从中间往两边开始画刻度线
*
* @param canvas
*/
private void drawScaleLine(Canvas canvas) {
canvas.save();
Paint linePaint = new Paint();
linePaint.setStrokeWidth(2);
linePaint.setColor(Color.BLACK);
LogUtil.d("jiaABC", "drawScaleLine2+++++++++++++++++++++++++++");
TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
textPaint.setTextSize(mTextSize * mDensity);
int width = mWidth;
int drawCount = 0;
float xPosition, textHeight;
float maxtextWidth = measureTextHeight(textPaint);
for (int i = 0; drawCount <= 4 * width; i++) {
LogUtil.d("jiaABC", "drawScaleLine2 drawCount="+drawCount);
LogUtil.d("jiaABC", "drawScaleLine2 mValue="+mValue);
xPosition = (width / 2 - mMove) + i * mLineDivider * mDensity;
//离中心的每一个单位字体大小下降2
textPaint.setTextSize((mTextSize-2*i) * mDensity);
textHeight = measureTextHeight(textPaint);
if (xPosition + getPaddingRight() < mWidth) {
if (mValue + i <= mMaxValue) {
if (mLabels != null && mLabels.size()>0) {
canvas.drawText(mLabels.get(mValue + i), xPosition - textPaint.measureText(mLabels.get(mValue + i),0,mLabels.get(mValue + i).length())/2, textHeight + 10, textPaint);
}else{
canvas.drawText(String.valueOf(mValue + i), xPosition - textPaint.measureText(String.valueOf(mValue + i),0,String.valueOf(mValue + i).length())/2, textHeight + 10, textPaint);
}
}
}
xPosition = (width / 2 - mMove) - i * mLineDivider * mDensity;
if (xPosition > getPaddingLeft()) {
if (mValue - i >= 0) {
if (mLabels != null && mLabels.size()>0) {
canvas.drawText(mLabels.get(mValue - i), xPosition - textPaint.measureText(mLabels.get(mValue - i),0, mLabels.get(mValue - i).length())/2, textHeight + 10, textPaint);
}else {
canvas.drawText(String.valueOf(mValue - i), xPosition - textPaint.measureText(String.valueOf(mValue - i),0,String.valueOf(mValue - i).length())/2, textHeight + 10, textPaint);
}
}
}
drawCount += 2 * mLineDivider * mDensity;
}
canvas.restore();
}
/**
* 测量文字的高度
* --经测试后发现,采用另一种带Rect的方式,获得的数据并不准确。
* 特别是在一些对文字有一些倾斜处理的时候
* @param paint
* @return
*/
public static float measureTextHeight(Paint paint){
float height = 0f;
if(null == paint){
return height;
}
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
height = fontMetrics.descent - fontMetrics.ascent;
return height;
}
/**
* 计算没有数字显示位置的辅助方法
*
* @param value
* @param xPosition
* @param textWidth
* @return
*/
private float countLeftStart(int value, float xPosition, float textWidth) {
float xp = 0f;
if (mLabels != null && mLabels.size()>0) {
int len = mLabels.get(value).length();
xp = xPosition - len/2;
}else {
if (value < 10) {
xp = xPosition - (textWidth * 1 / 2);
} else if (value < 100){
xp = xPosition - (textWidth * 2 / 2);
} else if (value < 1000){
xp = xPosition - (textWidth * 3 / 2);
} else {
xp = xPosition - (textWidth * 4 / 2);
}
}
return xp;
}
/**
* 画中间的红色指示线、阴影等。指示线两端简单的用了两个矩形代替
*
* @param canvas
*/
private void drawMiddleLine(Canvas canvas) {
int color = Color.YELLOW;
int height = 30;
int lens = 40;
int pos_y = 10;
canvas.save();
Paint paint = new Paint();
paint.setColor(color);
//设置实心
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
// 设置画笔的锯齿效果
paint.setAntiAlias(true);
Path path = new Path();
path.moveTo(mWidth / 2, mHeight-height-pos_y);
//从起始位置划线到(200, 200)坐标
path.lineTo((mWidth / 2) - (lens/2), mHeight - pos_y);
path.lineTo((mWidth / 2) + (lens/2), mHeight - pos_y);
//将mpath封闭,也可以写 mpath.lineTo(100, 100);代替
path.close();
//绘制path路径
canvas.drawPath(path, paint);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int xPosition = (int) event.getX();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (action) {
case MotionEvent.ACTION_DOWN:
mScroller.forceFinished(true);
mLastX = xPosition;
mMove = 0;
break;
case MotionEvent.ACTION_MOVE:
mMove += (mLastX - xPosition);
changeMoveAndValue();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
countMoveEnd();
countVelocityTracker(event);
return false;
// break;
default:
break;
}
mLastX = xPosition;
return true;
}
private void countVelocityTracker(MotionEvent event) {
mVelocityTracker.computeCurrentVelocity(1000);
float xVelocity = mVelocityTracker.getXVelocity();
if (Math.abs(xVelocity) > mMinVelocity) {
mScroller.fling(0, 0, (int) xVelocity, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
}
}
private void changeMoveAndValue() {
int tValue = (int) (mMove / (mLineDivider * mDensity));
if (Math.abs(tValue) > 0) {
mValue += tValue;
mMove -= tValue * mLineDivider * mDensity;
if (mValue <= 0 || mValue > mMaxValue) {
mValue = mValue <= 0 ? 0 : mMaxValue;
mMove = 0;
mScroller.forceFinished(true);
}
notifyValueChange();
}
postInvalidate();
}
private void countMoveEnd() {
int roundMove = Math.round(mMove / (mLineDivider * mDensity));
mValue = mValue + roundMove;
mValue = mValue <= 0 ? 0 : mValue;
mValue = mValue > mMaxValue ? mMaxValue : mValue;
mLastX = 0;
mMove = 0;
notifyValueChange();
postInvalidate();
}
private void notifyValueChange() {
if (null != mListener) {
mListener.onValueChange(mValue);
}
LogUtil.d("jiaABC", "notifyValueChange mValue="+mValue);
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
if (mScroller.getCurrX() == mScroller.getFinalX()) { // over
countMoveEnd();
} else {
int xPosition = mScroller.getCurrX();
mMove += (mLastX - xPosition);
changeMoveAndValue();
mLastX = xPosition;
}
}
}
}
参考: