<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android自定義Canvas之繪制基本形狀

          共 10633字,需瀏覽 22分鐘

           ·

          2021-08-13 19:37

          在本篇文章中,我們先了解Canvas的基本用法,最后用一個小示例來結(jié)束本次教程。


          一.Canvas簡介


          Canvas我們可以稱之為畫布,能夠在上面繪制各種東西,是安卓平臺2D圖形繪制的基礎(chǔ),非常強(qiáng)大。


          一般來說,比較基礎(chǔ)的東西有兩大特點:
          1.可操作性強(qiáng):由于這些是構(gòu)成上層的基礎(chǔ),所以可操作性必然十分強(qiáng)大。
          2.比較難用:各種方法太過基礎(chǔ),想要完美的將這些操作組合起來有一定難度。


          不過不必?fù)?dān)心,本系列文章不僅會介紹到Canvas的操作方法,還會簡單介紹一些設(shè)計思路和技巧。


          二.Canvas的常用操作速查表



          PS:Canvas常用方法在上面表格中已經(jīng)全部列出了,當(dāng)然還存在一些其他的方法未列出,具體可以參考官方文檔。


          三.Canvas詳解


          本篇內(nèi)容主要講解如何利用Canvas繪制基本圖形。


          繪制顏色:


          繪制顏色是填充整個畫布,常用于繪制底色。

            canvas.drawColor(Color.BLUE); //繪制藍(lán)色



          創(chuàng)建畫筆:


          要想繪制內(nèi)容,首先需要先創(chuàng)建一個畫筆,如下:

            // 1.創(chuàng)建一個畫筆  private Paint mPaint = new Paint();
          // 2.初始化畫筆 private void initPaint() { mPaint.setColor(Color.BLACK); //設(shè)置畫筆顏色 mPaint.setStyle(Paint.Style.FILL); //設(shè)置畫筆模式為填充 mPaint.setStrokeWidth(10f); //設(shè)置畫筆寬度為10px }
          // 3.在構(gòu)造函數(shù)中初始化 public SloopView(Context context, AttributeSet attrs) { super(context, attrs); initPaint();    }


          在創(chuàng)建完畫筆之后,就可以在Canvas中繪制各種內(nèi)容了。


          繪制點:


          可以繪制一個點,也可以繪制一組點,如下:

                  canvas.drawPoint(200, 200, mPaint);     //在坐標(biāo)(200,200)位置繪制一個點        canvas.drawPoints(new float[]{          //繪制一組點,坐標(biāo)位置由float數(shù)組指定                500,500,                500,600,                500,700        },mPaint);


          關(guān)于坐標(biāo)原點默認(rèn)在左上角,水平向右為x軸增大方向,豎直向下為y軸增大方向。



          繪制直線:


          繪制直線需要兩個點,初始點和結(jié)束點,同樣繪制直線也可以繪制一條或者繪制一組:

                  canvas.drawLine(300,300,500,600,mPaint);    // 在坐標(biāo)(300,300)(500,600)之間繪制一條直線        canvas.drawLines(new float[]{               // 繪制一組線 每四數(shù)字(兩個點的坐標(biāo))確定一條線                100,200,200,200,                100,300,200,300        },mPaint);



          繪制矩形:


          我們都知道,確定一個矩形最少需要四個數(shù)據(jù),就是對角線的兩個點的坐標(biāo)值,這里一般采用左上角和右下角的兩個點的坐標(biāo)


          關(guān)于繪制矩形,Canvas提供了三種重載方法,第一種就是提供四個數(shù)值(矩形左上角和右下角兩個點的坐標(biāo))來確定一個矩形進(jìn)行繪制。其余兩種是先將矩形封裝為Rect或RectF(實際上仍然是用兩個坐標(biāo)點來確定的矩形),然后傳遞給Canvas繪制,如下:

          // 第一種canvas.drawRect(100,100,800,400,mPaint);
          // 第二種Rect rect = new Rect(100,100,800,400);canvas.drawRect(rect,mPaint);
          // 第三種RectF rectF = new RectF(100,100,800,400);canvas.drawRect(rectF,mPaint);


          以上三種方法所繪制出來的結(jié)果是完全一樣的。



          看到這里,相信很多觀眾會產(chǎn)生一個疑問,為什么會有Rect和RectF兩種?兩者有什么區(qū)別嗎?


          答案當(dāng)然是存在區(qū)別的,兩者最大的區(qū)別就是精度不同,Rect是int(整形)的,而RectF是float(單精度浮點型)的。除了精度不同,兩種提供的方法也稍微存在差別,在這里我們暫時無需關(guān)注。


          繪制圓角矩形:


          繪制圓角矩形也提供了兩種重載方式,如下:

                  // 第一種        RectF rectF = new RectF(100,100,800,400);        canvas.drawRoundRect(rectF,30,30,mPaint);
          // 第二種        canvas.drawRoundRect(100,100,800,400,30,30,mPaint);


          上面兩種方法繪制效果也是一樣的,但鑒于第二種方法在API21的時候才添加上,所以我們一般使用的都是第一種。



          下面簡單解析一下圓角矩形的幾個必要的參數(shù)的意思。


          很明顯可以看出,第二種方法前四個參數(shù)和第一種方法的RectF作用是一樣的,都是為了確定一個矩形,最后一個參數(shù)Paint是畫筆,無需多說,與矩形相比,圓角矩形多出來了兩個參數(shù)rx 和 ry,這兩個參數(shù)是干什么的呢?


          稍微分析一下,既然是圓角矩形,他的角肯定是圓弧(圓形的一部分),我們一般用什么確定一個圓形呢?


          答案是圓心 和 半徑,其中圓心用于確定位置,而半徑用于確定大小。


          由于矩形位置已經(jīng)確定,所以其邊角位置也是確定的,那么確定位置的參數(shù)就可以省略,只需要用半徑就能描述一個圓弧了。


          但是,半徑只需要一個參數(shù),但這里怎么會有兩個呢?


          好吧,讓你發(fā)現(xiàn)了,這里圓角矩形的角實際上不是一個正圓的圓弧,而是橢圓的圓弧,這里的兩個參數(shù)實際上是橢圓的兩個半徑,他們看起來個如下圖:



          紅線標(biāo)注的 rx 與 ry 就是兩個半徑,也就是相比繪制矩形多出來的那兩個參數(shù)。


          我們了解到原理后,就可以為所欲為了,通過計算可知我們上次繪制的矩形寬度為700,高度為300,當(dāng)你讓 rx大于350(寬度的一半), ry大于150(高度的一半) 時奇跡就出現(xiàn)了, 你會發(fā)現(xiàn)圓角矩形變成了一個橢圓, 他們畫出來是這樣的 ( 為了方便確認(rèn)我更改了畫筆顏色, 同時繪制出了矩形和圓角矩形 ):

                  // 矩形        RectF rectF = new RectF(100,100,800,400);  
          // 繪制背景矩形 mPaint.setColor(Color.GRAY); canvas.drawRect(rectF,mPaint);
          // 繪制圓角矩形 mPaint.setColor(Color.BLUE);        canvas.drawRoundRect(rectF,700,400,mPaint);



          其中灰色部分是我們所選定的矩形,而里面的圓角矩形則變成了一個橢圓,實際上在rx為寬度的一半,ry為高度的一半時,剛好是一個橢圓,通過上面我們分析的原理推算一下就能得到,而當(dāng)rx大于寬度的一半,ry大于高度的一半時,實際上是無法計算出圓弧的,所以drawRoundRect對大于該數(shù)值的參數(shù)進(jìn)行了限制(修正),凡是大于一半的參數(shù)均按照一半來處理。



          繪制橢圓:


          相對于繪制圓角矩形,繪制橢圓就簡單的多了,因為他只需要一個矩形作為參數(shù):

                  // 第一種        RectF rectF = new RectF(100,100,800,400);        canvas.drawOval(rectF,mPaint);
          // 第二種        canvas.drawOval(100,100,800,400,mPaint);


          同樣,以上兩種方法效果完全一樣,但一般使用第一種。



          繪制橢圓實際上就是繪制一個矩形的內(nèi)切圖形,原理如下,就不多說了:


          PS:如果你傳遞進(jìn)來的是一個長寬相等的矩形(即正方形),那么繪制出來的實際上就是一個圓。


          繪制圓:


          繪制圓形也比較簡單, 如下:

              canvas.drawCircle(500,500,400,mPaint);  // 繪制一個圓心坐標(biāo)在(500,500),半徑為400 的圓。


          繪制圓形有四個參數(shù),前兩個是圓心坐標(biāo),第三個是半徑,最后一個是畫筆。



          繪制圓弧:


          繪制圓弧就比較神奇一點了,為了理解這個比較神奇的東西,我們先看一下它需要的幾個參數(shù):

          // 第一種public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){}
          // 第二種public void drawArc(float left, float top, float right, float bottom, float startAngle,            float sweepAngle, boolean useCenter, @NonNull Paint paint) {}


          從上面可以看出,相比于繪制橢圓,繪制圓弧還多了三個參數(shù):

          startAngle  // 開始角度sweepAngle  // 掃過角度useCenter   // 是否使用中心


          通過字面意思我們基本能猜測出來前兩個參數(shù)(startAngle, sweepAngel)的作用,就是確定角度的起始位置和掃過角度, 不過第三個參數(shù)是干嘛的?試一下就知道了,上代碼:

                  RectF rectF = new RectF(100,100,800,400);        // 繪制背景矩形        mPaint.setColor(Color.GRAY);        canvas.drawRect(rectF,mPaint);
          // 繪制圓弧 mPaint.setColor(Color.BLUE); canvas.drawArc(rectF,0,90,false,mPaint);
          //-------------------------------------
          RectF rectF2 = new RectF(100,600,800,900); // 繪制背景矩形 mPaint.setColor(Color.GRAY); canvas.drawRect(rectF2,mPaint);
          // 繪制圓弧 mPaint.setColor(Color.BLUE);        canvas.drawArc(rectF2,0,90,true,mPaint);


          上述代碼實際上是繪制了一個起始角度為0度,掃過90度的圓弧,兩者的區(qū)別就是是否使用了中心點,結(jié)果如下:



          可以發(fā)現(xiàn)使用了中心點之后繪制出來類似于一個扇形,而不使用中心點則是圓弧起始點和結(jié)束點之間的連線加上圓弧圍成的圖形。這樣中心點這個參數(shù)的作用就很明顯了,不必多說想必大家試一下就明白了。 


          相比于使用橢圓,我們還是使用正圓比較多的,使用正圓展示一下效果:

                  RectF rectF = new RectF(100,100,600,600);        // 繪制背景矩形        mPaint.setColor(Color.GRAY);        canvas.drawRect(rectF,mPaint);
          // 繪制圓弧 mPaint.setColor(Color.BLUE); canvas.drawArc(rectF,0,90,false,mPaint);
          //-------------------------------------
          RectF rectF2 = new RectF(100,700,600,1200); // 繪制背景矩形 mPaint.setColor(Color.GRAY); canvas.drawRect(rectF2,mPaint);
          // 繪制圓弧 mPaint.setColor(Color.BLUE);        canvas.drawArc(rectF2,0,90,true,mPaint);




          簡要介紹Paint


          看了上面這么多,相信有一部分人會產(chǎn)生一個疑問,如果我想繪制一個圓,只要邊不要里面的顏色怎么辦?


          很簡單,繪制的基本形狀由Canvas確定,但繪制出來的顏色,具體效果則由Paint確定。


          如果你注意到了的話,在一開始我們設(shè)置畫筆樣式的時候是這樣的:

            mPaint.setStyle(Paint.Style.FILL);  //設(shè)置畫筆模式為填充


          為了展示方便,容易看出效果,之前使用的模式一直為填充模式,實際上畫筆有三種模式,如下:

          STROKE                //描邊FILL                  //填充FILL_AND_STROKE       //描邊加填充


          為了區(qū)分三者效果我們做如下實驗:

                  Paint paint = new Paint();        paint.setColor(Color.BLUE);        paint.setStrokeWidth(40);     //為了實驗效果明顯,特地設(shè)置描邊寬度非常大
          // 描邊 paint.setStyle(Paint.Style.STROKE); canvas.drawCircle(200,200,100,paint);
          // 填充 paint.setStyle(Paint.Style.FILL); canvas.drawCircle(200,500,100,paint);
          // 描邊加填充 paint.setStyle(Paint.Style.FILL_AND_STROKE);        canvas.drawCircle(200, 800, 100, paint);



          一圖勝千言,通過以上實驗我們可以比較明顯的看出三種模式的區(qū)別,如果只需要邊緣不需要填充內(nèi)容的話只需要設(shè)置模式為描邊(STROKE)即可。


          其實關(guān)于Paint的內(nèi)容也是有不少的,這些只是冰山一角,在后續(xù)內(nèi)容中會詳細(xì)的講解Paint。


          小示例


          簡要介紹畫布的操作:


          畫布操作詳細(xì)內(nèi)容會在下一篇文章中講解, 不是本文重點,但以下示例中可能會用到,所以此處簡要介紹一下。



          制作一個餅狀圖


          在展示百分比數(shù)據(jù)的時候經(jīng)常會用到餅狀圖,像這樣:



          簡單分析


          其實根據(jù)我們上面的知識已經(jīng)能自己制作一個餅狀圖了。不過制作東西最重要的不是制作結(jié)果,而是制作思路。相信我貼上代碼大家一看就立刻明白了,非常簡單的東西。不過嘛,咱們還是想了解一下制作思路:


          先分析餅狀圖的構(gòu)成,非常明顯,餅狀圖就是一個又一個的扇形構(gòu)成的,每個扇形都有不同的顏色,對應(yīng)的有名字,數(shù)據(jù)和百分比。


          經(jīng)以上信息可以得出餅狀圖的最基本數(shù)據(jù)應(yīng)包括:名字 數(shù)據(jù)值 百分比 對應(yīng)的角度 顏色。


          用戶關(guān)心的數(shù)據(jù) :名字 數(shù)據(jù)值 百分比
          需要程序計算的數(shù)據(jù):百分比 對應(yīng)的角度
          其中顏色這一項可以用戶指定也可以用程序指定(我們這里采用程序指定)。


          封裝數(shù)據(jù):

          public class PieData {    // 用戶關(guān)心數(shù)據(jù)    private String name;        // 名字    private float value;        // 數(shù)值    private float percentage;   // 百分比
          // 非用戶關(guān)心數(shù)據(jù) private int color = 0; // 顏色 private float angle = 0; // 角度
          public PieData(@NonNull String name, @NonNull float value) { this.name = name; this.value = value; }}


          PS: 以上省略了get set方法


          自定義View:


          先按照自定義View流程梳理一遍(確定各個步驟應(yīng)該做的事情):



          代碼如下:

          public class PieView extends View {    // 顏色表(注意: 此處定義顏色使用的是ARGB,帶Alpha通道的)    private int[] mColors = {0xFFCCFF00, 0xFF6495ED, 0xFFE32636, 0xFF800000, 0xFF808000, 0xFFFF8C69, 0xFF808080,            0xFFE6B800, 0xFF7CFC00};    // 餅狀圖初始繪制角度    private float mStartAngle = 0;    // 數(shù)據(jù)    private ArrayList<PieData> mData;    // 寬高    private int mWidth, mHeight;    // 畫筆    private Paint mPaint = new Paint();
          public PieView(Context context) { this(context, null); }
          public PieView(Context context, AttributeSet attrs) { super(context, attrs); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); }
          @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; }
          @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (null == mData) return; float currentStartAngle = mStartAngle; // 當(dāng)前起始角度 canvas.translate(mWidth / 2, mHeight / 2); // 將畫布坐標(biāo)原點移動到中心位置 float r = (float) (Math.min(mWidth, mHeight) / 2 * 0.8); // 餅狀圖半徑 RectF rect = new RectF(-r, -r, r, r); // 餅狀圖繪制區(qū)域
          for (int i = 0; i < mData.size(); i++) { PieData pie = mData.get(i); mPaint.setColor(pie.getColor()); canvas.drawArc(rect, currentStartAngle, pie.getAngle(), true, mPaint); currentStartAngle += pie.getAngle(); }
          }
          // 設(shè)置起始角度 public void setStartAngle(int mStartAngle) { this.mStartAngle = mStartAngle; invalidate(); // 刷新 }
          // 設(shè)置數(shù)據(jù) public void setData(ArrayList<PieData> mData) { this.mData = mData; initData(mData); invalidate(); // 刷新 }
          // 初始化數(shù)據(jù) private void initData(ArrayList<PieData> mData) { if (null == mData || mData.size() == 0) // 數(shù)據(jù)有問題 直接返回 return;
          float sumValue = 0; for (int i = 0; i < mData.size(); i++) { PieData pie = mData.get(i);
          sumValue += pie.getValue(); //計算數(shù)值和
          int j = i % mColors.length; //設(shè)置顏色 pie.setColor(mColors[j]); }
          float sumAngle = 0; for (int i = 0; i < mData.size(); i++) { PieData pie = mData.get(i);
          float percentage = pie.getValue() / sumValue; // 百分比 float angle = percentage * 360; // 對應(yīng)的角度
          pie.setPercentage(percentage); // 記錄百分比 pie.setAngle(angle); // 記錄角度大小 sumAngle += angle;
          Log.i("angle", "" + pie.getAngle()); } }}


          PS: 在更改了數(shù)據(jù)需要重繪界面時要調(diào)用invalidate()這個函數(shù)重新繪制。


          效果圖



          PS: 這個餅狀圖并沒有添加百分比等數(shù)據(jù),僅作為示例使用。


          總結(jié):


          其實自定義View只要按照流程一步步的走,也是比較容易的。不過里面也有不少坑,這些坑還是自己踩過印象比較深,建議大家不要直接copy源碼,自己手打體驗一下。

          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  做爱完整视频网址大全免费 | 天天干天天怕 | xjgggyxgs.com高价收liang,请涟系@qdd2000 | 黄色网久久 | 韩国免费一区二区 |