畫布(Canvas)是圖形編程中一個(gè)很普通的概念,通常由三個(gè)基本的繪圖組件組成:
Canvas 提供了繪圖方法,可以向底層的位圖繪制基本圖形。
Paint 也稱為"刷子",Paint可以指定如何將基本圖形繪制到位圖上。
Bitmap 繪圖的表面。
Android繪圖API支持透明度、漸變填充、圓邊矩形和抗鋸齒。遺憾的是,由于資源限制,它還不支持矢量圖形,它使用的是傳統(tǒng)光柵樣式的重新繪圖。
這種光柵方法的結(jié)果是提高了效率,但是改變一個(gè)Paint對象不會(huì)影響已經(jīng)畫好的基本圖形,它只會(huì)影響新的元素。
提示:
如果你擁有Windows開發(fā)背景,那么Android的2D繪圖能力大致相當(dāng)于GDI+的能力。
1. 可以畫什么?
Canvas類封裝了用作繪圖表面的位圖;它還提供了draw*方法來實(shí)現(xiàn)設(shè)計(jì)。
下面的列表提供了對可用的基本圖形的簡要說明,但并沒有深入地探討每一個(gè)draw方法的詳細(xì)內(nèi)容:
drawARGB / drawRGB / drawColor 使用單一的顏色填充畫布。
drawArc 在一個(gè)矩形區(qū)域的兩個(gè)角之間繪制一個(gè)弧。
drawBitmap 在畫布上繪制一個(gè)位圖。可以通過指定目標(biāo)大小或者使用一個(gè)矩陣來改變目標(biāo)位圖的外觀。
drawBitmapMesh 使用一個(gè)mesh(網(wǎng))來繪制一個(gè)位圖,它可以通過移動(dòng)網(wǎng)中的點(diǎn)來操作目標(biāo)的外觀。
drawCircle 以給定的點(diǎn)為圓心,繪制一個(gè)指定半徑的圓。
drawLine(s) 在兩個(gè)點(diǎn)之間畫一條(多條)直線。
drawOval 以指定的矩形為邊界,畫一個(gè)橢圓。
drawPaint 使用指定的Paint填充整個(gè)Canvas
drawPath 繪制指定的Path。Path對象經(jīng)常用來保存一個(gè)對象中基本圖形的集合。
drawPicture 在指定的矩形中繪制一個(gè)Picture對象。
drawPosText 繪制指定了每一個(gè)字符的偏移量的文本字符串。
drawRect 繪制一個(gè)矩形。
drawRoundRect 繪制一個(gè)圓角矩形。
drawText 在Canvas上繪制一個(gè)文本串。文本的字體、大小和渲染屬性都設(shè)置在用來渲染文本的Paint對象中。
drawTextOnPath 在一個(gè)指定的path上繪制文本。
drawVertices 繪制一系列三角形面片,通過一系列頂點(diǎn)來指定它們。
這些繪圖方法中的每一個(gè)都需要指定一個(gè)Paint對象來渲染它。在下面的部分中,將學(xué)習(xí)如何創(chuàng)建和修改Paint對象,從而在繪圖中完成大部分工作。
2. 從Paint中完成工作
Paint類相當(dāng)于一個(gè)筆刷和調(diào)色板。它可以選擇如何使用上面描述的draw方法來渲染繪制在畫布上的基本圖形。通過修改Paint對象,可以在繪圖的時(shí)候控制顏色、樣式、字體和特殊效果。最簡單地,setColor可以讓你選擇一個(gè)Paint的顏色,而Paint對象的樣式(使用setStyle控制)則可以決定是繪制繪圖對象的輪廓(STROKE),還是只填充每一部分(FILL),或者是兩者都做(STROKE_AND_FILL)
除了這些簡單的控制之外,Paint類還支持透明度,另外,它也可以通過使用各種各樣的陰影、過濾器和效果進(jìn)行修改,從而提供由更豐富的、復(fù)雜的畫筆和顏料組成的調(diào)色板。
Android SDK包含了一些非常好的實(shí)例,它們說明了Paint類中可用的大部分功能。你可以在API demos的graphics子目錄中找到它們:
sdk root folder]\samples\ApiDemos\src\com\android\samples\graphics
在下面的部分中,將學(xué)習(xí)和使用其中的部分功能。這些部分只是簡單地羅列了它們能實(shí)現(xiàn)的效果(例如漸變和邊緣浮雕),而沒有詳細(xì)地列出所有可能的情況。
使用透明度
Android中的所有顏色都包含了一個(gè)不透明組件(alpha通道)。
當(dāng)創(chuàng)建一個(gè)顏色的時(shí)候,可以使用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);
創(chuàng)建一個(gè)不是100%透明的顏色意味著,使用它繪制的任何基本圖形都將是部分透明的--也就是說,在它下面繪制的所有基本圖形都是部分可見的。
可以在任何使用了顏色的類或者方法中使用透明效果,包括Paint、Shader和Mask Filter。
Shader介紹
Shader類的派生類可以創(chuàng)建允許使用多種固體顏色填充繪圖對象的Paint。
對Shader最常見的使用是定義漸變填充;漸變是在2D圖像中添加深度和紋理的最佳方式之一。Android包含了一個(gè)Bitmap Shader和一個(gè)Compose Shader,同時(shí),還包含了三個(gè)漸變的Shader。
試圖用語言來描述繪圖的效果本來就是沒有意義的,所以看一下圖就應(yīng)該可以知道每一種Shader是如何工作的。圖中從左到右依次代表的是LinearGradient、RadialGradient和 SweepGradient.
提示:
沒有包含的是ComposerShader,它可以創(chuàng)建多個(gè)Shader和BitmapShader的組合,從而可以在一個(gè)位圖圖像的基礎(chǔ)上創(chuàng)建一個(gè)繪圖刷。
要在繪圖的時(shí)候使用一個(gè)Shader,可以使用setShader方法將其應(yīng)用到一個(gè)Paint中,如下面的代碼所示:
Paint shaderPaint = new Paint();
shaderPaint.setShader(myLinearGradient);
你使用這個(gè)Paint所繪制的任何東西都將使用你指定的Shader進(jìn)行填充,而不是使用Paint本身的顏色進(jìn)行填充。
定義漸變Shader
如上所示,使用漸變Shader可以讓你使用交替改變的顏色來填充圖片;你可以將顏色漸變定義為兩種顏色的簡單交替,如下所示:
java代碼:
-
int colorFrom = Color.BLACK;
-
int colorTo = Color.WHITE;
-
LinearGradient linearGradientShader = new LinearGradient(x1, y1, x2, y2, colorFrom, colorTo, TileMode.CLAMP);
-
或者,你還可以定義更復(fù)雜的按照設(shè)定比例進(jìn)行分布的顏色序列,如下面的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(線性的、輻射形的和掃描狀的)都可以使用以上這兩種技術(shù)來定義漸變填充。
使用Shader TileModes
漸變Shader的畫刷大小既可以顯式地使用有邊界的矩形來定義,也可以使用中心點(diǎn)和半徑長度來定義。Bitmap Shader可以通過它的位圖大小來決定它的畫刷大小。
如果Shader畫刷所定義的區(qū)域比要填充的區(qū)域小,那么TileMode將會(huì)決定如何處理剩余的區(qū)域:
CLAMP 使用Shader的邊界顏色來填充剩余的空間。
MIRROR 在水平和垂直方向上拉伸Shader圖像,這樣每一個(gè)圖像就都能與上一個(gè)縫合了。
REPEAT 在水平和垂直方向上重復(fù)Shader圖像,但不拉伸它。
使用MaskFilter
MaskFilter類可以為Paint分配邊緣效果。
對MaskFilter的擴(kuò)展可以對一個(gè)Paint邊緣的alpha通道應(yīng)用轉(zhuǎn)換。Android包含了下面幾種MaskFilter:
BlurMaskFilter 指定了一個(gè)模糊的樣式和半徑來處理Paint的邊緣。
EmbossMaskFilter 指定了光源的方向和環(huán)境光強(qiáng)度來添加浮雕效果。
要應(yīng)用一個(gè)MaskFilter,可以使用setMaskFilter方法,并傳遞給它一個(gè)MaskFilter對象。下面的例子是對一個(gè)已經(jīng)存在的Paint應(yīng)用一個(gè)EmbossMaskFilter:
java代碼:
-
// 設(shè)置光源的方向
-
float[] direction = new float[]{ 1, 1, 1 };
-
//設(shè)置環(huán)境光亮度
-
float light = 0.4f;
-
// 選擇要應(yīng)用的反射等級
-
float specular = 6;
-
// 向mask應(yīng)用一定級別的模糊
-
float blur = 3.5f;
-
EmbossMaskFilter emboss=new EmbossMaskFilter(direction,light,specular,blur);
-
// 應(yīng)用mask myPaint.setMaskFilter(emboss);
-
SDK中包含的FingerPaint API demo是說明如何使用MaskFilter的一個(gè)非常好的例子。它展示了這兩種filter的效果。
使用ColorFilter
MaskFilter是對一個(gè)Paint的alpha通道的轉(zhuǎn)換,而ColorFilter則是對每一個(gè)RGB通道應(yīng)用轉(zhuǎn)換。所有由ColorFilter所派生的類在執(zhí)行它們的轉(zhuǎn)換時(shí),都會(huì)忽略alpha通道。
Android包含三個(gè)ColorFilter:
ColorMatrixColorFilter 可以指定一個(gè)4×5的ColorMatrix并將其應(yīng)用到一個(gè)Paint中。ColorMatrixes通常在程序中用于對圖像進(jìn)行處理,而且由于它們支持使用矩陣相乘的方法來執(zhí)行鏈接轉(zhuǎn)換,所以它們很有用。
LightingColorFilter 乘以第一個(gè)顏色的RGB通道,然后加上第二個(gè)顏色。每一次轉(zhuǎn)換的結(jié)果都限制在0到255之間。
PorterDuffColorFilter 可以使用數(shù)字圖像合成的16條Porter-Duff 規(guī)則中的任意一條來向Paint應(yīng)用一個(gè)指定的顏色。
使用setColorFilter方法應(yīng)用ColorFilter,如下所示:
myPaint.setColorFilter(new LightingColorFilter(Color.BLUE, Color.RED));
API中的ColorMatrixSample是說明如何使用ColorFilter和Color Matrix的非常好的例子。
使用PathEffect
到目前為止,所有的效應(yīng)都會(huì)影響到Paint填充圖像的方式;PathEffect是用來控制繪制輪廓(線條)的方式。
PathEffect對于繪制Path基本圖形特別有用,但是它們也可以應(yīng)用到任何Paint中從而影響線條繪制的方式。
使用PathEffect,可以改變一個(gè)形狀的邊角的外觀并且控制輪廓的外表。Android包含了多個(gè)PathEffect,包括:
CornerPathEffect 可以使用圓角來代替尖銳的角從而對基本圖形的形狀尖銳的邊角進(jìn)行平滑。
?
DashPathEffect 可以使用DashPathEffect來創(chuàng)建一個(gè)虛線的輪廓(短橫線/小圓點(diǎn)),而不是使用實(shí)線。你還可以指定任意的虛/實(shí)線段的重復(fù)模式。
DiscretePathEffect 與DashPathEffect相似,但是添加了隨機(jī)性。當(dāng)繪制它的時(shí)候,需要指定每一段的長度和與原始路徑的偏離度。
PathDashPathEffect 這種效果可以定義一個(gè)新的形狀(路徑)并將其用作原始路徑的輪廓標(biāo)記。
下面的效果可以在一個(gè)Paint中組合使用多個(gè)Path Effect。
SumPathEffect 順序地在一條路徑中添加兩種效果,這樣每一種效果都可以應(yīng)用到原始路徑中,而且兩種結(jié)果可以結(jié)合起來。
ComposePathEffect 將兩種效果組合起來應(yīng)用,先使用第一種效果,然后在這種效果的基礎(chǔ)上應(yīng)用第二種效果。
對象形狀的PathEffect的改變會(huì)影響到形狀的區(qū)域。這就能夠保證應(yīng)用到相同形狀的填充效果將會(huì)繪制到新的邊界中。
使用setPathEffect方法可以把PathEffect應(yīng)用到Paint對象中,如下所示:
java代碼:
-
borderPaint.setPathEffect(new CornerPathEffect(5));
-
PathEffect API示例給出了如何應(yīng)用每一種效果的指導(dǎo)說明。
修改Xfermode
可以通過修改Paint的Xfermode來影響在Canvas已有的圖像上面繪制新的顏色的方式。
在正常的情況下,在已有的圖像上繪圖將會(huì)在其上面添加一層新的形狀。如果新的Paint是完全不透明的,那么它將完全遮擋住下面的Paint;如果它是部分透明的,那么它將會(huì)被染上下面的顏色。
下面的Xfermode子類可以改變這種行為:
AvoidXfermode 指定了一個(gè)顏色和容差,強(qiáng)制Paint避免在它上面繪圖(或者只在它上面繪圖)。
PixelXorXfermode 當(dāng)覆蓋已有的顏色時(shí),應(yīng)用一個(gè)簡單的像素XOR操作。
PorterDuffXfermode 這是一個(gè)非常強(qiáng)大的轉(zhuǎn)換模式,使用它,可以使用圖像合成的16條Porter-Duff規(guī)則的任意一條來控制Paint如何與已有的Canvas圖像進(jìn)行交互。
要應(yīng)用轉(zhuǎn)換模式,可以使用setXferMode方法,如下所示:
java代碼:
-
AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID); borderPen.setXfermode(avoid);
-
3. 使用抗鋸齒效果提高Paint質(zhì)量
在繪制一個(gè)新的Paint對象時(shí),可以通過傳遞給它一些標(biāo)記來影響它被渲染的方式。ANTI_ALIAS_FLAG是其中一種很有趣的標(biāo)記,它可以保證在繪制斜線的時(shí)候使用抗鋸齒效果來平滑該斜線的外觀。
在繪制文本的時(shí)候,抗鋸齒效果尤為重要,因?yàn)榻?jīng)過抗鋸齒效果處理之后的文本非常容易閱讀。要?jiǎng)?chuàng)建更加平滑的文本效果,可以應(yīng)用SUBPIXEL_TEXT_FLAG,它將會(huì)應(yīng)用子像素抗鋸齒效果。
也可以手工地使用setSubpixelText和setAntiAlias方法來設(shè)置這些標(biāo)記,如下所示:
java代碼:
-
myPaint.setSubpixelText(true);
-
myPaint.setAntiAlias(true);
-
2D圖形的硬件加速
在當(dāng)前這個(gè)到處都是2D圖形愛好者的時(shí)代,Android允許你使用硬件加速來渲染你的應(yīng)用程序。
如果設(shè)備可以使用硬件加速,那么通過設(shè)置這個(gè)標(biāo)記可以讓活動(dòng)中的每一個(gè)View都能使用硬件渲染。盡管減少了系統(tǒng)處理程序的負(fù)載,但在極大地提高了圖像處理速度的同時(shí),硬件加速也帶來了相應(yīng)的負(fù)面效果。
使用requestWindowFeature方法,可以在你的活動(dòng)中應(yīng)用Window.FEATURE_OPENGL標(biāo)記來打開硬件加速,如下所示:
java代碼:
-
myActivity.requestWindowFeature(Window.FEATURE_OPENGL);
-
并不是Android中所有的2D繪圖基本圖形都被硬件支持(特別是前面描述的大部分PathEffect)。
與此同時(shí),由于整個(gè)活動(dòng)實(shí)際上是作為一個(gè)Canvas進(jìn)行渲染的,所以對任何View的無效請求都將會(huì)導(dǎo)致整個(gè)活動(dòng)被重新繪制。
Canvas繪圖最佳實(shí)踐經(jīng)驗(yàn)
2D自繪操作是非常耗費(fèi)處理程序資源的;低效的繪圖方法會(huì)阻塞GUI線程,并且會(huì)對應(yīng)用程序的響應(yīng)造成不利的影響。對于那些只有一個(gè)處理程序的資源受限的環(huán)境來說,這一點(diǎn)就更加現(xiàn)實(shí)了。
這里需要注意onDraw方法的資源消耗以及CPU周期的耗費(fèi),這樣才能保證不會(huì)把一個(gè)看起來很吸引人的應(yīng)用程序變得完全沒有響應(yīng)。
目前有很多技術(shù)可以幫助將與自繪控件相關(guān)的資源消耗最小化。我們關(guān)心的不是一般的原則,而是某些Android特定的注意事項(xiàng),從而保證你可以創(chuàng)建外觀時(shí)尚、而且能夠保持交互的活動(dòng)(注意,以下這個(gè)列表并不完整):
考慮硬件加速 OpenGL硬件加速對2D圖形的支持是非常好的,所以你總是應(yīng)該考慮它是否適合你的活動(dòng)。另一種比較優(yōu)秀的方法是只用一個(gè)單獨(dú)的View和迅速的、耗時(shí)的更新來組成活動(dòng)。一定要保證你使用的基本圖形能夠被硬件支持。
考慮大小和方向 當(dāng)在設(shè)計(jì)View和布局的時(shí)候,一定要保證考慮(和測試)它們在不同的分辨率和大小下的外觀。
只創(chuàng)建一次靜態(tài)對象 在Android中對象的創(chuàng)建是相當(dāng)昂貴的。因此,在可能的地方,應(yīng)用只創(chuàng)建一次像Paint對象、Path和Shader這樣的繪圖對象,而不是在View每次無效的時(shí)候都重新創(chuàng)建它們。
記住onDraw是很消耗資源的 執(zhí)行onDraw方法是很消耗資源的處理,它會(huì)強(qiáng)制Android執(zhí)行多個(gè)圖片組合和位圖構(gòu)建操作。下面有幾點(diǎn)建議可以讓你修改Canvas的外觀,而不用重新繪制它:
使用Canvas轉(zhuǎn)換 可以使用像rotate和translate這樣的轉(zhuǎn)換,來簡化Canvas中元素復(fù)雜的相關(guān)位置。例如,相比放置和旋轉(zhuǎn)一個(gè)表盤周圍的每一個(gè)文本元素,你可以簡單地將canvas旋轉(zhuǎn)22.5?,然后在相同的位置繪制文本。
使用動(dòng)畫 可以考慮使用動(dòng)畫來執(zhí)行View的預(yù)設(shè)置的轉(zhuǎn)換,而不是手動(dòng)地重新繪制它。在活動(dòng)的View中可以執(zhí)行縮放、旋轉(zhuǎn)和轉(zhuǎn)換動(dòng)畫,并可以提供一種能夠有效利用資源的方式來提供縮放、旋轉(zhuǎn)或者抖動(dòng)效果。
考慮使用位圖和9 Patch 如果View使用了靜態(tài)背景,那么你應(yīng)該考慮使用一個(gè)圖片,如位圖或者9 patch,而不是手動(dòng)地重新繪制。
高級指南針表盤的例子
已經(jīng)創(chuàng)建了一個(gè)簡單的指南針。而在上一章,你又回到了這個(gè)例子,對它進(jìn)行了擴(kuò)展從而使它夠使用加速計(jì)硬件來顯示橫向和縱向方向。
那些例子中的UI都很簡單,從而保證了那些章節(jié)中的代碼都盡可能地清晰。
在下面的例子中,將對CompassView的onDraw方法做一些重要的改動(dòng),從而把它從一個(gè)簡單的、平面的指南針,變成一個(gè)動(dòng)態(tài)的航空地平儀(artificial horizon ),如圖所示。
?
由于上面的圖片是黑白的,所以需要實(shí)際動(dòng)手創(chuàng)建這個(gè)控件來看到完全的效果。
?
(1) 首先,通過修改colors.xml資源文件來包含邊界、表盤陰影以及天空和地面的顏色值。同時(shí)還要更新邊界和盤面標(biāo)記所使用的顏色。
?
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對象是根據(jù)當(dāng)前View的大小創(chuàng)建的,所以它們不能像你在創(chuàng)建的Paint對象那樣,是靜態(tài)的。因此,不再創(chuàng)建Paint對象,取而代之的是構(gòu)造它們所使用的漸變數(shù)組和顏色。
java代碼:
-
int[] borderGradientColors;
-
float[] borderGradientPositions;
-
int[] glassGradientColors;
-
float[] glassGradientPositions;
-
int skyHorizonColorFrom;
-
int skyHorizonColorTo;
-
int groundHorizonColorFrom;
-
int groundHorizonColorTo;
-
(3) 更新CompassView的initCompassView方法,來使用第(1)步中所創(chuàng)建的資源來初始化第(2)步中所創(chuàng)建的變量。現(xiàn)存的方法代碼大部分可以保留,而只需要對textPaint、circlePaint和markerPaint變量做些許改動(dòng),如下所示:
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. 創(chuàng)建徑向Shader用來繪制外邊界所使用的顏色和位置數(shù)組。
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. 現(xiàn)在創(chuàng)建徑向漸變的顏色和位置數(shù)組,它們將用來創(chuàng)建半透明的"glass dome"(玻璃圓頂),它放置在View的上面,從而使人產(chǎn)生深度的幻覺。
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. 最后,獲得創(chuàng)建線性顏色漸變所使用的顏色,它們將用來表示航空地平儀中的天空和地面。
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圍繞圓心,按照與當(dāng)前翻轉(zhuǎn)角相反的方向進(jìn)行旋轉(zhuǎn),并且使用在第(4)步中所創(chuàng)建的Paint來繪制天空和地面路徑。
java代碼:
-
canvas.rotate(-rollDegree, px, py);
-
canvas.drawOval(innerBoundingBox, groundPaint);
-
canvas.drawPath(skyPath, skyPaint);
-
canvas.drawPath(skyPath, markerPaint);
-
(8) 接下來是盤面標(biāo)記,首先計(jì)算水平的水平標(biāo)記的起止點(diǎn)。
java代碼:
-
int markWidth = radius / 3; int startX = center.x - markWidth; int endX = center.x + markWidth;
-
(9) 要讓水平值更易于讀取,應(yīng)該保證俯仰角刻度總是從當(dāng)前值開始。下面的代碼計(jì)算了天空和地面的接口在水平面上的位置:
java代碼:
-
double h = innerRadius*Math.cos(Math.toRadians(90-tiltDegree)); double justTiltY = center.y - h;
-
(10) 找到表示每一個(gè)傾斜角的像素的數(shù)目。
java代碼:
-
float pxPerDegree = (innerBoundingBox.height()/2)/45f;
-
(11) 現(xiàn)在遍歷180度,以當(dāng)前的傾斜值為中心,給出一個(gè)可能的俯仰角的滑動(dòng)刻度。
java代碼:
-
for (int i = 90; i >= -90; i -= 10) {
-
double ypos = justTiltY + i*pxPerDegree;
-
// 只顯示內(nèi)表盤的刻度
-
if ((ypos < (innerBoundingBox.top + textHeight)) || (ypos > innerBoundingBox.bottom - textHeight)) continue;
-
// 為每一個(gè)刻度增加畫一個(gè)直線和一個(gè)傾斜角
-
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) 現(xiàn)在,在大地/天空接口處繪制一條更粗的線。在畫線之前,改變markerPaint對象的線條粗度(然后把它設(shè)置回以前的值)。
java代碼:
-
markerPaint.setStrokeWidth(2);
-
canvas.drawLine(center.x - radius / 2, (float)justTiltY, center.x + radius / 2, (float)justTiltY, markerPaint);
-
markerPaint.setStrokeWidth(1);
-
(13) 要讓用戶能夠更容易地讀取精確的翻轉(zhuǎn)值,應(yīng)該畫一個(gè)箭頭,并顯示一個(gè)文本字符串來表示精確值。
創(chuàng)建一個(gè)新的Path,并使用moveTo/lineTo方法構(gòu)建一個(gè)開放的箭頭,它指向直線的前方。然后繪制路徑和一個(gè)文本字符串來展示當(dāng)前的翻轉(zhuǎn)。
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旋轉(zhuǎn)到正上方,這樣就可以繪制其他的盤面標(biāo)記了。
java代碼:
-
canvas.restore();
-
(15) 每次將Canvas旋轉(zhuǎn)10度,然后畫一個(gè)標(biāo)記或者一個(gè)值,直到畫完翻轉(zhuǎn)值表盤為止。當(dāng)完成表盤之后,把Canvas恢復(fù)為正上方的方向。
java代碼:
-
canvas.save();
-
canvas.rotate(180, center.x, center.y);
-
for (int i = -180; i < 180; i += 10) {
-
// 每30度顯示一個(gè)數(shù)字值
-
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);
-
}
-
// 否則,繪制一個(gè)標(biāo)記直線
-
else { canvas.drawLine(center.x, (int)innerBoundingBox.top, center.x, (int)innerBoundingBox.top + 5, markerPaint);
-
}
-
canvas.rotate(10, center.x, center.y);
-
}
- canvas.restore();
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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