- 前言:記得那是2014年8月份13號,在親戚的鼓勵下延塑,C#轉(zhuǎn)android,自學了15天網(wǎng)上下載的“黑馬視頻”后答渔,懷著忐忑不安的心情被帶來了深圳关带,趕鴨子上架般接手了人生中的第一個android項目“橙果新聞”。當時毫無框架的概念沼撕,listview也沒有viewholder優(yōu)化宋雏,整個app可以說各種卡頓!然鵝务豺,磨总,,對于初學的我來講笼沥,當時的追求也只是app不閃退就好,哈哈蚪燕。
轉(zhuǎn)眼三年有余,前段時間新公司app項目重構奔浅,采用的主框架正好是當下比較熱門的MVP+RXJava2+Retrofit2.0馆纳,當時由于時間緊任務重,粗略的看了看使用方法就開始編碼了乘凸,這段時間項目小版本迭代空閑時間多厕诡,于是決定將別人整理,封裝的框架自己再整理完善一下营勤,以后開發(fā)項目都可以用這一套熟練的進行開發(fā)了灵嫌!
構建順序:
1.常用基礎工具類(包括File,Bitmap葛作,字符串處理寿羞,圖片加載等,UI輔助等等)
2.基類BaseActivity赂蠢,BaseFragment的構建(titlebar,statebar,loadingview,defalutview四大模塊的封裝)
3.屏幕適配(圖片绪穆,長度)
4.mvp模式(使用MVPPluge插件,自動生成MVP的類文件以及該插件的改裝)
5.網(wǎng)絡框架的接入(RXJava2+Retrofit2.0)
6.gradle多環(huán)境配置(測試,預發(fā)布玖院,正式)
7.CI自動化打包上傳(jenkins+git+碼云or蒲公英)
*其中1,2,3跟業(yè)務邏輯關系不大菠红,我抽取出來作為一個lib,下載過去直接應用就行难菌。
本次系列文章试溯,本人一改之前懶得傳代碼的尿性,完整的demo項目地址將會在系列結束后貼上(目前還在整理中)郊酒,恩遇绞,拿去就用!你值得clone
一.常用工具類清單:
Base64Util:圖片文件與Base64互轉(zhuǎn)燎窘。
BaseBitmapUtil:處理圖片的壓縮摹闽,縮放,裁剪褐健,旋轉(zhuǎn)付鹿,并且含有代碼創(chuàng)建一些Drawable XML的方法
BaseConstant:定義一下常量
BaseFileUtil:文件的常用操作方法
BasePackageUtil:獲取包信息,檢查包名應用是否安裝等pack相關方法
BaseStringUtil:常規(guī)例如:手機號铝量,郵箱等的正則檢驗倘屹,全半角轉(zhuǎn)換,等字符串處理方法
DateUtil:給我時間戳~給你各種日期慢叨,時間
KeyboardHelper:專業(yè)處理軟鍵盤遮擋纽匙,軟鍵盤隱藏,顯示等
NetworkUtil:獲取網(wǎng)絡狀態(tài)等方法
PreferUtil:專業(yè)處理shareperder數(shù)據(jù)
UiUtil:.提供常見的獲取屏幕寬高拍谐、獲取各種資源等方法烛缔,px dp轉(zhuǎn)換,提供延時處理的Handle
這部分沒啥好說轩拨,都是一些常用的方法践瓷,開發(fā)必備,當然也不那么完善亡蓉,根據(jù)需求晕翠,后期再添加
二.(重點來了)基類BaseActivity,BaseFragment的構建:
一個功能完善砍濒,封裝優(yōu)雅的基類無疑可以很大程度上減少重復代碼淋肾,使得開發(fā)時可以專注于業(yè)務邏輯,而不是在什么導航欄啊爸邢,缺省頁啊樊卓,加載框啊之類的東西上反復花時間!BaseActivity里面封裝了titlebar,statebar,loadingview,defalutview四大模塊杠河,開發(fā)時界面Activity的相關的UI直接幾行代碼配置即可碌尔,十分方面浇辜。
且BaseFragment里面會獲取父容器activity,然后直接復用BaseActivity的各種方法唾戚。既然是重點柳洋,那么下面就來詳細講講四大模塊的封裝。
TitleBar
導航欄颈走,toolbar有自身的一些缺陷膳灶,還是感覺不夠靈活,所以自己封裝一個公用的TitleBar是很有必要的立由。
封裝過程:
1.畫一個導航欄布局layout,明確導航欄的基本組成序厉,這里我是直接封裝了三個Textview锐膜,由于Textview有一個drawableX屬性,這就使得每個Textview既能做純文本弛房,又能圖文混合道盏, 左右中,三個Textview文捶,基本夠用了荷逞。
2.定義一個BaseTitleBar接口,里面定義好常用方法:
/*設置整體背景色*/
BaseTitleBar setBgColor(int color);
/*標題欄相關*/
BaseTitleBar setTitle(@StringRes int StringResId);
BaseTitleBar setTitle(String text);
BaseTitleBar setTitleIcon(@DrawableRes int drawableId);
BaseTitleBar setTitleTextColor(int color);
/*右側(cè)文本或按鈕相關*/
BaseTitleBar setRightText(String text);
BaseTitleBar setRightText(@StringRes int stringResId);
BaseTitleBar setRightIcon(@DrawableRes int drawableId);
BaseTitleBar showRightTextView();
BaseTitleBar hideRightTextView();
BaseTitleBar setRightTextColor(int color);
BaseTitleBar setRightTextClickListener(View.OnClickListener listener);
/*左側(cè)文本或按鈕相關*/
BaseTitleBar setLeftText(String text);
BaseTitleBar setLeftText(@StringRes int stringResId);
BaseTitleBar setLeftIcon(@DrawableRes int drawableId);
BaseTitleBar showLeftTextView();
BaseTitleBar hideLeftTextVeiw();
BaseTitleBar setLeftTextColor(int color);
BaseTitleBar setLeftTextClickListener(View.OnClickListener listener);
int getId();
OK,機智如你粹排,一看這些方法名字就懂了吧种远。
3.創(chuàng)建TitleBar,繼承FrameLayout顽耳,實現(xiàn)BaseTitleBar坠敷,具體的代碼就不貼了,之后自己看射富。
StateBar
關于狀態(tài)欄膝迎,網(wǎng)上有太多教程,太多方法胰耗,太多框架了限次,這里針對SDK>Build.VERSION_CODES.KITKAT,統(tǒng)一隱藏狀態(tài)欄柴灯,然后建個StateBar去覆蓋卖漫,StateBar的顏色自定義,還可以隱藏弛槐,效果感覺還不錯懊亡,就不去使用框架了,如果您對狀態(tài)欄要求比較高乎串,一定要多種效果店枣,一定要適配側(cè)滑等等 那你可以略過StateBar速警,自己去封裝下就好。
封裝過程:
1.定義接口BaseStateBar鸯两,明確需要提供的方法
void hide();
void show();
void setBackgroundColor(int color);
void setBackgroundDrawable(@DrawableRes int resId);
View getView();
int getId();
boolean isEnabled();
2.創(chuàng)建StateBar闷旧,實現(xiàn)BaseStateBar接口
至于具體在BaseActivity中如何去初始化StateBar,可以詳見BaseActivity代碼
Loadingview
終于到了Loadingview钧唐,想想還有點小激動忙灼!因為這次,再也不用gif钝侠,不用幀動畫该园,不同一張破圖旋啊轉(zhuǎn),用上了大名鼎鼎的lottie帅韧,然如您還沒有聽說過或者使用過lottie(好low啊里初,掩面偷笑中..)可以看看這篇簡單的介紹,內(nèi)有大量免費炫酷示例忽舟,down一下就進自己的app了双妨,,這X裝的豪不費功夫有木有叮阅?
http://www.reibang.com/p/15c18049f642
LoadingView的封裝相對簡單刁品,畫個xml,在baseactivity中提供兩個方法show浩姥,hide 你懂的挑随,重點就是引入了lottie,炫酷不止一點點~打了好多字及刻,這里放一段demo里用lottie實現(xiàn)的啟動動畫來緩解一下木有圖的尷尬吧镀裤!
缺省頁Defaultview
總結起來,app中的缺省頁其實無外乎以下幾種:
1.無網(wǎng)絡缺省頁面
2.網(wǎng)絡請求錯誤缺省頁
3.空數(shù)據(jù)缺省頁
另外除了缺省頁有時候只是toast一下缴饭,并不需要缺省頁暑劝,具體如何,得看業(yè)務颗搂,得聽產(chǎn)品的担猛!哈哈
這里針對最為復雜的情況做一下封裝,其他簡單情況自然好處理
復雜情況:
進入頁面一瞬掉咔嚓斷網(wǎng)丢氢,顯示帶有““”刷新看看”按鈕的無網(wǎng)絡缺省頁
點擊刷新看看請求網(wǎng)絡后服務錯拋出錯誤傅联,顯示網(wǎng)絡錯誤缺省頁,并且?guī)в邪粹o“再試試"
點擊再試試疚察,請求正常了蒸走,可是沒有業(yè)務數(shù)據(jù),顯示空數(shù)據(jù)缺省頁貌嫡,帶有按鈕“XXXX”
點擊按鈕XXXX比驻,響應別的業(yè)務邏輯
思路有兩種:
1.多個defaultview實例该溯,分別控制各個view的層級,顯示狀態(tài)
2.一個實例别惦,根據(jù)需要動態(tài)變換view中的文本狈茉,按鈕,圖片
對比一下就明白掸掸,思路1處理起來會比較麻煩氯庆,萬一有更復雜的情況需要繼續(xù)添加不同的defaultview
所以這次的框架中采用思路2,一個defaultview扰付,各種變換堤撵!
封裝過程和loading大同小異,只不過提供的方法會多一些羽莺,三個核心view
圖片Image粒督,文本Textview,按鈕Textview
BaseActivty中需要封裝的四個模塊已經(jīng)分析完禽翼,那么他們?nèi)绾畏庋b進BaseActivity里呢?
大家都知道族跛,ViewGroup有個Addview方法可以添加子類闰挡,那么在BaseActivity中設置一個根RelativeLayout,初始化添加StateBar和TitleBar后礁哄,將子界面的contentview添加到TitleBar下方:
//mContainer為根RelativeLayout
mContainer.addView(view, getLayoutParams());
private RelativeLayout.LayoutParams getLayoutParams() {
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
if (titleBar != null) {
lp.addRule(RelativeLayout.BELOW, titleBar.getId());
} else if (stateBar.isEnabled()) {
lp.addRule(RelativeLayout.BELOW, stateBar.getId());
}
return lp;
}
而LoadingView和DefaultVie在需要的時候直接addView進根RelativeLayout长酗,不用addrule。
到此為止桐绒,BaseActivity的封裝基本完成夺脾。至于BaseFragment中就更簡單了,除了DefultView之外茉继,其他的地方直接:
public void XXXXX() {
BaseActivity baseActivity = getBaseActivity();
if (baseActivity != null) {
baseActivity.XXXXX();
}
}
意思就是任何BaseActivity里面封裝過調(diào)度UI的方法在BaseFragment里直接通過獲取父容器Activity后復用一下咧叭。 可以是對于DefaultVie卻不能這么用,這是為神馬呢烁竭?哈哈菲茬,留個課后習題,歡迎留言里回答~
三.屏幕適配
網(wǎng)上關于anroid適配的文章太多太多派撕,這里就不去復制了婉弹,直接說簡單處理方法
適配圖片:使用mipmap系列文件夾放圖片,mipmap系統(tǒng)會在縮放上提供一定的性能優(yōu)化终吼,
讓UI切一套720P的圖(或者用ios 750的镀赌,如果UI太懶,你又搞不定他)际跪,放入mipmap-xhdpi文件夾 這一套其實就夠了商佛,不同分辨率手機上系統(tǒng)會自動去縮放喉钢,如果擔心高分辨率圖片變的模糊可以再適配一套xxx的,反正我的S7edge上基本看不出差別威彰。
長度適配:
如圖出牧,簡單解釋一下,w300dp表示手機 分辨率和手機屏幕密度經(jīng)過計算后得出該手機寬度300dp
框架中設置了300-420范圍的寬度文件夾歇盼,絕對涵蓋了96%+主流的手機寬度舔痕。
像素px =dp* (屏幕密度/160)
一個720px的手機,如果屏幕密度是320豹缀,那么他的寬度用dp表示就是360dp伯复,會使用w360里面的dimen,而點開W360里面的deimens看一下
我們設置的dp1正好也是1dp邢笙,那么如果UI按照720P給你標注啸如,你直接按標注的px除以2用dp就好。
你可能會問氮惯,如果我的手機不是W360的呢叮雳? 例如去年的機皇S7edge:
按照公式像素px =dp* (屏幕密度/160)
算出 S7的屏幕寬度是1440/(534/160)= w431.46
系統(tǒng)會根據(jù)手機的寬度去選擇接近的尺寸文件夾(聽說是向下取,431的手機還是會用w420妇汗,不會用w440帘不?未實測哦),如果UI按照720P給你標注一個頭像Image的寬度是100px杨箭, 你還是除以2寞焙,用50dp
<ImageView
android:layout_width="@dimen/dp50"
android:layout_height="@dimen/dp50"
/>
注意,這時候是W420文件夾里的dp50哦互婿,同樣 打開看看
可以看到捣郊,同樣取dp50,這時候設置的是58.3慈参, 然后你再算算
58.3/50 是不是 約= 420/360
至于明明是w431呛牲,可是取的是w420,或者一個w359的設備向下取到w340怎么辦懂牧?
其實不用太糾結侈净,正常設備寬度350+,359和340差了屏幕寬度的1/17僧凤,也就是說如果50dp最多相差3dp畜侦,基本可以忽略。如果你不能接受這個說法躯保,那么我會繼續(xù)說服你旋膳,359-340也是極端情況了,哪里去找正好359的設備呢途事? 如果你還不服验懊,那你去建立一個w350擅羞,甚至w355的吧,誤差可以控制在1dp义图。
如果你真的打算這么做的話减俏,那你一定是處女座!處女座<罟ぁ娃承!
處女座追求完美也沒有錯,至于w350怕篷,甚至w430+的dimens怎么產(chǎn)生的 其實很簡單历筝,網(wǎng)上有腳本,找來跑一下廊谓,或者直接一個for循環(huán)啊,比如wXXX的
for(int i=1:i<500;i++){
float value=XXX/360*i梳猪;
Logger.d("<dimen name="dp"+i+">"+value+"dp</dimen>")
}
好了,篇幅原因蒸痹,上篇到此為止春弥,可能文中有些不準確或者錯誤的地方歡迎指出,大家一起進步叠荠!下個月25號見惕稻。