BaseActivity:統(tǒng)一編寫共有邏輯

在上一篇文章NetworkStateView的結尾說到可以在BaseActivity中對NetworkStateView進行統(tǒng)一設置,從而進行界面多狀態(tài)的加載,那么今天就說一說BaseActivity,在BaseActivity怎么進行NetworkStateView的設置以及BaseActivity的一些其他作用,還沒有看過NetworkStaetView的可以先看一下這一篇文章

在項目中,我們在寫Activity時一般都不會直接繼承AppCompatActivity,而是先寫一個基類BaseActivity绰疤,讓其他的Activity繼承BaseActivity,這樣就有一個好處舞终,就是我們可以把Activity的共有邏輯寫在BaseActivity中轻庆,例如NetworkStateView的邏輯,從而不需要在每個Activity都寫一遍

下面就對BaseActivityNetworkStateView以及一些其他共有的邏輯內容進行介紹

  • 定義加載布局敛劝,進行共同界面(NetworkStateView)的加載

其實在BaseActivity中余爆,我們可以先給BaseActivity先定義一個布局文件如activity_base,在activity_base布局文件中先定義好共同的布局控件如NetworkStateView夸盟,Toolbar等蛾方,并且在最后定義一個FrameLayout,這個FrameLayout是關鍵人物上陕,后面再說他的作用桩砰,哈哈

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/view_network_state" />

   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent">

   </FrameLayout>

</LinearLayout>

定義好布局文件之后,我們需要在BaseActivity中重寫setContentView方法释簿,讓其加載activity_base的布局文件并進行設置填充亚隅,接著再加載子類Activity的布局文件

@SuppressLint("InflateParams")
@Override
public void setContentView(@LayoutRes int layoutResID) {
    View view = getLayoutInflater().inflate(R.layout.activity_base, null);
    //設置填充activity_base布局
    super.setContentView(view);

    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
        view.setFitsSystemWindows(true);
    }

    //加載子類Activity的布局
    initDefaultView(layoutResID);
}

看到這里可能有人會有疑惑,先將activity_base的布局設置填充了之后庶溶,還怎么進行設置子類Activity的布局文件煮纵?哈哈,這個時候上面的FrameLayout就要發(fā)揮作用了偏螺,其實這里設置子類Activity的布局并不是直接填充給BaseActivity醉途,而是讓其作為一個子View添加到activity_base中的FrameLayout中,這樣不就讓BaseActivity和子類Activity的布局相結合了砖茸,看看initDefaultView(layoutResID)

private void initDefaultView(int layoutResId) {
    networkStateView = (NetworkStateView) findViewById(R.id.nsv_state_view);
    FrameLayout container = (FrameLayout) findViewById(R.id.fl_activity_child_container);
    View childView = LayoutInflater.from(this).inflate(layoutResId, null);
    container.addView(childView, 0);
}

嗯,對NetworkStateView進行findViewById查找殴穴,接著定義NetworkStateView的相關方法之后凉夯,我們就能在子類Activity中進行相關調用了

/**
 * 顯示加載中的布局
 */
public void showLoadingView() {
    networkStateView.showLoading();
}

/**
 * 顯示加載完成后的布局(即子類Activity的布局)
 */
public void showContentView() {
    networkStateView.showSuccess();
}

/**
 * 顯示沒有網(wǎng)絡的布局
 */
public void showNoNetworkView() {
    networkStateView.showNoNetwork();
    networkStateView.setOnRefreshListener(this);
}

/**
 * 顯示沒有數(shù)據(jù)的布局
 */
public void showEmptyView() {
    networkStateView.showEmpty();
    networkStateView.setOnRefreshListener(this);
}

/**
 * 顯示數(shù)據(jù)錯誤,網(wǎng)絡錯誤等布局
 */
public void showErrorView() {
    networkStateView.showError();
    networkStateView.setOnRefreshListener(this);
}

嗯采幌,這樣我們就可以統(tǒng)一設置NetworkStateView了劲够,不過大家可能注意到,這樣其實會給每個Activity增加多了一層FrameLayout休傍,如果子類Activity本身就是LinearLayout還會增加多一層征绎,有人可能會覺得影響性能,不過個人認為這樣會極大增加便利性,而且對于一層FrameLayout的影響并不是很大人柿,所以覺得這種方法還是可取的柴墩,不過讀者可自行考慮

  • 定義Activity棧,進行Activity的保存

在開發(fā)的時候凫岖,我們可能會遇到這樣的情況江咳,在啟動了多個Activity之后需要一次性銷毀并退出應用, 又或者是銷毀頂部一個或多個Activity哥放,顯示指定的Activity歼指,在這樣的情形下,我們可以定義一個 Activity棧甥雕,在Activity執(zhí)行onCreate時在棧里添加此Activity踩身,在Activity指定onDestroy 時在Activity棧中移除

在這樣的每次添加和移除操作的共有邏輯,我們就可以在BaseActivity進行書寫社露,只要Activity繼承BaseActivity便可以在Activity進行添加移除了

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(getLayoutId());
    ...
    ActivityUtils.addActivity(this);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    ...
    ActivityUtils.removeActivity(this);
}

ActivityUtils就是我們定義的Activity棧工具類

public class ActivityUtils {

    private static Stack<Activity> mActivityStack;

    /**
     * 添加一個Activity到堆棧中
     * @param activity
     */
    public static void addActivity(Activity activity) {
        if (null == mActivityStack) {
            mActivityStack = new Stack<>();
        }
        mActivityStack.add(activity);
    }

    /**
     * 從堆棧中移除指定的Activity
     * @param activity
     */
    public static void removeActivity(Activity activity) {
        if (activity != null) {
            mActivityStack.remove(activity);
        }
    }

    /**
     * 獲取頂部的Activity
     * @return 頂部的Activity
     */
     public static Activity getTopActivity() {
        if (mActivityStack.isEmpty()) {
            return null;
        } else {
            return mActivityStack.get(mActivityStack.size() - 1);
        }
    }

    /**
     * 結束所有的Activity挟阻,退出應用
     */
    public static void removeAllActivity() {
        if (mActivityStack != null && mActivityStack.size() > 0) {
            for (Activity activity : mActivityStack) {
                activity.finish();
            }
        }
    }
}
  • 定義共有的UI操作

為了方便子類Activity可以執(zhí)行共有的UI操作,我們可以將共有的UI操作寫在BaseActivity中呵哨,例如顯示Dialog赁濒,ToastSnackbar

private void initDialog() {
    mDialog = new Dialog(this, R.style.dialog_transparent_style);
    mDialogContentView = LayoutInflater.from(this).inflate(R.layout.dialog_loading, null);
    tv_loadText = (TextView) mDialogContentView.findViewById(R.id.tv_loading_text);
    iv_loadImage = (ImageView) mDialogContentView.findViewById(R.id.iv_load_image);
    pb_loadProgress = (ProgressBar) mDialogContentView.findViewById(R.id.pb_load_progress);
    mDialog.setCanceledOnTouchOutside(false);
    mDialog.setContentView(mDialogContentView);
    Window window = mDialog.getWindow();
    if (null != window) {
        window.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
    }
}

public void showProgressDialog() {
    if (mDialog != null && !mDialog.isShowing()) {
        pb_loadProgress.setVisibility(View.VISIBLE);
        iv_loadImage.setVisibility(View.GONE);
        tv_loadText.setVisibility(View.GONE);
        mDialog.show();
    }
}

public void showToast(String text) {
    if (!TextUtils.isEmpty()) {
        Toast.makeText(App.getApplication(), text, Toast.LENGTH_LONG).show();
    }
}

BaseActivity寫了顯示Toast,Dialog等的UI操作后孟害,在子類Activity就可以共同調用了拒炎,可以減少共有操作的重寫,當然了挨务,不寫在BaseActivity也是可以击你,我們可以封裝成一個工具類,在需要用的時候再調用工具類的相關方法也是可以的谎柄,這就看個人的喜好了丁侄,哈哈

  • 友盟消息推送,統(tǒng)計

在項目發(fā)布之后朝巫,我們可能需要向用戶推送消息鸿摇,需要知道用戶在界面的點擊次數(shù),異常信息等劈猿,以方便我們對產(chǎn)品的改進

由于在每個Activity或Fragment界面我們都要進行統(tǒng)計拙吉,所以我們需要讓每個Activity或Fragment都要調用到友盟的API,但是如果一個一個Activity或Fragment的去進行調用揪荣,是非常繁瑣的筷黔,特別是如果項目比較大,Activity和Fragment的數(shù)量較多仗颈,更是不可想象

所以我們應該在BaseActivity的生命周期方法去調用友盟API

@Override
protected void onResume() {
    MobclickAgent.onResume(this);
}
@Override
protected void onPause() {
    MobclickAgent.onPause(this);
}
  • ButterKnife佛舱,EventBus等

使用過ButterKnife和EventBus的同學都知道,ButterKnife需要進行View的綁定和解綁,而EventBus需要對Activity對象進行注冊和反注冊请祖,這樣我們也不會在每個Activity都去重復進行這一操作订歪,而是在BaseActivity進行bindunbind的操作,用到EventBus時损拢,也可以在BaseActivity進行注冊和反注冊

private Unbinder unbinder;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    unbinder = ButterKnife.bind(this);
    EventBus.getDefault().register(this);
    ActivityUtils.addActivity(this);
    initDialog();
    ...
}

public void onEventMainThread(BaseEvent event) {
    ...do...
}

@Override
protected void onDestroy() {
    super.onDestroy();
    unbinder.unbind();
    EventBus.getDefault().unregister(this);
    ActivityUtils.removeActivity(this);
}
  • Android6.0運行時權限處理

在Android6.0版本以上陌粹,對于一些危險權限,需要用戶授權之后才能使用福压,這也需要調用對危險權限進行申請

在申請危險權限時掏秩,需要在Activity中進行代碼編寫申請,雖然對于權限處理的代碼并不是很復雜荆姆,但是如果在多個Activity中都需要申請權限蒙幻,就需要編寫很多重復的代碼,所以最好就能做一個封裝胆筒,最簡單的封裝方法就是將其統(tǒng)一進行設置在BaseActivity中邮破,這樣子類Activity能統(tǒng)一調用

在進行封裝時,先寫一個權限回調接口

public interface PermissionListener {
    void onGranted();

    void onDenied(List<String> deniedPermissions);
}

在接口中定義兩個方法仆救,分別是統(tǒng)一授權和取消授權抒和,定義接口之后,在BaseActvity中定義一個申請權限的方法彤蔽,該方法的代碼如下

public static void requestPermissions(String[] permissions, PermissionListener listener) {
    Activity activity = ActivityUtils.getTopActivity();
    if (null == activity) {
        return;
    }

    mPermissionListener = listener;
    List<String> permissionList = new ArrayList<>();
    for (String permission : permissionList) {
        //權限沒有授權
        if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(permission);
        }
    }

    if (!permissionList.isEmpty()) {
        ActivityCompat.requestPermissions(activity, permissionList.toArray(new String[permissionList.size()]), CODE_REQUEST_PERMISSION);
    } else {
        mPermissionListener.onGranted();
    }
}

可以看到摧莽,方法需要有兩個參數(shù),分別是需要申請的權限顿痪,是一個String數(shù)組镊辕,另一個則是權限的回調接口,用于在授權或取消授權后能做出相應的處理蚁袭,接著在方法里面對權限數(shù)組進行一個循環(huán)判斷其是否已經(jīng)授權征懈,對于沒有授權的會添加到List中,可以用于再次申請揩悄,在申請之后卖哎,需要重寫Activity的onRequestPermissionsResult方法,判斷授權結果

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case CODE_REQUEST_PERMISSION:
            if (grantResults.length > 0) {
                List<String> deniedPermissions = new ArrayList<>();
                for (int i = 0; i < grantResults.length; i++) {
                    int result = grantResults[i];
                    if (result != PackageManager.PERMISSION_GRANTED) {
                        String permission = permissions[i];
                        deniedPermissions.add(permission);
                    }
                }

                if (deniedPermissions.isEmpty()) {
                    mPermissionListener.onGranted();
                } else {
                    mPermissionListener.onDenied(deniedPermissions);
                }
            }
            break;

        default:
            break;
    }
}

這樣統(tǒng)一封裝在BaseActivity之后删性,在子類Activity只要調用requestPermissions方法并傳入權限數(shù)組和回調接口的參數(shù)就能對結果進行對應的處理了

在上面的分析知道亏娜,基類BaseActivity可以用于處理子類共有的邏輯,這樣可以比main重復進行處理同一邏輯镇匀,在本篇文章中,講到的內容還是有限的袜啃,對于其他的共有邏輯汗侵,可以自行補充

本篇文章的代碼已上傳到github,在github上對應著BaseProject項目,該項目將會對于在開發(fā)中經(jīng)常需要使用到的例如BaseActivity晰韵,BaseFragment发乔,網(wǎng)絡請求等進行一些基本封裝,方便以后使用雪猪,項目將會持續(xù)更新栏尚,歡迎大家進行star和關注

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市只恨,隨后出現(xiàn)的幾起案子译仗,更是在濱河造成了極大的恐慌,老刑警劉巖官觅,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纵菌,死亡現(xiàn)場離奇詭異,居然都是意外死亡休涤,警方通過查閱死者的電腦和手機咱圆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來功氨,“玉大人序苏,你說我怎么就攤上這事〗萜啵” “怎么了忱详?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纵势。 經(jīng)常有香客問我踱阿,道長,這世上最難降的妖魔是什么钦铁? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任软舌,我火速辦了婚禮,結果婚禮上牛曹,老公的妹妹穿的比我還像新娘佛点。我一直安慰自己,他們只是感情好黎比,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布超营。 她就那樣靜靜地躺著,像睡著了一般阅虫。 火紅的嫁衣襯著肌膚如雪演闭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天颓帝,我揣著相機與錄音米碰,去河邊找鬼窝革。 笑死,一個胖子當著我的面吹牛吕座,可吹牛的內容都是我干的虐译。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼吴趴,長吁一口氣:“原來是場噩夢啊……” “哼漆诽!你這毒婦竟也來了?” 一聲冷哼從身側響起锣枝,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤厢拭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惊橱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚪腐,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年税朴,在試婚紗的時候發(fā)現(xiàn)自己被綠了回季。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡正林,死狀恐怖泡一,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情觅廓,我是刑警寧澤鼻忠,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站杈绸,受9級特大地震影響帖蔓,放射性物質發(fā)生泄漏。R本人自食惡果不足惜瞳脓,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一塑娇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧劫侧,春花似錦埋酬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至审姓,卻和暖如春珍特,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背魔吐。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工扎筒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呼猪,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓砸琅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轴踱。 傳聞我的和親對象是個殘疾皇子症脂,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,071評論 25 707
  • 1.什么是Activity?問的不太多诱篷,說點有深度的 四大組件之一,一般的,一個用戶交互界面對應一個activit...
    JoonyLee閱讀 5,730評論 2 51
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,401評論 0 17
  • 【1】 女兒出生了雳灵。 全家忙手忙腳的照顧只懂得哭和笑的小家伙棕所。 第一次當媽媽,很累悯辙。沒有睡過一天安穩(wěn)覺琳省。時刻注意女...
    Da白紙閱讀 616評論 1 1
  • 公元2061年,人類社會飛速發(fā)展躲撰。中國科技發(fā)展程度已經(jīng)超越世界上任何的一個國家针贬。人工智能,從45年前就開始萌...
    missyxy閱讀 319評論 0 2