为了进一步学习Canvas,弄了个简单的可以左右滑动的尺子,没有进一步优化,希望以后做到可以想加多少就加多少。现在说下基本思路:
在画线跟画文字的时候用到了Canvas.translate、Canvas.save、Canvas.restore,这几个函数有利于尺寸计算。
/**
* 初始化属性
*/
private void initAttrs (AttributeSet attrs) {
TypedArray a = mContext .obtainStyledAttributes(attrs ,
R.styleable. RulerView ) ;
lineColor = a.getColor(R.styleable. RulerView_lineColor , Color. BLACK ) ;
mLineHeight = a.getDimensionPixelOffset(R.styleable. RulerView_lineHeight , LINE_HEIGHT ) ;
a.recycle() ;
* 初始化属性
*/
private void initAttrs (AttributeSet attrs) {
TypedArray a = mContext .obtainStyledAttributes(attrs ,
R.styleable. RulerView ) ;
lineColor = a.getColor(R.styleable. RulerView_lineColor , Color. BLACK ) ;
mLineHeight = a.getDimensionPixelOffset(R.styleable. RulerView_lineHeight , LINE_HEIGHT ) ;
a.recycle() ;
}
在这里主要用到了自定义属性,可以修改线的高度跟线的颜色。
/**
* 初始化画笔
*/
private void initPaint () {
mLinePaint = new Paint() ; // 初始绘制线的画笔
mLinePaint .setAntiAlias( true ) ; // 去除画笔锯齿
mLinePaint .setStyle(Paint.Style. FILL ) ; // 设置风格为实线
mLinePaint .setColor( lineColor ) ; // 设置画笔颜色
mOuterPaint = new Paint(Paint. ANTI_ALIAS_FLAG ) ;
mOuterPaint .setColor(Color. BLACK ) ;
mOuterPaint .setStyle(Paint.Style. STROKE ) ;
mOuterPaint .setStrokeWidth( 1 ) ;
mTextPaint = new Paint(Paint. ANTI_ALIAS_FLAG ) ; // 初始绘制线的画笔
mTextPaint .setAntiAlias( true ) ; // 去除画笔锯齿
mTextPaint .setStyle(Paint.Style. STROKE ) ; // 设置风格为实线
mTextPaint .setTextSize( 10 ) ;
mTextPaint .setColor(Color. BLACK ) ; // 设置画笔颜色
mBitmapPaint = new Paint() ;
mBitmapPaint .setAntiAlias( true ) ;
mBitmapPaint .setDither( true ) ;
mBitmapPaint .setFilterBitmap( true ) ;
}
/**
* 将 dp 转化为 px, 为了适配
*/
private void initData () {
touchSlop = ViewConfiguration.get( mContext ).getScaledTouchSlop() ;
this . mScroller = new Scroller( mContext ) ;
mHalfLineHeight = mLineHeight / 2 ;
mMiddleLineHeight = mLineHeight / 3 ;
mRulerLeftRightMargin = UiUtils.dipToPx( mContext , RULER_MARGIN_LEFT_RIGHT ) ;
mRulerTopBottomMargin = UiUtils.dipToPx( mContext , RULER_MARGIN_TOP_BOTTOM ) ;
mFirstLineMargin = UiUtils. dipToPx( mContext , FIRST_LINE_MARGIN ) ;
rulerBitmap = ((BitmapDrawable) mContext .getResources().getDrawable(R.mipmap. ruler )).getBitmap() ;
mRulerWidth = UiUtils. dipToPx( mContext , mRulerWidth ) ;
mRulerHeight = UiUtils. dipToPx( mContext , mRulerHeight ) ;
* 初始化画笔
*/
private void initPaint () {
mLinePaint = new Paint() ; // 初始绘制线的画笔
mLinePaint .setAntiAlias( true ) ; // 去除画笔锯齿
mLinePaint .setStyle(Paint.Style. FILL ) ; // 设置风格为实线
mLinePaint .setColor( lineColor ) ; // 设置画笔颜色
mOuterPaint = new Paint(Paint. ANTI_ALIAS_FLAG ) ;
mOuterPaint .setColor(Color. BLACK ) ;
mOuterPaint .setStyle(Paint.Style. STROKE ) ;
mOuterPaint .setStrokeWidth( 1 ) ;
mTextPaint = new Paint(Paint. ANTI_ALIAS_FLAG ) ; // 初始绘制线的画笔
mTextPaint .setAntiAlias( true ) ; // 去除画笔锯齿
mTextPaint .setStyle(Paint.Style. STROKE ) ; // 设置风格为实线
mTextPaint .setTextSize( 10 ) ;
mTextPaint .setColor(Color. BLACK ) ; // 设置画笔颜色
mBitmapPaint = new Paint() ;
mBitmapPaint .setAntiAlias( true ) ;
mBitmapPaint .setDither( true ) ;
mBitmapPaint .setFilterBitmap( true ) ;
}
/**
* 将 dp 转化为 px, 为了适配
*/
private void initData () {
touchSlop = ViewConfiguration.get( mContext ).getScaledTouchSlop() ;
this . mScroller = new Scroller( mContext ) ;
mHalfLineHeight = mLineHeight / 2 ;
mMiddleLineHeight = mLineHeight / 3 ;
mRulerLeftRightMargin = UiUtils.dipToPx( mContext , RULER_MARGIN_LEFT_RIGHT ) ;
mRulerTopBottomMargin = UiUtils.dipToPx( mContext , RULER_MARGIN_TOP_BOTTOM ) ;
mFirstLineMargin = UiUtils. dipToPx( mContext , FIRST_LINE_MARGIN ) ;
rulerBitmap = ((BitmapDrawable) mContext .getResources().getDrawable(R.mipmap. ruler )).getBitmap() ;
mRulerWidth = UiUtils. dipToPx( mContext , mRulerWidth ) ;
mRulerHeight = UiUtils. dipToPx( mContext , mRulerHeight ) ;
}
/**
* 绘制处边框
* @param canvas
*/
private void drawOuter (Canvas canvas) {
canvas.drawRect( mOutRect , mOuterPaint ) ;
}
/**
* 绘制线
* @param canvas
*/
private void drawLine (Canvas canvas) {
canvas.save() ;
// 第一条线间隔
canvas.translate(( float ) ( mFirstLineMargin * 1.5 + mRulerLeftRightMargin ) , 0 ) ;
int top = 0 ;
for ( int i = 1 ; i <= DEFAULT_LINE_COUNT * DEFAULT_LINE_COUNT ; i++) {
// 绘制最长线
if (i % 10 == 0 || i == 1 ) {
top = mTotalHeight - mLineHeight ;
}
// 绘制中间线
else if (i % 5 == 0 ) {
top = mTotalHeight - mHalfLineHeight ;
}
// 绘制短线
else {
top = mTotalHeight - mMiddleLineHeight ;
}
canvas.drawLine( 0 , top , 0 , mTotalHeight - mRulerTopBottomMargin , mLinePaint ) ;
// 增加相应的间隔
canvas.translate( mLineDivider , 0 ) ;
}
canvas.restore() ;
}
/**
* 绘制文本
* @param canvas
*/
private void drawText (Canvas canvas) {
canvas.save() ;
// 第一条线间隔
canvas.translate(( float ) ( mFirstLineMargin * 1.5 + mRulerLeftRightMargin ) - 2 , 0 ) ;
for ( int i = 1 ; i <= DEFAULT_LINE_COUNT * DEFAULT_LINE_COUNT ; i++) {
canvas.drawText(i + "" , 0 , mTotalHeight , mTextPaint ) ;
// 增加相应的间隔
canvas.translate( mLineDivider , 0 ) ;
}
canvas.restore() ;
* 绘制处边框
* @param canvas
*/
private void drawOuter (Canvas canvas) {
canvas.drawRect( mOutRect , mOuterPaint ) ;
}
/**
* 绘制线
* @param canvas
*/
private void drawLine (Canvas canvas) {
canvas.save() ;
// 第一条线间隔
canvas.translate(( float ) ( mFirstLineMargin * 1.5 + mRulerLeftRightMargin ) , 0 ) ;
int top = 0 ;
for ( int i = 1 ; i <= DEFAULT_LINE_COUNT * DEFAULT_LINE_COUNT ; i++) {
// 绘制最长线
if (i % 10 == 0 || i == 1 ) {
top = mTotalHeight - mLineHeight ;
}
// 绘制中间线
else if (i % 5 == 0 ) {
top = mTotalHeight - mHalfLineHeight ;
}
// 绘制短线
else {
top = mTotalHeight - mMiddleLineHeight ;
}
canvas.drawLine( 0 , top , 0 , mTotalHeight - mRulerTopBottomMargin , mLinePaint ) ;
// 增加相应的间隔
canvas.translate( mLineDivider , 0 ) ;
}
canvas.restore() ;
}
/**
* 绘制文本
* @param canvas
*/
private void drawText (Canvas canvas) {
canvas.save() ;
// 第一条线间隔
canvas.translate(( float ) ( mFirstLineMargin * 1.5 + mRulerLeftRightMargin ) - 2 , 0 ) ;
for ( int i = 1 ; i <= DEFAULT_LINE_COUNT * DEFAULT_LINE_COUNT ; i++) {
canvas.drawText(i + "" , 0 , mTotalHeight , mTextPaint ) ;
// 增加相应的间隔
canvas.translate( mLineDivider , 0 ) ;
}
canvas.restore() ;
}
这样就完成了基本的绘制,但为了可以左右滑动,增加了一个scroller,scoroller滑动的只是内容,位置不变。
/**
* 画标志
* @param canvas
*/
private void drawSymbol (Canvas canvas) {
canvas.save() ;
// 第一条线间隔
canvas.translate(( float ) ( mFirstLineMargin * 1.5 + mRulerLeftRightMargin ) - 5 , mRulerTopBottomMargin ) ;
canvas.drawBitmap( rulerBitmap , mRulerSrcRect , mRulerDestRect , mBitmapPaint ) ;
canvas.restore() ;
}
@Override
protected void onSizeChanged ( int w , int h , int oldw , int oldh) {
super .onSizeChanged(w , h , oldw , oldh) ;
// 记录下 view 的宽高
mTotalWidth = w ;
mTotalHeight = h ;
mLineDivider = ( int ) Math. round(( mTotalWidth - mRulerLeftRightMargin * 2 - mFirstLineMargin * 2 ) / ( DEFAULT_LINE_COUNT * DEFAULT_LINE_MIDDLE_COUNT - 1.0 )) ;
mRulerSrcRect = new Rect( 0 , 0 , mRulerWidth , mRulerHeight ) ;
mRulerDestRect = new Rect( 0 , 0 , mRulerWidth , mRulerHeight ) ;
mWholeRulerWidth = ( DEFAULT_LINE_COUNT * ( DEFAULT_LINE_COUNT - DEFAULT_LINE_MIDDLE_COUNT )) * mLineDivider ;
mOutRect = new Rect( mRulerLeftRightMargin , mRulerTopBottomMargin , mWholeRulerWidth + mTotalWidth , mTotalHeight - mRulerTopBottomMargin ) ;
}
@Override
public boolean onTouchEvent (MotionEvent event) {
int action = event.getAction() ;
int x = ( int ) event.getX() ;
int y = ( int ) event.getY() ;
switch (action) {
case MotionEvent. ACTION_DOWN :
break;
case MotionEvent. ACTION_MOVE :
if ( (x > mRulerLeftRightMargin + mFirstLineMargin ) && (x < mTotalWidth - mRulerLeftRightMargin - mFirstLineMargin )) {
int temp = ( int ) (x - ( mRulerLeftRightMargin + mFirstLineMargin * 1.5 )) - 5 ;
temp = temp > 0 ? temp : 0 ;
mRulerDestRect = new Rect(temp , 0 , temp + mRulerWidth , mRulerHeight ) ;
postInvalidate() ;
scrollerSmoothScrollBy( lastX - x , 0 ) ;
}
break;
case MotionEvent. ACTION_UP :
break;
}
lastX = x ;
return true;
}
@Override
public void computeScroll () {
// 先判断 mScroller 滚动是否完成
if ( mScroller .computeScrollOffset()) {
// 这里调用 View 的 scrollTo() 完成实际的滚动
scrollTo( mScroller .getCurrX() , mScroller .getCurrY()) ;
// 必须调用该方法,否则不一定能看到滚动效果
postInvalidate() ;
}
}
// 调用此方法滚动到目标位置
public void scrollerSmoothScrollTo ( int fx , int fy) {
int dx = fx - mScroller .getFinalX() ;
int dy = fy - mScroller .getFinalY() ;
scrollerSmoothScrollBy(dx , dy) ;
}
// 调用此方法设置滚动的相对偏移
public void scrollerSmoothScrollBy ( int dx , int dy) {
// 设置 mScroller 的滚动偏移量
mScroller .startScroll( mScroller .getFinalX() , mScroller .getFinalY() , dx , dy) ;
invalidate() ; // 这里必须调用 invalidate() 才能保证 computeScroll() 会被调用,否则不一定会刷新界面,看不到滚动效果
* 画标志
* @param canvas
*/
private void drawSymbol (Canvas canvas) {
canvas.save() ;
// 第一条线间隔
canvas.translate(( float ) ( mFirstLineMargin * 1.5 + mRulerLeftRightMargin ) - 5 , mRulerTopBottomMargin ) ;
canvas.drawBitmap( rulerBitmap , mRulerSrcRect , mRulerDestRect , mBitmapPaint ) ;
canvas.restore() ;
}
@Override
protected void onSizeChanged ( int w , int h , int oldw , int oldh) {
super .onSizeChanged(w , h , oldw , oldh) ;
// 记录下 view 的宽高
mTotalWidth = w ;
mTotalHeight = h ;
mLineDivider = ( int ) Math. round(( mTotalWidth - mRulerLeftRightMargin * 2 - mFirstLineMargin * 2 ) / ( DEFAULT_LINE_COUNT * DEFAULT_LINE_MIDDLE_COUNT - 1.0 )) ;
mRulerSrcRect = new Rect( 0 , 0 , mRulerWidth , mRulerHeight ) ;
mRulerDestRect = new Rect( 0 , 0 , mRulerWidth , mRulerHeight ) ;
mWholeRulerWidth = ( DEFAULT_LINE_COUNT * ( DEFAULT_LINE_COUNT - DEFAULT_LINE_MIDDLE_COUNT )) * mLineDivider ;
mOutRect = new Rect( mRulerLeftRightMargin , mRulerTopBottomMargin , mWholeRulerWidth + mTotalWidth , mTotalHeight - mRulerTopBottomMargin ) ;
}
@Override
public boolean onTouchEvent (MotionEvent event) {
int action = event.getAction() ;
int x = ( int ) event.getX() ;
int y = ( int ) event.getY() ;
switch (action) {
case MotionEvent. ACTION_DOWN :
break;
case MotionEvent. ACTION_MOVE :
if ( (x > mRulerLeftRightMargin + mFirstLineMargin ) && (x < mTotalWidth - mRulerLeftRightMargin - mFirstLineMargin )) {
int temp = ( int ) (x - ( mRulerLeftRightMargin + mFirstLineMargin * 1.5 )) - 5 ;
temp = temp > 0 ? temp : 0 ;
mRulerDestRect = new Rect(temp , 0 , temp + mRulerWidth , mRulerHeight ) ;
postInvalidate() ;
scrollerSmoothScrollBy( lastX - x , 0 ) ;
}
break;
case MotionEvent. ACTION_UP :
break;
}
lastX = x ;
return true;
}
@Override
public void computeScroll () {
// 先判断 mScroller 滚动是否完成
if ( mScroller .computeScrollOffset()) {
// 这里调用 View 的 scrollTo() 完成实际的滚动
scrollTo( mScroller .getCurrX() , mScroller .getCurrY()) ;
// 必须调用该方法,否则不一定能看到滚动效果
postInvalidate() ;
}
}
// 调用此方法滚动到目标位置
public void scrollerSmoothScrollTo ( int fx , int fy) {
int dx = fx - mScroller .getFinalX() ;
int dy = fy - mScroller .getFinalY() ;
scrollerSmoothScrollBy(dx , dy) ;
}
// 调用此方法设置滚动的相对偏移
public void scrollerSmoothScrollBy ( int dx , int dy) {
// 设置 mScroller 的滚动偏移量
mScroller .startScroll( mScroller .getFinalX() , mScroller .getFinalY() , dx , dy) ;
invalidate() ; // 这里必须调用 invalidate() 才能保证 computeScroll() 会被调用,否则不一定会刷新界面,看不到滚动效果
}