先看效果图先:
- 定义绘制的变量(可拓展,方便以后的业务需求)
/*一秒所占据的像素*/
private float mSecWidth = 0;
/*一分钟所占据的像素*/
private float mMinWidth = 0;
/*一个小时占据的*/
private float mHouWidth = 0;
/*一个像素占据多少时间*/
private float mPxTime;
/*View 的长度*/
private float mViewWidth;
/*View的高度*/
private float mViewHeight;
/*一个小时最大刻度*/
private int mHourMaxNum = 0;
/*屏幕宽度占据多少个小时*/
private int mViewMaxNum = 0;
初始化(会有精度的损失,忽略不计):
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViewHeight = getMeasuredHeight();
mViewWidth = getMeasuredWidth();
mHouWidth = mViewWidth / mViewMaxNum;
mMinWidth = mHouWidth / 60f;
mSecWidth = mMinWidth / 60f;
mPxTime = 1 / mSecWidth;
Log.d(TAG, "onMeasure: ------->" + mViewWidth + "---小时:"
+ mHouWidth + "---分钟:" + mMinWidth + "---秒:" + mSecWidth + "---像素:" + mPxTime);
}
- 开始绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
drawLine(canvas);
drawCenterLine(canvas);
}
1:绘制背景色(默认是黑色):
/**
* 绘制背景
*
* @param canvas
*/
private void drawBackground(Canvas canvas) {
canvas.drawColor(Color.BLACK);
}
2:绘制分割线(默认是白色):
时间是GMT格式,不进行地区偏移
绘制的流程相对比较简单只要理清楚时间的计算就行了:'
1:从中间开始画线,向两边展开,绘制的开始时间可以通过外部接口设置,这里是以小时进行单位运算,把右边最近的小时分割线绘制出来(nextHourWidth)
2:计算出nextHourWidth的长度坐标后,通过循环计算绘制右边的刻度
3:最后循环计算绘制左边的刻度
/**
* 绘制刻度线
*
* @param canvas
*/
private void drawLine(Canvas canvas) {
String tmpDate = mDateFormat.format(new Date(mCurrentTime));
mPaintLine.setColor(Color.WHITE);
// Log.d(TAG, "drawLine: -------->" + tmpDate);
String timeDate = tmpDate.split(" ")[1];
String[] timeDates = timeDate.split(":");
int hours = Integer.parseInt(timeDates[0]);
int minute = Integer.parseInt(timeDates[1]);
int sec = Integer.parseInt(timeDates[2]);
hours++;
Rect rect = new Rect();
mPaintLine.getTextBounds("00:00", 0, "00:00".length(), rect);
int strWidth = rect.width();
int strHeight = rect.height();
float halfViewWidth = mViewWidth / 2;
float nextHourWidth = halfViewWidth + ((60 - minute) * 60 + (60 - sec)) * mSecWidth;
// Log.d(TAG, "drawLine: --------->" + halfViewWidth + "---" + nextHourWidth);
float tmpMaxTopHeight = mViewHeight * DEFAULT_HOUR_BOTTOM_PADDING;
float tmpMaxBottomHeight = mViewHeight * DEFAULT_HOUR_TOP_PADDING;
float tmpMaxHalfHeight = mViewHeight * DEFAULT_HALF_PADDING;
/*绘制右边*/
float tmpRightLocal = nextHourWidth;
int rightCount = 0;
int rightHour = hours;
do {
if (rightCount % mHourMaxNum == 0) {
canvas.drawLine(tmpRightLocal, tmpMaxTopHeight, tmpRightLocal, tmpMaxBottomHeight, mPaintLine);
String timeText = String.format("%02d:00", (rightHour % 24));
canvas.drawText(timeText, tmpRightLocal - strWidth, tmpMaxTopHeight + strHeight - 10, mPaintText);
rightHour++;
} else {
canvas.drawLine(tmpRightLocal, tmpMaxHalfHeight, tmpRightLocal, tmpMaxBottomHeight, mPaintLine);
}
tmpRightLocal += mMinWidth * (60 / mHourMaxNum);
rightCount++;
} while (tmpRightLocal < mViewWidth);
/*绘制左边*/
float tmpLeftLocal = nextHourWidth;
int leftCount = 0;
int leftHour = hours;
do {
// canvas.drawLine(tmpLeftLocal, 0, tmpLeftLocal, mViewHeight, mPaintLine);
if (leftCount % mHourMaxNum == 0) {
canvas.drawLine(tmpLeftLocal, tmpMaxTopHeight, tmpLeftLocal, tmpMaxBottomHeight, mPaintLine);
String timeText = String.format("%02d:00", ((leftHour + 24) % 24));
canvas.drawText(timeText, tmpLeftLocal - strWidth, tmpMaxTopHeight + strHeight - 10, mPaintText);
leftHour--;
} else {
canvas.drawLine(tmpLeftLocal, tmpMaxHalfHeight, tmpLeftLocal, tmpMaxBottomHeight, mPaintLine);
}
tmpLeftLocal -= mMinWidth * (60 / mHourMaxNum);
leftCount++;
} while (tmpLeftLocal > -1);
canvas.drawLine(0, mViewHeight * DEFAULT_HOUR_TOP_PADDING, mViewWidth, mViewHeight * DEFAULT_HOUR_TOP_PADDING, mPaintLine);
}
用于绘制连接的线
canvas.drawLine(0, mViewHeight * DEFAULT_HOUR_TOP_PADDING, mViewWidth, mViewHeight * DEFAULT_HOUR_TOP_PADDING, mPaintLine);
3:绘制中间的线
/**
* 绘制中间的线
*
* @param canvas
*/
private void drawCenterLine(Canvas canvas) {
canvas.drawLine(mViewWidth / 2, 0, mViewWidth / 2, mViewHeight, mPaintCenterLine);
}
以上步骤可以看出显示的画面了
- 移动
移动的原理是通过滑动的距离不断计算偏移量,来改变mCurrentTime的值,然后重新绘制,达到了移动的目的
@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.d(TAG, "onTouchEvent: ---------->" + event.getAction());
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mActionDownFirstX = event.getX();
mActionDownFirstTime = mCurrentTime;
Log.d(TAG, "onTouchEvent: ------> MotionEvent.ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "onTouchEvent: ------> MotionEvent.ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
// L
// if (mDoubleMoveIndex >= 2) {
// float moveX = Math.abs(getBetweenX(event));
//
// if (moveX != mDoubleMoveX) {
// mDoubleMoveX = moveX;
// float tmp = mDoubleMoveX - mDoubleFirstBet; /*计算出缩放的距离*/
// Log.d(TAG, "onTouchEvent: -------->" + "双指操控!!!" + tmp + "------" + mDoubleFirstBet);
// scale(tmp / 100.0f);
//
// }
//
// } else {
float moveX = event.getX();
float tmpBetX = moveX - mActionDownFirstX;
//Android按住不动的时候也会回调,会导致View抖动,不像IOS,虽然是回调了,但是可以通过计算,防止抖动
if (tmpBetX != mTmpMoveX) {
mTmpMoveX = tmpBetX;
Log.d(TAG, "onTouchEvent: ------------->" + Math.round(mTmpMoveX * mPxTime));
long tmpTime = mActionDownFirstTime - Math.round(mTmpMoveX * mPxTime) * 1000;
mCurrentTime = tmpTime;
invalidate();
if (mOnTimeBarMoveListener != null) {
mOnTimeBarMoveListener.onTimeBarMove(mCurrentTime);
}
}
// }
break;
case MotionEvent.ACTION_POINTER_DOWN:
// mDoubleMoveIndex += 1;
//// isActionPointerDown = true;
// mDoubleFirstBet = Math.abs(getBetweenX(event));
// Log.d(TAG, "onTouchEvent: ------> MotionEvent.双指操控!!!" + mDoubleFirstBet);
break;
case MotionEvent.ACTION_POINTER_UP:
isActionPointerDown = false;
Log.d(TAG, "onTouchEvent: ------> MotionEvent.ACTION_POINTER_UP");
break;
}
return true;
}
- 其它接口
/**
* 设置当前时间
*
* @param time
*/
public void setCurrentTime(long time) {
mCurrentTime = time;
invalidate();
}
private OnTimeBarMoveListener mOnTimeBarMoveListener;
public void setOnTimeBarMoveListener(OnTimeBarMoveListener listener) {
this.mOnTimeBarMoveListener = listener;
}
public OnTimeBarMoveListener getOnTimeBarMoveListener() {
return mOnTimeBarMoveListener;
}
/**
* 时间轴滑动回调
*/
public interface OnTimeBarMoveListener {
void onTimeBarMove(long time);
}
如果有什么疑问或者是在需求业务上该时间轴无法满足的,请在评论留言,后期会增加时间轴的录像块,类似于做摄像头的录像显示,如果觉得好用,就在Demo给个星吧!!
项目Demo链接