Paint 講解開篇
創(chuàng)新互聯(lián)從2013年成立,先為德州等服務(wù)建站,德州等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為德州企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
android中繪制特定圖案類似顯示中的繪畫需要畫筆和畫紙,為此android提供了Paint和Canvas。
Paint和Canvas分別代表畫筆和畫布。
The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
Paint類似畫筆,保存著繪制文本,圖形,圖片的樣式和顏色信息。(顏色,寬度,粗細(xì),透明度,字體樣式,字體大?。?。
提供了三種初始化方法:
Paint(),
Paint(int flags),
Paint(Paint paint)。
第二種構(gòu)造函數(shù)傳入了flag,相當(dāng)于無參構(gòu)造之后調(diào)用了setFlags(int),有很多種flag,這里舉個(gè)例子UNDERLINE_TEXT_FLAG(其他的flag見developers文檔),繪制文字時(shí)會(huì)有下劃線,多個(gè)flag或可以達(dá)到設(shè)置多個(gè)flag的目的。
Paint常用的設(shè)置函數(shù):
setAlpha()透明度
setAntiAlias()抗鋸齒
setColor(),setARGB()設(shè)置顏色
setStyle(Paint.Style style) 設(shè)置填充樣式
setStrokeCap(Paint.Cap cap) 畫筆的樣式(落筆,收筆時(shí))
setStrokeJoin(Paint.Join join)連接點(diǎn)的樣式
setStrokeWidth(float width)設(shè)置畫筆寬度
setShadowLayer(float radius, float dx, float dy, int shadowColor) 設(shè)置陰影
setTextSize(float textSize) 字體大小
setTextAlign(Paint.Align.RIGHT)設(shè)置字體對(duì)齊方式
后期講解:
setColorFilter(ColorFilter filter) 設(shè)置顏色過濾
setUnderlineText(true) 下劃線
setPathEffect() 設(shè)置路徑效果
setTypeface() 設(shè)置字體風(fēng)格
setFilterBitmap() 設(shè)置圖片過濾
setXfermode(Xfermode xfermode) xfermode設(shè)置圖像混合模式
setShader(Shader shader) 設(shè)置shader包括漸變shader,圖片shader
。。。。。。。。
setAlpha(int a) 設(shè)置透明度,a 值得范圍 [0..255],僅僅改變setColor()的顏色的透明度,不改變顏色值,0是完全透明,255完全不透明。
注意:需要先調(diào)用setColor(),再調(diào)用setAlpha才會(huì)生效,否則將會(huì)被覆蓋,因?yàn)閟etColor中包含了alpha。
setStyle設(shè)置填充樣式,所謂填充的樣式指只繪制線或者繪制同時(shí)填充:
Paint.Style.FILL 填充內(nèi)部,會(huì)把閉合區(qū)域填充顏色
Paint.Style.FILL_AND_STROKE 填充內(nèi)部和描邊
Paint.Style.STROKE 僅描邊,僅僅繪制邊界
默認(rèn)FILL 填充內(nèi)部,
STROKE樣式
FILL_AND_STROKE樣式和FILL 類似但當(dāng)畫筆寬度很寬時(shí)會(huì)產(chǎn)生不同
setColor,setARGB設(shè)置顏色,設(shè)置畫筆的顏色,setARGB()參數(shù)范圍0-255。
setAntiAlias(boolean) 抗鋸齒,使邊界更順滑(有些屏幕分辨率不高,導(dǎo)致像素點(diǎn)比較大,繪制邊界可能會(huì)有顆粒感,打開抗鋸齒邊界顆粒感會(huì)減少)。
setTextSize(float textSize) 字體大小,單位是px,如果是dp要注意轉(zhuǎn)換。
setTextAlign(Paint.Align.RIGHT)設(shè)置字體對(duì)齊方式,根據(jù)下面的實(shí)例可以,對(duì)齊方式基于開始繪制的點(diǎn)。
一起講解:
mPaint.setTextSize(50);
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("android Paint 學(xué)習(xí) LEFT",300,300,mPaint);
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("android Paint 學(xué)習(xí) CENTER",300,400,mPaint);
mPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("android Paint 學(xué)習(xí) RIGHT",300,500,mPaint);
setTextAlign對(duì)齊是相對(duì)于開始繪制的坐標(biāo)點(diǎn)。
setShadowLayer(float radius, float dx, float dy, int shadowColor) 設(shè)置陰影
mPaint.setShadowLayer(10,-20,30,Color.GRAY);
canvas.drawText("android Paint 學(xué)習(xí) CENTER",getWidth()/2,450,mPaint);
setShadowLayer(float radius, float dx, float dy, int shadowColor)
Radius設(shè)置角度,dx,dy控制字體的上下左右出現(xiàn),有正負(fù)之分,dx的正負(fù)代表右左,dy的正負(fù)代表下上。
setStrokeCap(Paint.Cap.ROUND)
設(shè)置繪制起始點(diǎn)和結(jié)尾點(diǎn)的樣式,
三種樣式ROUND,BUTT,SQUARE
Cap.ROUND(圓形)、Cap.SQUARE(方形)、Paint.Cap.BUTT(無)
ROUND:
BUTT和SQUARE差別不大:
setStrokeJoin(Paint.Join join),設(shè)置繪制path連接點(diǎn)的樣式
mPaint.setStrokeJoin(Paint.Join.ROUND);
// mPaint.setStrokeJoin(Paint.Join.MITER);
// mPaint.setStrokeJoin(Paint.Join.BEVEL);
Join.MITER(結(jié)合處為銳角)、
Join.Round(結(jié)合處為圓弧)、
Join.BEVEL(結(jié)合處為直線)
ROUND樣式:
MITER樣式:
BEVEL樣式:
setXfermode(Xfermode xfermode) xfermode設(shè)置圖像混合模式
setShader(Shader shader) 設(shè)置shader包括漸變shader,圖片shader
還有很多其他屬性會(huì)后續(xù)講解。
android繪圖之Paint(1)
android繪圖之Canvas基礎(chǔ)(2)
Android繪圖之Path(3)
Android繪圖之drawText繪制文本相關(guān)(4)
Android繪圖之Canvas概念理解(5)
Android繪圖之Canvas變換(6)
Android繪圖之Canvas狀態(tài)保存和恢復(fù)(7)
Android繪圖之PathEffect (8)
Android繪圖之LinearGradient線性漸變(9)
Android繪圖之SweepGradient(10)
Android繪圖之RadialGradient 放射漸變(11)
Android繪制之BitmapShader(12)
Android繪圖之ComposeShader,PorterDuff.mode及Xfermode(13)
Android繪圖之drawText,getTextBounds,measureText,FontMetrics,基線(14)
Android繪圖之貝塞爾曲線簡(jiǎn)介(15)
Android繪圖之PathMeasure(16)
Android 動(dòng)態(tài)修改漸變 GradientDrawable
Xfermode表示圖層的混合模式,用于描述兩個(gè)圖層之間進(jìn)行融合時(shí),像素點(diǎn)進(jìn)行計(jì)算的規(guī)則。
在API16之前,Xfermode有3個(gè)子類:AvoidXfermode、PixelXorXfermode、PorterDuffXfermode。但在API16以后,前兩個(gè)已經(jīng)過時(shí),甚至從源碼里移除,所以我們只需學(xué)習(xí) PorterDuffXfermode 即可。
PorterDuffXfermode 最早是在1984年由Porter和Duff兩人發(fā)表的論文《Compositing Digital Images》中出現(xiàn),所以該混合模式也根據(jù)作者來命名。
PorterDuffXfermode 構(gòu)造函數(shù)需要指定一個(gè) PorterDuff.Mode ,而PorterDuff.Mode在以下地方都會(huì)涉及:
它提供18種模式可選項(xiàng):
各種模式下的效果如下圖所示:
這里可以發(fā)現(xiàn),兩種效果是不一樣的,谷歌官方給的是第一種,但是,通常情況應(yīng)該是第二種,具體原因可 參考該文章 。比如我們畫一個(gè)矩形,應(yīng)該按第二種效果來考慮,因?yàn)樵磮D和目標(biāo)圖大小不一致;如果畫相同大小的Bitmap,則按第一種做。
在實(shí)際應(yīng)用中,我們可以從以下三個(gè)方面來決定使用哪種模式:
1、沒有硬件加速:
invalidate the view hierarchy ------ draw the view hierarchy
2、有硬件加速:
invalidate the view hierarchy ------ record and update the display list ------ draw the display list
1、繪制不正確:可能使用了不支持硬件加速的操作, 需要關(guān)閉硬件加速或者繞過該操作
2、拋出異常:可能使用了不支持硬件加速的操作, 需要關(guān)閉硬件加速或者繞過該操作
在Android系統(tǒng)中,有4個(gè)不同級(jí)別的打開或者關(guān)閉硬件加速操作:
1、Application級(jí)別:
application android:hardwareAccelerated="false"
默認(rèn)為true,用于控制這個(gè)app是否開啟硬件加速。
2、Activity級(jí)別:
activity android:hardwareAccelerated="false"
3、Window級(jí)別:(只支持開啟操作)
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
4、View級(jí)別:(只支持關(guān)閉操作)
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
LAYER_TYPE_HARDWARE ,使用硬件加速(GPU)進(jìn)行繪制
LAYER_TYPE_SOFTWARE ,使用CPU進(jìn)行繪制
或者布局文件中,指定以下屬性:
android:layerType="software"
1、view.isHardwareAccelerated()
如果返回true,表示view掛在一個(gè)開啟了硬件加速的Window之下,也就意味著,它在繪制時(shí),并不一定開啟了硬件加速。
2、canvas.isHardwareAccelerated()
如果返回true,因?yàn)橹鴆anvas在繪制的時(shí)候啟用了硬件加速,盡量采用此方法來判斷是否開啟了硬件加速。
我們知道,在 Android 中,View 繪制主要包含 3 大流程:
Android 中,主要有兩種視圖: View 和 ViewGroup ,其中:
雖然 ViewGroup 繼承于 View ,但是在 View 繪制三大流程中,某些流程需要區(qū)分 View 和 ViewGroup ,它們之間的操作并不完全相同,比如:
對(duì) View 進(jìn)行測(cè)量,主要包含兩個(gè)步驟:
對(duì)于第一個(gè)步驟,即求取 View 的 MeasureSpec ,首先我們來看下 MeasureSpec 的源碼定義:
MeasureSpec 是 View 的一個(gè)公有靜態(tài)內(nèi)部類,它是一個(gè) 32 位的 int 值,高 2 位表示 SpecMode(測(cè)量模式),低 30 位表示 SpecSize(測(cè)量尺寸/測(cè)量大?。?/p>
MeasureSpec 將兩個(gè)數(shù)據(jù)打包到一個(gè) int 值上,可以減少對(duì)象內(nèi)存分配,并且其提供了相應(yīng)的工具方法可以很方便地讓我們從一個(gè) int 值中抽取出 View 的 SpecMode 和 SpecSize。
一個(gè) MeasureSpec 表達(dá)的是:該 View 在該種測(cè)量模式(SpecMode)下對(duì)應(yīng)的測(cè)量尺寸(SpecSize)。其中,SpecMode 有三種類型:
對(duì) View 進(jìn)行測(cè)量,最關(guān)鍵的一步就是計(jì)算得到 View 的 MeasureSpec ,子View 在創(chuàng)建時(shí),可以指定不同的 LayoutParams (布局參數(shù)), LayoutParams 的源碼主要內(nèi)容如下所示:
其中:
LayoutParams 會(huì)受到父容器的 MeasureSpec 的影響,測(cè)量過程會(huì)依據(jù)兩者之間的相互約束最終生成子View 的 MeasureSpec ,完成 View 的測(cè)量規(guī)格。
簡(jiǎn)而言之,View 的 MeasureSpec 受自身的 LayoutParams 和父容器的 MeasureSpec 共同決定( DecorView 的 MeasureSpec 是由自身的 LayoutParams 和屏幕尺寸共同決定,參考后文)。也因此,如果要求取子View 的 MeasureSpec ,那么首先就需要知道父容器的 MeasureSpec ,層層逆推而上,即最終就是需要知道頂層View(即 DecorView )的 MeasureSpec ,這樣才能一層層傳遞下來,這整個(gè)過程需要結(jié)合 Activity 的啟動(dòng)過程進(jìn)行分析。
我們知道,在 Android 中, Activity 是作為視圖組件存在,主要就是在手機(jī)上顯示視圖界面,可以供用戶操作, Activity 就是 Andorid 中與用戶直接交互最多的系統(tǒng)組件。
Activity 的基本視圖層次結(jié)構(gòu)如下所示:
Activity 中,實(shí)際承載視圖的組件是 Window (更具體來說為 PhoneWindow ),頂層View 是 DecorView ,它是一個(gè) FrameLayout , DecorView 內(nèi)部是一個(gè) LinearLayout ,該 LinearLayout 由兩部分組成(不同 Android 版本或主題稍有差異): TitleView 和 ContentView ,其中, TitleView 就是標(biāo)題欄,也就是我們常說的 TitleBar 或 ActionBar , ContentView 就是內(nèi)容欄,它也是一個(gè) FrameLayout ,主要用于承載我們的自定義根布局,即當(dāng)我們調(diào)用 setContentView(...) 時(shí),其實(shí)就是把我們自定義的布局設(shè)置到該 ContentView 中。
當(dāng) Activity 啟動(dòng)完成后,最終就會(huì)渲染出上述層次結(jié)構(gòu)的視圖。
因此,如果我們要求取得到子View 的 MeasureSpec ,那么第一步就是求取得到頂層View(即 DecorView )的 MeasureSpec 。大致過程如下所示:
經(jīng)過上述步驟求取得到 View 的 MeasureSpec 后,接下來就可以真正對(duì) View 進(jìn)行測(cè)量,求取 View 的最終測(cè)量寬/高:
Android 內(nèi)部對(duì)視圖進(jìn)行測(cè)量的過程是由 View#measure(int, int) 方法負(fù)責(zé)的,但是對(duì)于 View 和 ViewGroup ,其具體測(cè)量過程有所差異。
因此,對(duì)于測(cè)量過程,我們分別對(duì) View 和 ViewGroup 進(jìn)行分析:
綜上,無論是對(duì) View 的測(cè)量還是 ViewGroup 的測(cè)量,都是由 View#measure(int widthMeasureSpec, int heightMeasureSpec) 方法負(fù)責(zé),然后真正執(zhí)行 View 測(cè)量的是 View 的 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法。
具體來說, View 直接在 onMeasure(...) 中測(cè)量并設(shè)置自己的最終測(cè)量寬/高。在默認(rèn)測(cè)量情況下, View 的測(cè)量寬/高由其父容器的 MeasureSpec 和自身的 LayoutParams 共同決定,當(dāng) View 自身的測(cè)量模式為 LayoutParams.UNSPECIFIED 時(shí),其測(cè)量寬/高為 android:minWidth / android:minHeight 和其背景寬/高之間的較大值,其余情況皆為自身 MeasureSpec 指定的測(cè)量尺寸。
而對(duì)于 ViewGroup 來說,由于布局特性的豐富性,只能自己手動(dòng)覆寫 onMeasure(...) 方法,實(shí)現(xiàn)自定義測(cè)量過程,但是總的思想都是先測(cè)量 子View 大小,最終才能確定自己的測(cè)量大小。
當(dāng)確定了 View 的測(cè)量大小后,接下來就可以來確定 View 的布局位置了,也即將 View 放置到屏幕具體哪個(gè)位置。
View 的布局過程由 View#layout(...) 負(fù)責(zé),其源碼如下:
View#layout(...) 主要就做了兩件事:
ViewGroup 的布局流程由 ViewGroup#layout(...) 負(fù)責(zé),其源碼如下:
可以看到, ViewGroup#layout(...) 最終也是通過 View#layout(...) 完成自身的布局過程,一個(gè)注意的點(diǎn)是, ViewGroup#layout(...) 是一個(gè) final 方法,因此子類無法覆寫該方法,主要是 ViewGroup#layout(...) 方法內(nèi)部對(duì)子視圖動(dòng)畫效果進(jìn)行了相關(guān)設(shè)置。
由于 ViewGroup#layout(...) 內(nèi)部最終調(diào)用的還是 View#layout(...) ,因此, ViewGroup#onLayout(...) 就會(huì)得到回調(diào),用于處理 子View 的布局放置,其源碼如下:
由于不同的 ViewGroup ,其布局特性不同,因此 ViewGroup#onLayout(...) 是一個(gè)抽象方法,交由 ViewGroup 子類依據(jù)自己的布局特性,擺放其 子View 的位置。
當(dāng) View 的測(cè)量大小,布局位置都確定后,就可以最終將該 View 繪制到屏幕上了。
View 的繪制過程由 View#draw(...) 方法負(fù)責(zé),其源碼如下:
其實(shí)注釋已經(jīng)寫的很清楚了, View#draw(...) 主要做了以下 6 件事:
我們知道,在 Activity 啟動(dòng)過程中,會(huì)調(diào)用到 ActivityThread.handleResumeActivity(...) ,該方法就是 View 視圖繪制的起始之處:
可以看到, ActivityThread.handleResumeActivity(...) 主要就是獲取到當(dāng)前 Activity 綁定的 ViewManager ,最后調(diào)用 ViewManager.addView(...) 方法將 DecorView 設(shè)置到 PhoneWindow 上,也即設(shè)置到當(dāng)前 Activity 上。 ViewManager 是一個(gè)接口, WindowManager 繼承 ViewManager ,而 WindowManagerImpl 實(shí)現(xiàn)了接口 WindowManager ,此處的 ViewManager.addView(...) 實(shí)際上調(diào)用的是 WindowManagerImpl.addView(...) ,源碼如下所示:
WindowManagerImpl.addView(...) 內(nèi)部轉(zhuǎn)發(fā)到 WindowManagerGlobal.addView(...) :
在 WindowManagerGlobal.addView(...) 內(nèi)部,會(huì)創(chuàng)建一個(gè) ViewRootImpl 實(shí)例,然后調(diào)用 ViewRootImpl.setView(...) 將 ViewRootImpl 與 DecorView 關(guān)聯(lián)到一起:
ViewRootImpl.setView(...) 內(nèi)部首先關(guān)聯(lián)了傳遞過來的 DecorView (通過屬性 mView 指向 DecorView 即可建立關(guān)聯(lián)),然后最終調(diào)用 requestLayout() ,而 requestLayout() 內(nèi)部又會(huì)調(diào)用方法 scheduleTraversals() :
ViewRootImpl.scheduleTraversals() 內(nèi)部主要做了兩件事:
Choreographer.postCallback(...) 會(huì)申請(qǐng)一次 VSYNC 中斷信號(hào),當(dāng) VSYNC 信號(hào)到達(dá)時(shí),便會(huì)回調(diào) Choreographer.doFrame(...) 方法,內(nèi)部會(huì)觸發(fā)已經(jīng)添加的回調(diào)任務(wù), Choreographer 的回調(diào)任務(wù)有以下四種類型:
因此, ViewRootImpl.scheduleTraversals(...) 內(nèi)部通過 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null) 發(fā)送的異步視圖渲染消息就會(huì)得到回調(diào),即回調(diào) mTra
分享名稱:android繪制,android繪制人像輪廓
鏈接分享:http://www.rwnh.cn/article32/dscojpc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、外貿(mào)建站、定制開發(fā)、定制網(wǎng)站、網(wǎng)站設(shè)計(jì)、微信公眾號(hào)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)