Android自定義一個屬于自己的時間鐘表操作方法
1、概述
本文主要講解的是如何自定義一個時間鐘表,通過簡單的練習可以簡單學習android當中自定義view的一些常用繪圖技巧,優(yōu)化android繪圖操作。言歸正傳,首先看下我們需要實現(xiàn)的效果:

當我們看到這個效果的時候腦子里應該有一定的思路了,我們應該把它分解成以下幾個步驟:
1、儀表盤(圓)
2、刻度線(長 中 短)
3、刻度值(1-12)
4、指針(時 分 秒)
5、移動指針,計算指針位置
現(xiàn)在我們已經(jīng)很清楚自己的思路了,那么我們一個一個來。
第一步:1、自定義View的屬性,首先在res/values/ 下建立一個attrs.xml , 在里面定義我們的屬性和聲明我們的整個樣式。
<declare-styleable name="ClockView">
<attr name="mRadius" format="dimension"/>
<attr name="mCircleColor" format="color"/>
<attr name="mCircleWidth" format="dimension"/>
<attr name="mTextSize" format="dimension"/>
<attr name="mTextColor" format="color"/>
<attr name="mBigScaleColor" format="color"/>
<attr name="mMiddlecaleColor" format="color"/>
<attr name="mSmallScaleColor" format="color"/>
<attr name="mHourHandColor" format="color"/>
<attr name="mMinuteHandColor" format="color"/>
<attr name="mSecondHandColor" format="color"/>
<attr name="mHourHandWidth" format="dimension"/>
<attr name="mMinuteHandWidth" format="dimension"/>
<attr name="mSecondHandWidth" format="dimension"/>
</declare-styleable>我們定義了鐘表的半徑,背景顏色 ,刻度值(大,中,小)的顏色及指針(時分秒)的顏色和寬度。
然后自定義一個class類 為ClockView,在MainActivity的布局中引用:
<com.dalong.customviewstudy.view.ClockView
app:mSecondHandColor="@color/colorAccent"
app:mCircleColor="@android:color/white"
app:mBigScaleColor="@android:color/black"
app:mMiddlecaleColor="@android:color/black"
app:mSmallScaleColor="@color/colorAccent"
app:mHourHandColor="@android:color/black"
app:mMinuteHandColor="@android:color/black"
app:mTextColor="@android:color/black"
app:mHourHandWidth="13dp"
app:mSecondHandWidth="5dp"
app:mMinuteHandWidth="8dp"
app:mTextSize="16sp"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />2、在自定義View的構造方法中,獲得我們的自定義的樣式
//文字畫筆對象
private Paint mTextPaint;
//圓,指針,刻度畫筆
private Paint mPaint;
//半徑
public float mRadius;
//外圓的顏色
public int mCircleColor;
// 外圓的寬度
public float mCircleWidth;
//文字的大小
public float mTextSize;
//文字的顏色
public int mTextColor;
//大刻度顏色
public int mBigScaleColor;
//中刻度
public int mMiddlecaleColor;
//小刻度顏色
public int mSmallScaleColor;
//時針顏色
public int mHourHandColor;
//分針顏色
public int mMinuteHandColor;
//秒針顏色
public int mSecondHandColor;
//時針寬度
public float mHourHandWidth;
//分針寬度
public float mMinuteHandWidth;
//秒針寬度
public float mSecondHandWidth;
//控件寬度
public int mWidth;
//控件高度
public int mHeght;
public ClockView(Context context) {
this(context,null);
}
public ClockView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.ClockView);
mRadius=typedArray.getDimension(R.styleable.ClockView_mRadius,400);
mCircleColor=typedArray.getColor(R.styleable.ClockView_mCircleColor, Color.WHITE);
mCircleWidth=typedArray.getDimension(R.styleable.ClockView_mCircleWidth,20);
mTextSize=typedArray.getDimension(R.styleable.ClockView_mCircleWidth,40);
mTextColor=typedArray.getColor(R.styleable.ClockView_mTextColor,Color.DKGRAY);
mBigScaleColor=typedArray.getColor(R.styleable.ClockView_mBigScaleColor,Color.BLACK);
mSmallScaleColor=typedArray.getColor(R.styleable.ClockView_mSmallScaleColor,Color.RED);
mMiddlecaleColor=typedArray.getColor(R.styleable.ClockView_mMiddlecaleColor,Color.BLACK);
mHourHandColor=typedArray.getColor(R.styleable.ClockView_mHourHandColor,Color.BLACK);
mMinuteHandColor=typedArray.getColor(R.styleable.ClockView_mMinuteHandColor,Color.BLACK);
mSecondHandColor=typedArray.getColor(R.styleable.ClockView_mSecondHandColor,Color.BLACK);
mHourHandWidth=typedArray.getDimension(R.styleable.ClockView_mHourHandWidth,20);
mMinuteHandWidth=typedArray.getDimension(R.styleable.ClockView_mMinuteHandWidth,10);
mSecondHandWidth=typedArray.getDimension(R.styleable.ClockView_mSecondHandWidth,5);
mPaint=new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mTextPaint=new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setStyle(Paint.Style.STROKE);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mTextColor);
}3、我們重寫onDraw,onMesure調用系統(tǒng)提供的:
onMeure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureSize(widthMeasureSpec),measureSize(heightMeasureSpec));
}
private int measureSize(int mMeasureSpec) {
int result;
int mode=MeasureSpec.getMode(mMeasureSpec);
int size=MeasureSpec.getSize(mMeasureSpec);
if(mode==MeasureSpec.EXACTLY){
result=size;
}else{
result=400;
if(mode==MeasureSpec.AT_MOST){
result=Math.min(result,size);
}
}
return result;
}onDraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//設置寬高、半徑
mWidth=getMeasuredWidth()-getPaddingLeft()-getPaddingRight();
mHeght=getMeasuredHeight()-getPaddingBottom()-getPaddingTop();
mRadius=Math.min(mWidth/2,mHeght/2);
//首先繪制圓
drawCircle(canvas);
//繪制刻度
drawScale(canvas);
//繪制指針
drawPointer(canvas);
//發(fā)送消息刷新ui
handler.sendEmptyMessageDelayed(START_CLOCK,1000);
}其中最核心的代碼就是這三個方法:
//首先繪制圓
drawCircle(canvas);
//繪制刻度
drawScale(canvas);
//繪制指針
drawPointer(canvas);首先講第一個方法:
/**
* 畫圓
* @param canvas
*/
private void drawCircle(Canvas canvas) {
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mCircleColor);
canvas.drawCircle(mWidth/2,mHeght/2,mRadius,mPaint);
}這個方法其實很簡單給我們的畫筆設置我們自定義的樣式之后取中心為圓心,以我們設定的半徑畫圓,這里設置的是Paint.Style.FILL一個實心的。也可以設置一個空心的??聪挛覀儓?zhí)行這個方法后的效果:

第二方法:
/**
* 刻度和文字
* @param canvas
*/
private void drawScale(Canvas canvas) {
for (int i=0;i<60;i++){
//設置大刻度
if(i==0||i==15||i==30||i==45){
mPaint.setStrokeWidth(6);
mPaint.setColor(mBigScaleColor);
canvas.drawLine(mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2,
mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2+60,mPaint);
String scaleTv=String.valueOf(i==0?12:i/5);
canvas.drawText(scaleTv,mWidth/2-mTextPaint.measureText(scaleTv)/2,
mHeght/2-mWidth/2+mCircleWidth/2+95,mTextPaint);
}else if (i==5||i==10||i==20||i==25||i==35||i==40||i==50||i==55)
//設置中刻度
{
mPaint.setStrokeWidth(4);
mPaint.setColor(mMiddlecaleColor);
canvas.drawLine(mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2,
mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2+40,mPaint);
String scaleTv=String.valueOf(i/5);
canvas.drawText(scaleTv,mWidth/2-mTextPaint.measureText(scaleTv)/2,
mHeght/2-mWidth/2+mCircleWidth/2+75,mTextPaint);
}else
//設置小刻度
{
mPaint.setColor(mSmallScaleColor);
mPaint.setStrokeWidth(2);
canvas.drawLine(mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2,
mWidth/2,mHeght/2-mWidth/2+mCircleWidth+30,mPaint);
}
canvas.rotate(6,mWidth/2,mHeght/2);
}
}這個方法代碼看起來也沒有什么主要是把一個圓分成60份,因為我們鐘表上是有60個刻度,其中設置了4個大刻度分別為0,15,30,45.分別對應的鐘表中12點 3點 6點和9點,如果這個地方你有什么疑惑的吧你可以看看你的手表或者鐘表就明白了,同時里面也設置了8個中等刻度分別為5,10,20,25,35,40,50,55為中刻度,其實對應的就是1,2,4,5,7,8,10,11點。這里主要是自己覺得這樣分明好看而已,如果沒有強迫癥的你可以直接設置都是大刻度就可以了。其他的都為小刻度,根據(jù)自己在attr設置的顏色和尺寸分別設置畫筆paint來繪制就可以了。看下我們的效果變成了這樣子:

第三個方法就是繪制指針:
/**
* 繪制指針
* @param canvas
*/
private void drawPointer(Canvas canvas) {
Calendar mCalendar=Calendar.getInstance();
//獲取當前小時數(shù)
int hours = mCalendar.get(Calendar.HOUR);
//獲取當前分鐘數(shù)
int minutes = mCalendar.get(Calendar.MINUTE);
//獲取當前秒數(shù)
int seconds=mCalendar.get(Calendar.SECOND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
//繪制時針
canvas.save();
mPaint.setColor(mHourHandColor);
mPaint.setStrokeWidth(mHourHandWidth);
//這里計算時針需要旋轉的角度 實現(xiàn)原理是計算出一共多少分鐘除以60計算出真實的小時數(shù)(帶有小數(shù),為了更加準確計算度數(shù)),已知12小時是360度,現(xiàn)在求出了實際小時數(shù)比例求出角度
Float hoursAngle = (hours * 60 + minutes) / 60f / 12f * 360;
canvas.rotate(hoursAngle, mWidth / 2, mHeght / 2);
canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.5f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
canvas.restore();
//繪制分針
canvas.save();
mPaint.setColor(mMinuteHandColor);
mPaint.setStrokeWidth(mMinuteHandWidth);
//這里計算分針需要旋轉的角度 60分鐘360度,求出實際分鐘數(shù)所占的度數(shù)
Float minutesAngle = (minutes*60+seconds) / 60f/ 60f * 360;
canvas.rotate(minutesAngle, mWidth / 2, mHeght / 2);
canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.7f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
canvas.restore();
//繪制中間的圓圈
canvas.save();
mPaint.setColor(mSecondHandColor);
mPaint.setStrokeWidth(mSecondHandWidth);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mWidth/2,mHeght/2,20,mPaint);
canvas.restore();
//繪制秒針
canvas.save();
mPaint.setColor(mSecondHandColor);
mPaint.setStrokeWidth(mSecondHandWidth);
mPaint.setStyle(Paint.Style.STROKE);
//這里計算秒針需要旋轉的角度 60秒360度,求出實際秒數(shù)所占的度數(shù)
Float secondAngle = seconds/60f*360;
canvas.rotate(secondAngle, mWidth / 2, mHeght / 2);
canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.8f, mWidth / 2, mHeght / 2 + mWidth/2f*0.2f, mPaint);
canvas.restore();
}其實這個方法我注釋已經(jīng)寫的很詳細了,首先我們需要獲取到當前的時間,這個大家都是經(jīng)常寫的沒啥問題。主要就是如何設定時分秒指針的位置才是關鍵。這里我使用一個很巧妙的方法,讓繪制變得簡單了些。
先看下繪制時針:
//繪制時針
canvas.save();
mPaint.setColor(mHourHandColor);
mPaint.setStrokeWidth(mHourHandWidth);
//這里計算時針需要旋轉的角度 實現(xiàn)原理是計算出一共多少分鐘除以60計算出真實的小時數(shù)(帶有小數(shù),為了更加準確計算度數(shù)),已知12小時是360度,現(xiàn)在求出了實際小時數(shù)比例求出角度
Float hoursAngle = (hours * 60 + minutes) / 60f / 12f * 360;
canvas.rotate(hoursAngle, mWidth / 2, mHeght / 2);
canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.5f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
canvas.restore();前面三行代碼就不詳細說了,Canvas.save方法作用就是將之前所有已經(jīng)繪制的圖片保存起來。為后續(xù)操作在新的圖層上操作。和photoshop有點一個意思。大家都知道當我們獲取到當前小時數(shù)了以后我們就應該直接把時針指到對應的時數(shù)上嗎?肯定不是吧,比如是3點半時針是指到3與4之間的位置對吧。所以我們這里需要獲取到當前的分鐘數(shù)再加上小時數(shù)才是真實的當前小時數(shù)(這里其實秒針也需要計算的,但是這里忽略不計了,如果你比我還強迫癥的話可以加上)。當我們知道當前實際的小時數(shù)的時候,就很簡單了,因為我們知道一圈360度平均分了12小時,這個別告訴我不知道啊,要是這個常識都不知道,你去面壁吧,所以只要360/12*真實的小時數(shù)就是需要旋轉的角度。這么想是不是很簡單了。計算出角度以后先吧canvas.rotate旋轉這個角度在繪制一個直線就ok了,哈哈哈,是不是so esey,其他分針和秒針一樣的道理,這里就不多說了,看代碼直接能看懂的。
//繪制分針
canvas.save();
mPaint.setColor(mMinuteHandColor);
mPaint.setStrokeWidth(mMinuteHandWidth);
//這里計算分針需要旋轉的角度 60分鐘360度,求出實際分鐘數(shù)所占的度數(shù)
Float minutesAngle = (minutes*60+seconds) / 60f/ 60f * 360;
canvas.rotate(minutesAngle, mWidth / 2, mHeght / 2);
canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.7f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
canvas.restore();
//繪制中間的圓圈
canvas.save();
mPaint.setColor(mSecondHandColor);
mPaint.setStrokeWidth(mSecondHandWidth);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mWidth/2,mHeght/2,20,mPaint);
canvas.restore();
//繪制秒針
canvas.save();
mPaint.setColor(mSecondHandColor);
mPaint.setStrokeWidth(mSecondHandWidth);
mPaint.setStyle(Paint.Style.STROKE);
//這里計算秒針需要旋轉的角度 60秒360度,求出實際秒數(shù)所占的度數(shù)
Float secondAngle = seconds/60f*360;
canvas.rotate(secondAngle, mWidth / 2, mHeght / 2);
canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.8f, mWidth / 2, mHeght / 2 + mWidth/2f*0.2f, mPaint);
canvas.restore();其中有個繪制中間的圓圈是我的強迫癥所致,覺得中間加個圓圈好看點。執(zhí)行這個方法后我們的效果就成這樣了:

哈哈下面就剩下最后一步了,就是讓指針動起來,沒錯就是動起來,其實大家也在意了我們繪制指針的時候是在方法里直接獲取了當前時間設置指針的位置的,所以說只要我們搞個定時器一秒刷新下ui就大功告成了,這里就搞個hander發(fā)個空消息。
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case START_CLOCK:
//更新時分秒
invalidate();
//每隔1秒更新一次
handler.sendEmptyMessageDelayed(START_CLOCK,1000);
break;
}
}
};就成了下面的效果了:

是不是很簡單呢?附上github:https://github.com/dalong982242260/CustomViewStudy
到此這篇關于Android自定義一個屬于自己的時間鐘表的文章就介紹到這了,更多相關Android時間鐘表內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MUI進行APP混合開發(fā)實現(xiàn)下拉刷新和上拉加載
給大家分析一下在用MUI進行APP混合開發(fā)的時候,如何用代碼實現(xiàn)下拉刷新和上拉加載這個普遍應用的功能。2017-11-11
Android AutoCompleteTextView連接數(shù)據(jù)庫自動提示的方法(附demo源碼下載)
這篇文章主要介紹了Android AutoCompleteTextView連接數(shù)據(jù)庫自動提示的方法,結合實例形式分析了AutoCompleteTextView操作數(shù)據(jù)庫的原理與具體技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2016-02-02
Android Framework Application Framework層簡單介紹
這篇文章主要介紹了 Android Framework Application Framework層簡單介紹的相關資料,需要的朋友可以參考下2016-11-11
android Textview文字監(jiān)控(Textview使用方法)
以手機號充值為例,當用戶輸入最后一位數(shù)時候,進行匯率的變換,本文就實現(xiàn)類似這樣的功能2013-11-11
Android實現(xiàn)TextView中文字鏈接的4種方式介紹及代碼
Android實現(xiàn)TextView中文字鏈接的方式有很多種;總結起來大概有4種:用Spannable或實現(xiàn)它的類,如SpannableString來格式,部分字符串等等,感興趣的你可以參考下2013-02-02
Android SQLite數(shù)據(jù)庫連接實現(xiàn)登錄功能
這篇文章主要為大家詳細介紹了Android SQLite數(shù)據(jù)庫連接實現(xiàn)登錄功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-10-10

