1、前言
在分析主頁面(MainActivity)之前,我們需要知道Activity基類(BaseActivity)都封裝了哪些功能,對外提供了哪些共通的方法。
此處使用了設計模式中的模板方法。我們先來看看它的定義是什麼。定義一個功能的框架或者說骨架,一部分功能是確定的,一部分功能是不確定的,先把確定的部分確定下來,把不確定的部分延遲到子類中實現的設計模式稱之爲模板方法。
其優點是通過繼承基類,從而具備共通的屬性和行爲,減少重複的工作。缺點是提高了耦合度,同時由於有些功能在部分畫面中使用不到,畫面渲染的時間也會加長,犧牲部分的性能,所以需要權衡哪些功能通過繼承的方式(基類的形式)提供,哪些功能通過組合的方式(工具類的形式)提供。
2、功能列表
通過閱讀源碼,我們可以知道BaseActivity主要封裝了以下功能。
- 畫面初始化(onCreate)
- 設置標題欄(ActionBar)
- 設置返回按鈕(BackButton)
- 設置友盟(umeng)打點服務
- 封裝提示框(Toast)
- 封裝加載框(WaitDialog)
關聯代碼文件:
- https://gitee.com/oschina/android-app/blob/v2.9.0/app/src/main/java/net/oschina/app/base/BaseActivity.java
2.1、畫面初始化(onCreate)
相關文件:
- app/src/main/java/net/oschina/app/interf/BaseViewInterface.java
說明:
- 接口View.OnClickListener :不是每一個界面都有點擊事件,所以此處不合理,應刪除
- 方法 onBeforeSetContentLayout():在設置界面佈局之前的處理,比如說子頁面需要重新設置主題樣式,那麼就可以重寫此方法。默認處理爲空。
- 方法 getLayoutId():獲取佈局文件的ResId,默認值爲0,ContentView爲Null。
public abstract class BaseActivity extends AppCompatActivity implements
DialogControl, View.OnClickListener, BaseViewInterface {
protected LayoutInflater mInflater;
protected ActionBar mActionBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 講解:在清單文件中已經設置全局主題樣式了(App_Theme_Light),
// 因爲此處優先級高,所以清單文件中Activity作用域下的主題將會被覆蓋
// 此處應刪除
setTheme(R.style.App_Theme_Light);
onBeforeSetContentLayout();
if (getLayoutId() != 0) {
setContentView(getLayoutId());
}
mActionBar = getSupportActionBar();
mInflater = getLayoutInflater();
......
// 講解:可以使用JetPack的成員組件Databinding綁定組件,ButterKnife應廢棄
// 通過註解綁定控件
ButterKnife.bind(this);
init(savedInstanceState);
initView();
// 講解:此處初始化數據並不合理,要優先加載頁面,數據加載後置。
initData();
......
}
protected void onBeforeSetContentLayout() {
}
protected void init(Bundle savedInstanceState) {
}
protected int getLayoutId() {
return 0;
}
}
2.2、設置標題欄(ActionBar)
protected ActionBar mActionBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
mActionBar = getSupportActionBar();
if (hasActionBar()) {
initActionBar(mActionBar);
}
......
}
// 講解:是否含有ActionBar,可以通過設置主題樣式便可以實現。
protected boolean hasActionBar() {
return getSupportActionBar() != null;
}
// 講解:參數actionBar可以使用mActionBar代替
protected void initActionBar(ActionBar actionBar) {
if (actionBar == null)
return;
if (hasBackButton()) {
mActionBar.setDisplayHomeAsUpEnabled(true);
mActionBar.setHomeButtonEnabled(true);
} else {
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
actionBar.setDisplayUseLogoEnabled(false);
int titleRes = getActionBarTitle();
if (titleRes != 0) {
actionBar.setTitle(titleRes);
}
}
}
// 講解:通過資源Id設置標題
public void setActionBarTitle(int resId) {
if (resId != 0) {
setActionBarTitle(getString(resId));
}
}
// 講解:通過文本設置標題
public void setActionBarTitle(String title) {
if (TextUtils.isEmpty(title)) {
title = getString(R.string.app_name);
}
if (hasActionBar() && mActionBar != null) {
mActionBar.setTitle(title);
}
}
2.3、設置返回按鈕(BackButton)
// 講解1:在子類中重寫該方法即可
protected boolean hasBackButton() {
return false;
}
protected void initActionBar(ActionBar actionBar) {
if (actionBar == null)
return;
// 講解2:當設置有返回按鈕的時候,在ActionBar顯示返回按鈕
if (hasBackButton()) {
mActionBar.setDisplayHomeAsUpEnabled(true);
mActionBar.setHomeButtonEnabled(true);
} else {
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
actionBar.setDisplayUseLogoEnabled(false);
int titleRes = getActionBarTitle();
if (titleRes != 0) {
actionBar.setTitle(titleRes);
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// 講解3:點擊ActionBar上的返回按鈕時,返回上一級頁面
case android.R.id.home:
onBackPressed();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
2.4、設置友盟(umeng)打點服務
此處應先定義接口,然後根據需要提供實現。同時只能觀測到用戶什麼時候打開頁面,什麼時候關閉頁面。用戶點擊什麼按鈕等事件沒有觀測到,收集的數據比較有限,不能給運維提供強有力的支持,需要改進。
private final String packageName4Umeng = this.getClass().getName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
// 講解初始化代理
MobclickAgent.setDebugMode(false);
MobclickAgent.openActivityDurationTrack(false);
MobclickAgent.setScenarioType(this, MobclickAgent.EScenarioType.E_UM_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
MobclickAgent.onPageEnd(this.packageName4Umeng);
// 講解:此處設置錯誤,應爲MobclickAgent.onPause(this)
MobclickAgent.onResume(this);
......
}
@Override
protected void onResume() {
super.onResume();
MobclickAgent.onPageStart(this.packageName4Umeng);
MobclickAgent.onResume(this);
}
路徑:app/src/main/AndroidManifest.xml
<manifest>
<application>
<meta-data
android:name="UMENG_APPKEY"
android:value="53cb520c56240bbd7d076ce5" />
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL}" />
</application>
</manifest>
2.5、封裝提示框(Toast)
系統在逐步迭代後,使用了至少四種提示框,SimplexToast應向上抽取成全局工具類,
以組合的方式在需要的位置調用。此處的代碼應刪除比較合理。
- android.widget.Toast
- app/src/main/java/net/oschina/app/base/BaseApplication.java
- app/src/main/java/net/oschina/app/ui/dialog/CommonToast.java
- app/src/main/java/net/oschina/app/improve/widget/SimplexToast.java
public void showToast(int msgResid, int icon, int gravity) {
showToast(getString(msgResid), icon, gravity);
}
public void showToast(String message, int icon, int gravity) {
CommonToast toast = new CommonToast(this);
toast.setMessage(message);
toast.setMessageIc(icon);
toast.setLayoutGravity(gravity);
toast.show();
}
2.6、封裝加載框(WaitDialog)
相關文件:
- app/src/main/java/net/oschina/app/ui/dialog/DialogControl.java
- app/src/main/java/net/oschina/app/improve/utils/DialogHelper.java
成員變量說明:
- _isVisible:確保加載框在畫面實例化完成以後纔可以表示或者隱藏
- _waitDialog:加載框定義
private boolean _isVisible;
private ProgressDialog _waitDialog;
@Override
public ProgressDialog showWaitDialog() {
return showWaitDialog(R.string.loading);
}
@Override
public ProgressDialog showWaitDialog(int resid) {
return showWaitDialog(getString(resid));
}
@Override
public ProgressDialog showWaitDialog(String message) {
if (_isVisible) {
if (_waitDialog == null) {
_waitDialog = DialogHelper.getProgressDialog(this, message);
}
if (_waitDialog != null) {
_waitDialog.setMessage(message);
_waitDialog.show();
}
return _waitDialog;
}
return null;
}
@Override
public void hideWaitDialog() {
if (_isVisible && _waitDialog != null) {
try {
_waitDialog.dismiss();
_waitDialog = null;
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
路徑:app/src/main/res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="loading">加載中…</string>
</resources>
3、總結
分析完以後才發現,原來是舊版Activity基類,已經被廢棄了,但是代碼文件仍然被保留沒有被刪除,也沒有添加已過時註解,建議改進。
- 舊版:app/src/main/java/net/oschina/app/base/BaseActivity.java
- 新版:app/src/main/java/net/oschina/app/improve/base/activities/BaseActivity.java