先扯兩句
上次寫的部分主要還是一些封裝的抽象方法赢织,這部分只是單純的為我這種懶漢提供了便利罷了,而本次寫的內(nèi)容呢馍盟,則是對Title的封裝于置,不過這篇是我自己寫的title封裝,并沒有使用Toolbar贞岭,也不是閑得沒事干八毯,之前使用Toolbar時UI要求title下邊加上一條1px的分割線,結(jié)果Toolbar的左側(cè)出現(xiàn)了16dp左右的空白無法處理瞄桨,沒找到解決方案就放棄了Toolbar的使用话速。過些時間我會好好研究一下Toolbar,畢竟除去這點還是不錯的讲婚,畢竟自己封裝尿孔,對于我這種懶漢來說還是太麻煩了不是。而關(guān)于上述的情況筹麸,如果大家誰知道如何解決方法也歡迎分享,小老兒不勝感激雏婶。
好了物赶,閑言少敘,老規(guī)矩還是先上我的Git留晚。
MyBaseApplication (https://github.com/BanShouWeng/MyBaseApplication)
并給大家展示個神器酵紫,叫Android知識點——目錄告嘲,好了,閑言少敘奖地,下面進(jìn)入正題橄唬。
正文
時間已經(jīng)過去好久了,不知道大家還記得我之前的封裝嗎参歹?好吧仰楚,反正我是忘得差不多了,只能重新查了查之前的博客犬庇。
首先是創(chuàng)建一個title_layout.xml存放title布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/title_height">
<RelativeLayout
android:id="@+id/base_bg"
android:layout_width="match_parent"
android:layout_height="@dimen/title_height"
android:background="@color/blue">
<ImageView
android:id="@+id/base_back"
android:layout_width="50dp"
android:layout_height="50dp"
android:padding="@dimen/size_13"
android:src="@mipmap/back"
android:tint="@android:color/white" />
<TextView
android:id="@+id/base_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:gravity="center"
android:text="@string/title"
android:textColor="@android:color/white"
android:textSize="@dimen/size_20" />
<ImageView
android:id="@+id/base_right_icon2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_toLeftOf="@+id/base_right_icon1"
android:contentDescription="@string/second_function_key"
android:padding="@dimen/size_13"
android:src="@mipmap/add"
android:tint="@android:color/white"
android:visibility="gone" />
<ImageView
android:id="@+id/base_right_icon1"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:contentDescription="@string/first_function_key"
android:padding="@dimen/size_13"
android:src="@mipmap/more"
android:tint="@android:color/white"
android:visibility="gone" />
<TextView
android:id="@+id/base_right_text"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:gravity="center"
android:text="@string/make_sure"
android:textColor="@android:color/white"
android:textSize="@dimen/size_17"
android:visibility="gone" />
</RelativeLayout>
</RelativeLayout>
隨后在BaseActivity的布局文件activity_base.xml中添加如下布局:
<include layout="@layout/title_layout"/>
就完成title的添加僧界,不過在項目中,有些時候并不需要title臭挽,或者是需要根據(jù)UI設(shè)計捂襟,去創(chuàng)建一些比較復(fù)雜的title,這個時候欢峰,就需要把這個刻板的title隱藏起來葬荷。因此這里便給title添加了一個id
<include
android:id="@+id/base_title_layout"
layout="@layout/title_layout"/>
隨后在BaseActivity中添加了如下方法:
/**
* 隱藏頭布局
*/
public void hideTitle() {
if (baseTitle == null) {
baseTitle = getView(R.id.base_title_layout);
}
baseTitle.setVisibility(View.GONE);
}
這樣就實現(xiàn)了上述的要求,可是雖然這里加了隱藏纽帖,但是大家都知道宠漩,實際上這個title的布局資源缺已經(jīng)加載了,所以在這些頁面中抛计,如果依舊這么使用的話哄孤,就會造成資源的浪費。因此吹截,在這次修改中瘦陈,我將這個部分也做了修改,而使用到的控件就是ViewStub波俄。
(其中g(shù)etView方法看過我之前博客的應(yīng)該知道晨逝,就是封裝的findViewById方法,大家可以直接使用findViewById替換懦铺,只是需要強(qiáng)轉(zhuǎn)一下類型即可)
布局優(yōu)化
說起ViewStub就不得不先說說重用布局文件(也就是傳說中的布局優(yōu)化)捉貌,其實網(wǎng)上可以查到大量的文章,而我卻是其中理解的相對淺顯的冬念,所以如果大家想要具體了解的話趁窃,郭霖郭神的 Android最佳性能實踐(四)——布局優(yōu)化技巧
,而我這里呢急前,而我呢醒陆,則根據(jù)自己的應(yīng)用簡單說兩句就好了(無奈我這話癆的毛病,大家就忍耐一下這段“狗尾續(xù)貂”吧)
include
使用
include一看就知道是英語單詞裆针,所以想知道他是做什么的刨摩,最簡單的就是查查翻譯
除了那個包住感覺有點不靠譜以外寺晌,其他的意思還都差不多,那就簡單了澡刹,我就理解為它的作用就是將關(guān)聯(lián)布局內(nèi)的所有控件都包括在當(dāng)前include的位置呻征。而既然可以把其他布局引用過來,那樣自然也就復(fù)用了控件罢浇,從而優(yōu)化了布局代碼陆赋。
就比如上述我之前的封裝,就是單獨封裝了title布局己莺,然后在BaseActivity以及BaseFragment中進(jìn)行了復(fù)用奏甫。而因為這個布局文件已經(jīng)包含在了這個Iinclude標(biāo)簽下,也就相當(dāng)于其中的所有控件都在當(dāng)前的這個頁面中凌受。因此阵子,在使用其中的控件時,就與當(dāng)前布局中的其他控件一樣胜蛉,直接根據(jù)ID獲取即可挠进。當(dāng)然,在我封裝的這個框架里誊册,直接調(diào)用getView即可领突。
xml:
<include
android:id="@+id/base_title_layout"
layout="@layout/title_layout"/>
java:
/**
* 隱藏頭布局
*/
public void hideTitle() {
if (baseTitle == null) {
baseTitle = getView(R.id.base_title_layout);
}
baseTitle.setVisibility(View.GONE);
}
注意事項
有些東西就是這樣,在提供方便的同時案怯,自然就會出現(xiàn)一些隱患君旦,而在使用include的時候,自然也有需要注意的地方嘲碱。
- include在沒有設(shè)置約束參數(shù)的時候金砍,會自動根據(jù)layout引入的去定義。而當(dāng)我們需要更改的時候麦锯,則需要復(fù)寫這些約束參數(shù)即可恕稠,這也就是為什么我們常用的控件必須填寫“android:layout_width”與“android:layout_height”兩個參數(shù)钾菊,而當(dāng)使用include的時候則不需要吧史,因為include復(fù)用布局的最外層布局已經(jīng)夠約束好了其長寬,至于有什么可以設(shè)置词爬,郭神的博客中已經(jīng)說明了:
非layout屬性則無法在<include>標(biāo)簽當(dāng)中進(jìn)行覆寫料祠。另外需要注意的是骆捧,如果我們想要在<include>標(biāo)簽當(dāng)中覆寫layout屬性,必須要將layout_width和layout_height這兩個屬性也進(jìn)行覆寫髓绽,否則覆寫效果將不會生效凑懂。
- 在同一個布局中,大家應(yīng)該都會出現(xiàn)過id設(shè)置重復(fù)的時候吧梧宫,當(dāng)然接谨,不會再程序運行中出問題,那是因為在程序運行之前塘匣,就AS已經(jīng)告訴我們了脓豪,那樣做是不對滴〖陕保可是在使用include的時候扫夜,AS還無法那么職能的判斷出究竟id是否設(shè)置重復(fù),因此只有在運行過程中才會出現(xiàn)錯誤(會調(diào)用include中的控件驰徊,而忽略當(dāng)前布局中同id的控件)笤闯,所以使用時一定要慎之又慎。
merge
同樣棍厂,看到這英文還是查一下什么意思
在使用git或者svn做版本控制的時候呢颗味,我們稱其為“和并”,這部分我在良秋的 Android 布局優(yōu)化之include與merge中查到了如下介紹:
merge翻譯成中文是合并的意思牺弹,在Android中通過使用merge能夠減少視圖的節(jié)點數(shù)浦马,從而減少視圖在繪制過程消耗的時間,達(dá)到提高UI性能的效果张漂。
對于一個菜鳥來說晶默,看到這么玄乎其玄的解釋,第一反應(yīng)就是蒙航攒,如果是個勤懇好學(xué)的菜鳥磺陡,下一反應(yīng)應(yīng)該是去查一下什么是“節(jié)點”。而懶點的估計就直接放棄了漠畜。實際上币他,對于我來說,就是所謂android開發(fā)中的常用布局方式盆驹。
下面就開始研究這東西怎么用圆丹,說實話,就我這種菜鳥躯喇,還真沒什么機(jī)會用到merge辫封,不是不知道怎么用,其一真的是使用環(huán)境沒有include或者后面要說到的ViewStub那么清晰廉丽,再者就是使用的條件比較苛刻倦微,最后就是在該使用的時候,估計merge早不知道被我們忘哪去了正压。
下面先列舉一下良秋列舉的merge注意事項:
- merge必須放在布局文件的根節(jié)點上欣福;
- merge并不是一個ViewGroup,也不是一個View焦履,它相當(dāng)于聲明了一些視圖拓劝,等待被添加雏逾。
- merge標(biāo)簽被添加到A容器下,那么merge下的所有視圖將被添加到A容器下郑临。
- 因為merge標(biāo)簽并不是View栖博,所以在通過LayoutInflate.inflate方法渲染的時候, 第二個參數(shù)必須指定一個父容器厢洞,且第三個參數(shù)必須為true仇让,也就是必須為merge下的視圖指定一個父親節(jié)點。
- 如果Activity的布局文件根節(jié)點是FrameLayout躺翻,可以替換為merge標(biāo)簽丧叽,這樣,執(zhí)行setContentView之后公你,會減少一層FrameLayout節(jié)點踊淳。
- 自定義View如果繼承LinearLayout,建議讓自定義View的布局文件根節(jié)點設(shè)置成merge省店,這樣能少一層結(jié)點嚣崭。
- 因為merge不是View,所以對merge標(biāo)簽設(shè)置的所有屬性都是無效的懦傍。
具體的內(nèi)容參見良秋的博客說雹舀,有詳細(xì)的說明,這里我就不加以贅述了粗俱,只是說兩點自己使用中走的彎路说榆,算是對上述內(nèi)容的一個佐證吧。
上圖就是對圖1的佐證寸认,也就是在布局的時候Android Studio會給予的提示签财,但是對于一些開發(fā)是不那么嚴(yán)謹(jǐn)?shù)模赡茏⒁獠坏絤erge的黃色提示偏塞,畢竟ImageView中沒有設(shè)置android:contentDescription(這個屬性是方便一些生理功能有缺陷的人使用應(yīng)用程序的唱蒸,比如一些視力有障礙的用戶,如果用戶安裝了輔助瀏覽工具比如TalkBack,TalkBack就會大聲朗讀出用戶目前正在瀏覽的內(nèi)容灸叼。TextView控件TalkBack可以直接讀出里面的內(nèi)容(contentDescription的值)神汹,告訴用戶這個圖片到底是什么)會出現(xiàn)這類提示,沒有使用到的一些布局控件也會出現(xiàn)黃色提示(添加一個為沒有子控件古今,且沒有id的RelativeLayout)等屁魏。畢竟大多數(shù)出現(xiàn)這個提示是因為不建議,而不是不能用捉腥。
所以我也任性的在測試機(jī)上跑了一下氓拼,說不定能用呢,結(jié)果:
于是我不得不死心了。
作為一個“生命不息桃漾,逗逼不止”的人坏匪,在使用中自然不可能只吃這一次虧,這次我創(chuàng)建了一個merge_test.xml布局文件:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/merge_btn"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
</merge>
這次是在根節(jié)點上了吧呈队,創(chuàng)建之后自然要使用了:
@SuppressLint("InflateParams")
@Override
protected void findViews() {
view = (Button) LayoutInflater.from(context).inflate(R.layout.merge_test, null).findViewById(R.id.merge_btn);
view.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.merge_btn:
view.setText(count++ + "");
break;
}
}
Shift + F10剥槐,merge,是時候展示你真正的技術(shù)了:
mmp宪摧,跪求我心里的陰影面積!難倒這就進(jìn)入了一條死路嗎B馈<赣凇!好吧沿后,作為一個行業(yè)菜鳥沿彭,這個時候不得不去翻看大神們的博客以求幫助。
<merge>標(biāo)簽是作為<include>標(biāo)簽的一種輔助擴(kuò)展來使用的尖滚,它的主要作用是為了防止在引用布局文件時產(chǎn)生多余的布局嵌套喉刘。
既然郭神已經(jīng)在博客中這么說了,作為小菜鳥漆弄,暫時先這么用著睦裳,至于有沒有其他的玩法,就看日后的使用與積累了撼唾。
ViewStub
看到這個詞呢廉邑,這里就不翻譯了,實在是字面理解真不知道它是做什么的倒谷。所以這次就直接說如何使用了蛛蒙。
其實在用法上,ViewStub與include還真有些類似渤愁,都是定義好的xml布局牵祟,然后在使用的時候,通過控件直接引用布局文件:
<ViewStub
android:id="@+id/base_title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/title_layout" />
大家在看看include的布局:
<include
android:id="@+id/base_title_layout"
layout="@layout/title_layout"/>
是不是區(qū)別并不大抖格,在這最基本的引用上:
- include引用布局時使用的是layout="@layout/title_layout"诺苹,而ViewStub使用的是android:layout="@layout/title_layout";
- ViewStub必須設(shè)置android:layout_width與android:layout_height他挎,而從上面include中已經(jīng)說明筝尾,include是不必須設(shè)置這兩個屬性的。
- 至于使用的環(huán)境办桨,ViewStub是一些不常使用到的地方筹淫,這樣在不使用的時候,ViewStub可以節(jié)省資源,當(dāng)需要使用的時候损姜,通過代碼解析一下饰剥,便可以得到對應(yīng)的布局;而include則更偏向于同一個布局的在多個頁面中的復(fù)用摧阅。
而關(guān)于ViewStub的是引用方法汰蓉,在良秋 Android UI布局優(yōu)化之ViewStub中,關(guān)于有如下介紹:
- ViewStub是一個繼承了View類的視圖棒卷。
- ViewStub是不可見的顾孽,實際上是把寬高都設(shè)置為0
- 可以通過布局文件的android:inflatedId或者調(diào)用ViewStub的setInflatedId方法為懶加載視圖的跟節(jié)點設(shè)置ID
- ViewStub視圖在首次調(diào)用setVisibility或者inflate方法之前,一直存在于視圖樹中
- 只需要調(diào)用ViewStub的setVisibility或者inflate方法即可顯示懶加載的視圖
- 調(diào)用setVisibility或者inflate方法之后比规,懶加載的視圖會把ViewStub從父節(jié)點中替換掉
- ViewStub的inflate只能被調(diào)用一次若厚,第二次調(diào)用會拋出異常,setVisibility可以被調(diào)用多次蜒什,但不建議這么做
- 為ViewStub賦值的android:layout_屬性會替換待加載布局文件的根節(jié)點對應(yīng)的屬性
- inflate方法會返回待加載視圖的根節(jié)點
這已經(jīng)基本上說明了ViewStub的用法以及相關(guān)的原理测秸,反正至少作為懶漢的我是沒辦法說的更明白了。當(dāng)然灾常,想了解更具體的原理的霎冯,可以去良秋的這篇博客中仔細(xì)學(xué)習(xí),而單純的使用而言钞瀑,則不需要那么麻煩沈撞。
上面已經(jīng)給出了ViewStub在xml文件中如何引用,不過往上翻多費勁啊仔戈,還是在下面再貼一下吧:
<ViewStub
android:id="@+id/base_title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/title_layout" />
總共四行屬性(具體參數(shù)可以修改):
- android:layout="@layout/title_layout":所要引用布局的layout xml文件关串;
- android:layout_width="match_parent" android:layout_height="wrap_content":所要引用布局的長寬設(shè)置;
- android:id="@+id/base_title_layout":需要解析時查找對應(yīng)ViewStub的id监徘。
誰都別問我為什么四行屬性晋修,我這里只寫了三點啊,至于強(qiáng)迫癥患者的話凰盔,那我只能非常抱歉的說一句————你管我D关浴!户敬!
xml部分完成了落剪,下面就該進(jìn)行java部分了,也就是ViewStub的inflate尿庐,其實也很簡單:
/**
* Title ViewStub
*/
private ViewStub titleStub;
/**
* 控件初始化
*/
protected void initBaseView() {
if (titleStub == null) {
titleStub = getView(R.id.base_title_layout);
titleStub.inflate();
}
}
想要把ViewStub解析忠怖,總共分幾步?四步抄瑟!
- 創(chuàng)建ViewStub對象凡泣;
- 判斷ViewStub是否已經(jīng)解析過了,若沒有進(jìn)行解析;
- 根據(jù)id找到需要解析的ViewStub鞋拟;
- viewStub.inflate()執(zhí)行解析操作骂维;
如此,viewStub就展示到我們的界面上了贺纲,再對其中的各個控件進(jìn)行操作航闺,實際上就與include相同了,例如:
/**
* 設(shè)置標(biāo)題
*
* @param title 標(biāo)題的文本
* @param showBack 是否顯示返回鍵
*/
public void setTitle(String title, boolean showBack) {
initBaseView();
((TextView) getView(R.id.base_title)).setText(title);
if (showBack) {
baseBack = getView(R.id.base_back);
baseBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}
最后的最后猴誊,思考了很久怎么寫結(jié)尾的我潦刃,終于還是抵不過懶漢性格,還是直接摘一段郭神的內(nèi)容放到這里吧:
另外需要提醒大家一點稠肘,ViewStub所加載的布局是不可以使用<merge>標(biāo)簽的福铅,因此這有可能導(dǎo)致加載出來的布局存在著多余的嵌套結(jié)構(gòu),具體如何去取舍就要根據(jù)各自的實際情況來決定了项阴,對于那些隱藏的布局文件結(jié)構(gòu)相當(dāng)復(fù)雜的情況,使用ViewStub還是一種相當(dāng)不錯的選擇的笆包,即使增加了一層無用的布局結(jié)構(gòu)环揽,仍然還是利大于弊。
注
原本是打算將BaseActivity的封裝放到這里的庵佣,不過寫得篇幅過長歉胶,而且布局優(yōu)化的部分總歸是要寫的,所以這里就將布局優(yōu)化的部分單獨提出來寫了一篇巴粪,而BaseActivity封裝的內(nèi)容只能放到后一篇再去寫了通今。