摘要
本篇總結(jié)了前人寫(xiě)的BaseActivity,自己在開(kāi)發(fā)過(guò)程中也添添補(bǔ)補(bǔ),刪刪改改,現(xiàn)在總結(jié)下。
本篇很多知識(shí)借鑒和學(xué)習(xí)了知乎上iYng大大的回答,先感謝一波。順便上原文鏈接:
https://www.zhihu.com/question/47045239/answer/105086885
正文
一般來(lái)說(shuō),不同的項(xiàng)目的BaseActivity不盡相同,根據(jù)不同的業(yè)務(wù)邏輯和功能需求,會(huì)有很多區(qū)別。這里總結(jié)了一些,如下:
視圖相關(guān)
一般的Activity里都會(huì)用到很多的findViewById這個(gè)方法,而且每次都要強(qiáng)制類(lèi)型轉(zhuǎn)換,這樣會(huì)顯得很繁瑣,如果在BaseActivity里封裝好,就能省事:
protected <T extends View> T findView(int id) {
return (T) findViewById(id);
}
這樣只要是繼承了BaseActivity就能輕松使用LinearLayout llContent = findView(R.id.ll_content);,免去了諸多類(lèi)型轉(zhuǎn)換的麻煩。
然后說(shuō)起視圖,一般的Activity里都會(huì)需要初始化視圖和數(shù)據(jù),所以可以暴露兩個(gè)方法initView()和initData():
ublic abstract void initData();
public abstract void initView();
然后在setContentView里去調(diào)用,一般都是先initView,然后再initData:
@Override
public void setContentView(@LayoutRes int layoutResID) {
super.setContentView(layoutResID);
initView();
initData();
}
@Override
public void setContentView(View view) {
super.setContentView(view);
initView();
initData();
}
這樣子類(lèi)里都必須重寫(xiě)initView()和initData()了,邏輯也能清晰點(diǎn),不然什么東西都放在onCreate里,就很亂了;
用戶(hù)模塊(業(yè)務(wù)相關(guān)【可選】)
不過(guò)一般的app,只要是有登錄的,就會(huì)有用戶(hù)模塊,也會(huì)根據(jù)用戶(hù)標(biāo)識(shí)id去進(jìn)行一些網(wǎng)絡(luò)操作,所以用戶(hù)模塊可以在BaseActivity中暴露一些方法,比如用戶(hù)id的獲?。?/p>
public int getUid() {
if (UserManager().getUid() != null) {
if (UserManager().getUid().equals("")) {
return 0 ;
}
}
return Integer.parseInt(UserManager().getUid()) ;
}
這里就是返回了SharedPreference里存儲(chǔ)的用戶(hù)id,在用戶(hù)id大量被使用的場(chǎng)景下,這樣的封裝還是很有必要的,使用起來(lái)也更便捷。當(dāng)然如果只是純展示的app就不一定需要了,或許顯得多余。
界面間跳轉(zhuǎn)傳參
很多時(shí)候,Activity之間都會(huì)傳參,所以可以封裝一個(gè)參數(shù)處理的函數(shù)initParam(),在BaseActivity的onCreate里去判斷是否有參數(shù)傳過(guò)來(lái);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
if (savedInstanceState != null) {
initParam(savedInstanceState);
} else if (getIntent() != null && getIntent().getExtras() != null) {
initParam(getIntent().getExtras());
}
}
然后把initParam()方法暴露給子類(lèi):
protected void initParam(Bundle bundle) {
}
這個(gè)方法并不是必須重寫(xiě)的,因?yàn)閭鲄⒁矝](méi)有想象中那么多,并不需要強(qiáng)制重寫(xiě)這個(gè)方法。
調(diào)試和吐司
一般會(huì)在Application類(lèi)里去定義一個(gè)isDebug來(lái)判斷是否開(kāi)啟調(diào)試(開(kāi)發(fā)者模式):
public class TApplication extends Application {
public static boolean isDebug = true ;
public static String APP_NAME ;
}
在BaseActivity里,我們可以把isDebug作為總開(kāi)關(guān),然后控制是否顯示調(diào)試信息:
public void TLog(String msg) {
if (isDebug) {
Log.d(APP_NAME, msg);
}
}
這樣一鍵關(guān)閉調(diào)試,不用去一個(gè)個(gè)刪項(xiàng)目里的Log信息,是不是很贊?
每次Toast,都用Toast.makeText(...).show();是不是很煩?那么可以在BaseActivity里封裝下,比如:
public void toast(String text) {
ToastUtils.show(text);
}
public void toast(int resId) {
ToastUtils.show(String.valueOf(resId));
}
這里ToastUtils就是一個(gè)Toast封裝類(lèi),里面的內(nèi)容估計(jì)大家都懂。然后這樣一來(lái),所有子類(lèi)在使用時(shí),只需要瀟灑寫(xiě)一句toast("xxxx")就行了,當(dāng)然也可以一并封裝Toast.LENGTH_LONG和Toast.LENGTH_SHORT,按需封裝吧。
其他
軟鍵盤(pán)
有的app里,用戶(hù)輸入的情景會(huì)比較多,這個(gè)時(shí)候,軟鍵盤(pán)的隱藏就用的多了,用戶(hù)輸入完之后,或者用戶(hù)點(diǎn)擊屏幕空白處,都應(yīng)該去隱藏軟鍵盤(pán),這樣的話(huà),可以考慮在BaseActivity里寫(xiě)隱藏的方法:
/**
* 隱藏軟件盤(pán)
*/
public void hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (getCurrentFocus() != null) {
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
}
/**
* 點(diǎn)擊軟鍵盤(pán)之外的空白處,隱藏軟件盤(pán)
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (ToolUtil.isShouldHideInput(v, ev)) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
return super.dispatchTouchEvent(ev);
}
// 必不可少,否則所有的組件都不會(huì)有TouchEvent了
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
/**
* 顯示軟鍵盤(pán)
*/
public void showInputMethod(){
if (getCurrentFocus() != null){
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.showSoftInputFromInputMethod(getCurrentFocus().getWindowToken(),0);
}
}
上面3個(gè)方法也是很實(shí)用的。dispatchTouchEvent方法不需要手動(dòng)調(diào)用,只要是有點(diǎn)擊事件,并且點(diǎn)擊在軟鍵盤(pán)和EditText區(qū)域外,就會(huì)隱藏軟鍵盤(pán)。
防止快速點(diǎn)擊
有時(shí)候,用戶(hù)(特別是測(cè)試猿)會(huì)瘋狂的點(diǎn)擊app,這一舉動(dòng)的原因和意義不明,但是我們可以設(shè)置防止快速點(diǎn)擊給app造成的傷害和負(fù)擔(dān):
private boolean fastClick() {
long lastClick = 0;
if (System.currentTimeMillis() - lastClick <= 1000) {
return false;
}
lastClick = System.currentTimeMillis();
return true;
}
這樣在1秒之內(nèi)只會(huì)響應(yīng)一次,麻麻再也不用擔(dān)心我手抽筋亂點(diǎn)了。
那么怎么用呢?舉個(gè)栗子,可以在onClick接口里去判斷下嘛:
/** View點(diǎn)擊 **/
public abstract void widgetClick(View v);
@Override
public void onClick(View v) {
if (fastClick())
widgetClick(v);
}
頁(yè)面跳轉(zhuǎn):startActivity、startActivityForResult
這個(gè)也是可選的,可以封裝下,達(dá)到每次跳轉(zhuǎn)不需要傳this或者XXXXX.this這種參數(shù):
public void startActivity(Class<?> clz) {
startActivity(clz, null);
}
/**
*攜帶數(shù)據(jù)的頁(yè)面跳轉(zhuǎn)
* @param clz
* @param bundle
*/
public void startActivity(Class<?> clz, Bundle bundle) {
Intent intent = new Intent();
intent.setClass(this, clz);
if (bundle != null) {
intent.putExtras(bundle);
}
startActivity(intent);
}
/**
* 含有Bundle通過(guò)Class打開(kāi)編輯界面
*
* @param cls
* @param bundle
* @param requestCode
*/
public void startActivityForResult(Class<?> cls, Bundle bundle,
int requestCode) {
Intent intent = new Intent();
intent.setClass(this, cls);
if (bundle != null) {
intent.putExtras(bundle);
}
startActivityForResult(intent, requestCode);
}
這些方法還是很便捷的,使用時(shí)可以簡(jiǎn)單的使用startActivity(MainActivity.class);,也可以傳Bundle參數(shù)。
是否允許全屏
設(shè)置一個(gè)成員變量mAllowFullScreen
/** 是否允許全屏 **/
private boolean mAllowFullScreen = true;
通過(guò)在BaseActivity的onCreate方法里判斷mAllowFullScreen來(lái)設(shè)置是否允許全屏:
if (mAllowFullScreen) {
this.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
然后給子類(lèi)暴露一個(gè)方法來(lái)設(shè)置mAllowFullScreen:
public void setAllowFullScreen(boolean allowFullScreen) {
this.mAllowFullScreen = allowFullScreen;
}
設(shè)置沉浸式狀態(tài)欄
跟設(shè)置全屏一樣一樣的:
/** 是否沉浸狀態(tài)欄 **/
private boolean isSetStatusBar = true;
然后BaseActivity的onCreate里:
if (isSetStatusBar) {
steepStatusBar();
}
然后定義steepStatusBar()方法,用來(lái)設(shè)置沉浸式狀態(tài)欄:
private void steepStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 透明狀態(tài)欄
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 透明導(dǎo)航欄
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
這里就要判斷系統(tǒng)版本了。只有在KITKAT以上才有作用。
最后給子類(lèi)暴露方法,設(shè)置 isSetStatusBar的值:
/**
* 是否設(shè)置沉浸狀態(tài)欄
* @param isSetStatusBar
*/
public void setSteepStatusBar(boolean isSetStatusBar) {
this.isSetStatusBar = isSetStatusBar;
}
設(shè)置是否允許屏幕旋轉(zhuǎn)
跟前面兩種思路一樣,通過(guò)判斷變量,在onCreate里設(shè)置咯:
/** 是否禁止旋轉(zhuǎn)屏幕 **/
private boolean isAllowScreenRoate = false;
BaseActivity里的onCreate方法:
if (!isAllowScreenRoate) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
最后暴露方法設(shè)置isAllowScreenRoate的值:
public void setScreenRoate(boolean isAllowScreenRoate) {
this.isAllowScreenRoate = isAllowScreenRoate;
}
總結(jié)
上面的這些方法大都是比較常用的,有些雖然不是很常用,但是寫(xiě)了也會(huì)方便一點(diǎn),把這篇文章當(dāng)做一個(gè)匯總,然后按需使用唄。