在上一篇文章NetworkStateView的結尾說到可以在BaseActivity中對NetworkStateView進行統(tǒng)一設置,從而進行界面多狀態(tài)的加載,那么今天就說一說BaseActivity,在BaseActivity怎么進行NetworkStateView的設置以及BaseActivity的一些其他作用,還沒有看過NetworkStaetView的可以先看一下這一篇文章
在項目中,我們在寫Activity時一般都不會直接繼承AppCompatActivity,而是先寫一個基類BaseActivity绰疤,讓其他的Activity繼承BaseActivity,這樣就有一個好處舞终,就是我們可以把Activity的共有邏輯寫在BaseActivity中轻庆,例如NetworkStateView的邏輯,從而不需要在每個Activity都寫一遍
下面就對BaseActivity對NetworkStateView以及一些其他共有的邏輯內容進行介紹
- 定義加載布局敛劝,進行共同界面(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赁濒,Toast和Snackbar等
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進行bind
和unbind
的操作,用到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和關注