1. 程式人生 > >一個鐘錶帶你進入Android繪圖的世界

一個鐘錶帶你進入Android繪圖的世界

前言

 

顧名思義,就是在Android手機螢幕中繪製我們需要的內容,根據繪製內容的大小(measure),佈局(layout)來講具體內容展示在螢幕中,通過繪製(draw)來實現我們需要的效果.

 

繪圖原理(三部曲)

Measure

measure操作主要用於計算檢視的大小,即檢視的寬度和長度。在view中定義為final型別,要求子類不能修改。measure()函式中又會呼叫下面的函式:

(1)onMeasure(),檢視大小的將在這裡最終確定,也就是說measure只是對onMeasure的一個包裝,子類可以覆寫onMeasure()方法實現自己的計算檢視大小的方式,並通過setMeasuredDimension(width, height)儲存計算結果。

 

Layout

 layout操作用於設定檢視在螢幕中顯示的位置。在view中定義為final型別,要求子類不能修改。layout()函式中有兩個基本操作:

     (1)setFrame(l,t,r,b),l,t,r,b即子檢視在父檢視中的具體位置,該函式用於將這些引數儲存起來;

     (2)onLayout(),在View中這個函式什麼都不會做,提供該函式主要是為viewGroup型別佈局子檢視用的;

 

Draw

draw操作利用前兩部得到的引數,將檢視顯示在螢幕上,到這裡也就完成了整個的檢視繪製工作。子類也不應該修改該方法,因為其內部定義了繪圖的基本操作:

     (1)繪製背景;

     (2)如果要檢視顯示漸變框,這裡會做一些準備工作;

     (3)繪製檢視本身,即呼叫onDraw()函式。在view中onDraw()是個空函式,也就是說具體的檢視都要覆寫該函式來實現自己的顯示(比如TextView在這裡實現了繪製文字的過程)。而對於ViewGroup則不需要實現該函式,因為作為容器是“沒有內容“的,其包含了多個子view,而子View已經實現了自己的繪製方法,因此只需要告訴子view繪製自己就可以了,也就是下面的dispatchDraw()方法;

     (4)繪製子檢視,即dispatchDraw()函式。在view中這是個空函式,具體的檢視不需要實現該方法,它是專門為容器類準備的,也就是容器類必須實現該方法;

     (5)如果需要(應用程式呼叫了setVerticalFadingEdge或者setHorizontalFadingEdge),開始繪製漸變框;

     (6)繪製滾動條;

      從上面可以看出自定義View需要最少覆寫onMeasure()和onDraw()兩個方法。

 

參考:http://blog.csdn.net/xu_fu/article/details/7829721                                     

分類

2D繪圖

基於Android SDK內部自己提供,也是我們學習的主要內容,2D繪圖的api大部分是android.graphics和android.graphics.drawable包中,他們提供了Canvas,ColorFilter,Point和RetcF等,以及一些動畫相關的:AnimationDrawable,BitmapDrawable和TransitionDrawable等.

3D繪圖

用Open GL ES 1.0

 

參考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html              

 

2D繪圖

內容

通過canvas物件可以繪製的[弧線],[Bitmap],[圓],[點],[矩形],[圓角矩形],[文字],[路徑]等圖形,其次還提供了旋轉(rotate),縮放(scale),漸變(translate)和扭曲(skew)等轉換方法.

 

正文

今天就以2D我學習例子,來完成對鐘錶的繪製實現.

 

知識點

1.Paint的創建於使用

2.Canvas的使用

3.Handler使用

4.onMeasure使用

5.Canvas.restore()和Canvas.save的使用

 

效果圖:

 

實現步驟:

1.準備工作,初始化畫筆工具,初始化時間,初始化圓環半徑

2.畫圓環

3.畫刻度

4.移動座標中心到畫布中心

5.畫指標(畫時針,畫分針,畫秒針)

6.畫時間文字

原始碼解析

第一步|準備工作

private Paint mPaintRing;// 圓環畫筆
private Paint mPaintDegree;// 刻度/圓心畫筆
private Paint mPaintText;// 文字畫筆
private float strokeWidthText = 2;// 文字畫筆厚度
private float strokeWidthRing = 4;// 圓環畫筆厚度
private float radius = 0;// 圓環半徑

//用於初始化時間的角度設定
private float hourDegree = 0;// 時針角度
private float minuteDegree = 0;// 分針角度
private float secondDegree = 0;// 秒針角度

private Date mCurrentDate=null;// 使用者設定時間,預設為當前時間
private void initPaint() {
    mCurrentDate=new Date();
    setDate(mCurrentDate);
    // 初始化圓環畫筆
    mPaintRing = new Paint();
    mPaintRing.setColor(Color.RED);
    mPaintRing.setStyle(Paint.Style.STROKE);
    mPaintRing.setStrokeWidth(strokeWidthRing);
    mPaintRing.setAntiAlias(true);
    // 初始化刻度畫筆
    mPaintDegree = new Paint();
    mPaintDegree.setColor(Color.RED);
    mPaintDegree.setStyle(Paint.Style.FILL);
    mPaintDegree.setAntiAlias(true);
    // 初始化文字畫筆
    mPaintText = new Paint();
    mPaintText.setColor(Color.RED);
    mPaintText.setStyle(Paint.Style.FILL);
    mPaintText.setAntiAlias(true);
    mPaintText.setStrokeWidth(strokeWidthText);
}

 

 

第二步|繪圖

 

//初始化半徑,由於圓環的厚度也要佔據一定的寬度,因此需要減除厚度值,這樣才能保證圓環
radius = Math.min(getWidth() / 2, getHeight() / 2) - strokeWidthRing;
// 1.畫圓環
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mPaintRing);
// 2.畫刻度
drawMark(canvas);
// 3.畫圓心
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 5, mPaintDegree);
// 4.移動座標中心到畫布中心
canvas.save();
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.save();
// 5.畫時針
drawHourMark(hourDegree, canvas);
// 6.畫分針
drawMinuteMark(minuteDegree, canvas);
// 7.畫秒針
drawSecondMark(secondDegree, canvas);
// 8.畫時間文字
mPaintText.setTextSize(30);
mPaintText.setTextAlign(Paint.Align.CENTER);
canvas.drawText(XDate.fmtDate(mCurrentDate,"HH:mm:ss"), 0, -20, mPaintText);

 

 

 

完整程式碼

 

/**
 * 繪圖基礎-時鐘<br>
 * 部落格:<a href="http://blog.csdn.net/qq243223991">安前鬆部落格</a>
 */
public class ClockView extends View {

    private Paint mPaintRing;// 圓環畫筆
    private Paint mPaintDegree;// 刻度/圓心畫筆
    private Paint mPaintText;// 文字畫筆
    private float strokeWidthText = 2;// 文字畫筆厚度
    private float strokeWidthRing = 4;// 圓環畫筆厚度
    private float radius = 0;// 圓環半徑

    //用於初始化時間的角度設定
    private float hourDegree = 0;// 時針角度
    private float minuteDegree = 0;// 分針角度
    private float secondDegree = 0;// 秒針角度

    private Date mCurrentDate=null;// 使用者設定時間,預設為當前時間


    private  Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==0){
                long times=mCurrentDate.getTime();
                mCurrentDate.setTime(times+1000);
               setDate(mCurrentDate);
                handler.sendEmptyMessageDelayed(0,1000);
            }
        }
    };

    public ClockView(Context context) {
        super(context);
        initPaint();
    }

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


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int min = Math.min(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));// 防止圓形變形,寬高度必須相等
        setMeasuredDimension(min, min);// 重新設定寬高
    }

    /**
     * 根據預設寬度測量寬度
     *
     * @param measureSpec
     * @return
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    /**
     * 根據預設高度測量高度
     *
     * @param measureSpec
     * @return
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    private void initPaint() {
        mCurrentDate=new Date();
        setDate(mCurrentDate);
        // 初始化圓環畫筆
        mPaintRing = new Paint();
        mPaintRing.setColor(Color.RED);
        mPaintRing.setStyle(Paint.Style.STROKE);
        mPaintRing.setStrokeWidth(strokeWidthRing);
        mPaintRing.setAntiAlias(true);
        // 初始化刻度畫筆
        mPaintDegree = new Paint();
        mPaintDegree.setColor(Color.RED);
        mPaintDegree.setStyle(Paint.Style.FILL);
        mPaintDegree.setAntiAlias(true);
        // 初始化文字畫筆
        mPaintText = new Paint();
        mPaintText.setColor(Color.RED);
        mPaintText.setStyle(Paint.Style.FILL);
        mPaintText.setAntiAlias(true);
        mPaintText.setStrokeWidth(strokeWidthText);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //初始化半徑,由於圓環的厚度也要佔據一定的寬度,因此需要減除厚度值,這樣才能保證圓環
        radius = Math.min(getWidth() / 2, getHeight() / 2) - strokeWidthRing;
        // 1.畫圓環
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mPaintRing);
        // 2.畫刻度
        drawMark(canvas);
        // 3.畫圓心
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, 5, mPaintDegree);
        // 4.移動座標中心到畫布中心
        canvas.save();
        canvas.translate(getWidth() / 2, getHeight() / 2);
        canvas.save();
        // 5.畫時針
        drawHourMark(hourDegree, canvas);
        // 6.畫分針
        drawMinuteMark(minuteDegree, canvas);
        // 7.畫秒針
        drawSecondMark(secondDegree, canvas);
        // 8.畫時間文字
        mPaintText.setTextSize(30);
        mPaintText.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(XDate.fmtDate(mCurrentDate,"HH:mm:ss"), 0, -20, mPaintText);

    }

    /**
     * 畫時針
     *
     * @param degree
     * @param canvas
     */
    private void drawHourMark(float degree, Canvas canvas) {
        canvas.rotate(degree);// 旋轉度數
        mPaintDegree.setStrokeWidth(6);
        canvas.drawLine(0, 0, 0, -radius / 2, mPaintDegree);
        canvas.restore();
        canvas.save();
    }

    /**
     * 畫分針
     *
     * @param degree
     * @param canvas
     */
    private void drawMinuteMark(float degree, Canvas canvas) {
        canvas.rotate(degree);// 旋轉度數
        mPaintDegree.setStrokeWidth(3);
        canvas.drawLine(0, 0, 0, -radius / 2 - 25, mPaintDegree);
        canvas.restore();
        canvas.save();
    }

    /**
     * 畫秒針
     *
     * @param degree
     * @param canvas
     */
    private void drawSecondMark(float degree, Canvas canvas) {
        canvas.rotate(degree);// 旋轉度數
        mPaintDegree.setStrokeWidth(2);
        canvas.drawLine(0, 0, 0, -radius / 2 - 40, mPaintDegree);
        canvas.restore();
        canvas.save();
    }

    /**
     * 畫刻度
     */
    private void drawMark(Canvas canvas) {
        for (int i = 0; i < 60; i++) {
            if (i % 5 == 0) {
                mPaintDegree.setStrokeWidth(strokeWidthRing);
                canvas.drawLine(getWidth() / 2, strokeWidthRing, getWidth() / 2, 40, mPaintDegree);
                mPaintText.setTextAlign(Paint.Align.CENTER);
                mPaintText.setTextSize(20);
                if (i / 5 == 0) {
                    canvas.drawText("12", getWidth() / 2, 60, mPaintText);
                } else {
                    canvas.drawText(i / 5 + "", getWidth() / 2, 60, mPaintText);
                }
            } else {
                mPaintDegree.setStrokeWidth(2);
                canvas.drawLine(getWidth() / 2, strokeWidthRing, getWidth() / 2, 30, mPaintDegree);
            }
            canvas.rotate(6, getWidth() / 2, getHeight() / 2);
        }
    }

    /**
     * 設定時間
     *
     * @param date
     */
    public void setDate(@NonNull Date date) {
        mCurrentDate=date;
        float hourDegree = getHourDegree(getHours(date), getMinutes(date), getSeconds(date));
        float minuteDegree = getMinuteDegree(getMinutes(date), getSeconds(date));
        float secondDegree = getSecondDegree(getSeconds(date));
        rotate(hourDegree, minuteDegree, secondDegree);
    }

    /**
     * 設定時間戳
     *
     * @param timeMills
     */
    public void setTimeMills(long timeMills) {
        Date date = new Date();
        date.setTime(timeMills);
        setDate(date);
    }

    /**
     * 開始時間旋轉
     */
    public void start(){
        handler.sendEmptyMessageDelayed(0,1000);
    }

    /**
     * 旋轉
     */
    private  void rotate(float hourDegree, float minuteDegree, float secondDegree) {
        this.hourDegree = hourDegree;
        this.minuteDegree = minuteDegree;
        this.secondDegree = secondDegree;
        invalidate();
    }


    /**
     * 根據小時獲取角度
     *
     * @param hour
     * @return
     */
    private float getHourDegree(int hour, int minute, int second) {
        float hourDegree = (hour + minute / 60.0f + second / 3600.0f) * 30;
        return hourDegree;
    }

    /**
     * 根據分鐘獲取角度
     *
     * @param minute
     * @return
     */
    private float getMinuteDegree(int minute, int second) {
        float minuteDegree = (minute + second / 60.0f) * 6;
        return minuteDegree;
    }

    /**
     * 根據秒鐘獲取角度
     *
     * @param second
     * @return
     */
    private float getSecondDegree(int second) {
        int secondDegree = second * 6;
        return secondDegree;
    }

    /**
     * 獲取時間資訊
     *
     * @param date
     * @return
     */
    private HashMap<Integer, Integer> getTime(Date date) {
        HashMap<Integer, Integer> time = new HashMap<>();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        time.put(Calendar.HOUR, calendar.get(Calendar.HOUR));//12小時制
        time.put(Calendar.MINUTE, calendar.get(Calendar.MINUTE));
        time.put(Calendar.SECOND, calendar.get(Calendar.SECOND));
        return time;
    }

    /**
     * 獲取當天中小時
     *
     * @param date
     * @return
     */
    private int getHours(Date date) {
        return getTime(date).get(Calendar.HOUR);
    }

    /**
     * 獲取分鐘
     *
     * @param date
     * @return
     */
    private int getMinutes(Date date) {
        return getTime(date).get(Calendar.MINUTE);
    }

    /**
     * 獲取秒鐘
     *
     * @param date
     * @return
     */
    private int getSeconds(Date date) {
        return getTime(date).get(Calendar.SECOND);
    }


}

 

 

 

覺得有用就支援一下,謝謝!