自定義View之點 線 面以及繪製一個綜合應用時鐘

            本來前天應該整理的了,但是臨時有任務給耽擱了,不過今天回過頭來發現好多注意點都忘了,還是記錄一下吧。

            canvas繪製點,繪製線,繪製各種圖形,其實這個可以說是最簡單的了,也沒有什麼具體可以講的,方法的參數含義基本上都可以理解,這裏只說一點:圓弧或者扇形是的繪製如何確定。圓弧的繪製是這樣的,他是將一個矩形(之所以不是正方形是因爲也可以是橢圓的一部分)的內切圓的一部分截取出來的。那問題就是矩形如何確定呢?這裏舉個例子:例如所需扇形的半徑爲100,圓心座標爲(200,200),那麼所需矩形的左上角座標就爲(100,100),右下角座標爲(300,300),我想規律已經出來了,沒錯就是這麼算的,矩形的左上角座標x=centerX-r,y=centerY-r ,矩形右下角座標爲 x=centerX+r   y=centerY+r 。這個自己敲一下代碼就看出來了。。。。其他方法,沒什麼好說的,自己敲一下代碼就理解了,不多說了也,我把我的代碼複製下來,裏面也有注意的地方。

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.YELLOW);
        canvas.drawPoint(50,50,mPaint);
        //實心圓  圓心座標x y  半徑 r  畫筆
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(100, 100, 80, mPaint);

        //繪製圓弧(實心)  這裏注意如何確定參數中第一個矩形的座標如何確定,(centerX-r,centerY-r,centerX+r,centerY+r)  開始的角度 -90度爲我們理解的0度方向   轉過的角度  是否連接圓心
         RectF oval = new RectF(0, 200, 200, 400);
//        canvas.drawArc(oval,-90,90,false,mPaint);//圓弧
        canvas.drawArc(oval, -90, 90, true, mPaint);//扇形

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        RectF ov = new RectF(0, 350, 200, 550);
//        canvas.drawArc(ov,-90,90,true,mPaint);//扇形
        canvas.drawArc(ov, -90, 90, false, mPaint);//弧線

        //橢圓
        canvas.drawOval(0, 650, 200, 800, mPaint);
        //矩形
        Rect rect = new Rect(0, 850, 200, 1050);
        canvas.drawRect(rect, mPaint);
        //圓角矩形
        canvas.drawRoundRect(250, 10, 450, 210, 15, 15, mPaint);
        // 按照path寫文字
        mPaint.setTextSize(DensityUtils.sp2px(getContext(), 16));
        mPaint.setStrokeWidth(1);
        mPaint.setStyle(Paint.Style.FILL);
        String te = "abcdefsgeg";
        Path pa = new Path();
        pa.addArc(300, 500, 900, 1000, -180, 120);
        canvas.drawTextOnPath(te, pa, 0, 0, mPaint);

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        Path p = new Path();
        p.moveTo(500, 800);
        p.quadTo(650, 850, 450, 1150);//賽貝爾曲線
//        p.cubicTo(100,200,200,500,6200,850);//兩個控制點的賽貝爾曲線  http://www.cnblogs.com/lenve/p/5865874.html
        canvas.drawPath(p, mPaint);

補充:看來確實是過了兩天忘了很多東西,有兩個重要的知識忘了記錄了

1.繪製線,必須要說Paint中與線有關的參數,因爲很重要,很重要

       //繪製線的一些設置
        mPaint.setStrokeCap(Paint.Cap.SQUARE);//Paint.Cap.BUTT  無延伸效果,是多大就多大   Paint.Cap.ROUND 延伸爲圓角   Paint.Cap.SQUARE 延伸爲矩形
        mPaint.setStrokeJoin(Paint.Join.ROUND);//設置連接處效果 Paint.Join.BEVEL  有倒角,爲直線倒角   Paint.Join.MITER  沒有倒角  Paint.Join.ROUND 圓形倒角  詳見:http://blog.csdn.net/abcdef314159/article/details/51720686
        mPaint.setStrokeMiter(Paint.ANTI_ALIAS_FLAG);//設置畫筆的傾斜度(不知有沒有用)
        mPaint.setHinting(Paint.HINTING_OFF);//設置畫筆的隱藏模式  Paint.HINTING_OFF 關閉  Paint.HINTING_ON 打開
        //TODO:特殊線需要單獨研究
        mPaint.setPathEffect(new CornerPathEffect(2));//有時候我們需要點畫線等特殊的線,這個可以設置連接線的形狀  CornerPathEffect 兩段線之間用圓角 DashPathEffect將線段虛線話   DiscretePathEffect打散path效果  其他效果詳見 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0120/2334.html
        //mPaint.setRasterizer();//設置光柵,被廢棄(無需研究)
下面舉例說明兩個參數的含義,其他可以看看裏面引用的參考文章,講的很好

        mPaint.setStrokeCap(Paint.Cap.BUTT);//設置線的兩端情況 默認爲此狀態即是多長就多長
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(25);
        Path path=new Path();
        path.moveTo(400,500);
        path.lineTo(600,500);
        canvas.drawPath(path,mPaint);

        mPaint.setStrokeCap(Paint.Cap.ROUND);//兩端爲圓角伸出
        Path path1=new Path();
        path1.moveTo(400,550);
        path1.lineTo(600,550);
        canvas.drawPath(path1,mPaint);

        mPaint.setStrokeCap(Paint.Cap.SQUARE);//兩端爲矩形伸出
        Path path2=new Path();
        path2.moveTo(400,600);
        path2.lineTo(600,600);
        canvas.drawPath(path2,mPaint);

        mPaint.setStrokeCap(Paint.Cap.BUTT);
        mPaint.setStrokeJoin(Paint.Join.BEVEL);//設置線與線的連接方式,此參數表示直切,還有圓角,直角,默認爲直角
        Path path3=new Path();
        path3.moveTo(400,650);
        path3.lineTo(600,650);
        path3.lineTo(600,750);
        canvas.drawPath(path3,mPaint);
效果就是最後第二張圖片中的幾條大粗線。

2.繪製點線面,不得不說一個path函數,非常非常重要 參見:http://www.cnblogs.com/lenve/p/5865874.html

下面開始繪製一個大家都會練習的一個例子:時鐘。裏面有很重要的內容,最好運行一下,仔細看思路。

public class TimeView extends TextView {
    private Paint mPaint;
    private int mSecond;
    private int mMinite;
    private int mHour;

    public TimeView(Context context) {
        this(context, null);
    }

    public TimeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        String time = getTimeShort();
        int hour = Integer.valueOf(time.substring(0, 2));
        mHour = hour % 12;
        mMinite = Integer.valueOf(time.substring(3, 5));
        mSecond = Integer.valueOf(time.substring(6, time.length()));
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    mSecond++;
                    if (mSecond == 60) {
                        mSecond = 0;
                        mMinite++;
                        if (mMinite == 60) {
                            mMinite = 0;
                            mHour++;
                            if (mHour == 12) {
                                mHour = 0;
                            }
                        }
                    }
                    invalidate();
                    break;
            }
        }
    };

    @Override
    protected void onDraw(Canvas canvas) {
        int screenX = getMeasuredWidth() / 2;
        int screenY = getMeasuredHeight() / 2;
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        mPaint.setColor(Color.GREEN);
        //繪製背景
        canvas.drawColor(Color.YELLOW);
        //繪製大圓
        canvas.drawCircle(screenX, screenY, 250, mPaint);
        //繪製表芯小圓
        mPaint.setColor(Color.GRAY);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(screenX, screenY, 18, mPaint);
        //繪製指針小圓
        mPaint.setColor(Color.GREEN);
        canvas.drawCircle(screenX, screenY, 10, mPaint);
        //繪製文字北京時間  問題:如何計算指定長度的字符所佔的角度(未解決)
        mPaint.setStrokeWidth(2);
        String s = "北京時間";
        mPaint.setTextSize(DensityUtils.sp2px(getContext(), 19));
        Path textPath = new Path();
        RectF rect = new RectF(screenX - 205, screenY - 205, screenX + 205, screenY + 205);
        textPath.arcTo(rect, -111, 90, false);//這裏沒有做適配,所以可能運行不是在文字正上方
        canvas.drawTextOnPath(s, textPath, 0, 0, mPaint);
        //繪製刻度  這個不是很好理解:思路是這樣的,首先將畫布平移到圓的中心(本例子中就是屏幕中央),那麼此時高中立體幾何的知識就用到了,畫布已經平移到屏幕中央了,那麼
                 // 此時屏幕中央的座標是多少呢?答案是(0,0),屏幕左上角的座標是多少呢(-screenWidth/2,-screenHeight/2),爲什麼?想想立體幾何中的知識(具體什麼知識我也忘了,不過確實是)
                //然後我們繪製刻度線就要以屏幕中央爲基準點,繪製。怎麼繪製?很簡單,畫筆只負責在同一個位置畫直線,然後讓畫布旋轉,相對運動嗎,這樣轉一圈,刻度就好了
                //現在是怎麼確定直線兩點座標爲題,我的思路是在正上方繪製直線,那麼座標就是多少呢?例如線長爲20,圓半徑爲100,則座標爲(0,-100)和(0,-120).理解一下,再讀讀上面內容畫個座標系就懂了
         canvas.save();//保存當前畫布的各種屬性狀態,配合canvas.restore();使用
        canvas.translate(screenX, screenY);
        for (int i = 0; i < 60; i++) {
            if (i % 5 == 0) {//長
                mPaint.setStrokeWidth(3);
                canvas.drawLine(0, -250, 0, -274, mPaint);
            } else {//短
                mPaint.setStrokeWidth(2);
                canvas.drawLine(0, -250, 0, -265, mPaint);
            }
            canvas.rotate(6);
        }
        //繪製數字(思路和繪製刻度一樣)
        Rect re = new Rect();
        for (int i = 1; i < 13; i++) {
            int t = i - 1;
            if (t == 0) {
                t = 12;
            }
            mPaint.setStrokeWidth(2);
            mPaint.setTextSize(DensityUtils.sp2px(getContext(), 16));
            String num = String.valueOf(t);
            mPaint.getTextBounds(num, 0, num.length(), re);
            canvas.drawText(num, -re.width() / 2, -280, mPaint);
            canvas.rotate(30);
        }
        canvas.restore();//回覆保存的畫布各種屬性狀態,這中間做的各種改變都放棄掉
        canvas.save();
        //繪製時針
        mPaint.setStrokeWidth(7);
        canvas.translate(screenX, screenY);
        canvas.rotate((float) 30 * mHour + 30 * (float) mMinite / 60);
        canvas.drawLine(0, 20, 0, -155, mPaint);
        canvas.restore();
        //繪製分針
        canvas.save();
        mPaint.setStrokeWidth(5);
        canvas.translate(screenX, screenY);
        canvas.rotate((float) 6 * mMinite + 6 * (float) mSecond / 60);
        canvas.drawLine(0, 25, 0, -175, mPaint);
        canvas.restore();
        canvas.save();
        //繪製秒針
        canvas.translate(screenX, screenY);
        mPaint.setStrokeWidth(3);
        canvas.rotate(6 * mSecond);
        canvas.drawLine(0, 35, 0, -190, mPaint);
        canvas.restore();
        mHandler.sendEmptyMessageDelayed(0, 1000);
    }

    /**
     * 獲取時間 小時:分;秒 HH:mm:ss
     *
     * @return
     */
    public static String getTimeShort() {
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        Date currentTime = new Date();
        return formatter.format(currentTime);
    }

    public void onDestory() {
        mHandler.removeCallbacksAndMessages(null);
        mHandler = null;
    }
}



發佈了62 篇原創文章 · 獲贊 28 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章