android自訂View之鐘錶誕生記

來源:互聯網
上載者:User

標籤:

很多筒子覺得自訂View是高手的象徵,其實不然。大家覺得自訂View難很多情況下可能是因為自訂View涉及到了太多的類和API,把人搞得暈乎乎的,那麼今天我們就從最簡單的繪圖API開始,帶大家來一步一步深入自訂View的世界。

先來看看我們今天要實現的一個:

整個效果很簡單,就是在螢幕上顯示一個鐘錶,該鐘錶可以自動走動。

OK,那就開始動工吧。

1.準備工作

首先,要實現這個時鐘,我得繼承自View來自己繪製時鐘,因為這種效果沒有辦法繼承已有控制項去完善功能。然後我們來看看我們這裡需要哪些變數?在這篇部落格中我暫時不打算介紹自訂屬性以及View的測量,這裡我只想介紹繪圖API,所以View的大小以及鐘錶錶針的顏色等我都暫時先給一個固定的值。OK,那麼我們需要的變數主要就是下面幾個:

/**     * 繪製錶盤的畫筆     */    private Paint circlePaint;    /**     * 繪製錶盤數字     */    private Paint numPaint;    /**     * 繪製表心     */    private Paint dotPaint;    /**     * 時針     */    private Paint hourPaint;    /**     * 分針     */    private Paint minutePaint;    /**     * 秒針     */    private Paint secondPaint;    /**     * View寬度,預設256dp     */    private int width;    /**     * View高度,預設256dp     */    private int height;    /**     * 日曆類,用來擷取目前時間     */    private Calendar calendar;    /**     * 當前時針顏色     */    private int hourColor;    /**     * 當前分針顏色     */    private int minuteColor;    /**     * 當前秒針顏色     */    private int secondColor;    /**     * 時針寬度     */    private int hourWidth;    /**     * 分針寬度     */    private int minuteWidth;    /**     * 秒針寬度     */    private int secondWidth;

一共就是這麼多個變數。

2.關於構造方法

大家看到,當我繼承View之後,系統要求我實現它的構造方法,構造方法主要有四個,如下:

1.

public ClockView(Context context)

該構造方法是當我在Java代碼中new一個View的時候調用的。

2.

public ClockView(Context context, AttributeSet attrs)

該構造方法是當我在布局檔案中添加一個View時調用的。

3.

public ClockView(Context context, AttributeSet attrs, int defStyleAttr)

很多筒子看到第三個參數defStyleAttr之後,誤以為如果我在布局檔案中寫了style就會調用該構造方法,其實不然,這個構造方法系統並不會自己去調用(大家有興趣可以自己寫一個style,然後在這個方法中列印日誌,看看該方法究竟會不會調用),要由我們自己顯式調用(可以在第二個構造方法中調用)。那麼這裡的defStyleAttr究竟是什麼意思呢?正如這個參數的字面意思,它是我們為自訂的View指定的一個預設樣式。(後面部落格我們再來詳細說一下這個方法)。

另外,還有一個高版本使用的構造方法,我們這裡暫不做介紹。

一般情況下,我們需要在構造方法中完成一些初始化的操作,比如讀取在XML檔案中定義的屬性,或者初始化畫筆等等,因為我們的控制項既有可能是通過Java代碼執行個體化的,也有可能是在布局檔案中通過xml添加的。如前所述,如果我們在Java代碼中初始化控制項,那麼將調用第一個構造方法,如果我們在xml布局檔案中添加控制項,那麼將調用第二個構造方法。這時問題來了,那我們的初始化控制項的方法應該寫在那個構造方法中呢?你可以按下面這種方式來寫:

    public ClockView(Context context) {        super(context);        initView();    }    public ClockView(Context context, AttributeSet attrs) {        super(context,attrs);        initView();    }

在兩個構造方法中分別調用初始化的方法,這種方式沒有問題,但是顯得你的思路沒有條理,參考Android提供的其他控制項的源碼,我建議在初始化控制項時按照下面這種方式來寫:

    //在代碼中建立控制項時調用    public ClockView(Context context) {        this(context, null);    }    //在布局檔案中建立View時調用    public ClockView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView();    }

雖然結果都一樣,但是下面這種寫法顯得你思路很清晰。

3.初始化控制項

我們在準備工作中定義了許多變數,包括鐘錶的顏色,指標的顏色等等許多變數,那麼接下來我們需要在initView這個方法中來初始化這些變數,以供下一步使用,OK,我們來看一看初始化代碼:

    private void initView() {        //擷取目前時間的執行個體        calendar = Calendar.getInstance();        //時鐘預設寬高        width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 256, getResources().getDisplayMetrics());        height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 256, getResources().getDisplayMetrics());        //初始化錶針的顏色        hourColor = Color.RED;        minuteColor = Color.GREEN;        secondColor = Color.BLUE;        //初始化錶針的寬度        hourWidth = 8;        minuteWidth = 5;        secondWidth = 2;        //初始化各種畫筆        circlePaint = new Paint();        //去鋸齒        circlePaint.setAntiAlias(true);        //設定畫筆顏色        circlePaint.setColor(Color.GREEN);        //設定畫筆style為描邊        circlePaint.setStyle(Paint.Style.STROKE);        //設定描邊的寬度        circlePaint.setStrokeWidth(6);        dotPaint = new Paint();        dotPaint.setAntiAlias(true);        dotPaint.setColor(Color.RED);        dotPaint.setStyle(Paint.Style.FILL);        numPaint = new Paint();        numPaint.setColor(Color.RED);        numPaint.setAntiAlias(true);        //文本對齊        numPaint.setTextAlign(Paint.Align.CENTER);        hourPaint = new Paint();        hourPaint.setColor(hourColor);        hourPaint.setStyle(Paint.Style.FILL);        hourPaint.setStrokeWidth(hourWidth);        minutePaint = new Paint();        minutePaint.setColor(minuteColor);        minutePaint.setStyle(Paint.Style.FILL);        minutePaint.setStrokeWidth(minuteWidth);        secondPaint = new Paint();        secondPaint.setColor(secondColor);        secondPaint.setStyle(Paint.Style.FILL);        secondPaint.setStrokeWidth(secondWidth);    }

首先是獲得一個目前時間的執行個體,因為我需要根據手機上的時間來顯示鐘錶的時間,其次就是對錶針的各種屬性和畫筆進行初始化,這裡的東西都很簡單,我就不再一一細說,大家看代碼注釋,相信都能看得懂。

4.繪製鐘錶

上面所有的工作做完之後,接下來就是繪製鐘錶了,繪製工作我們放在了onDraw方法中執行,在自訂控制項時,如果該控制項是我們繼承自View來實現的,那麼基本上這個控制項就是需要我們自己來繪製了。

OK,那我們來看看鐘錶的繪製吧。

鐘錶不算複雜,但是我們也需要一步一步來:

首先是繪製錶盤,這個最簡單:

        //1.圓心X軸座標,2.圓心Y軸座標,3.半徑,4.畫筆        int radius = width / 2 - 10;        //畫錶盤        canvas.drawCircle(width / 2, height / 2, radius, circlePaint);

radius表示錶盤的半徑,通過drawCircle繪製一個圓環,四個參數分別是圓環的中心點座標,圓環的半徑以及繪製圓環的畫筆。

圓環畫好之後,那麼接下來就是繪製表心了,也就是錶盤正中心那個紅色的圓心。

canvas.drawCircle(width / 2, height / 2, 15, dotPaint);

很簡單吧。

OK,兩個最簡單的東東畫完之後,那麼接下來就是繪製錶盤的時幅了,時幅的繪製除了數字之外,還有一個綠色的短線,我們一共要畫十二個這種東西,那麼這個要怎麼繪製呢?思路有很多,你可以按照高中的數學知識,計算出每一個數位座標以及每一個短線起始位置和結束位置的座標,然後繪製出來,毫無疑問,這種方式的計算量太大,那我們這裡採取一個簡單的方式:我每次只在十二點的那個位置上進行繪製,如果需要繪製一點,那麼我把畫布逆時針旋轉30度到十二點的位置,然後畫上1和一個短線之後再將畫布順時針旋轉30度,如果是繪製2點,那麼我把畫布逆時針旋轉60度到十二點的位置,然後繪製上2和一個短線,繪製完成之後再將畫布順時針旋轉60度,思路就是這樣,下面我們來看看代碼:

        for (int i = 1; i < 13; i++) {            //在旋轉之前儲存畫布狀態            canvas.save();            canvas.rotate(i * 30, width / 2, height / 2);            //1.2表示起點座標,3.4表示終點座標,5.畫筆            canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 10, circlePaint);            //畫錶盤數字1.要繪製的文本,2.文本x軸座標,3.文本基準,4.文本畫筆            canvas.drawText(i + "", width / 2, height / 2 - radius + 22, numPaint);            //恢複畫布狀態            canvas.restore();        }

我用一個迴圈來繪製這十二個刻度,在每次旋轉畫布之前,我都通過一個canvas.save()方法來儲存畫布當前的狀態,儲存之後再對畫布進行旋轉操作,旋轉完成之後就是畫線畫數字,這些都很簡單,在繪製完成之後,我需要調用canvas的restore()方法,該方法可以讓畫布恢複到旋轉之前的角度。一般情況下,canvas.save()方法和canvas.restore()方法都是成對出現的,這一點大家要注意。

OK,這些東西都繪製完成之後,接下來就該繪製錶針了,錶針的繪製思路和上面一樣,也是先旋轉錶盤,然後繪製錶針,繪製完成之後,再把錶盤旋轉回之前的狀態。這裡我就不再詳細說明了,大家看代碼:

        //獲得當前小時        int hour = calendar.get(Calendar.HOUR);        canvas.save();        //旋轉螢幕        canvas.rotate(hour * 30, width / 2, height / 2);        //畫時針        canvas.drawLine(width / 2, height / 2 + 20, width / 2, height / 2 - 90, hourPaint);        canvas.restore();        int minute = calendar.get(Calendar.MINUTE);        canvas.save();        canvas.rotate(minute * 6, width / 2, height / 2);        canvas.drawLine(width / 2, height / 2 + 30, width / 2, height / 2 - 110, minutePaint);        canvas.restore();        int second = calendar.get(Calendar.SECOND);        canvas.save();        canvas.rotate(second * 6, width / 2, height / 2);        canvas.drawLine(width / 2, height / 2 + 40, width / 2, height / 2 - 130, secondPaint);        canvas.restore();

OK,所有的事情做完之後,我們可以在布局檔案中添加如下代碼,來查看我們的工作做得怎麼樣:

    <org.mobiletrain.clockview.ClockView        android:layout_width="match_parent"        android:layout_height="match_parent"/>

添加完成之後,運行,不出意外的話,你的App上應該已經顯示了一個時鐘了,但是這個時鐘是靜止的,那麼我們該怎麼讓時鐘動起來呢?其實很簡答,我們只需要每隔一秒重新擷取calendar執行個體,然後重繪鐘錶即可,所以,在onDraw方法的開始和結束,我們還要分別添加如下兩行代碼:

1.開始處添加:

calendar = Calendar.getInstance();

這行代碼用來擷取最新的時間的執行個體

2.結束處添加:

postInvalidateDelayed(1000);

這行代碼用來重繪鐘錶,不過重繪是在1秒之後。

OK,至此,我們的自訂鐘錶就完成了,完整的代碼應該是這個樣子:

/** * Created by wangsong on 2016/3/29. */public class ClockView extends View {    /**     * 繪製錶盤的畫筆     */    private Paint circlePaint;    /**     * 繪製錶盤數字     */    private Paint numPaint;    /**     * 繪製表心     */    private Paint dotPaint;    /**     * 時針     */    private Paint hourPaint;    /**     * 分針     */    private Paint minutePaint;    /**     * 秒針     */    private Paint secondPaint;    /**     * View寬度,預設256dp     */    private int width;    /**     * View高度,預設256dp     */    private int height;    /**     * 日曆類,用來擷取目前時間     */    private Calendar calendar;    /**     * 當前時針顏色     */    private int hourColor;    /**     * 當前分針顏色     */    private int minuteColor;    /**     * 當前秒針顏色     */    private int secondColor;    /**     * 時針寬度     */    private int hourWidth;    /**     * 分針寬度     */    private int minuteWidth;    /**     * 秒針寬度     */    private int secondWidth;    //在代碼中建立控制項時調用    public ClockView(Context context) {        this(context, null);    }    //在布局檔案中建立View時調用    public ClockView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView();    }    private void initView() {        //擷取目前時間的執行個體        calendar = Calendar.getInstance();        //時鐘預設寬高        width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 256, getResources().getDisplayMetrics());        height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 256, getResources().getDisplayMetrics());        //初始化錶針的顏色        hourColor = Color.RED;        minuteColor = Color.GREEN;        secondColor = Color.BLUE;        //初始化錶針的寬度        hourWidth = 8;        minuteWidth = 5;        secondWidth = 2;        //初始化各種畫筆        circlePaint = new Paint();        //去鋸齒        circlePaint.setAntiAlias(true);        //設定畫筆顏色        circlePaint.setColor(Color.GREEN);        //設定畫筆style為描邊        circlePaint.setStyle(Paint.Style.STROKE);        //設定描邊的寬度        circlePaint.setStrokeWidth(6);        dotPaint = new Paint();        dotPaint.setAntiAlias(true);        dotPaint.setColor(Color.RED);        dotPaint.setStyle(Paint.Style.FILL);        numPaint = new Paint();        numPaint.setColor(Color.RED);        numPaint.setAntiAlias(true);        //文本對齊        numPaint.setTextAlign(Paint.Align.CENTER);        hourPaint = new Paint();        hourPaint.setColor(hourColor);        hourPaint.setStyle(Paint.Style.FILL);        hourPaint.setStrokeWidth(hourWidth);        minutePaint = new Paint();        minutePaint.setColor(minuteColor);        minutePaint.setStyle(Paint.Style.FILL);        minutePaint.setStrokeWidth(minuteWidth);        secondPaint = new Paint();        secondPaint.setColor(secondColor);        secondPaint.setStyle(Paint.Style.FILL);        secondPaint.setStrokeWidth(secondWidth);    }    //繪製View    @Override    protected void onDraw(Canvas canvas) {        calendar = Calendar.getInstance();        //1.圓心X軸座標,2.圓心Y軸座標,3.半徑,4.畫筆        int radius = width / 2 - 10;        //畫錶盤        canvas.drawCircle(width / 2, height / 2, radius, circlePaint);        canvas.drawCircle(width / 2, height / 2, 15, dotPaint);        for (int i = 1; i < 13; i++) {            //在旋轉之前儲存畫布狀態            canvas.save();            canvas.rotate(i * 30, width / 2, height / 2);            //1.2表示起點座標,3.4表示終點座標,5.畫筆            canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 10, circlePaint);            //畫錶盤數字1.要繪製的文本,2.文本x軸座標,3.文本基準,4.文本畫筆            canvas.drawText(i + "", width / 2, height / 2 - radius + 22, numPaint);            //恢複畫布狀態            canvas.restore();        }        //獲得當前小時        int hour = calendar.get(Calendar.HOUR);        canvas.save();        //旋轉螢幕        canvas.rotate(hour * 30, width / 2, height / 2);        //畫時針        canvas.drawLine(width / 2, height / 2 + 20, width / 2, height / 2 - 90, hourPaint);        canvas.restore();        int minute = calendar.get(Calendar.MINUTE);        canvas.save();        canvas.rotate(minute * 6, width / 2, height / 2);        canvas.drawLine(width / 2, height / 2 + 30, width / 2, height / 2 - 110, minutePaint);        canvas.restore();        int second = calendar.get(Calendar.SECOND);        canvas.save();        canvas.rotate(second * 6, width / 2, height / 2);        canvas.drawLine(width / 2, height / 2 + 40, width / 2, height / 2 - 130, secondPaint);        canvas.restore();        //每隔1秒重繪View,重繪會調用onDraw()方法        postInvalidateDelayed(1000);    }}

以上。

android自訂View之鐘錶誕生記

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.