畫布(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");
-
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);
-
3. 使用抗鋸齒效果提高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 ),如圖所示。
?
由于上面的圖片是黑白的,所以需要實際動手創建這個控件來看到完全的效果。
?
(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);
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();
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
