Android群英傳知識點回顧——第六章:Android繪圖機制與處理技巧
- 6.1 屏幕的尺寸信息
- 6.1.1 屏幕參數
- 6.1.2 系統屏幕密度
- 6.1.3 獨立像素密度dp
- 6.1.4 單位轉換
- 6.2 2D繪圖基礎
- 6.3 Android XML繪圖
- 6.3.1 Bitmap
- 6.3.2 Shape
- 6.3.3 Layer
- 6.3.4 Selector
- 6.4 Android繪圖技巧
- 6.4.1 Canvas
- 6.4.2 Layer圖層
- 6.5 Android圖像處理之色彩特效處理
- 6.5.1 色彩矩陣分析
- 6.5.2 Android顏色矩陣——ColorMatrix
- 6.5.3 常用圖像顏色矩陣處理效果
- 6.5.4 像素點分析
- 6.5.5 常用圖像像素點處理效果
- 6.6 Android圖像處理之圖形特效處理
- 6.6.1 Android變形矩陣——Matrix
- 6.6.2 像素塊分析
- 6.7 Android圖像處理之畫筆特效處理
- 6.7.1 PorterDuffXfermode
- 6.7.2 Shader
- 6.7.3 PathEffect
- 6.8 View之孿生兄弟——SurfaceView
- 6.8.1 SurfaceView與View的區別
- 6.8.2 SurfaceView的使用
- 6.8.3 SurfaceView實例
由於這一章比較難理解,所以大部分知識點都是摘抄原文的,如果是沒學習過線性代數的同學,那就難上加難了,不過克服它,是你進階高級工程師的必經之路,文章比較長,請耐心觀看
無知識點
- 屏幕大小:屏幕對角線的長度,通常用寸來度量
- 分辨率:手機屏幕的像素點個數
- PPI:每英寸像素,又稱DPI,由對角線像素點除以屏幕大小獲得
Android系統使用mdpi即密度值為160的屏幕作為標準,在這個屏幕上1px=1dp
ldpi:mdpi:hdpi:xhdpi:xxhdpi=3:4:6:8:12
這裏加0.5f的巧用目的是:四舍五入
dp==dip
系統提供了TypedValue類幫助我們轉換
Paint作為一個重要的元素有以下方法:
- setAntiAlias():設置畫筆的鋸齒效果
- setColor():設置畫筆的顏色
- setARGB():設置畫筆的A、R、G、B值
- setAlpha():設置畫筆的Alpha值
- setTextSize():設置字體的尺寸
- setStyle():設置畫筆的風格(空心或實心)
- setStrokeWidth():設置空心邊框的寬度
設置Paint的Style可以畫出空心或者實心的矩形:
- paint.setStyle(Paint.Style.STROKE):空心效果
- paint.setStyle(Paint.Style.FILL):實心效果
系統通過提供的Canvas對象來提供繪圖方法:
- canvas.drawPoint(x, y, paint):繪制點
- canvas.drawLine(startX, startY ,endX, endY, paint):繪制直線
- canvas.drawLines(new float[]{startX1, startY1, endX1, endY1,……,startXn, startYn, endXn, endYn}, paint):繪制多條直線
- canvas.drawRect(left, top, right, bottom, paint):繪制矩形
- canvas.drawRoundRect(left, top, right, bottom, radiusX, radiusY, paint):繪制圓角矩形
- canvas.drawCircle(circleX, circleY, radius, paint):繪制圓
- canvas.drawOval(left, top, right, bottom, paint):繪制橢圓
- canvas.drawText(text, startX, startY, paint):繪制文本
- canvas.drawPosText(text, new float[]{X1,Y1,X2,Y2,……Xn,Yn}, paint):在指定位置繪制文本
- Path path=new Path();
path.moveTo(50, 50);
path.lineTo(100, 100);
path.lineTo(100, 300);
path.lineTo(300, 50);
canvas.drawPath(path, paint):繪制路徑
- paint.setStyle(Paint.Style.STROKE);
drawArc(left, top, right,bottom, startAngle, sweepAngle, true, paint):繪制扇形- paint.setStyle(Paint.Style.STROKE);
drawArc(left, top, right,bottom, startAngle, sweepAngle, false, paint):繪制弧形- paint.setStyle(Paint.Style.FILL);
drawArc(left, top, right,bottom, startAngle, sweepAngle, true, paint):繪制實心扇形- paint.setStyle(Paint.Style.FILL);
drawArc(left, top, right,bottom, startAngle, sweepAngle, false, paint):繪制實心弧形
無知識點
通過這樣在XML中使用Bitmap就可以將圖片直接轉換成了Bitmap在程序中使用
通過Shape可以在XML中繪制各種形狀
下面通過漸變來實現的陰影效果
通過Layer會產生圖片依次疊加的效果
Selector的作用在於幫助開發者實現靜態繪圖中的事件反饋,通過給不同的事件設置不同的圖像,從而在程序中根據用戶輸入,返回不同的效果
下面這個例子實現了一個具有點擊反饋效果的、圓角舉證Selector
無知識點
Canvas提供了以下幾種非常有用的方法:
- Canvas.save():保存畫布,將之前所有已繪制圖像保存起來,讓後續的操作就好像在一個新的圖層上操作一樣
- Canvas.restore():在save()之後繪制的所有圖像與save()之前的圖像進行合並,可以理解為Photoshop中的合並圖層操作
- Canvas.translate():畫布平移
- Canvas.roate():畫布翻轉
通過一個實例——儀表盤,來理解這幾個方法,將儀表盤分為以下幾個元素:
- 儀表盤:外面的大圓盤
- 刻度線:包含四個長的刻度線和其他短的刻度線
- 刻度值:包含長刻度線對應的大的刻度值和其他小的刻度值
- 指針:中間的指針,一粗一細兩根指針
儀表盤……見經典代碼案例一
一張復雜的畫可以由很多個圖層疊加起來,形成一個復雜的圖像,使用saveLayer()方法來創建一個圖層,圖層同樣是基於棧的結構進行管理的
Android通過調用saveLayer()方法,saveLayerAlpha()方法將一個圖層入棧,使用restore()方法、restoreToCount()方法將一個圖層出棧,仿照API Demos裏面的一個實例來使用Layer
- 當透明度為127時,即半透明
- 當透明度為255時,即完全不透明
- 當透明度為0時,即完全透明
Bitmap圖片都是由點陣和顏色值組成的,所謂點陣就是一個包含像素的矩陣,每一個元素對應著圖片的一個像素。而顏色值——ARGB,分別對應透明度、紅、綠、藍這四個通道分量,它們共同決定了每個像素點顯示的顏色
在色彩處理中,我們通常用三個角度描述一張圖片:
- 色調:物體傳播的顏色
- 飽和度:顏色的純度,從0(灰)-100%(飽和)來進行描述
- 亮度:顏色的相對明暗程度
而在Android中,系統會使用一個顏色矩陣——ColorMatrix,來處理這些色彩的效果,Android中的顏色矩陣是4X5的數字矩陣,他用來對顏色色彩進行處理,而對於每一個像素點,都有一個顏色分量矩陣來保存ARGB值
根據前面對矩陣A、C的定義,通過矩陣乘法運算法則,可以得到:
矩陣運算的乘法計算過程如下:
我們觀察顏色矩陣A
從這個公式可以發現
- 第一行的abcde用來決定新的顏色值R——紅色
- 第二行的fghij用來決定新的顏色值G——綠色
- 第三行的kimno用來決定新的顏色值B——藍色
- 第四行的pqrst用來決定新的顏色值A——透明度
- 矩陣A中的第五列——ejot值分別用來決定每個分量中的offset,即偏移量
通過一個小例子來講解:
首先重新看一下矩陣變換計算公式,以R分量為例,計算過程如下:
如果讓a=1,b,c,d,e都等於0,那麽計算的結果為R1=R,因此我們可以構建一個矩陣
如果把這個矩陣公式帶入R1=AC,那麽根據矩陣的乘法運算法則,可以得到R1=R。因此,這個矩陣通常是用來作為初始的顏色矩陣來使用,他不會對原有顏色進行任何變化
那麽當我們要變換顏色值的時候,通常有兩種方法。一個是直接改變顏色的offset,即偏移量的值來修改顏色的分量。另一種方法直接改變對應RGBA值的系數來調整顏色分量的值
從前面的分析中,可以知道要修改R1的值,只要將第五列的值進行修改即可。即改變顏色的偏移量,其它值保存初始矩陣的值,如圖:
在上面這個矩陣中,我們修改了R、G所對應的顏色偏移量,那麽最後的處理結果就是圖像的紅色、綠色分別增加了100。而我們知道,紅色混合綠色會得到黃色,所以最終的顏色處理結果就是讓整個圖片的色調偏黃色
如果修改顏色分量中的某個系數值,而其他只依然保存初始矩陣的值,如圖:
在上面這個矩陣中,改變了G分量所對應的系數g,這樣在矩形運算後G分量會變成以前的兩倍,最終效果就是圖像的色調更加偏綠
下面通過實例看看如何通過矩陣改變圖像的色調、飽和度和亮度:
- 色調:setRotate(int axis, float degree),第一個參數分別使用0、1、2代表Red、Green、Blue三種顏色,第二參數需要處理的值
- 飽和度:setSaturation(float sat),參數代表設置飽和度的值
- 亮度:setScale(float rscale,float gscale,float bscale,float ascale),參數分別是紅、綠、藍、透明度的亮度大小值
除了單獨使用上面三種方式來進行顏色效果處理之外,還提供了postConcat()方法來將矩陣的作用效果混合,從而疊加處理效果
通過SeekBar調節色調、飽和度、亮度……見經典代碼回顧案例二
模擬4x5的顏色矩陣……見經典代碼回顧案例三
- 灰色效果
- 圖像反轉
- 懷舊效果
- 去色效果
- 高飽和度
在Android中,系統系統提供了Bitmap.getPixels()方法來幫我們提取整個Bitmap中的像素點,並保存在一個數組中:
這幾個參數的具體含義如下:
- pixels:接收位圖顏色值的數組
- offset:寫入到pixels[]中的第一個像素索引值
- stride:pixels[]中的行間距
- x:從位圖中讀取的第一個像素的x坐標值
- y:從位圖中讀取的第一個像素的y坐標值
- width:從每一行中讀取的像素寬度
- height:讀取的行數
通常使用如下代碼:
接下來獲取每個像素具體的ARGB值:
接下來就是修改像素值,產生新的像素值
最後使用我們的新像素值
底片效果、老照片效果、浮雕效果……見經典代碼回顧案例四
無知識點
對於圖形變換,系統提供了3x3的舉證來處理:
與顏色矩陣一樣,計算方法通過矩陣乘法:
與顏色矩陣一樣,也有一個初始矩陣:
圖像的變形處理包含以下四類基本變換:
- Translate:平移變換
- Rotate:旋轉變換
- Scale:縮放變換
- Skew:錯切變換
平移變換:即對每個像素點都進行平移變換,通過計算可以發現如下平移公式:
旋轉變換:通過以下三步驟完成以任意點為旋轉中心的旋轉變換
- 將坐標原點平移到O點
- 使用前面講的以坐標原點為中心的旋轉方法進行旋轉變換
- 將坐標原點還原
縮放變換:縮放變換的效果計算公式如下
錯切變換:錯切變換的效果計算公式如下
了解四種圖形變換矩陣,可以通過setValues()方法將一個一維數組轉換為圖形變換矩陣:
Android中Matrix類也幫我們封裝好了幾個操作方法:
- matrix.setRotate():旋轉變換
- matrix.setTranslate():平移變換
- matrix.setScale():縮放變換
- matrix.setSkew():錯切變換
- pre()和post():提供矩陣的前乘和後乘運算
舉個例子說明前乘和後乘的不同運算方式:
- 先平移到(300, 100)
- 再旋轉45度
- 最後平移到(200, 200)
如果使用後乘運算,代碼如下:
如果使用前乘運算,代碼如下:
drawBitmapMesh()與操縱像素點來改變色彩的原理類似,只不過是把圖像分成了一個個的小塊,然後通過改變每一個圖像塊來修改整個圖像:
參數分析:
- bitmap:將要扭曲的圖像
- meshWidth:需要的橫向網格數目
- meshHeight:需要的縱向網格數目
- verts:網格交叉點的坐標數組
- vertOffset:verts數組中開始跳過的(X,Y)坐標對的數目
飄動的旗子……見經典代碼回顧案例五
之前繪圖的時候也提到過畫筆的一些方法,這裏就不再介紹
PorterDuffXfermod設置的是兩個圖層交集區域的顯示方式,dst是先畫的圖形,而src是後畫的圖形
以一個圓角圖片為例子:
效果圖,由於圖片過大,只能看出一邊角
刮刮卡效果……見經典代碼回顧案例六
Shader又被稱為著色器。渲染器,它可以實現渲染,漸變等效果,Android中的Shader包括以下幾種:
- BitmapShader:位圖Shader
- LinearGradient:線性Shader
- RadialGradient:光束Shader
- SweepGradient:梯形Shader
- ComposeShader:混合Shader
其中BitmapShader有三種模式可以選擇:
- CLAMP拉伸:拉伸的是圖片最後的那一個像素,不斷重復
- REPEAT重復:橫向,縱向不斷重復
- MIRROR鏡像:橫向不斷翻轉重復,縱向不斷翻轉重復
下面看下例子說明,圓形圖片:
效果圖
下面把TileMode改為REPEAT:
使用LinearGradient:
效果圖
結合前面的PorterDuffXfermode和剛學的LinearGradient,制作出倒影的效果圖片
倒影圖片效果……見經典代碼回顧案例七
先上一張直觀的圖:
Android提供的幾種繪制PathEffect方式:
- 沒效果
- CornerPathEffect:拐彎角變得圓滑
- DiscretePathEffect:線段上會產生許多雜點
- DashPathEffect:繪制虛線,用一個數據來設置各個點之間的間隔
- PathDashPathEffect:繪制虛線,可以使用方形點虛線和圓形點虛線
- ComposePathEffect:可任意組合兩種路徑(PathEffect)的特性
我們通過一個實例來認識這些效果:
每繪制一個Path,就將畫布平移,從而讓各種PathEffect依次繪制出來
無知識點
View的繪制刷新間隔時間為16ms,如果在16ms內完成你所需要執行的所有操作,那麽在用戶視覺上,就不會產生卡頓的感覺,否則,就會出現卡頓,所以可以考慮使用SurfaceView來替代View的繪制
通常在Log會看到這樣的提示:
SurfaceView與View的主要區別:
- View主要適用於主動更新的情況下,而surfaceVicw主要適用於被動更新,例如頻繁刷新
- View在主線程中對畫面進行刷新,而surfaceView通常會通過一 個子線程來進行頁面的刷新
- View在繪制時沒有使用雙緩沖機制,而surfaceVicw在底層實現機制中就已經實現了雙緩沖機制
總結一句話就是,如果你的自定義View需要頻繁刷新,或者刷新數據處理量比較大,托福機經就可以考慮使用SurfaceView替代View
SurfaceView使用步驟:
- 創建SurfaceView繼承自SurfaceView,並實現兩個接口——SurfaceHolder.Callback和Runnable
- 初始化SurfacHolder對象,並註冊SurfaceHolder的回調方法
- 通過SurfacHolder對象lockCanvas()方法獲得Canvas對象進行繪制,並通過unlockCanvasAndPost(mCanvas)方法對畫布內容進行提交
整個使用SurfaceView的模板代碼:
唯一註意的是,在繪制中將mHolder.unlockCanvasAndPost(mCanvas)方法放到finally代碼塊中,保證每次都能將內容提交
正弦曲線……見經典代碼回顧案例八
繪圖板……見經典代碼回顧案例九
布局文件
Activity文件
布局文件
Activity文件
關鍵點:將一個顏色矩陣傳入畫筆,然後畫出原始的圖在新建的圖上面
布局文件
Activity文件
工具類
本人也是懵逼,沒有搞懂這個例子
經典回顧源碼下載
github:https://github.com/CSDNHensen/QunYingZhuang
Android群英傳知識點回顧——第六章:Android繪圖機制與處理技巧