Android畫個(gè)時(shí)鐘玩玩

先看下最終的效果

開始實(shí)現(xiàn)

新建一個(gè)ClockView集成View

    public class ClockView extends View {
    
    }

先重寫onMeasure方法闷旧,這里要先說一下View的測量模式刷允,一共有三種:

  • EXACTLY

    即精確值模式椭豫,當(dāng)我們將控件的layout_width屬性或layout_height屬性指定為具體數(shù)值時(shí)洽损,比如android:layout_width="100dp"蛉幸,或者指定為math_parent屬性時(shí)(占據(jù)父View的大行ち浮)孤页,系統(tǒng)使用的是EXACTLY模式。

  • AT_MOST

    即最大值模式尿赚,當(dāng)控件的layout_width屬性或layout_height屬性指定為wrap_content時(shí)散庶,控件大小一般隨著控件的子控件或內(nèi)容的變化而變化,此時(shí)控件的尺寸只要不超過父控件允許的最大尺寸即可凌净。

  • UNSPECIFIED

    這個(gè)屬性比較奇怪——它不指定其大小測量模式悲龟,View想多大就多大,通常情況下在繪制自定義View時(shí)才會(huì)使用冰寻。

    因?yàn)閂iew的onMeasure方法只支持EXACTLY模式须教,當(dāng)layout_widthlayout_heightwrap_content時(shí),View的大小就顯得很奇怪了斩芭,如下圖轻腺。

所以我們重寫一下onMeasure方法可以指定View width、height的最小值

    /**
     * 當(dāng)布局為wrap_content時(shí)設(shè)置默認(rèn)長寬
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measure(widthMeasureSpec), measure(heightMeasureSpec));
    }

    private int measure(int origin){
        int result = DEFAULT_MIN_WIDTH;
        int specMode = MeasureSpec.getMode(origin);
        int specSize = MeasureSpec.getSize(origin);
        if(specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else{
            if(specMode == MeasureSpec.AT_MOST){
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

下面就是最重要的重寫onDraw方法來繪制表盤划乖、刻度贬养、指針……,大致流程如下

  • 畫表盤琴庵,用drawCircle繪制一個(gè)圓作為表盤, 圓心坐標(biāo)為(getWidth()/2, getHeight()/2)误算,半徑為Math.min(getHeight()/2, getWidth()/2)
//畫外圓
float borderWidth = DEFAULT_BORDER_WIDTH;
Paint paintCircle = new Paint();
paintCircle.setStyle(Paint.Style.STROKE);
paintCircle.setAntiAlias(true);
paintCircle.setStrokeWidth(borderWidth);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, Math.min(getHeight() / 2, getWidth() / 2) - borderWidth / 2, paintCircle);
  • 畫刻度線迷殿,在這里我們可以利用一個(gè)`canvas.rotate'方法就可以不用計(jì)算角度了
//畫刻度線
float degreeLength = 0f;
Paint paintDegree = new Paint();
paintDegree.setAntiAlias(true);
for(int i=0;i<60;i++){
    if(i % 5 == 0){
        paintDegree.setStrokeWidth(6);
        degreeLength = DEFAULT_LONG_DEGREE_LENGTH;
    }else{
        paintDegree.setStrokeWidth(3);
        degreeLength = DEFAULT_SHORT_DEGREE_LENGTH;
    }
    canvas.drawLine(getWidth()/2, Math.abs(getWidth()/2 - getHeight()/2), getWidth()/2, Math.abs(getWidth()/2 - getHeight()/2) + degreeLength, paintDegree);
    canvas.rotate(360/60, getWidth()/2, getHeight()/2);
}
  • 畫刻度上的數(shù)字
//刻度數(shù)字
int degressNumberSize = 30;
canvas.translate(getWidth() / 2, getHeight() / 2);
Paint paintDegreeNumber = new Paint();
paintDegreeNumber.setTextAlign(Paint.Align.CENTER);
paintDegreeNumber.setTextSize(degressNumberSize);
paintDegreeNumber.setFakeBoldText(true);
for(int i=0;i<12;i++){
    float[] temp = calculatePoint((i+1)*30, r - DEFAULT_LONG_DEGREE_LENGTH - degressNumberSize/2 - 15);
    canvas.drawText((i+1)+"", temp[2], temp[3] + degressNumberSize/2-6, paintDegreeNumber);
}

/**
 * 根據(jù)角度和長度計(jì)算線段的起點(diǎn)和終點(diǎn)的坐標(biāo)
 * @param angle
 * @param length
 * @return
 */
private float[] calculatePoint(float angle, float length){
    float[] points = new float[4];
    if(angle <= 90f){
        points[0] = -(float) Math.sin(angle*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[1] = (float) Math.cos(angle*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[2] = (float) Math.sin(angle*Math.PI/180) * length;
        points[3] = -(float) Math.cos(angle*Math.PI/180) * length;
    }else if(angle <= 180f){
        points[0] = -(float) Math.cos((angle-90)*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[1] = -(float) Math.sin((angle-90)*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[2] = (float) Math.cos((angle-90)*Math.PI/180) * length;
        points[3] = (float) Math.sin((angle-90)*Math.PI/180) * length;
    }else if(angle <= 270f){
        points[0] = (float) Math.sin((angle-180)*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[1] = -(float) Math.cos((angle-180)*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[2] = -(float) Math.sin((angle-180)*Math.PI/180) * length;
        points[3] = (float) Math.cos((angle-180)*Math.PI/180) * length;
    }else if(angle <= 360f){
        points[0] = (float) Math.cos((angle-270)*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[1] = (float) Math.sin((angle-270)*Math.PI/180) * DEFAULT_POINT_BACK_LENGTH;
        points[2] = -(float) Math.cos((angle-270)*Math.PI/180) * length;
        points[3] = -(float) Math.sin((angle-270)*Math.PI/180) * length;
    }
    return points;
}
  • 畫指針
//畫指針
Paint paintHour = new Paint();
paintHour.setAntiAlias(true);
paintHour.setStrokeWidth(15);
Paint paintMinute = new Paint();
paintMinute.setAntiAlias(true);
paintMinute.setStrokeWidth(10);
Paint paintSecond = new Paint();
paintSecond.setAntiAlias(true);
paintSecond.setStrokeWidth(5);
Calendar now = Calendar.getInstance();
float[] hourPoints = calculatePoint(now.get(Calendar.HOUR_OF_DAY)%12/12f*360, hourPointerLength);
canvas.drawLine(hourPoints[0], hourPoints[1], hourPoints[2], hourPoints[3], paintHour);
float[] minutePoints = calculatePoint(now.get(Calendar.MINUTE)/60f*360, minutePointerLength);
canvas.drawLine(minutePoints[0], minutePoints[1], minutePoints[2], minutePoints[3], paintMinute);
float[] secondPoints = calculatePoint(now.get(Calendar.SECOND)/60f*360, secondPointerLength);
canvas.drawLine(secondPoints[0], secondPoints[1], secondPoints[2], secondPoints[3], paintSecond);
  • 畫圓心
//畫圓心
Paint paintCenter = new Paint();
paintCenter.setColor(Color.WHITE);
canvas.drawCircle(0, 0, 2, paintCenter);

最后只要啟動(dòng)一個(gè)無限循環(huán)的線程儿礼,每隔1秒針重繪一下View就能讓指針動(dòng)起來了

private Thread timeThread = new Thread() {
    @Override
    public void run() {
        try {
            while(true){
                updateHandler.sendEmptyMessage(0);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

private Handler updateHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        invalidate();
    }
};

全部測試代碼下載地址:

https://github.com/hellsam/clockdemo_android

歡迎留言交流,如有描述不當(dāng)或錯(cuò)誤的地方還請留言告知

本博客網(wǎng)址 hellsam.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庆寺,一起剝皮案震驚了整個(gè)濱河市蚊夫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌懦尝,老刑警劉巖知纷,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壤圃,死亡現(xiàn)場離奇詭異,居然都是意外死亡琅轧,警方通過查閱死者的電腦和手機(jī)埃唯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹰晨,“玉大人墨叛,你說我怎么就攤上這事∧@” “怎么了漠趁?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忍疾。 經(jīng)常有香客問我闯传,道長,這世上最難降的妖魔是什么卤妒? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任甥绿,我火速辦了婚禮,結(jié)果婚禮上则披,老公的妹妹穿的比我還像新娘共缕。我一直安慰自己,他們只是感情好士复,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布图谷。 她就那樣靜靜地躺著,像睡著了一般阱洪。 火紅的嫁衣襯著肌膚如雪便贵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天冗荸,我揣著相機(jī)與錄音承璃,去河邊找鬼。 笑死蚌本,一個(gè)胖子當(dāng)著我的面吹牛盔粹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播魂毁,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼玻佩,長吁一口氣:“原來是場噩夢啊……” “哼出嘹!你這毒婦竟也來了席楚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤税稼,失蹤者是張志新(化名)和其女友劉穎烦秩,沒想到半個(gè)月后垮斯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡只祠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年兜蠕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抛寝。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡熊杨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盗舰,到底是詐尸還是另有隱情晶府,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布钻趋,位于F島的核電站川陆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蛮位。R本人自食惡果不足惜较沪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望失仁。 院中可真熱鬧尸曼,春花似錦、人聲如沸萄焦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楷扬。三九已至解幽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烘苹,已是汗流浹背躲株。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留镣衡,地道東北人霜定。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像廊鸥,于是被迫代替她去往敵國和親望浩。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容