Android打造屬于自己的時(shí)間鐘表
1、概述
本文主要講解的是如何自定義一個(gè)時(shí)間鐘表,通過(guò)簡(jiǎn)單的練習(xí)可以簡(jiǎn)單學(xué)習(xí)Android當(dāng)中自定義view的一些常用繪圖技巧,優(yōu)化android繪圖操作。言歸正傳,首先看下我們需要實(shí)現(xiàn)的效果:
當(dāng)我們看到這個(gè)效果的時(shí)候腦子里應(yīng)該有一定的思路了,我們應(yīng)該把它分解成以下幾個(gè)步驟:
1、儀表盤(pán)(圓)
2、刻度線(xiàn)(長(zhǎng) 中 短)
3、刻度值(1-12)
4、指針(時(shí) 分 秒)
5、移動(dòng)指針,計(jì)算指針位置
現(xiàn)在我們已經(jīng)很清楚自己的思路了,那么我們一個(gè)一個(gè)來(lái)。
第一步:1、自定義View的屬性,首先在res/values/下建立一個(gè)attrs.xml,在里面定義我們的屬性和聲明我們的整個(gè)樣式。
<span style="font-size:14px;"> <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></span>
我們定義了鐘表的半徑,背景顏色 ,刻度值(大,中,?。┑念伾爸羔槪〞r(shí)分秒)的顏色和寬度。
然后自定義一個(gè)class類(lèi) 為ClockView,在MainActivity的布局中引用:
<span style="font-size:14px;"> <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" /></span>
2、在自定義View的構(gòu)造方法中,獲得我們的自定義的樣式
<span style="font-size:14px;"> //文字畫(huà)筆對(duì)象 private Paint mTextPaint; //圓,指針,刻度畫(huà)筆 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; //時(shí)針顏色 public int mHourHandColor; //分針顏色 public int mMinuteHandColor; //秒針顏色 public int mSecondHandColor; //時(shí)針寬度 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); } </span>
3、我們重寫(xiě)onDraw,onMesure調(diào)用系統(tǒng)提供的:
onMeure方法
<span style="font-size:14px;"> @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; }</span>
onDraw方法
<span style="font-size:14px;"> @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //設(shè)置寬高、半徑 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); } </span>
其中最核心的代碼就是這三個(gè)方法:
<span style="font-size:14px;"> //首先繪制圓 drawCircle(canvas); //繪制刻度 drawScale(canvas); //繪制指針 drawPointer(canvas);</span>
首先講第一個(gè)方法:
<span style="font-size:14px;"> /** * 畫(huà)圓 * @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); }</span>
這個(gè)方法其實(shí)很簡(jiǎn)單給我們的畫(huà)筆設(shè)置我們自定義的樣式之后取中心為圓心,以我們?cè)O(shè)定的半徑畫(huà)圓,這里設(shè)置的是Paint.Style.FILL一個(gè)實(shí)心的。也可以設(shè)置一個(gè)空心的??聪挛覀儓?zhí)行這個(gè)方法后的效果:
第二方法:
<span style="font-size:14px;"> /** * 刻度和文字 * @param canvas */ private void drawScale(Canvas canvas) { for (int i=0;i<60;i++){ //設(shè)置大刻度 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) //設(shè)置中刻度 { 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 //設(shè)置小刻度 { 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); } }</span>
這個(gè)方法代碼看起來(lái)也沒(méi)有什么主要是把一個(gè)圓分成60份,因?yàn)槲覀冪姳砩鲜怯?0個(gè)刻度,其中設(shè)置了4個(gè)大刻度分別為0,15,30,45.分別對(duì)應(yīng)的鐘表中12點(diǎn) 3點(diǎn) 6點(diǎn)和9點(diǎn),如果這個(gè)地方你有什么疑惑的吧你可以看看你的手表或者鐘表就明白了,同時(shí)里面也設(shè)置了8個(gè)中等刻度分別為5,10,20,25,35,40,50,55為中刻度,其實(shí)對(duì)應(yīng)的就是1,2,4,5,7,8,10,11點(diǎn)。這里主要是自己覺(jué)得這樣分明好看而已,如果沒(méi)有強(qiáng)迫癥的你可以直接設(shè)置都是大刻度就可以了。其他的都為小刻度,根據(jù)自己在attr設(shè)置的顏色和尺寸分別設(shè)置畫(huà)筆paint來(lái)繪制就可以了??聪挛覀兊男Ч兂闪诉@樣子:
第三個(gè)方法就是繪制指針:
<span style="font-size:14px;"> /** * 繪制指針 * @param canvas */ private void drawPointer(Canvas canvas) { Calendar mCalendar=Calendar.getInstance(); //獲取當(dāng)前小時(shí)數(shù) int hours = mCalendar.get(Calendar.HOUR); //獲取當(dāng)前分鐘數(shù) int minutes = mCalendar.get(Calendar.MINUTE); //獲取當(dāng)前秒數(shù) int seconds=mCalendar.get(Calendar.SECOND); mPaint.setStrokeCap(Paint.Cap.ROUND); //繪制時(shí)針 canvas.save(); mPaint.setColor(mHourHandColor); mPaint.setStrokeWidth(mHourHandWidth); //這里計(jì)算時(shí)針需要旋轉(zhuǎn)的角度 實(shí)現(xiàn)原理是計(jì)算出一共多少分鐘除以60計(jì)算出真實(shí)的小時(shí)數(shù)(帶有小數(shù),為了更加準(zhǔn)確計(jì)算度數(shù)),已知12小時(shí)是360度,現(xiàn)在求出了實(shí)際小時(shí)數(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); //這里計(jì)算分針需要旋轉(zhuǎn)的角度 60分鐘360度,求出實(shí)際分鐘數(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); //這里計(jì)算秒針需要旋轉(zhuǎn)的角度 60秒360度,求出實(shí)際秒數(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(); }</span>
其實(shí)這個(gè)方法我注釋已經(jīng)寫(xiě)的很詳細(xì)了,首先我們需要獲取到當(dāng)前的時(shí)間,這個(gè)大家都是經(jīng)常寫(xiě)的沒(méi)啥問(wèn)題。主要就是如何設(shè)定時(shí)分秒指針的位置才是關(guān)鍵。這里我使用一個(gè)很巧妙的方法,讓繪制變得簡(jiǎn)單了些。
先看下繪制時(shí)針:
<span style="font-size:14px;"> //繪制時(shí)針 canvas.save(); mPaint.setColor(mHourHandColor); mPaint.setStrokeWidth(mHourHandWidth); //這里計(jì)算時(shí)針需要旋轉(zhuǎn)的角度 實(shí)現(xiàn)原理是計(jì)算出一共多少分鐘除以60計(jì)算出真實(shí)的小時(shí)數(shù)(帶有小數(shù),為了更加準(zhǔn)確計(jì)算度數(shù)),已知12小時(shí)是360度,現(xiàn)在求出了實(shí)際小時(shí)數(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();</span>
前面三行代碼就不詳細(xì)說(shuō)了,Canvas.save方法作用就是將之前所有已經(jīng)繪制的圖片保存起來(lái)。為后續(xù)操作在新的圖層上操作。和photoshop有點(diǎn)一個(gè)意思。大家都知道當(dāng)我們獲取到當(dāng)前小時(shí)數(shù)了以后我們就應(yīng)該直接把時(shí)針指到對(duì)應(yīng)的時(shí)數(shù)上嗎?肯定不是吧,比如是3點(diǎn)半時(shí)針是指到3與4之間的位置對(duì)吧。所以我們這里需要獲取到當(dāng)前的分鐘數(shù)再加上小時(shí)數(shù)才是真實(shí)的當(dāng)前小時(shí)數(shù)(這里其實(shí)秒針也需要計(jì)算的,但是這里忽略不計(jì)了,如果你比我還強(qiáng)迫癥的話(huà)可以加上)。當(dāng)我們知道當(dāng)前實(shí)際的小時(shí)數(shù)的時(shí)候,就很簡(jiǎn)單了,因?yàn)槲覀冎酪蝗?60度平均分了12小時(shí),這個(gè)別告訴我不知道啊,要是這個(gè)常識(shí)都不知道,你去面壁吧,所以只要360/12*真實(shí)的小時(shí)數(shù)就是需要旋轉(zhuǎn)的角度。這么想是不是很簡(jiǎn)單了。計(jì)算出角度以后先吧canvas.rotate旋轉(zhuǎn)這個(gè)角度在繪制一個(gè)直線(xiàn)就ok了,哈哈哈,是不是so esey,其他分針和秒針一樣的道理,這里就不多說(shuō)了,看代碼直接能看懂的。
<span style="font-size:14px;"> //繪制分針 canvas.save(); mPaint.setColor(mMinuteHandColor); mPaint.setStrokeWidth(mMinuteHandWidth); //這里計(jì)算分針需要旋轉(zhuǎn)的角度 60分鐘360度,求出實(shí)際分鐘數(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); //這里計(jì)算秒針需要旋轉(zhuǎn)的角度 60秒360度,求出實(shí)際秒數(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();</span>
其中有個(gè)繪制中間的圓圈是我的強(qiáng)迫癥所致,覺(jué)得中間加個(gè)圓圈好看點(diǎn)。執(zhí)行這個(gè)方法后我們的效果就成這樣了:
哈哈下面就剩下最后一步了,就是讓指針動(dòng)起來(lái),沒(méi)錯(cuò)就是動(dòng)起來(lái),其實(shí)大家也在意了我們繪制指針的時(shí)候是在方法里直接獲取了當(dāng)前時(shí)間設(shè)置指針的位置的,所以說(shuō)只要我們搞個(gè)定時(shí)器一秒刷新下ui就大功告成了,這里就搞個(gè)hander發(fā)個(gè)空消息。
private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case START_CLOCK: //更新時(shí)分秒 invalidate(); //每隔1秒更新一次 handler.sendEmptyMessageDelayed(START_CLOCK,1000); break; } } };
就成了下面的效果了:
是不是很簡(jiǎn)單呢?附上github:https://github.com/dalong982242260/CustomViewStudy
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android短信發(fā)送器實(shí)現(xiàn)方法
這篇文章主要介紹了Android短信發(fā)送器實(shí)現(xiàn)方法,以實(shí)例形式較為詳細(xì)的分析了Android短信發(fā)送器從界面布局到功能實(shí)現(xiàn)的完整步驟與相關(guān)技巧,需要的朋友可以參考下2015-09-09Android OpenGLES2.0等腰直角三角形和彩色的三角形(三)
這篇文章主要為大家詳細(xì)介紹了Android OpenGLES2.0等腰直角三角形和彩色的三角形,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android網(wǎng)絡(luò)連接判斷與相關(guān)處理
這篇文章主要為大家詳細(xì)介紹了Android網(wǎng)絡(luò)連接判斷操作,幫助大家判斷WIFI網(wǎng)絡(luò)是否可用,判斷MOBILE網(wǎng)絡(luò)是否可用,感興趣的小伙伴們可以參考一下2016-08-08Android滑動(dòng)動(dòng)態(tài)分頁(yè)實(shí)現(xiàn)方法
這篇文章主要介紹了Android滑動(dòng)動(dòng)態(tài)分頁(yè)實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)滑動(dòng)動(dòng)態(tài)分頁(yè)的操作步驟與核心實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-10-10Android計(jì)時(shí)器控件Chronometer應(yīng)用實(shí)例
這篇文章主要為大家詳細(xì)介紹了Android計(jì)時(shí)器控件Chronometer應(yīng)用實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09RecyclerView實(shí)現(xiàn)橫向滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)橫向滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01