1. 程式人生 > >Android 利用canvas畫各種圖形(點、直線、弧、圓、橢圓、文字、矩形、多邊形、曲線、圓角矩形)

Android 利用canvas畫各種圖形(點、直線、弧、圓、橢圓、文字、矩形、多邊形、曲線、圓角矩形)

1、首先說一下canvas類:
Class Overview
The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
這個類相當於一個畫布,你可以在裡面畫很多東西;
我們可以把這個Canvas理解成系統提供給我們的一塊記憶體區域(但實際上它只是一套畫圖的API,真正的記憶體是下面的Bitmap),而且它還提供了一整套對這個記憶體區域進行操作的方法,所有的這些操作都是畫圖API。也就是說在這種方式下我們已經能一筆一劃或者使用Graphic來畫我們所需要的東西了,要畫什麼要顯示什麼都由我們自己控制。

這種方式根據環境還分為兩種:一種就是使用普通View的canvas畫圖,還有一種就是使用專門的SurfaceView的canvas來畫圖。兩種的主要是區別就是可以在SurfaceView中定義一個專門的執行緒來完成畫圖工作,應用程式不需要等待View的刷圖,提高效能。前面一種適合處理量比較小,幀率比較小的動畫,比如說象棋遊戲之類的;而後一種主要用在遊戲,高品質動畫方面的畫圖。
下面是Canvas類常用的方法:

drawRect(RectF rect, Paint paint) //繪製區域,引數一為RectF一個區域
drawPath(Path path, Paint paint) //繪製一個路徑,引數一為Path路徑物件
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //貼圖,引數一就是我們常規的Bitmap物件,引數二是源區域(這裡是bitmap),引數三是目標區域(應該在canvas的位置和大小),引數四是Paint畫刷物件,因為用到了縮放和拉伸的可能,當原始Rect不等於目標Rect時效能將會有大幅損失。
drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) //畫線,引數一起始點的x軸位置,引數二起始點的y軸位置,引數三終點的x軸水平位置,引數四y軸垂直位置,最後一個引數為Paint 畫刷物件。
drawPoint(float x, float y, Paint paint) //畫點,引數一水平x軸,引數二垂直y軸,第三個引數為Paint物件。
drawText(String text, float x, floaty, Paint paint) //渲染文字,Canvas類除了上面的還可以描繪文字,引數一是String型別的文字,引數二x軸,引數三y軸,引數四是Paint物件。
drawOval(RectF oval, Paint paint)//畫橢圓,引數一是掃描區域,引數二為paint物件;
drawCircle(float cx, float cy, float radius,Paint paint)// 繪製圓,引數一是中心點的x軸,引數二是中心點的y軸,引數三是半徑,引數四是paint物件;
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//畫弧,
引數一是RectF物件,一個矩形區域橢圓形的界限用於定義在形狀、大小、電弧,引數二是起始角(度)在電弧的開始,
引數三掃描角(度)開始順時針測量的,引數四是如果這是真的話,包括橢圓中心的電弧,並關閉它,如果它是假這將是一個弧線,引數五是Paint物件;
還要理解一個paint類:

Class Overview
The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
paint類擁有風格和顏色資訊如何繪製幾何學,文字和點陣圖。

Paint 代表了Canvas上的畫筆、畫刷、顏料等等;

Paint類常用方法:
setARGB(int a, int r, int g, int b) // 設定 Paint物件顏色,引數一為alpha透明值
setAlpha(int a) // 設定alpha不透明度,範圍為0~255
setAntiAlias(boolean aa) // 是否抗鋸齒
setColor(int color) // 設定顏色,這裡Android內部定義的有Color類包含了一些常見顏色定義
setTextScaleX(float scaleX) // 設定文字縮放倍數,1.0f為原始
setTextSize(float textSize) // 設定字型大小
setUnderlineText(booleanunderlineText) // 設定下劃線
2、直接看案例
看一下效果圖:

這裡寫圖片描述

Android利用canvas畫各種圖形(點、直線、弧、圓、橢圓、文字、矩形、多邊形、曲線、圓角矩形)

在此案例中我們用到的是自定義view類;
CustomActivity.java

public class CustomActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
}

private void init() {  
    LinearLayout layout=(LinearLayout) findViewById(R.id.root);  
    final DrawView view=new DrawView(this);  
    view.setMinimumHeight(500);  
    view.setMinimumWidth(300);  
    //通知view元件重繪    
    view.invalidate();  
    layout.addView(view);  

}  

}

重要的類自定義View元件要重寫View元件的onDraw(Canvase)方法,接下來是在該 Canvas上繪製大量的幾何圖形,點、直線、弧、圓、橢圓、文字、矩形、多邊形、曲線、圓角矩形,等各種形狀!
DrawView.java

public class DrawView extends View {

public DrawView(Context context) {  
    super(context);  
}  

@Override  
protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  

    // 建立畫筆  
    Paint p = new Paint();  
    p.setColor(Color.RED);// 設定紅色  

    canvas.drawText("畫圓:", 10, 20, p);// 畫文字  
    canvas.drawCircle(60, 20, 10, p);// 小圓  
    p.setAntiAlias(true);// 設定畫筆的鋸齒效果。 true是去除,大家一看效果就明白了  
    canvas.drawCircle(120, 20, 20, p);// 大圓  

    canvas.drawText("畫線及弧線:", 10, 60, p);  
    p.setColor(Color.GREEN);// 設定綠色  
    canvas.drawLine(60, 40, 100, 40, p);// 畫線  
    canvas.drawLine(110, 40, 190, 80, p);// 斜線  
    //畫笑臉弧線  
    p.setStyle(Paint.Style.STROKE);//設定空心  
    RectF oval1=new RectF(150,20,180,40);  
    canvas.drawArc(oval1, 180, 180, false, p);//小弧形  
    oval1.set(190, 20, 220, 40);  
    canvas.drawArc(oval1, 180, 180, false, p);//小弧形  
    oval1.set(160, 30, 210, 60);  
    canvas.drawArc(oval1, 0, 180, false, p);//小弧形  

    canvas.drawText("畫矩形:", 10, 80, p);  
    p.setColor(Color.GRAY);// 設定灰色  
    p.setStyle(Paint.Style.FILL);//設定填滿  
    canvas.drawRect(60, 60, 80, 80, p);// 正方形  
    canvas.drawRect(60, 90, 160, 100, p);// 長方形  

    canvas.drawText("畫扇形和橢圓:", 10, 120, p);  

    Shader mShader = new LinearGradient(0, 0, 100, 100,  
            new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW,  
                    Color.LTGRAY }, null, Shader.TileMode.REPEAT); // 一個材質,打造出一個線性梯度沿著一條線。  
    p.setShader(mShader);  
    // p.setColor(Color.BLUE);  
    RectF oval2 = new RectF(60, 100, 200, 240);// 設定個新的長方形,掃描測量  
    canvas.drawArc(oval2, 200, 130, true, p);  
    // 畫弧,第一個引數是RectF:該類是第二個引數是角度的開始,第三個引數是多少度,第四個引數是真的時候畫扇形,是假的時候畫弧線  
    //畫橢圓,把oval改一下  
    oval2.set(210,100,250,130);  
    canvas.drawOval(oval2, p);  

    canvas.drawText("畫三角形:", 10, 200, p);  
    // 繪製這個三角形,你可以繪製任意多邊形  
    Path path = new Path();  
    path.moveTo(80, 200);// 此點為多邊形的起點  
    path.lineTo(120, 250);  
    path.lineTo(80, 250);  
    path.close(); // 使這些點構成封閉的多邊形  
    canvas.drawPath(path, p);  

    // 你可以繪製很多任意多邊形,比如下面畫六連形  
    p.reset();//重置  
    p.setColor(Color.LTGRAY);  
    p.setStyle(Paint.Style.STROKE);//設定空心  
    Path path1=new Path();  
    path1.moveTo(180, 200);  
    path1.lineTo(200, 200);  
    path1.lineTo(210, 210);  
    path1.lineTo(200, 220);  
    path1.lineTo(180, 220);  
    path1.lineTo(170, 210);  
    path1.close();//封閉  
    canvas.drawPath(path1, p);  


    //畫圓角矩形  
    p.setStyle(Paint.Style.FILL);//充滿  
    p.setColor(Color.LTGRAY);  
    p.setAntiAlias(true);// 設定畫筆的鋸齒效果  
    canvas.drawText("畫圓角矩形:", 10, 260, p);  
    RectF oval3 = new RectF(80, 260, 200, 300);// 設定個新的長方形  
    canvas.drawRoundRect(oval3, 20, 15, p);//第二個引數是x半徑,第三個引數是y半徑  

    //畫貝塞爾曲線  
    canvas.drawText("畫貝塞爾曲線:", 10, 310, p);  
    p.reset();  
    p.setStyle(Paint.Style.STROKE);  
    p.setColor(Color.GREEN);  
    Path path2=new Path();  
    path2.moveTo(100, 320);//設定Path的起點  
    path2.quadTo(150, 310, 170, 400); //設定貝塞爾曲線的控制點座標和終點座標  
    canvas.drawPath(path2, p);//畫出貝塞爾曲線  

    //畫點  
    p.setStyle(Paint.Style.FILL);  
    canvas.drawText("畫點:", 10, 390, p);  
    canvas.drawPoint(60, 390, p);//畫一個點  
    canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//畫多個點  

    //畫圖片,就是貼圖  
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);  
    canvas.drawBitmap(bitmap, 250,360, p);  
}  

}

ok!哈哈!

今天要給大家介紹的是在android中畫餅圖:
畫扇形:
RectF oval2 = new RectF(60, 100, 200, 240);
// 設定個新的長方形,60為左上點的x座標,100為左上點的y座標;200為右下點的 x座標,240為右下點的y座標。
// 畫弧,第一個引數是RectF:該類是第二個引數是角度的開始,第三個引數是多少度,第四個引數是真的時候畫扇形,是假的時候畫弧 線
canvas.drawArc(oval2, 200, 130, true, p);
一、 無網情況下:
由於Android 畫圖API為提供直接畫餅圖的方法,採用了比較原始的方法,畫扇形,然後拼接在一起,由於為了顯示立體效果,程式畫了20次,每次改變上下的位置,結果看起來就會有立體感(如果誰有更好的方式,非常願意學習)
canvas.drawArc(new RectF(0, 0, 300, 100), 0,60, true, paint);
會畫一個寬300,高100,水平 順時針方向的60度的扇形,程式中就是用這種方式拼接成整個餅圖的.

二、有網情況下:
使用Google API實現:
WebView webView = new WebView(this);
String url = “http://chart.apis.google.com/chart?cht=p3&chs=350x180&chd=t:30,30,40“;
webView.loadUrl(url);
setContentView(webView);
給Google API傳遞URL形式的引數,其中cht=p3表示圖形型別為餅圖,chs=350x180表示寬和高,chd=t:30,30,40表示餅圖分為三份,每一份所佔的顯示比例;
返回結果是一個包含根據引數生成餅圖圖片的網頁,所以以Android控制元件WebView接收進行顯示。
下面為效果圖。

這裡寫圖片描述

Android利用canvas畫各種圖形(點、直線、弧、圓、橢圓、文字、矩形、多邊形、曲線、圓角矩形)

我們已經介紹了Canvas,在那裡,已經學習瞭如何建立自己的View。在第7章中也使用了Canvas來為MapView標註覆蓋。

      畫布(Canvas)是圖形程式設計中一個很普通的概念,通常由三個基本的繪圖元件組成: 

Canvas 提供了繪圖方法,可以向底層的點陣圖繪製基本圖形。
Paint 也稱為”刷子”,Paint可以指定如何將基本圖形繪製到點陣圖上。
Bitmap 繪圖的表面。

       Android繪圖API支援透明度、漸變填充、圓邊矩形和抗鋸齒。遺憾的是,由於資源限制,它還不支援向量圖形,它使用的是傳統光柵樣式的重新繪圖。 
      這種光柵方法的結果是提高了效率,但是改變一個Paint物件不會影響已經畫好的基本圖形,它只會影響新的元素。 

      提示: 
      如果你擁有Windows開發背景,那麼Android的2D繪圖能力大致相當於GDI+的能力。 

   1. 可以畫什麼? 

      Canvas類封裝了用作繪圖表面的點陣圖;它還提供了draw*方法來實現設計。 

      下面的列表提供了對可用的基本圖形的簡要說明,但並沒有深入地探討每一個draw方法的詳細內容: 

      drawARGB / drawRGB / drawColor   使用單一的顏色填充畫布。 
      drawArc   在一個矩形區域的兩個角之間繪製一個弧。 
      drawBitmap   在畫布上繪製一個位圖。可以通過指定目標大小或者使用一個矩陣來改變目標點陣圖的外觀。 
      drawBitmapMesh   使用一個mesh(網)來繪製一個位圖,它可以通過移動網中的點來操作目標的外觀。 
      drawCircle   以給定的點為圓心,繪製一個指定半徑的圓。 
      drawLine(s)   在兩個點之間畫一條(多條)直線。 
      drawOval   以指定的矩形為邊界,畫一個橢圓。 
      drawPaint   使用指定的Paint填充整個Canvas 
      drawPath   繪製指定的Path。Path物件經常用來儲存一個物件中基本圖形的集合。 
      drawPicture   在指定的矩形中繪製一個Picture物件。 
      drawPosText   繪製指定了每一個字元的偏移量的文字字串。 
      drawRect   繪製一個矩形。 
      drawRoundRect   繪製一個圓角矩形。 
      drawText   在Canvas上繪製一個文字串。文字的字型、大小和渲染屬性都設定在用來渲染文字的Paint物件中。 
      drawTextOnPath   在一個指定的path上繪製文字。 
      drawVertices   繪製一系列三角形面片,通過一系列頂點來指定它們。 

      這些繪圖方法中的每一個都需要指定一個Paint物件來渲染它。在下面的部分中,將學習如何建立和修改Paint物件,從而在繪圖中完成大部分工作。 

   2. 從Paint中完成工作 

      Paint類相當於一個筆刷和調色盤。它可以選擇如何使用上面描述的draw方法來渲染繪製在畫布上的基本圖形。通過修改Paint物件,可以在繪圖的時候控制顏色、樣式、字型和特殊效果。最簡單地,setColor可以讓你選擇一個Paint的顏色,而Paint物件的樣式(使用setStyle控制)則可以決定是繪製繪圖物件的輪廓(STROKE),還是隻填充每一部分(FILL),或者是兩者都做(STROKE_AND_FILL) 

      除了這些簡單的控制之外,Paint類還支援透明度,另外,它也可以通過使用各種各樣的陰影、過濾器和效果進行修改,從而提供由更豐富的、複雜的畫筆和顏料組成的調色盤。 

      Android SDK包含了一些非常好的例項,它們說明了Paint類中可用的大部分功能。你可以在API demos的graphics子目錄中找到它們: 

sdk root folder]\samples\ApiDemos\src\com\android\samples\graphics

      在下面的部分中,將學習和使用其中的部分功能。這些部分只是簡單地羅列了它們能實現的效果(例如漸變和邊緣浮雕),而沒有詳細地列出所有可能的情況。 

   使用透明度 

       Android中的所有顏色都包含了一個不透明元件(alpha通道)。 
       當建立一個顏色的時候,可以使用argb或者parseColor方法來定義它的alpha值,如下所示: 

Java程式碼:
// 使用紅色,並讓它50%透明
int opacity = 127;
int intColor = Color.argb(opacity, 255, 0, 0);
int parsedColor = Color.parseColor(“#7FFF0000”);

複製程式碼
或者,也可以使用setAlpha方法來設定已存在的Paint物件的透明度:

Java程式碼:
// 讓顏色50%透明
int opacity = 127;
myPaint.setAlpha(opacity);
複製程式碼

      建立一個不是100%透明的顏色意味著,使用它繪製的任何基本圖形都將是部分透明的--也就是說,在它下面繪製的所有基本圖形都是部分可見的。 

      可以在任何使用了顏色的類或者方法中使用透明效果,包括Paint、Shader和Mask Filter。 

   Shader介紹 

      Shader類的派生類可以建立允許使用多種固體顏色填充繪圖物件的Paint。 

      對Shader最常見的使用是定義漸變填充;漸變是在2D影象中新增深度和紋理的最佳方式之一。Android包含了一個Bitmap Shader和一個Compose Shader,同時,還包含了三個漸變的Shader。 
      試圖用語言來描述繪圖的效果本來就是沒有意義的,所以看一下圖就應該可以知道每一種Shader是如何工作的。圖中從左到右依次代表的是LinearGradient、RadialGradient和 SweepGradient. 

提示:

      沒有包含的是ComposerShader,它可以建立多個Shader和BitmapShader的組合,從而可以在一個位圖影象的基礎上建立一個繪圖刷。 

      要在繪圖的時候使用一個Shader,可以使用setShader方法將其應用到一個Paint中,如下面的程式碼所示: 
      Paint shaderPaint = new Paint();   
      shaderPaint.setShader(myLinearGradient); 


      你使用這個Paint所繪製的任何東西都將使用你指定的Shader進行填充,而不是使用Paint本身的顏色進行填充。 

定義漸變Shader

      如上所示,使用漸變Shader可以讓你使用交替改變的顏色來填充圖片;你可以將顏色漸變定義為兩種顏色的簡單交替,如下所示: 

java程式碼:
int colorFrom = Color.BLACK;
int colorTo = Color.WHITE;
LinearGradient linearGradientShader = new LinearGradient(x1, y1, x2, y2, colorFrom, colorTo, TileMode.CLAMP);

複製程式碼

       或者,你還可以定義更復雜的按照設定比例進行分佈的顏色序列,如下面的RadialGradientShader例子所示: 

java程式碼:
int[] gradientColors = new int[3];
gradientColors[0] = Color.GREEN;
gradientColors[1] = Color.YELLOW;
gradientColors[2] = Color.RED;
float[] gradientPositions = new float[3];
gradientPositions[0] = 0.0f;
gradientPositions[1] = 0.5f;
gradientPositions[2] = 1.0f;
RadialGradient radialGradientShader=new RadialGradient(centerX,centerY, radius, gradientColors, gradientPositions, TileMode.CLAMP);

複製程式碼

      每一種漸變Shader(線性的、輻射形的和掃描狀的)都可以使用以上這兩種技術來定義漸變填充。 

使用Shader TileModes
漸變Shader的畫刷大小既可以顯式地使用有邊界的矩形來定義,也可以使用中心點和半徑長度來定義。Bitmap Shader可以通過它的點陣圖大小來決定它的畫刷大小。

       如果Shader畫刷所定義的區域比要填充的區域小,那麼TileMode將會決定如何處理剩餘的區域: 
       CLAMP   使用Shader的邊界顏色來填充剩餘的空間。 
       MIRROR   在水平和垂直方向上拉伸Shader影象,這樣每一個影象就都能與上一個縫合了。 
       REPEAT   在水平和垂直方向上重複Shader影象,但不拉伸它。 

使用MaskFilter

       MaskFilter類可以為Paint分配邊緣效果。 
       對MaskFilter的擴充套件可以對一個Paint邊緣的alpha通道應用轉換。Android包含了下面幾種MaskFilter: 

       BlurMaskFilter     指定了一個模糊的樣式和半徑來處理Paint的邊緣。 
       EmbossMaskFilter   指定了光源的方向和環境光強度來新增浮雕效果。 

       要應用一個MaskFilter,可以使用setMaskFilter方法,並傳遞給它一個MaskFilter物件。下面的例子是對一個已經存在的Paint應用一個EmbossMaskFilter: 

java程式碼:
// 設定光源的方向
float[] direction = new float[]{ 1, 1, 1 };
//設定環境光亮度
float light = 0.4f;
// 選擇要應用的反射等級
float specular = 6;
// 向mask應用一定級別的模糊
float blur = 3.5f;
EmbossMaskFilter emboss=new EmbossMaskFilter(direction,light,specular,blur);
// 應用mask myPaint.setMaskFilter(emboss);

複製程式碼

   SDK中包含的FingerPaint API demo是說明如何使用MaskFilter的一個非常好的例子。它展示了這兩種filter的效果。

使用ColorFilter

    MaskFilter是對一個Paint的alpha通道的轉換,而ColorFilter則是對每一個RGB通道應用轉換。所有由ColorFilter所派生的類在執行它們的轉換時,都會忽略alpha通道。

Android包含三個ColorFilter:

    ColorMatrixColorFilter  可以指定一個4×5的ColorMatrix並將其應用到一個Paint中。ColorMatrixes通常在程式中用於對影象進行處理,而且由於它們支援使用矩陣相乘的方法來執行連結轉換,所以它們很有用。

     LightingColorFilter  乘以第一個顏色的RGB通道,然後加上第二個顏色。每一次轉換的結果都限制在0到255之間。
     PorterDuffColorFilter  可以使用數字影象合成的16條Porter-Duff 規則中的任意一條來向Paint應用一個指定的顏色。

    使用setColorFilter方法應用ColorFilter,如下所示:
    myPaint.setColorFilter(new LightingColorFilter(Color.BLUE, Color.RED));

     API中的ColorMatrixSample是說明如何使用ColorFilter和Color Matrix的非常好的例子。

     使用PathEffect

     到目前為止,所有的效應都會影響到Paint填充影象的方式;PathEffect是用來控制繪製輪廓(線條)的方式。

PathEffect對於繪製Path基本圖形特別有用,但是它們也可以應用到任何Paint中從而影響線條繪製的方式。

     使用PathEffect,可以改變一個形狀的邊角的外觀並且控制輪廓的外表。Android包含了多個PathEffect,包括:
     CornerPathEffect  可以使用圓角來代替尖銳的角從而對基本圖形的形狀尖銳的邊角進行平滑。

DashPathEffect 可以使用DashPathEffect來建立一個虛線的輪廓(短橫線/小圓點),而不是使用實線。你還可以指定任意的虛/實線段的重複模式。

   DiscretePathEffect  與DashPathEffect相似,但是添加了隨機性。當繪製它的時候,需要指定每一段的長度和與原始路徑的偏離度。

   PathDashPathEffect  這種效果可以定義一個新的形狀(路徑)並將其用作原始路徑的輪廓標記。
   下面的效果可以在一個Paint中組合使用多個Path Effect。

   SumPathEffect  順序地在一條路徑中新增兩種效果,這樣每一種效果都可以應用到原始路徑中,而且兩種結果可以結合起來。

   ComposePathEffect  將兩種效果組合起來應用,先使用第一種效果,然後在這種效果的基礎上應用第二種效果。
   物件形狀的PathEffect的改變會影響到形狀的區域。這就能夠保證應用到相同形狀的填充效果將會繪製到新的邊界中。

   使用setPathEffect方法可以把PathEffect應用到Paint物件中,如下所示:

java程式碼:

borderPaint.setPathEffect(new CornerPathEffect(5));

複製程式碼

   PathEffect API示例給出瞭如何應用每一種效果的指導說明。

修改Xfermode

   可以通過修改Paint的Xfermode來影響在Canvas已有的影象上面繪製新的顏色的方式。
   在正常的情況下,在已有的影象上繪圖將會在其上面新增一層新的形狀。如果新的Paint是完全不透明的,那麼它將完全遮擋住下面的Paint;如果它是部分透明的,那麼它將會被染上下面的顏色。

   下面的Xfermode子類可以改變這種行為:
   AvoidXfermode  指定了一個顏色和容差,強制Paint避免在它上面繪圖(或者只在它上面繪圖)。
   PixelXorXfermode  當覆蓋已有的顏色時,應用一個簡單的畫素XOR操作。
   PorterDuffXfermode  這是一個非常強大的轉換模式,使用它,可以使用影象合成的16條Porter-Duff規則的任意一條來控制Paint如何與已有的Canvas影象進行互動。

   要應用轉換模式,可以使用setXferMode方法,如下所示:

java程式碼:

AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID); borderPen.setXfermode(avoid);

複製程式碼

  1. 使用抗鋸齒效果提高Paint質量

    在繪製一個新的Paint物件時,可以通過傳遞給它一些標記來影響它被渲染的方式。ANTI_ALIAS_FLAG是其中一種很有趣的標記,它可以保證在繪製斜線的時候使用抗鋸齒效果來平滑該斜線的外觀。

    在繪製文字的時候,抗鋸齒效果尤為重要,因為經過抗鋸齒效果處理之後的文字非常容易閱讀。要建立更加平滑的文字效果,可以應用SUBPIXEL_TEXT_FLAG,它將會應用子畫素抗鋸齒效果。

    也可以手工地使用setSubpixelText和setAntiAlias方法來設定這些標記,如下所示:

java程式碼:

myPaint.setSubpixelText(true);
myPaint.setAntiAlias(true);

複製程式碼

2D圖形的硬體加速

   在當前這個到處都是2D圖形愛好者的時代,Android允許你使用硬體加速來渲染你的應用程式。

   如果裝置可以使用硬體加速,那麼通過設定這個標記可以讓活動中的每一個View都能使用硬體渲染。儘管減少了系統處理程式的負載,但在極大地提高了影象處理速度的同時,硬體加速也帶來了相應的負面效果。

   使用requestWindowFeature方法,可以在你的活動中應用Window.FEATURE_OPENGL標記來開啟硬體加速,如下所示:

java程式碼:

myActivity.requestWindowFeature(Window.FEATURE_OPENGL);

複製程式碼

   並不是Android中所有的2D繪圖基本圖形都被硬體支援(特別是前面描述的大部分PathEffect)。

   與此同時,由於整個活動實際上是作為一個Canvas進行渲染的,所以對任何View的無效請求都將會導致整個活動被重新繪製。

Canvas繪圖最佳實踐經驗

   2D自繪操作是非常耗費處理程式資源的;低效的繪圖方法會阻塞GUI執行緒,並且會對應用程式的響應造成不利的影響。對於那些只有一個處理程式的資源受限的環境來說,這一點就更加現實了。

   這裡需要注意onDraw方法的資源消耗以及CPU週期的耗費,這樣才能保證不會把一個看起來很吸引人的應用程式變得完全沒有響應。

    目前有很多技術可以幫助將與自繪控制元件相關的資源消耗最小化。我們關心的不是一般的原則,而是某些Android特定的注意事項,從而保證你可以建立外觀時尚、而且能夠保持互動的活動(注意,以下這個列表並不完整):

   考慮硬體加速  OpenGL硬體加速對2D圖形的支援是非常好的,所以你總是應該考慮它是否適合你的活動。另一種比較優秀的方法是隻用一個單獨的View和迅速的、耗時的更新來組成活動。一定要保證你使用的基本圖形能夠被硬體支援。

   考慮大小和方向  當在設計View和佈局的時候,一定要保證考慮(和測試)它們在不同的解析度和大小下的外觀。

   只建立一次靜態物件  在Android中物件的建立是相當昂貴的。因此,在可能的地方,應用只建立一次像Paint物件、Path和Shader這樣的繪圖物件,而不是在View每次無效的時候都重新建立它們。

   記住onDraw是很消耗資源的  執行onDraw方法是很消耗資源的處理,它會強制Android執行多個圖片組合和點陣圖構建操作。下面有幾點建議可以讓你修改Canvas的外觀,而不用重新繪製它:

   使用Canvas轉換  可以使用像rotate和translate這樣的轉換,來簡化Canvas中元素複雜的相關位置。例如,相比放置和旋轉一個錶盤周圍的每一個文字元素,你可以簡單地將canvas旋轉22.5?,然後在相同的位置繪製文字。

   使用動畫  可以考慮使用動畫來執行View的預設定的轉換,而不是手動地重新繪製它。在活動的View中可以執行縮放、旋轉和轉換動畫,並可以提供一種能夠有效利用資源的方式來提供縮放、旋轉或者抖動效果。

   考慮使用點陣圖和9 Patch  如果View使用了靜態背景,那麼你應該考慮使用一個圖片,如點陣圖或者9 patch,而不是手動地重新繪製。

高階指南針錶盤的例子

   已經建立了一個簡單的指南針。而在上一章,你又回到了這個例子,對它進行了擴充套件從而使它夠使用加速計硬體來顯示橫向和縱向方向。

   那些例子中的UI都很簡單,從而保證了那些章節中的程式碼都儘可能地清晰。

   在下面的例子中,將對CompassView的onDraw方法做一些重要的改動,從而把它從一個簡單的、平面的指南針,變成一個動態的航空地平儀(artificial horizon ),如圖所示。

Android利用canvas畫各種圖形(點、直線、弧、圓、橢圓、文字、矩形、多邊形、曲線、圓角矩形)
由於上面的圖片是黑白的,所以需要實際動手建立這個控制元件來看到完全的效果。

(1) 首先,通過修改colors.xml資原始檔來包含邊界、錶盤陰影以及天空和地面的顏色值。同時還要更新邊界和盤面標記所使用的顏色。

java程式碼:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="text_color">#FFFF</color>
<color name="background_color">#F000</color>
<color name="marker_color">#FFFF</color>
<color name="shadow_color">#7AAA</color>
<color name="outer_border">#FF444444</color>
<color name="inner_border_one">#FF323232</color>
<color name="inner_border_two">#FF414141</color>
<color name="inner_border">#FFFFFFFF</color>
<color name="horizon_sky_from">#FFA52A2A</color>
<color name="horizon_sky_to">#FFFFC125</color>
<color name="horizon_ground_from">#FF5F9EA0</color>
<color name="horizon_ground_to">#FF00008B</color>
</resources>

複製程式碼
(2) 用作航空地平儀的天空和地面的Paint和Shader物件是根據當前View的大小建立的,所以它們不能像你在建立的Paint物件那樣,是靜態的。因此,不再建立Paint物件,取而代之的是構造它們所使用的漸變陣列和顏色。

java程式碼:

int[] borderGradientColors;
float[] borderGradientPositions;
int[] glassGradientColors;
float[] glassGradientPositions;
int skyHorizonColorFrom;
int skyHorizonColorTo;
int groundHorizonColorFrom;
int groundHorizonColorTo;

複製程式碼
(3) 更新CompassView的initCompassView方法,來使用第(1)步中所建立的資源來初始化第(2)步中所建立的變數。現存的方法程式碼大部分可以保留,而只需要對textPaint、circlePaint和markerPaint變數做些許改動,如下所示:

java程式碼:

protected void initCompassView() {
setFocusable(true);
// 獲得外部資源
Resources r = this.getResources();
circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(R.color.background_color);
circlePaint.setStrokeWidth(1);
circlePaint.setStyle(Paint.Style.STROKE);
northString = r.getString(R.string.cardinal_north);
eastString = r.getString(R.string.cardinal_east);
southString = r.getString(R.string.cardinal_south);
westString = r.getString(R.string.cardinal_west);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(r.getColor(R.color.text_color));
textPaint.setFakeBoldText(true);
textPaint.setSubpixelText(true);
textPaint.setTextAlign(Align.LEFT);
textHeight = (int)textPaint.measureText("yY");
markerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
markerPaint.setColor(r.getColor(R.color.marker_color));
markerPaint.setAlpha(200);
markerPaint.setStrokeWidth(1);
markerPaint.setStyle(Paint.Style.STROKE);
markerPaint.setShadowLayer(2, 1, 1, r.getColor(R.color.shadow_color));

複製程式碼

    a. 建立徑向Shader用來繪製外邊界所使用的顏色和位置陣列。

java程式碼:

borderGradientColors = new int[4];
borderGradientPositions = new float[4];
borderGradientColors[3] = r.getColor(R.color.outer_border);
borderGradientColors[2] = r.getColor(R.color.inner_border_one);
borderGradientColors[1] = r.getColor(R.color.inner_border_two);
borderGradientColors[0] = r.getColor(R.color.inner_border);
borderGradientPositions[3] = 0.0f;
borderGradientPositions[2] = 1-0.03f;
borderGradientPositions[1] = 1-0.06f;

複製程式碼

   b. 現在建立徑向漸變的顏色和位置陣列,它們將用來建立半透明的"glass dome"(玻璃圓頂),它放置在View的上面,從而使人產生深度的幻覺。

java程式碼:

glassGradientColors = new int[5];
glassGradientPositions = new float[5];
int glassColor = 245;
glassGradientColors[4]=Color.argb(65,glassColor,glassColor, glassColor);
glassGradientColors[3]=Color.argb(100,glassColor,glassColor,glassColor);
glassGradientColors[2]=Color.argb(50,glassColor,glassColor, glassColor);
glassGradientColors[1]=Color.argb(0,glassColor,glassColor, glassColor);
glassGradientColors[0]=Color.argb(0,glassColor,glassColor, glassColor);
glassGradientPositions[4] = 1-0.0f;
glassGradientPositions[3] = 1-0.06f;
glassGradientPositions[2] = 1-0.10f;
glassGradientPositions[1] = 1-0.20f;
glassGradientPositions[0] = 1-1.0f;

複製程式碼

    c. 最後,獲得建立線性顏色漸變所使用的顏色,它們將用來表示航空地平儀中的天空和地面。

java程式碼:

skyHorizonColorFrom = r.getColor(R.color.horizon_sky_from);
skyHorizonColorTo = r.getColor(R.color.horizon_sky_to);
groundHorizonColorFrom = r.getColor(R.color.horizon_ground_from);
groundHorizonColorTo = r.getColor(R.color.horizon_ground_to);
複製程式碼

(6) 建立用來填充圓的每個部分(地面和天空)的路徑。每一部分的比例應該與形式化之後的俯仰值有關。

java程式碼:
Path skyPath = new Path();
skyPath.addArc(innerBoundingBox, -tiltDegree, (180 + (2 * tiltDegree)));

複製程式碼

   (7) 將Canvas圍繞圓心,按照與當前翻轉角相反的方向進行旋轉,並且使用在第(4)步中所建立的Paint來繪製天空和地面路徑。 

java程式碼:
canvas.rotate(-rollDegree, px, py);
canvas.drawOval(innerBoundingBox, groundPaint);
canvas.drawPath(skyPath, skyPaint);
canvas.drawPath(skyPath, markerPaint);

複製程式碼

    (8) 接下來是盤面標記,首先計算水平的水平標記的起止點。 

java程式碼:
int markWidth = radius / 3; int startX = center.x - markWidth; int endX = center.x + markWidth;

複製程式碼

   (9) 要讓水平值更易於讀取,應該保證俯仰角刻度總是從當前值開始。下面的程式碼計算了天空和地面的介面在水平面上的位置: 

java程式碼:
double h = innerRadius*Math.cos(Math.toRadians(90-tiltDegree)); double justTiltY = center.y - h;

複製程式碼

    (10) 找到表示每一個傾斜角的畫素的數目。 

java程式碼:
float pxPerDegree = (innerBoundingBox.height()/2)/45f;

複製程式碼

   (11) 現在遍歷180度,以當前的傾斜值為中心,給出一個可能的俯仰角的滑動刻度。 

java程式碼:
for (int i = 90; i >= -90; i -= 10) {
double ypos = justTiltY + i*pxPerDegree;
// 只顯示內錶盤的刻度
if ((ypos < (innerBoundingBox.top + textHeight)) || (ypos > innerBoundingBox.bottom - textHeight)) continue;
// 為每一個刻度增加畫一個直線和一個傾斜角
canvas.drawLine(startX, (float)ypos, endX, (float)ypos, markerPaint);
t displayPos = (int)(tiltDegree - i);
String displayString = String.valueOf(displayPos);
float stringSizeWidth = textPaint.measureText(displayString);
canvas.drawText(displayString, (int)(center.x-stringSizeWidth/2), (int)(ypos)+1, textPaint);
}

複製程式碼

   (12) 現在,在大地/天空介面處繪製一條更粗的線。在畫線之前,改變markerPaint物件的線條粗度(然後把它設定回以前的值)。 

java程式碼:
markerPaint.setStrokeWidth(2);
canvas.drawLine(center.x - radius / 2, (float)justTiltY, center.x + radius / 2, (float)justTiltY, markerPaint);
markerPaint.setStrokeWidth(1);

複製程式碼

   (13) 要讓使用者能夠更容易地讀取精確的翻轉值,應該畫一個箭頭,並顯示一個文字字串來表示精確值。 

   建立一個新的Path,並使用moveTo/lineTo方法構建一個開放的箭頭,它指向直線的前方。然後繪製路徑和一個文字字串來展示當前的翻轉。 

java程式碼:
// 繪製箭頭
Path rollArrow = new Path();
rollArrow.moveTo(center.x - 3, (int)innerBoundingBox.top + 14);
rollArrow.lineTo(center.x, (int)innerBoundingBox.top + 10);
rollArrow.moveTo(center.x + 3, innerBoundingBox.top + 14);
rollArrow.lineTo(center.x, innerBoundingBox.top + 10);
canvas.drawPath(rollArrow, markerPaint);
// 繪製字串
String rollText = String.valueOf(rollDegree);
double rollTextWidth = textPaint.measureText(rollText);
canvas.drawText(rollText, (float)(center.x - rollTextWidth / 2), innerBoundingBox.top + textHeight + 2, textPaint);

複製程式碼

   (14) 將Canvas旋轉到正上方,這樣就可以繪製其他的盤面標記了。 

java程式碼:
canvas.restore();

複製程式碼

     (15) 每次將Canvas旋轉10度,然後畫一個標記或者一個值,直到畫完翻轉值錶盤為止。當完成錶盤之後,把Canvas恢復為正上方的方向。 

java程式碼:
canvas.save();
canvas.rotate(180, center.x, center.y);
for (int i = -180; i < 180; i += 10) {
// 每30度顯示一個數字值
if (i % 30 == 0) {
String rollString = String.valueOf(i*-1);
float rollStringWidth = textPaint.measureText(rollString);
PointF rollStringCenter = new PointF(center.x-rollStringWidth / 2, innerBoundingBox.top+1+textHeight);
canvas.drawText(rollString, rollStringCenter.x, rollStringCenter.y, textPaint);
}
// 否則,繪製一個標記直線
else { canvas.drawLine(center.x, (int)innerBoundingBox.top, center.x, (int)innerBoundingBox.top + 5, markerPaint);
}
canvas.rotate(10, center.x, center.y);
}

canvas.restore();

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1224/738.html
Android繪圖:繪製直線的 drawLine方法
【功能說明】該方法用於在畫布上繪製直線,通過指定直線的兩個端點座標來繪製。該方法只能繪製單條直線;如果需要同時繪製多條直線,則可以使用drawLines方法。
【基本語法】public void drawLine (float startX, float startY, float stopX, float stopY, Paint paint)
引數說明
startX:起始端點的X座標。
startY:起始端點的Y座標。
stopX:終止端點的X座標。
stopY:終止端點的Y座標。
paint:繪製直線所使用的畫筆。
【例項演示】下面通過程式碼來演示如何在畫布上繪製直線。
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
mPaint.setColor(Color.BLACK); //設定畫筆顏色
canvas.drawColor(Color.WHITE); //設定背景顏色
mPaint.setStrokeWidth((float) 1.0); //設定線寬
canvas.drawLine(50, 50, 450, 50, mPaint); //繪製直線
mPaint.setStrokeWidth((float) 5.0); //設定線寬
canvas.drawLine(50, 150, 450, 150, mPaint); //繪製直線
mPaint.setStrokeWidth((float) 10.0); //設定線寬
canvas.drawLine(50, 250, 450, 250, mPaint); //繪製直線
mPaint.setStrokeWidth((float) 15.0); //設定線寬
canvas.drawLine(50, 350, 450, 350, mPaint); //繪製直線
mPaint.setStrokeWidth((float) 20.0); //設定線寬
canvas.drawLine(50, 450, 450, 450, mPaint); //繪製直線
}
每條直線的線寬都不一樣。