




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android開(kāi)發(fā)實(shí)踐:自定義ViewGroup的onLayout()分析
前一篇文章主要講了自定義View為什么要重載onMeasure()方法,那么,自定義ViewGroup又都有哪些方法需要重載或者實(shí)現(xiàn)呢?Android開(kāi)發(fā)中,對(duì)于自定義View,分為兩種,一種是自定義控件(繼承View類(lèi)),另一種是自定義布局容器(繼承ViewGroup)。如果是自定義控件,則一般需要重載兩個(gè)方法,一個(gè)是onMeasure(),用來(lái)測(cè)量控件尺寸,另一個(gè)是onDraw(),用來(lái)繪制控件的UI。而自定義布局容器,則一般需要實(shí)現(xiàn)/重載三個(gè)方法,一個(gè)是onMeasure(),也是用來(lái)測(cè)量尺寸;一個(gè)是onLayout(),用來(lái)布局子控件;還有一個(gè)是dispatchDraw(),用來(lái)繪制UI。本文主要分析自定義ViewGroup的onLayout()方法的實(shí)現(xiàn)。ViewGroup類(lèi)的onLayout()函數(shù)是abstract型,繼承者必須實(shí)現(xiàn),由于ViewGroup的定位就是一個(gè)容器,用來(lái)盛放子控件的,所以就必須定義要以什么的方式來(lái)盛放,比如LinearLayout就是以橫向或者縱向順序存放,而RelativeLayout則以相對(duì)位置來(lái)擺放子控件,同樣,我們的自定義ViewGroup也必須給出我們期望的布局方式,而這個(gè)定義就通過(guò)onLayout()函數(shù)來(lái)實(shí)現(xiàn)。我們通過(guò)實(shí)現(xiàn)一個(gè)水平優(yōu)先布局的視圖容器來(lái)更加深入地了解onLayout()的實(shí)現(xiàn)吧,效果如圖所示(黑色方塊為子控件,白色部分為自定義布局容器)。該容器的布局方式是,首先水平方向上擺放子控件,水平方向放不下了,則另起一行繼續(xù)水平擺放。
1.
自定義ViewGroup的派生類(lèi)
第一步,則是自定ViewGroup的派生類(lèi),繼承默認(rèn)的構(gòu)造函數(shù)。public
class
CustomViewGroup
extends
ViewGroup
{
public
CustomViewGroup(Context
context)
{
super(context);
}
public
CustomViewGroup(Context
context,
AttributeSet
attrs)
{
super(context,
attrs);
}
public
CustomViewGroup(Context
context,
AttributeSet
attrs,
intdefStyle)
{
super(context,
attrs,
defStyle);
}
}
2.
重載onMeasure()方法
為什么要重載onMeasure()方法這里就不贅述了,上一篇文章已經(jīng)講過(guò),這里需要注意的是,自定義ViewGroup的onMeasure()方法中,除了計(jì)算自身的尺寸外,還需要調(diào)用measureChildren()函數(shù)來(lái)計(jì)算子控件的尺寸。
onMeasure()的定義不是本文的討論重點(diǎn),因此這里我直接使用默認(rèn)的onMeasure()定義,當(dāng)然measureChildren()是必須得加的。@Override
protected
void
onMeasure(int
widthMeasureSpec,
int
heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec,
heightMeasureSpec);
measureChildren(widthMeasureSpec,
heightMeasureSpec);
}
3.
實(shí)現(xiàn)onLayout()方法
onLayout()函數(shù)的原型如下://@param
changed
該參數(shù)指出當(dāng)前ViewGroup的尺寸或者位置是否發(fā)生了改變
//@param
left
top
right
bottom
當(dāng)前ViewGroup相對(duì)于其父控件的坐標(biāo)位置
protected
void
onLayout(boolean
changed,int
left,
int
top,
int
right,
int
bottom);
由于我們希望優(yōu)先橫向布局子控件,那么,首先,我們知道總寬度是多少,這個(gè)值可以通過(guò)getMeasuredWidth()來(lái)得到,當(dāng)然子控件的寬度也可以通過(guò)子控件對(duì)象的getMeasuredWidth()來(lái)得到。
這樣,就不復(fù)雜了,具體的實(shí)現(xiàn)代碼如下所示:protected
void
onLayout(boolean
changed,
int
left,
int
top,
int
right,
int
bottom)
{
int
mViewGroupWidth
=
getMeasuredWidth();
//當(dāng)前ViewGroup的總寬度
int
mPainterPosX
=
left;
//當(dāng)前繪圖光標(biāo)橫坐標(biāo)位置
int
mPainterPosY
=
top;
//當(dāng)前繪圖光標(biāo)縱坐標(biāo)位置
int
childCount
=
getChildCount();
for
(
int
i
=
0;
i
<
childCount;
i++
)
{
View
childView
=
getChildAt(i);
int
width
=
childView.getMeasuredWidth();
int
height
=
childView.getMeasuredHeight();
//如果剩余的空間不夠,則移到下一行開(kāi)始位置
if(
mPainterPosX
+
width
>
mViewGroupWidth
)
{
mPainterPosX
=
left;
mPainterPosY
+=
height;
}
//執(zhí)行ChildView的繪制
childView.layout(mPainterPosX,mPainterPosY,mPainterPosX+width,
mPainterPosY+height);
//記錄當(dāng)前已經(jīng)繪制到的橫坐標(biāo)位置
mPainterPosX
+=
width;
}
}
4.布局文件測(cè)試
下面我們就嘗試寫(xiě)一個(gè)簡(jiǎn)單的xml文件,來(lái)測(cè)試一下我們的自定義ViewGroup,我們把子View的背景顏色都設(shè)置為黑色,方便我們辨識(shí)。<com.titcktick.customview.CustomViewGroup
xmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black"/>
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black"/>
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black"/>
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black"/>
</com.titcktick.customview.CustomViewGroup>
5.添加layout_margin
為了讓核心邏輯更加清晰,上面的onLayout()實(shí)現(xiàn)我隱去了margin的計(jì)算,這樣就會(huì)導(dǎo)致子控件的layout_margin不起效果,所以上述效果是子控件一個(gè)個(gè)緊挨著排列,中間沒(méi)有空隙。那么,下面我們來(lái)研究下如何添加margin效果。
其實(shí),如果要自定義ViewGroup支持子控件的layout_margin參數(shù),則自定義的ViewGroup類(lèi)必須重載generateLayoutParams()函數(shù),并且在該函數(shù)中返回一個(gè)ViewGroup.MarginLayoutParams派生類(lèi)對(duì)象,這樣才能使用margin參數(shù)。
ViewGroup.MarginLayoutParams的定義關(guān)鍵部分如下,它記錄了子控件的layout_margin值:public
static
class
MarginLayoutParams
extends
ViewGroup.LayoutParams
{
public
int
leftMargin;
public
int
topMargin;
public
int
rightMargin;
public
int
bottomMargin;
}
你可以跟蹤源碼看看,其實(shí)XML文件中View的layout_xxx參數(shù)都是被傳遞到了各種自定義ViewGroup.LayoutParams派生類(lèi)對(duì)象中。例如LinearLayout的LayoutParams定義的關(guān)鍵部分如下:public
class
LinearLayout
extends
ViewGroup
{
public
static
class
LayoutParams
extends
ViewGroup.MarginLayoutParams
{
public
float
weight;
public
int
gravity
=
-1;
public
LayoutParams(Context
c,
AttributeSet
attrs)
{
super(c,
attrs);
TypedArray
a
=
c.obtainStyledAttributes(attrs,
ernal.R.styleable.LinearLayout_Layout);
weight
=
a.getFloat(ernal.R.styleable.LinearLayout_Layout_layout_weight,
0);
gravity
=
a.getInt(ernal.R.styleable.LinearLayout_Layout_layout_gravity,
-1);
a.recycle();
}
}
@Override
public
LayoutParams
generateLayoutParams(AttributeSet
attrs)
{
return
new
LinearLayout.LayoutParams(getContext(),
attrs);
}
}
這樣你大概就可以理解為什么LinearLayout的子控件支持weight和gravity的設(shè)置了吧,當(dāng)然我們也可以這樣自定義一些屬于我們ViewGroup特有的params,這里就不詳細(xì)討論了,我們只繼承MarginLayoutParams來(lái)獲取子控件的margin值。public
class
CustomViewGroup
extends
ViewGroup
{
public
static
class
LayoutParams
extends
ViewGroup.MarginLayoutParams
{
public
LayoutParams(Context
c,
AttributeSet
attrs)
{
super(c,
attrs);
}
}
@Override
public
LayoutParams
generateLayoutParams(AttributeSet
attrs)
{
return
new
CustomViewGroup.LayoutParams(getContext(),
attrs);
}
}
這樣修改之后,我們就可以在onLayout()函數(shù)中獲取子控件的layout_margin值了,添加了layout_margin的onLayout()函數(shù)實(shí)現(xiàn)如下所示:@Override
protected
void
onLayout(boolean
changed,
int
left,
int
top,
int
right,
int
bottom)
{
int
mViewGroupWidth
=
getMeasuredWidth();
//當(dāng)前ViewGroup的總寬度
int
mViewGroupHeight
=
getMeasuredHeight();
//當(dāng)前ViewGroup的總高度
int
mPainterPosX
=
left;
//當(dāng)前繪圖光標(biāo)橫坐標(biāo)位置
int
mPainterPosY
=
top;
//當(dāng)前繪圖光標(biāo)縱坐標(biāo)位置
int
childCount
=
getChildCount();
for
(
int
i
=
0;
i
<
childCount;
i++
)
{
View
childView
=
getChildAt(i);
int
width
=
childView.getMeasuredWidth();
int
height
=
childView.getMeasuredHeight();
CustomViewGroup.LayoutParams
margins
=
(CustomViewGroup.LayoutParams)(childView.getLayoutParams());
//ChildView占用的width
=
width+leftMargin+rightMargin
//ChildV
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年中考英語(yǔ)模刷題擬試卷(含答案)
- 電力電纜敷設(shè)施工方案
- 別墅裝修保潔合同范例
- 代理酒類(lèi)合同范本
- 個(gè)人租房合同范例乙方版本
- 物流行業(yè)安全運(yùn)輸監(jiān)管方案計(jì)劃
- 師生互動(dòng)提升計(jì)劃
- 親子溝通與情感交流計(jì)劃
- 中學(xué)教育教學(xué)創(chuàng)新計(jì)劃
- 《貴州新仁新能源科技有限公司貴州省六枝特區(qū)大壩螢石礦(新建)礦產(chǎn)資源綠色開(kāi)發(fā)利用方案(三合一)》評(píng)審意見(jiàn)
- 2025年高考百日誓師大會(huì)校長(zhǎng)致辭(二)
- 2025年高考數(shù)學(xué)復(fù)習(xí)核心考點(diǎn)(新高考專(zhuān)用)專(zhuān)題5.3平面向量的數(shù)量積及其應(yīng)用【八大題型】特訓(xùn)(學(xué)生版+解析)
- 2025年中國(guó)萬(wàn)寶工程有限公司校園招聘筆試參考題庫(kù)附帶答案詳解
- 2025年常州機(jī)電職業(yè)技術(shù)學(xué)院?jiǎn)握新殬I(yè)適應(yīng)性測(cè)試題庫(kù)有完整答案
- 2025年河南機(jī)電職業(yè)學(xué)院?jiǎn)握新殬I(yè)技能測(cè)試題庫(kù)及參考答案
- 第11課《山地回憶》課件-2024-2025學(xué)年統(tǒng)編版語(yǔ)文七年級(jí)下冊(cè)
- 成本經(jīng)理試用期轉(zhuǎn)正工作匯報(bào)
- 2023年廣西本科對(duì)口中職考試中職英語(yǔ)試題
- 閃耀離子束瘢痕治療飛頓醫(yī)療激光公司客戶(hù)支持部講解
- 《莖和葉》說(shuō)課稿-2023-2024學(xué)年科學(xué)四年級(jí)下冊(cè)教科版
- 2024年皖西衛(wèi)生職業(yè)學(xué)院?jiǎn)握新殬I(yè)適應(yīng)性測(cè)試題庫(kù)及答案解析
評(píng)論
0/150
提交評(píng)論