編輯:關於Android編程
自定義view
一.View的MeasureSpec
1.MeasureSpec包SpecMode和SpecSize。其中SpecMode包括
UNSPECIFIED:父容器不對該view做限制,要多大給多大,一般用於系統內部
EXACTLY:父容器已經得到該view的確切大小,對應於match_parent和給出確定大小值。
AT_MOST:父容器給出該view可能的最大尺寸,對應於wrap_content
2.一個View的大小由其MeasureSpec確定,而其MeasureSpec一般由該View的LayoutParams和父容器確定(父容器提供調用子view的measure()並傳遞過去MeasureSpec)。這裡說一般是因為我們可以重寫view的onMeasure()從而自己設置view的MeasureSpec
一個View的MeasureSpec的確定過程
childLayoutParams
parentSpecMode
EXACTLY
AT_MOST
UNSPECIFIED
dp/px
EXACTLY+childsize
EXACTLY+childSize
EXACTLY+childSize
Match_parent
EXACTLY+parentSize
AT_MOST+parentSize
UNSPECIFIED+0
Wrap_content
AT_MOST+parentSize
AT_MOST+parentSize
UNSPECIFIED+0
二.measure,layout,draw等簡要分析
1. 從函數內部實現角度分析
view:
measure():調用onMeasure()
onMeasure(MeasureSpec):根據參數設置本view的寬高(調用setMeasureDimension ()方法)
layout(int):根據參數設置本view的位置
onLayout():空方法
draw():主要做四件事1.畫背景2.畫自己即調用自己的onDraw()3. 畫子view即 調用dispatchDraw()4.畫裝飾
onDraw():空方法
ViewGroup:
measure():同view
onMeasure:同view
layout():同view(在ViewGroup中被標記為final)
onLayout():同view
draw():同view
onDraw():同view
measureChild(): 獲取到子view再根據ViewGroup的MeasureSpec和子view的 LayoutParams計算其MeasureSpec,然後調用子view的measure (MeasureSpec)
可以看到ViewGroup在這裡函數的實現上都是直接繼承view的
2.從函數被賦予(期望)的功能的角度分析
view:
measure():調用onMeasure(),final無法重寫
onMeasure(MeasureSpec):設置自己的寬高
layout(int):設置自己的位置
onLayout():空方法,子view不需要實現
draw():1.畫背景,2.調用onDraw(),3.畫修飾
onDraw():畫view自己圖案。
ViewGroup:
measure():調用onMeasure(),final無法被重寫
onMeasure:1.遍歷調用子view的measure()2.設置自己的寬高
layout():在ViewGroup中被標記為final
onLayout():設置子view的位置即遍歷調用子view的layout()
draw():final不可重寫,作用1.畫背景,2.調用onDraw(),3.調用dispatchDraw() 畫子view,4.畫修飾
onDraw():我們可以重寫該方法,當不要在該方法裡去調用子view的draw(),因為 draw()裡面已經這樣做了,該方法的唯一作用是,如果我們想在ViewGroup裡畫一 些東西,比如背景,線條等,才來重寫該方法畫畫。
三.measure祥解
ViewGroup的 measure
ViewGroup的measure()由外部調用,並且傳入外界設置的寬高(MeasureSpec)。measure()是final的,所以不可重寫。接著measure()會調用ViewGroup自己的onMeasure(),同時把measure()傳進來的寬高傳給它。ViewGroup是不會自動調用子view的measure()的,需要在ViewGroup的onMeasure()裡自己調用子view的measure(),並且傳進去期望的寬高。ViewGroup並沒有重寫onMeasure()
1.ViewGroup得到自己的寬高:通過measure()傳進來的參數,在onMeasure()中得到
2.ViewGroup設置自己的寬高:通過重寫onMeasure()
3.ViewGroup要得到所有子view的寬高:可以通過獲取所有子view的layoutParams來計算。
4.ViewGroup設置子view的寬高:通過調用子view的measure()並傳進去參數。當然,ViewGroup對子view設置的寬高只是子View的一個參考值,子view完全可以自己來定義寬高。
雖然ViewGroup沒有重寫view的onMeasure()方法,但是它提供了measureChild()和measureChildren()來對子view繼續測量,其實內部就是獲取到子view再計算其MeasureSpec(通過子view的LayoutParams,可以是通過xml得到)
,然後調用子view的measure()。
子View的measure
子view的measure()方法由外界調用,一般為其父容器,並且傳進來寬高(MeasureSpec)。measure()是final,不能被重寫。接著measure()會調用自己的onMeasure(),同時傳進去寬高。從而確定view的寬高。在這裡,我們可以重寫onMeasure(),來設置view的寬高。
1.子View得到自己的寬高:通過measure()傳進來的參數,在onMeasure()中得到
2.子View設置自己的寬高:通過重寫onMeasure()
注意事項
1.重寫view,如果要在xml布局文件裡使用,並且允許使用wrap_content屬性,則必須在重寫onMeasure()時設定wrap_content的大小,否則就相當於match_parent(原因:當設置view的屬性為wrap_content時,其SpecMode為AT_MOST,specSize為parentSize,邏輯上跟match_parent一樣)做法如下:
其中mWidth和mHeight為原先設定的大小。
2.當view的onMeasure()執行完後,可以通過調用getMeasuredWidth/Height()來得到測量的結果。應該注意,Activity的生命周期和view的周期是不同步的,要先獲得view的准確測量結果,可以通過Activity的onWindowFocusChanged()方法回調中獲取,或者通過對view設置post。
3.view裡面有一個重要的方法setWillNotDraw(boolean),當View不需要畫任何東西時(一般是ViewGroup)可以吧它設置為true。view默認設置為false,ViewGroup默認設置為true
4.子view在onMeasure()裡設置的寬高最好跟layout()裡的t-b,r-l對應。雖然我們可以不這樣做,當為避免混亂,最好做到一致,在不一致的情況下以layout()裡的為准。getMeasureWidth/Height()獲取的是onMeasure()裡設置的寬高,getWidth/Height()獲取的是最終的寬高但必須結果layout()。
5.一個view原始的onMeasure()和layout()必須被調用過,父ViewGroup的dispatchDraw()才會調用該view的onDraw()
6.自定義view的xx(Context context)構造函數為Java中創建view對象時使用
xx(Context context,AttributeSet attrs)為在xml布局文件中使用
四.具體自定義過程及細節
Android的頂級view是DecorView(是一個FrameLayout),包括了整個屏幕。DecorView包含了一個LinearLayout,該LinearLayout的上面是titleBar,下面是一個id為content的view,就是我們在Activity中調用setContentView()時設置的view的位置。該view的寬為屏幕的寬度,mode為EXACTLY。高為屏幕除去titleBar後的高度,mode為EXACTLY
自定義view
1.重寫onMeasure()
其中參數是MeasureSpec格式mode+size(具體查看表)該參數是根據父容器的MeasureSpec和該子view自身在xml裡設置的寬高參數得到的(當然前提得是父容器是調用measureChild()來觸發子view的onMeasure()的;或者父容器有根據自己的MeasureSpec和子view在xml裡的設置改變傳遞給子view的MeasureSpec)
view在onMeasure()裡要做的事情就是設置自己的寬高即調用setMeasuredDimension(int width ,int height)或者直接super.onMeasure()。而view的高寬是要根據父容器傳給它的MeasureSpec來計算的。當SpecMode為EXACTLY時,直接把寬高設置為SpecSize就好。當SpecMode為AT_MOST時,此時的SpecSize為view所能設置的寬高的最大值,view一般在這種情況下回吧自己的尺寸設置為原先設置的默認尺寸。
2.重寫layout()
子view的layout()一般不需要做太多修改,因為父容器已經給它設置好需要的位置。
3.重寫onDraw()
子view的onDraw()功能和ViewGroup的不同,子view在這裡要把自己的內容圖案都畫出來。
自定義ViewGroup
1.重寫onMeasure()
其中參數是MeasureSpec格式mode+size(具體查看表)該參數是根據父容器的MeasureSpec和自身在xml裡設置的寬高參數得到的(當然前提是父容器是調用measureChild()來觸發子view的onMeasure()的;或者父容器有根據自己的MeasureSpec和子view在xml裡的設置改變傳遞給子view的MeasureSpec)
ViewGroup在onMeasure()裡面主要要做倆件事:
第一件事是觸發所有子view的onMeasure():
在onMeasure()裡面ViewGroup要先觸發所有子view的onMeasure(),可以通過兩種方法1.調用measureChildren(),然後等子view的onMeasure()執行後就可以調用子view的函數入getMeasureWidth/Height()等來獲取測量結果。2.自己調用子view的measure(),然後傳進去MeasureSpec參數,從而觸發子view的onMeasure(),這裡要注意的是在傳給子view MeasureSepc的時候要根據ViewGroup自己的MeasureSpec和子view的LayoutParams設置好傳遞給子view的MeasureSpec(即measureChild()的原理)。
一般情況下推薦使用第1種方法。
第二件事是設置自己的寬和高:
設置自己的寬高可以調用setMeasuredDimension(int width ,int height);而設置寬高需要根據父容器傳進來的MeasureSpec(包含了父容器和自身xml布局設置的影響)和所有子view的參數。當ViewGroup在xml布局裡設置為Match_parent或者具體的數值時最好辦,可以直接確定寬高;當ViewGroup在xml布局裡設置為wrap_content時需要特殊處理(注意ViewGroup和View處理wrap_content是不同的),具體看該ViewGroup的功能。比如LinearLayout(vertical)在處理wrap_content的時候是獲取所有子view的高度和margin,相加的值和parentSize比,然後選擇小的那個設置為自己的高度。
2.重寫onLayout()
ViewGroup的onLayout()是一個空實現,我們必須重寫它。onLayout()其作用就是調用所有子view的layout()來設置子view的位置即左上角的坐標和右下角的坐標。在設置子view的位置的時候需要根據在子view的layoutParams和ViewGroup的特點。要做到子view的寬=|l - r|,高=|t - b|
3.重寫onDraw()
在ViewGroup中,onDraw()要做的事就是畫自己的一些背景,線條之類的。不要在onDraw()裡面其調用子view的draw(),因為在ViewGroup的draw()裡已經遍歷了子view的draw()了。
五.view的自定義屬性步驟
1.在res/values裡創建attrs.xml文件,再在裡面聲明屬性,如下
其中sutext為屬性名,string為屬性的特性,而declare-styleable則聲明了一個屬性集,其name要與自定義的view的名字相同。
2.在布局文件中使用自定義屬性
首先要聲明命名空間
在Android studio中統一用如下格式,su可自定義
eclipse中用以下格式
後面為包名。
聲明命名空間後就可以使用了
3.在view中獲取自定義屬性
最近寫了一個簡單的聊天室應用,可以發送表情,更改頭像這些功能。主要技術點就是怎樣把表情圖片放到textview等Ui控件中展示。這裡廢話不多說,下面是效果圖:
前言相信很多朋友在開發中都會遇到圖片上傳的情況,尤其是多圖上傳,最經典的莫過於微信的圖片選擇了。所有很多情況下會使用到多圖選擇,所以就有了這篇文章,今天抽點時間寫了個控件
linux 主機 android sdk安裝忽略jdk安裝忽略hudson安裝忽略 gradle安裝1:下載對應的gradle(這裡是gradle-2
經常會網友遇到手機使用時間較久後會遇到提示“SD卡已損壞,您可能必須將其重新格式化”故障,導致手機SD卡無法使用。最近身邊有朋友手機