Android开发之时间刻度盘
生活随笔
收集整理的這篇文章主要介紹了
Android开发之时间刻度盘
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、最近的一個項目中有遇到時間刻度盤的需求,在網上沒找到合適的,于是自己就花點時間實現了,現在分享出來,效果如下圖:
在介紹如何實現之前,先大概介紹一個這個時間刻度盤的功能:
1、顯示當前時間,并且可以左右拖動至上一天或者下一天,
2、根據傳入的時間塊來繪制藍色部分
二、代碼實現
public class ScalePanel extends View {public interface OnValueChangeListener {public void onValueChange(float value);/*** value不再變化,終點* * @param mCalendar* 刻度盤上當前時間*/public void onValueChangeEnd(Calendar mCalendar);}public static final int MOD_TYPE_HALF = 2;public static final int MOD_TYPE_ONE = 10;private static final int ITEM_HALF_DIVIDER = 60;private static final int ITEM_MAX_HEIGHT = 10;private static final int TEXT_SIZE = 14;private float mDensity;/*** 當前刻度值*/private int mValue = 12;private int mLineDivider = ITEM_HALF_DIVIDER;private float mLastX;/*** 記錄刻度盤滑動的偏移量*/private float mMove;private float mWidth, mHeight;private int mMinVelocity;private Scroller mScroller;private VelocityTracker mVelocityTracker;private OnValueChangeListener mListener;/*** 日期文字的寬度*/float textWidth = 0;private TextPaint textPaint, dateAndTimePaint;private Paint linePaint;private boolean isNeedDrawableLeft, isNeedDrawableRight;private Calendar mCalendar;private Paint middlePaint, bgColorPaint;/****/private boolean isChangeFromInSide;public boolean isEnd;// 為了畫背景色,從左向右畫,記錄下屏幕最左,最右處的時間點private Calendar leftCalendar, rightCalendar;private List<TVideoFile> data;private int hour, minute, second;int gap = 12, indexWidth = 4, indexTitleWidth = 24, indexTitleHight = 10,shadow = 6;String color = "#FA690C";String dateStr, timeStr;public ScalePanel(Context context, AttributeSet attrs) {super(context, attrs);mScroller = new Scroller(getContext());mDensity = getContext().getResources().getDisplayMetrics().density;mMinVelocity = ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();linePaint = new Paint();linePaint.setStrokeWidth(2);linePaint.setColor(Color.parseColor("#464646"));bgColorPaint = new Paint();bgColorPaint.setStrokeWidth(2);bgColorPaint.setColor(Color.parseColor("#00a3dd"));textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);textPaint.setTextSize(TEXT_SIZE * mDensity);dateAndTimePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);dateAndTimePaint.setTextSize(18 * mDensity);middlePaint = new Paint();scaleUnit = mLineDivider * mDensity;mCalendar = Calendar.getInstance();initDateAndTime(mCalendar);leftCalendar = Calendar.getInstance();rightCalendar = Calendar.getInstance();}/*** 根據時間來計算偏差,(minute*60+second)*scaleUnit/3600*/private void initOffSet() {mMove = (minute * 60 + second) * scaleUnit / 3600;}private void initDateAndTime(Calendar mCalendar) {this.mCalendar = mCalendar;hour = mCalendar.get(Calendar.HOUR_OF_DAY);minute = mCalendar.get(Calendar.MINUTE);second = mCalendar.get(Calendar.SECOND);mValue = hour;initOffSet();}/*** 通過設置calendar來設置刻度盤當前的時間* * @param mCalendar*/public void setCalendar(Calendar mCalendar) {// 用戶手指拖動刻度盤的時候,不接收外部的更新,以免沖突if (!isChangeFromInSide) {initDateAndTime(mCalendar);initOffSet();invalidate();}}/*** 設置用于接收結果的監聽器* * @param listener*/public void setValueChangeListener(OnValueChangeListener listener) {mListener = listener;}/*** 獲取當前刻度值* * @return*/public float getValue() {return mValue;}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) {mWidth = getWidth();mHeight = getHeight();super.onLayout(changed, left, top, right, bottom);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawMiddleLine(canvas);drawScaleLine(canvas);}private float offsetPercent;private float scaleUnit;private boolean isChange = false;/*** 線條底部的位置*/float lineBottom;/*** 線條頂部得到位置*/float lineTop;/*** 從中間往兩邊開始畫刻度線* * @param canvas*/private void drawScaleLine(Canvas canvas) {canvas.save();isNeedDrawableLeft = true;isNeedDrawableRight = true;float width = mWidth;float xPosition = 0;lineBottom = mHeight - getPaddingBottom();lineTop = lineBottom - mDensity * ITEM_MAX_HEIGHT;if (data != null && data.size() > 0) {calulateDrawPosition(canvas);}//mValue的值控制在0~23之間if (mValue > 0) {mValue = mValue % 24;} else if (mValue < 0) {mValue = mValue % 24 + 24;}if (mMove < 0) {//向左滑動if (mValue == 0 && hour != 23) {mCalendar.set(Calendar.DAY_OF_MONTH,mCalendar.get(Calendar.DAY_OF_MONTH) - 1);}hour = mValue - 1;//滑到上一日23點if (hour == -1) {hour = 23;}offsetPercent = 1 + mMove / scaleUnit;} else if (mMove >= 0) {//向右滑動,offsetPercent = mMove / scaleUnit;hour = mValue;//滑到次日0點,if (hour == 0 && !isChange) {//如果沒有ischange,那么在hour==0時,day會重復加一mCalendar.set(Calendar.DAY_OF_MONTH,mCalendar.get(Calendar.DAY_OF_MONTH) + 1);// 避免重復把day+1isChange = true;}}if (hour != 0) {// 在hour切換成別的值的時候再把標志設為默認值isChange = false;}countMinAndSecond(offsetPercent);drawTimeText(canvas);for (int i = 0; true; i++) {// 往右邊開始畫xPosition = (width / 2 - mMove) + i * scaleUnit;if (isNeedDrawableRight && xPosition + getPaddingRight() < mWidth) {// 在view范圍內畫刻度canvas.drawLine(xPosition, lineTop, xPosition, lineBottom,linePaint);textWidth = Layout.getDesiredWidth(int2Str(mValue + i),textPaint);canvas.drawText(int2Str(mValue + i), xPosition- (textWidth / 2), lineTop - 5, textPaint);} else {isNeedDrawableRight = false;}// 往左邊開始畫if (i > 0) {// 防止中間的刻度畫兩遍xPosition = (width / 2 - mMove) - i * scaleUnit;if (isNeedDrawableLeft && xPosition > getPaddingLeft()) {canvas.drawLine(xPosition, lineTop, xPosition, lineBottom,linePaint);textWidth = Layout.getDesiredWidth(int2Str(mValue - i),textPaint);canvas.drawText(int2Str(mValue - i), xPosition- (textWidth / 2), lineTop - 5, textPaint);} else {isNeedDrawableLeft = false;}}// 當不需要向左或者向右畫的時候就退出循環,結束繪制操作if (!isNeedDrawableLeft && !isNeedDrawableRight) {break;}}canvas.restore();}/*** 還存在問題,如果data數據量過大,也就是用戶搜索的時間跨度過大,這種方式肯定不行會卡死。* 所以以后得通過獲得當前回放所處的位置,然后選擇前后一天左右的時間,這樣數據量就不會太大* 現在本著先做出來再優化的原則,記錄下此問題,以后再做修改優化* * @param canvas*/private void calulateDrawPosition(Canvas canvas) {// 距離和時間對應起來 ((mWidth/2/scaleUnit)*3600*1000)long timeOffset = (long) ((mWidth / 2 / scaleUnit) * 3600 * 1000);long middleTime = mCalendar.getTimeInMillis();// 根據時間偏移算出左右的時間leftCalendar.setTimeInMillis(middleTime - timeOffset);rightCalendar.setTimeInMillis(middleTime + timeOffset);// 找到時間開始點,然后順序向右畫,直到畫到屏幕最右側,關鍵是找到時間開始點// 時間開始點就是從什么地方開始畫背景色for (int position = 0; position < data.size(); position++) {TVideoFile tVideoFile = data.get(position);Calendar startCalendar = tVideoFile.startTime;Calendar endCalendar = tVideoFile.endTime;if (leftCalendar.before(startCalendar)&& rightCalendar.after(startCalendar)) {// 從start從開始畫drawBgColor(canvas, startCalendar, endCalendar, position);break;} else if (leftCalendar.after(startCalendar)&& leftCalendar.before(endCalendar)) {// 從left從開始畫drawBgColor(canvas, leftCalendar, endCalendar, position);break;}}}/*** * @param canvas* @param start* 第一塊背景色開始的位置* @param distance* 第一塊背景色的長度* @param position* 第一塊背景色所在時間片段在data中所處的position,下一塊從position+1開始*/public void drawBgColor(Canvas canvas, Calendar startTime,Calendar endTime, int position) {// 根據時間獲得在刻度盤上具體的位置float startPosition = getPositionByTime(startTime);float endPosition = getPositionByTime(endTime);drawBgColorRect(startPosition, lineTop, endPosition, lineBottom, canvas);for (int i = position + 1; i < data.size(); i++) {TVideoFile tVideoFile = data.get(i);Calendar startCalendar = tVideoFile.startTime;Calendar endCalendar = tVideoFile.endTime;startPosition = getPositionByTime(startCalendar);endPosition = getPositionByTime(endCalendar);if (startPosition <= mWidth) {// 只畫屏幕屏幕區域以內的drawBgColorRect(startPosition, lineTop, endPosition,lineBottom, canvas);} else {break;}}}/*** 畫背景色* * @param canvas*/private void drawBgColorRect(float left, float top, float right,float bottom, Canvas canvas) {canvas.drawRect(left, top, right, bottom, bgColorPaint);}/*** 根據時間獲得在刻度盤上具體的位置* * @param calendar* @return*/public float getPositionByTime(Calendar calendar) {long middleTime = mCalendar.getTimeInMillis();float position = 0;long timeOffset = middleTime - calendar.getTimeInMillis();if (timeOffset >= 0) {position = (float) (mWidth / 2 - (1.0 * timeOffset / 3600 / 1000)* scaleUnit);} else {position = (float) (mWidth / 2 - (1.0 * timeOffset / 3600 / 1000)* scaleUnit);}return position;}/*** 準備畫背景色的數據*/public void setTimeData(List<TVideoFile> data) {this.data = data;}/*** 畫日期時間的文字* * @param canvas*/private void drawTimeText(Canvas canvas) {mCalendar.set(Calendar.HOUR_OF_DAY, hour);mCalendar.set(Calendar.MINUTE, minute);mCalendar.set(Calendar.SECOND, second);timeStr = date2timeStr(mCalendar.getTime());textWidth = Layout.getDesiredWidth(timeStr, textPaint);canvas.drawText(timeStr, mWidth / 2 + 15 * mDensity, 50,dateAndTimePaint);drawDateText(canvas);}private void drawDateText(Canvas canvas) {dateStr = date2DateStr(mCalendar.getTime());textWidth = Layout.getDesiredWidth(dateStr, textPaint);canvas.drawText(dateStr, mWidth / 2 - textWidth - 35 * mDensity, 50,dateAndTimePaint);}/*** 計算分鐘和秒鐘* @param percent* @return*/public int[] countMinAndSecond(float percent) {minute = (int) (3600 * percent / 60);second = (int) (3600 * percent % 60);return new int[] { minute, second };}/*** 畫中間的紅色指示線、陰影等。指示線兩端簡單的用了兩個矩形代替* * @param canvas*/private void drawMiddleLine(Canvas canvas) {canvas.save();middlePaint.setStrokeWidth(indexWidth);middlePaint.setColor(Color.parseColor(color));canvas.drawLine(mWidth / 2, 0, mWidth / 2, mHeight, middlePaint);canvas.restore();}@Overridepublic 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;isChangeFromInSide = true;break;case MotionEvent.ACTION_MOVE:mMove += (mLastX - xPosition);changeMoveAndValue();break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:countMoveEnd();countVelocityTracker(event);return false;default:break;}mLastX = xPosition;return true;}private void changeMoveAndValue() {float fValue = mMove / scaleUnit;int tValue = (int) fValue;//滑動超過一格以后,記錄下當前刻度盤上的值if (Math.abs(fValue) > 0) {mValue += tValue;//偏移量永遠都小于一格mMove -= tValue * scaleUnit;notifyValueChange();postInvalidate();}}private void countVelocityTracker(MotionEvent event) {mVelocityTracker.computeCurrentVelocity(1000, 1500);float xVelocity = mVelocityTracker.getXVelocity();if (Math.abs(xVelocity) > mMinVelocity) {mScroller.fling(0, 0, (int) xVelocity, 0, Integer.MIN_VALUE,Integer.MAX_VALUE, 0, 0);} else {notifyChangeOver();}}private void countMoveEnd() {mLastX = 0;notifyValueChange();postInvalidate();}private void notifyValueChange() {if (null != mListener) {mListener.onValueChange(mValue);}}private void notifyChangeOver() {if (null != mListener) {mListener.onValueChangeEnd(mCalendar);}isChangeFromInSide = false;}@Overridepublic void computeScroll() {super.computeScroll();if (mScroller.computeScrollOffset()) {if (mScroller.getCurrX() == mScroller.getFinalX()) { // overcountMoveEnd();notifyChangeOver();} else {int xPosition = mScroller.getCurrX();mMove += (mLastX - xPosition);changeMoveAndValue();mLastX = xPosition;}}}public String int2Str(int i) {if (i > 0) {i = i % 24;} else if (i < 0) {i = i % 24 + 24;}String str = String.valueOf(i);if (str.length() == 1) {return "0" + str + ":00";} else if (str.length() == 2) {return str + ":00";}return "";}public String date2DateStr(Date date) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");return dateFormat.format(date);}public String date2timeStr(Date date) {SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");return dateFormat.format(date);} }我提供了setCalendar方法供外界來設置刻度盤的當前時間,并且提供了onValueChange(float value)和onValueChangeEnd(Calendar mCalendar)來分別提供實時監聽和滑動結束的監聽,如果想要繪制時間塊的背景色可以這樣 public class MainActivity extends Activity implements OnValueChangeListener {/*** 時間刻度盤*/private ScalePanel scalePanel;List<TVideoFile> data = new ArrayList<TVideoFile>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initData();scalePanel = (ScalePanel) findViewById(R.id.scalePanel);scalePanel.setValueChangeListener(this);Calendar mCalendar = Calendar.getInstance();//設置時間塊數據scalePanel.setTimeData(data);//設置當前時間scalePanel.setCalendar(mCalendar);}private void initData() {for (int hourOffset = -5; Math.abs(hourOffset) <= 5; hourOffset++) {addTimeBloack(hourOffset);}}private void addTimeBloack(int hourOffset) {TVideoFile file = new TVideoFile();Calendar startTime = Calendar.getInstance();startTime.set(Calendar.HOUR_OF_DAY, startTime.get(Calendar.HOUR_OF_DAY) + hourOffset);startTime.set(Calendar.MINUTE, 0);file.startTime = startTime;Calendar endTime = Calendar.getInstance();endTime.set(Calendar.HOUR_OF_DAY, endTime.get(Calendar.HOUR_OF_DAY) + hourOffset);endTime.set(Calendar.MINUTE, 50);file.endTime = endTime;data.add(file);}@Overridepublic void onValueChange(float value) {}@Overridepublic void onValueChangeEnd(Calendar mCalendar) {} }具體的實現可以細看代碼和注釋,代碼中有些關于scroller的使用我沒有做任何說明,如果你對scroller的使用還不是很熟悉,可以閱讀下這篇文章Android開發之Scroller的使用詳解
如果有不明白的地方可以和我討論。
最后留下demo,如有需要可以看看,歡迎留下你寶貴的意見。
總結
以上是生活随笔為你收集整理的Android开发之时间刻度盘的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mediawiki修改用mysql数据库
- 下一篇: Cookie实例:显示用户上次访问的时间