自定义控件是件很有乐趣的事,慢慢的会爱上它,哈哈哈。废话不多说先上设计图
接下来我们我们要先分析一下页面中需要到哪些东西
1.一条无限长的底线;
2.一根在屏幕中间的红色标记线,代表当前位置;
3.一个半透明的蓝色背景,改背景超过部分才会被展现;
4.刻度 整数高一点刻度 非整第一点的刻度
5.一个大刻度上都会标的数值
6.滚动
接下来进入正题,让我们开始绘制:
首先我们先给控件定义它的属性,根据刚才的分析我给他定义了一下的属性
接下来在控件的构造方法中获取这些值,里面还涉及到一些需要初始化的值都有备注
public RulerView(Context context, AttributeSet attrs) {
super(context, attrs);
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(dm);
screenWidth = dm.widthPixels;
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RulerView);
maxValue = typedArray.getDimensionPixelOffset(R.styleable.RulerView_max_value, 200000);
minValue = typedArray.getDimensionPixelOffset(R.styleable.RulerView_min_value, 0);
scaleTextSize = typedArray.getDimensionPixelOffset(R.styleable.RulerView_scale_text_size, 24);
scaleTextColor = typedArray.getColor(R.styleable.RulerView_scale_text_color, Color.parseColor("#d8d8d8"));
scaleSelectColor = typedArray.getColor(R.styleable.RulerView_scale_select_color, Color.parseColor("#76e4ff"));
scaleSelectBackgroundColor = typedArray.getColor(R.styleable.RulerView_scale_select_background_color, Color.parseColor("#6676e4ff"));
scaleUnSelectColor = typedArray.getColor(R.styleable.RulerView_scale_unselect_color, Color.parseColor("#d8d8d8"));
cursorColor = typedArray.getColor(R.styleable.RulerView_tag_color, Color.parseColor("#ff5555"));
if (currLocation == 0) {
currLocation = typedArray.getDimensionPixelOffset(R.styleable.RulerView_start_location, 0);
}
showItemSize = typedArray.getInteger(R.styleable.RulerView_show_item_size, 5);
oneItemValue = typedArray.getInteger(R.styleable.RulerView_show_item_size, 1000);
typedArray.recycle();
//一个刻度的宽度
scaleWidth = (screenWidth / (showItemSize * 10));
//尺子长度总的个数*一个的宽度
viewWidth = maxValue / oneItemValue * scaleWidth;
//滚动计算器
scroller = new Scroller(context);
//手势解析器
gestureDetector = new GestureDetector(context, gestureListener);
gestureDetector.setIsLongpressEnabled(false);
}
我们先从简单的开始画一条在底部的线,那问题来了这个View底部坐标在哪里呢?不用担心View有个onMeasure方法,在里面我们可以直接获取到View的高度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
}
private void drawBottomLine(Canvas canvas) {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(3);
paint.setColor(scaleUnSelectColor);
canvas.drawLine(0, viewHeight, viewWidth, viewHeight, paint);
}
绘制刻度以及刻度值,以及超过时的颜色
private void drawScale(Canvas canvas) {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(2);
//计算游标开始绘制的位置
float startLocation = (screenWidth / 2) - ((scaleWidth * (currLocation / oneItemValue)));
for (int i = 0; i < maxValue / oneItemValue; i++) {
//判断当前刻度是否小于当前刻度
if (i * oneItemValue <= currLocation) {
paint.setColor(scaleSelectColor);
} else {
paint.setColor(scaleUnSelectColor);
}
float location = startLocation + i * scaleWidth;
if (i % 10 == 0) {
canvas.drawLine(location, viewHeight - scaleHeight, location, viewHeight, paint);
paintText = new Paint(Paint.ANTI_ALIAS_FLAG);
paintText.setTextSize(scaleTextSize);
if (i * oneItemValue <= currLocation) {
paintText.setColor(scaleSelectColor);
} else {
paintText.setColor(scaleTextColor);
}
String drawStr = oneItemValue * i + "";
Rect bounds = new Rect();
paintText.getTextBounds(drawStr, 0, drawStr.length(), bounds);
canvas.drawText(drawStr, location - bounds.width() / 2, viewHeight - (scaleHeight + 5), paintText);
} else {
canvas.drawLine(location, viewHeight - scaleHeight / 2, location, viewHeight, paint);
}
//绘制选中的背景
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(scaleSelectBackgroundColor);
canvas.drawRect(startLocation, viewHeight - scaleHeight / 3, startLocation + currLocation / oneItemValue * scaleWidth, viewHeight, paint);
}
}
最后绘制一条中线
private void drawCursor(Canvas canvas) {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(3);
paint.setColor(cursorColor);
canvas.drawLine(screenWidth / 2, viewHeight / 5, screenWidth / 2, viewHeight, paint);
}
上面的方法都要在View的onDraw方法中调用一遍,这样刻度尺就绘制完成了。最后我们的工作就是让刻度尺跟着我们的手指动起来,直接重写onTouchEvent方法将所有事件交给手势处理器来处理然后重写onScroll(滚动),onFling(抛事件)方法
@Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
return true;
}
private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (!isScrollingPerformed) {
isScrollingPerformed = true;
}
doScroll((int) -distanceX);
invalidate();
return true;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
lastScrollX = getCurrentItem() * getItemWidth() + scrollingOffset;
int maxX = getItemsCount()
* getItemWidth();
int minX = 0;
scroller.fling(lastScrollX, 0, (int) (-velocityX / 1.5), 0, minX, maxX, 0, 0);
setNextMessage(MESSAGE_SCROLL);
return true;
}
};
private void doScroll(int delta) {
//偏移量叠加
scrollingOffset += delta;
//总共滚动了多少个Item
int count = scrollingOffset / getItemWidth();
//当前位置
int pos = getCurrentItem() - count;
//限制滚到范围
if (isScrollingPerformed) {
if (pos < 0) {
count = getCurrentItem();
pos = 0;
} else if (pos >= getItemsCount()) {
count = getCurrentItem() - getItemsCount() + 1;
pos = getItemsCount() - 1;
}
}
int offset = scrollingOffset;
//移动了一个Item的距离,就更新页面
if (pos != getCurrentItem()) {
setCurrentItem(pos);
}
// 重新更新一下偏移量
scrollingOffset = offset - count * getItemWidth();
}
public int getItemWidth() {
return scaleWidth;
}
public int getCurrentItem() {
return currLocation / oneItemValue;
}
public int getItemsCount() {
return maxValue / oneItemValue;
}
public void setCurrentItem(int index) {
scrollingOffset = 0;
currLocation = index * oneItemValue;
invalidate();
if (onRulerChangeListener != null) {
onRulerChangeListener.onChanged(currLocation);
}
}
private void setNextMessage(int message) {
animationHandler.removeMessages(MESSAGE_SCROLL);
animationHandler.sendEmptyMessage(message);
}
// 动画处理
private Handler animationHandler = new Handler() {
public void handleMessage(Message msg) {
puteScrollOffset();
int currX = scroller.getCurrX();
int delta = lastScrollX - currX;
lastScrollX = currX;
if (delta != 0) {
doScroll(delta);
}
// 滚动还没有完成,到最后,完成手动
if (Math.abs(currX - scroller.getFinalX()) < MIN_DELTA_FOR_SCROLLING) {
scroller.forceFinished(true);
}
if (!scroller.isFinished()) {
animationHandler.sendEmptyMessage(msg.what);
} else {
finishScrolling();
}
}
};
private void finishScrolling() {
if (isScrollingPerformed) {
isScrollingPerformed = false;
}
invalidate();
}
最后上一张效果图
源码地址:/detail/fengzhongdeyang/9638371