Android开发之时间刻度盘
生活随笔
收集整理的這篇文章主要介紹了
Android开发之时间刻度盘
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、最近的一個(gè)項(xiàng)目中有遇到時(shí)間刻度盤的需求,在網(wǎng)上沒找到合適的,于是自己就花點(diǎn)時(shí)間實(shí)現(xiàn)了,現(xiàn)在分享出來,效果如下圖:
在介紹如何實(shí)現(xiàn)之前,先大概介紹一個(gè)這個(gè)時(shí)間刻度盤的功能:
1、顯示當(dāng)前時(shí)間,并且可以左右拖動(dòng)至上一天或者下一天,
2、根據(jù)傳入的時(shí)間塊來繪制藍(lán)色部分
二、代碼實(shí)現(xiàn)
public class ScalePanel extends View {public interface OnValueChangeListener {public void onValueChange(float value);/*** value不再變化,終點(diǎn)* * @param mCalendar* 刻度盤上當(dāng)前時(shí)間*/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;/*** 當(dāng)前刻度值*/private int mValue = 12;private int mLineDivider = ITEM_HALF_DIVIDER;private float mLastX;/*** 記錄刻度盤滑動(dòng)的偏移量*/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;// 為了畫背景色,從左向右畫,記錄下屏幕最左,最右處的時(shí)間點(diǎn)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();}/*** 根據(jù)時(shí)間來計(jì)算偏差,(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();}/*** 通過設(shè)置calendar來設(shè)置刻度盤當(dāng)前的時(shí)間* * @param mCalendar*/public void setCalendar(Calendar mCalendar) {// 用戶手指拖動(dòng)刻度盤的時(shí)候,不接收外部的更新,以免沖突if (!isChangeFromInSide) {initDateAndTime(mCalendar);initOffSet();invalidate();}}/*** 設(shè)置用于接收結(jié)果的監(jiān)聽器* * @param listener*/public void setValueChangeListener(OnValueChangeListener listener) {mListener = listener;}/*** 獲取當(dāng)前刻度值* * @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) {//向左滑動(dòng)if (mValue == 0 && hour != 23) {mCalendar.set(Calendar.DAY_OF_MONTH,mCalendar.get(Calendar.DAY_OF_MONTH) - 1);}hour = mValue - 1;//滑到上一日23點(diǎn)if (hour == -1) {hour = 23;}offsetPercent = 1 + mMove / scaleUnit;} else if (mMove >= 0) {//向右滑動(dòng),offsetPercent = mMove / scaleUnit;hour = mValue;//滑到次日0點(diǎn),if (hour == 0 && !isChange) {//如果沒有ischange,那么在hour==0時(shí),day會(huì)重復(fù)加一mCalendar.set(Calendar.DAY_OF_MONTH,mCalendar.get(Calendar.DAY_OF_MONTH) + 1);// 避免重復(fù)把day+1isChange = true;}}if (hour != 0) {// 在hour切換成別的值的時(shí)候再把標(biāo)志設(shè)為默認(rèn)值isChange = false;}countMinAndSecond(offsetPercent);drawTimeText(canvas);for (int i = 0; true; i++) {// 往右邊開始畫xPosition = (width / 2 - mMove) + i * scaleUnit;if (isNeedDrawableRight && xPosition + getPaddingRight() < mWidth) {// 在view范圍內(nèi)畫刻度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;}}// 當(dāng)不需要向左或者向右畫的時(shí)候就退出循環(huán),結(jié)束繪制操作if (!isNeedDrawableLeft && !isNeedDrawableRight) {break;}}canvas.restore();}/*** 還存在問題,如果data數(shù)據(jù)量過大,也就是用戶搜索的時(shí)間跨度過大,這種方式肯定不行會(huì)卡死。* 所以以后得通過獲得當(dāng)前回放所處的位置,然后選擇前后一天左右的時(shí)間,這樣數(shù)據(jù)量就不會(huì)太大* 現(xiàn)在本著先做出來再優(yōu)化的原則,記錄下此問題,以后再做修改優(yōu)化* * @param canvas*/private void calulateDrawPosition(Canvas canvas) {// 距離和時(shí)間對(duì)應(yīng)起來 ((mWidth/2/scaleUnit)*3600*1000)long timeOffset = (long) ((mWidth / 2 / scaleUnit) * 3600 * 1000);long middleTime = mCalendar.getTimeInMillis();// 根據(jù)時(shí)間偏移算出左右的時(shí)間leftCalendar.setTimeInMillis(middleTime - timeOffset);rightCalendar.setTimeInMillis(middleTime + timeOffset);// 找到時(shí)間開始點(diǎn),然后順序向右畫,直到畫到屏幕最右側(cè),關(guān)鍵是找到時(shí)間開始點(diǎn)// 時(shí)間開始點(diǎn)就是從什么地方開始畫背景色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* 第一塊背景色的長(zhǎng)度* @param position* 第一塊背景色所在時(shí)間片段在data中所處的position,下一塊從position+1開始*/public void drawBgColor(Canvas canvas, Calendar startTime,Calendar endTime, int position) {// 根據(jù)時(shí)間獲得在刻度盤上具體的位置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) {// 只畫屏幕屏幕區(qū)域以內(nèi)的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);}/*** 根據(jù)時(shí)間獲得在刻度盤上具體的位置* * @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;}/*** 準(zhǔn)備畫背景色的數(shù)據(jù)*/public void setTimeData(List<TVideoFile> data) {this.data = data;}/*** 畫日期時(shí)間的文字* * @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);}/*** 計(jì)算分鐘和秒鐘* @param percent* @return*/public int[] countMinAndSecond(float percent) {minute = (int) (3600 * percent / 60);second = (int) (3600 * percent % 60);return new int[] { minute, second };}/*** 畫中間的紅色指示線、陰影等。指示線兩端簡(jiǎn)單的用了兩個(gè)矩形代替* * @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;//滑動(dòng)超過一格以后,記錄下當(dāng)前刻度盤上的值if (Math.abs(fValue) > 0) {mValue += tValue;//偏移量永遠(yuǎn)都小于一格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方法供外界來設(shè)置刻度盤的當(dāng)前時(shí)間,并且提供了onValueChange(float value)和onValueChangeEnd(Calendar mCalendar)來分別提供實(shí)時(shí)監(jiān)聽和滑動(dòng)結(jié)束的監(jiān)聽,如果想要繪制時(shí)間塊的背景色可以這樣 public class MainActivity extends Activity implements OnValueChangeListener {/*** 時(shí)間刻度盤*/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();//設(shè)置時(shí)間塊數(shù)據(jù)scalePanel.setTimeData(data);//設(shè)置當(dāng)前時(shí)間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) {} }具體的實(shí)現(xiàn)可以細(xì)看代碼和注釋,代碼中有些關(guān)于scroller的使用我沒有做任何說明,如果你對(duì)scroller的使用還不是很熟悉,可以閱讀下這篇文章Android開發(fā)之Scroller的使用詳解
如果有不明白的地方可以和我討論。
最后留下demo,如有需要可以看看,歡迎留下你寶貴的意見。
總結(jié)
以上是生活随笔為你收集整理的Android开发之时间刻度盘的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mediawiki修改用mysql数据库
- 下一篇: Android APP开机启动,安卓AP