Android 基類(lèi)BaseActivity的封裝

摘要
本篇總結(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è)匯總,然后按需使用唄。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容