安卓开发中,经常会遇到项目的标题栏基本样式都是一致的,只有个别界面需要定制化,而TitltBar或许跟我们的业务需求不是很相符,所以很多时候我们都想要去封装一个含有通用标题栏并且可定制化的BaseActivity。本文希望对有这方面需求的童鞋提供一种参考。效果图类似于这样

首先很多开发者比较喜欢的一种做法是写一份通用的标题栏布局,然后在每次创建的Activity的布局文件中去选择include的方式加入布局,这种方式其实在我看来工作量其实还是挺大,为了使用的时候更方便,可以将写好的标题栏布局作为一个View添加到根布局中。

下面先贴出标题栏的布局:

<?xml version="1.0" encoding="utf-8"?>
<!-- 默认TitleBar布局文件 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="44dp"android:background="@color/white"><TextViewandroid:id="@+id/tv_titlebar_name"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:paddingLeft="90dp"android:paddingRight="90dp"android:singleLine="true"android:text="@string/app_name"android:textColor="@color/color_333333"android:textSize="18sp" /><TextViewandroid:id="@+id/tv_titlebar_left"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentLeft="true"android:drawableLeft="@mipmap/icon_back"android:gravity="center"android:paddingLeft="8dp"android:paddingRight="8dp"android:layout_centerVertical="true"android:textColor="@color/white"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_titlebar_right"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:gravity="center"android:paddingLeft="5dp"android:paddingRight="15dp"android:text="@string/app_name"android:textColor="@color/color_333333"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_titlebar_more"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_toLeftOf="@id/tv_titlebar_right"android:gravity="center"android:paddingLeft="5dp"android:paddingRight="5dp"android:text="@string/app_name"android:textColor="@color/color_333333"android:textSize="14sp" /></RelativeLayout></LinearLayout>

这里讲一下为什么返回按键,中间标题,右边两个小标题都选用了TextView,因为我的想法是为了让标题栏按钮是图标的时候,可以更快捷的使用。毕竟TextView还可以在它的上下左右动态添加Drawable嘛。还有一点,为什么选用线性布局中嵌套了一个相对布局,那是因为我考虑到以后可能会存在需要在标题栏下面添加一些类似于间隔啊之类的布局,所以就给预留出来了,如果不需要的话,可以选择去掉外层的线性布局。标题栏的右侧我自己是添加了两个预留的按钮,可以方便右上角存在比如保存、分享之类的操作。

下面贴一下不含标题栏的基类BaseActivity的代码:

/*** Activity基类*/
public class BaseActivity extends FragmentActivity {protected int               mResultCode = RESULT_CANCELED;private int                  mStatusHeight;// 状态栏高度private int                   mTitleHeight;// 标题栏高度private int                    mScreenWidth, mScreenHeight;// 屏幕宽度、高度protected boolean         mIsFirst = true;// true-界面第一次聚焦private RequestUploadDialog mUploadDialog;// 加载框@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityStackUtils.getInstance().pushActivity(this);// activity入栈管理}@Overrideprotected void onResume() {super.onResume();}@Overrideprotected void onPause() {super.onPause();}@Overrideprotected void onStart() {super.onStart();}@Overrideprotected void onStop() {super.onStop();}@Overrideprotected void onDestroy() {super.onDestroy();ActivityStackUtils.getInstance().finishActivity(this);// activity出栈管理}/*** 隐藏键盘*/public void hideKeyBoard() {try {View rootView = this.getWindow().getDecorView();View focusView = rootView.findFocus();if (focusView != null) {int viewId = focusView.getId();View view = findViewById(viewId);if (view instanceof EditText) {InputMethodManager manager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);manager.hideSoftInputFromWindow(view.getWindowToken(), 0);}}} catch (Exception e) {e.printStackTrace();}}/*** 获取状态栏高度* @return 状态栏高度(px)*/public int statusHeight() {if (mStatusHeight == 0) {try {Class<?> c = Class.forName("com.android.internal.R$dimen");int x = Integer.parseInt(c.getField("status_bar_height").get(c.newInstance()).toString());mStatusHeight = getResources().getDimensionPixelSize(x);} catch (Exception e1) {e1.printStackTrace();}}return mStatusHeight;}/*** 获取屏幕宽度* @return 屏幕宽度(px)*/public int screenWidth() {if (mScreenWidth == 0) {DisplayMetrics displayMetrics = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);mScreenWidth = displayMetrics.widthPixels;}return mScreenWidth;}/*** 获取屏幕高度* @return 屏幕高度(px)*/public int screenHeight() {if (mScreenHeight == 0) {DisplayMetrics displayMetrics = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);mScreenHeight = displayMetrics.heightPixels;}return mScreenHeight;}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK) {return executeKeyDownBack(keyCode, event);}return super.onKeyDown(keyCode, event);}/*** 监听返回键* @param keyCode 键值* @param event 事件* @return true-消耗事件,false-不消耗事件*/protected boolean executeKeyDownBack(int keyCode, KeyEvent event) {return super.onKeyDown(keyCode, event);}/*** Home键监听回调方法* @param context 上下文环境* @param intent 意图*/public void onHomeWatcherReceiver(Context context, Intent intent) {}/*** 显示加载框* @param uploadResId 资源ID*/public void showUpload(int uploadResId) {showUpload(getString(uploadResId));}/*** 显示加载框* @param uploadContent 文本*/public void showUpload(String uploadContent) {if (mUploadDialog == null)mUploadDialog = new RequestUploadDialog(this);mUploadDialog.showText(uploadContent).show();}/*** 隐藏加载框*/public void hideUpload() {if (mUploadDialog == null)return;mUploadDialog.dismiss();}/*** 显示提示语* @param toastResId 资源ID*/public void showToast(int toastResId) {Toast.makeText(this, toastResId, Toast.LENGTH_SHORT).show();}/*** 显示提示语* @param toastText 文本*/public void showToast(String toastText) {Toast.makeText(this, toastText, Toast.LENGTH_SHORT).show();}/*** 设置TextView的left图片(不添加文字)* @param textView 控件* @param resIds 资源ID*/public void drawableLeft(TextView textView, int resIds) {textView.setVisibility(TextView.VISIBLE);textView.setText("");Drawable drawable = getResources().getDrawable(resIds);drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());textView.setCompoundDrawables(drawable, null, null, null);}/*** 设置TextView的right图片(不添加文字)* @param textView 控件* @param resIds 资源ID*/public void drawableRight(TextView textView, int resIds) {textView.setVisibility(TextView.VISIBLE);textView.setText("");Drawable drawable = getResources().getDrawable(resIds);drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());textView.setCompoundDrawables(null, null, drawable, null);}/*** 设置TextView颜色* @param textView 控件* @param resId 资源ID*/public void textColor(TextView textView, int resId) {textView.setTextColor(getResources().getColor(resId));}/*** 着色状态栏(4.4以上系统有效)*/protected void SetStatusBarColor() {StatusBarCompat.setStatusBarColor(this, ContextCompat.getColor(this, R.color.base_color));}/*** 着色状态栏(4.4以上系统有效)*/protected void SetStatusBarColor(int color) {StatusBarCompat.setStatusBarColor(this, color);}/*** 沉浸状态栏(4.4以上系统有效),全屏模式*/protected void SetTranslanteBar() {StatusBarCompat.translucentStatusBar(this);}@Overridepublic void startActivity(Intent intent) {super.startActivity(intent);overridePendingTransition(R.anim.anim_right2enter, R.anim.anim_left2exit);}/*** 通过 Class 跳转界面,不含参数**/public void startActivity(Class<?> cls) {startActivity(cls, null);}/*** 含有 Bundle 通过 Class 跳转界面* 带有跳转动画**/public void startActivity(Class<?> cls, Bundle bundle) {Intent intent = new Intent();intent.setClass(this, cls);if (bundle != null) {intent.putExtras(bundle);}startActivity(intent);overridePendingTransition(R.anim.anim_right2enter, R.anim.anim_left2exit);}/*** 通过 Class 跳转界面**/public void startActivityForResult(Class<?> cls, int requestCode) {startActivityForResult(cls, null, requestCode);overridePendingTransition(R.anim.anim_right2enter, R.anim.anim_left2exit);}/*** 含有Bundle通过Class跳转界面**/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);overridePendingTransition(R.anim.anim_right2enter, R.anim.anim_left2exit);}/*** 跳转到登录界面*/public void jumpToLoginActivity(int requestCode) {Intent intent = new Intent(IntentAction.ACTION_LOGIN);startActivityForResult(intent, requestCode);overridePendingTransition(R.anim.anim_bottom_in, R.anim.anim_bottom_window);}@Overridepublic void finish() {super.finish();}/*** finish 当前界面,手动控制页面关闭动画** @param closeAnim true:关闭时有动画,false:关闭是无动画*/public void finish(boolean closeAnim) {super.finish();if (closeAnim) {overridePendingTransition(R.anim.anim_left2enter, R.anim.anim_right2exit);}}}

里面封装了一些其他比较常用的方法,有几个细节的点可以描述一下

1.里面封装了通用的弹toast和弹加载框的方法,当然加载框大家也可以封装到网络请求模块和本地数据库操作中去,这个看个人的喜好

2.基类中包含有软键盘的关闭操作,因为安卓的软键盘一直是一个比较头疼的问题,所以我选择直接封装到基类中,如果需要用到某些操作需要关闭软键盘的可以直接调用

3.封装的有对某个TextView添加Drawable的方法,这样标题栏需要加载图标的时候,方便调用

4.封装的有修改状态栏颜色和主题的方法,但是StatusBarCompat这个工具类我没有贴出来,不知道其中操作细节的可以去网上自行搜索查看

下面就是对含有标题栏的Activity的封装了,思路文章开头已经提到过,就直接上代码吧:

/*** Activity基类:含TitleBar*/
public class BaseTitleBarActivity extends BaseViewStubActivity {protected TextView       mTvCenter, mTvLeft, mTvRight, mTvMore;protected View            mViewTitleBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overridepublic void setContentView(int layoutResID) {super.setContentView(layoutResID);}@Overrideprotected void addTitleBarView() {mViewTitleBar = View.inflate(this, addTitleBarLayout(), null);mTvCenter = mViewTitleBar.findViewById(R.id.tv_titlebar_name);mTvLeft  = mViewTitleBar.findViewById(R.id.tv_titlebar_left);mTvRight = mViewTitleBar.findViewById(R.id.tv_titlebar_right);mTvMore = mViewTitleBar.findViewById(R.id.tv_titlebar_more);mTvLeft.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {onTitleBarClickLeft(v);}});mTvRight.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {onTitleBarClickRight(v);}});mTvCenter.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {onTitleBarClickCenter(v);}});mTvMore.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {onTitleBarClickMore(v);}});onChangedTitleBar(mTvLeft, mTvCenter, mTvRight);mLayoutContent.addView(mViewTitleBar);}/*** 设置TitleBar布局* @return TitleBar布局*/protected int addTitleBarLayout() {return R.layout.layout_base_titlebar;}/*** 修改TitleBar* @param left 左边按钮* @param center 中间文本* @param right 右边按钮,默认隐藏*/protected void onChangedTitleBar(TextView left, TextView center, TextView right) {right.setVisibility(View.GONE);mTvMore.setVisibility(View.GONE);}/*** TitleBar左边按钮点击事件* @param view 控件*/protected void onTitleBarClickLeft(View view) {setResult(mResultCode);finish();}/*** TitleBar右边按钮点击事件* @param view 控件*/protected void onTitleBarClickRight(View view) {}/*** TitleBar右边(更多)按钮点击事件* @param view 控件*/protected void onTitleBarClickMore(View view) {}/*** TitleBar中间按钮点击事件* @param view 控件*/protected void onTitleBarClickCenter(View view) {}/*** 获取焦点* @param view 控件*/public void requestFocus(final View view) {view.postDelayed(new Runnable() {@Overridepublic void run() {view.setFocusable(true);view.setFocusableInTouchMode(true);view.requestFocus();}}, 100);}
}

BaseViewStubActivity的代码如下:

/*** Activity基类:不含TitleBar*/
public class BaseViewStubActivity extends BaseActivity {protected LinearLayout      mLayoutContent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overridepublic void setContentView(int layoutResID) {// 设置状态栏颜色
//      SetTranslanteBar();super.setContentView(R.layout.activity_base_titlebar);mLayoutContent = findViewById(R.id.ll_content_view);addTitleBarView();            // 添加TitleBaraddContentView(layoutResID);// 添加ContentView}/*** 添加TitleBar*/protected void addTitleBarView() {}/*** 添加ContentView* @param layoutResID 布局*/protected void addContentView(int layoutResID) {View.inflate(this, layoutResID, mLayoutContent);}
}

这里相关的activity_base_titlebar布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- Activity基类默认加载的布局文件 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:id="@+id/ll_content_view"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"></LinearLayout></RelativeLayout>

这样一来,使用的时候只需要继承自BaseTitlebarActivity并实现onChangedTitleBar就可以随意对标题栏进行定制化了,对左上角的按钮点击方法是默认的对出操作,如果需要重写,也可以自己重新对该方法进行实现,标题栏每个按钮的点击操作都是单独出来的,需要点击操作的时候再进行重写。

效果图就不上了,谈一谈封装过程中遇到的一个最主要的问题吧

最初在封装BaseViewStubActivity的时候addContentView方法中我是使用的

mLayoutContent.addView(View.inflate(this, layoutResID, null));

这种方式进行添加,运行起来最初没有发现什么问题,但是一旦遇到相对布局的时候,需要在底部添加一个按钮的时候,发现最底部的控件的margin_bottom会失效,后来查阅了很多资料,原来是执行这句代码的时候,子布局其实并没有挂载到父布局中,所以造成了控件的布局宽高属性都为默认的LayoutParams.WRAP_CONTENT自适应,不清楚的可以去阅读以下addView的源码,所以解决这个的方法有两种:

第一种:修改添加子布局的方式为View.inflate(this, layoutResID, mLayoutContent);

第二种:挂载的方式修改为

mLayoutContent.addView(View.inflate(this, layoutResID, null), new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

也就是手动赋予子布局一个布局参数。

补充:

在继承自BaseTitlebarActivity的activity中同样也可以调用addTitleBarLayout方法来加载另外的标题栏布局,但是此时由于在上一级基类Activity中已经对标题栏进行了操作,所以请务必保持加载的新标题栏布局的每个控件id和通用标题栏的控件id一致,否则会造成空指针异常,当然如果只是对标题栏的颜色或者字体颜色那些修改的话,也提供两种思路,一种是定义另外一个主题颜色的标题栏布局,但是id与通用标题栏每个控件id一致;第二种是在上一级基类Activity中开放另外一个方法,调用时动态更新主题颜色等。

如果有问题欢迎指出,非常感谢!

Android封装含有通用标题栏的基类BaseActivity相关推荐

  1. 【struts2+hibernate+spring项目实战】分页功能的完整的实现(通用分页、基类实现)

    一.概述 今天自己做了个项目练练,然后有一些分页的功能,自己把分页的功能做了一个简单的总结,然后,为了以后能够方便自己的开发,做了一个baseDao的实现. 二.代码实现 2.1.分页的实体类page ...

  2. Android 封装handler,android封装工作线程跟Handler工具类

    直接上代码,不解说 - - 基于MVP封装P的基类 AbsHandlerThreadHelper.java import java.lang.ref.WeakReference; import jav ...

  3. Android 封装一个通用的PopupWindow

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 先上效果图: 完整代码地址已上传Github:CommonPopupWindow PopupWindow这个类用来实现一个弹出框, ...

  4. 封装一个通用的数据库操作类

    最近写一个项目,我开发的方式首先设计数据库,然后用网上免费的代码生成软件CodePlus V2.0生成mode对象,同时封装操作方法,在与数据库操作方面我封装了一个数据操作类,很方便,贡献出来. Co ...

  5. 虚继承c语言例子,C/C++ 多继承{虚基类,虚继承,构造顺序,析构顺序}

    C/C++:一个基类继承和多个基类继承的区别 1.对多个基类继承会出现类之间嵌套时出现的同名问题,如果同名变量或者函数出现不在同一层次,则底层派生隐藏外层比如继承基类的同名变量和函数,不会出现二义性, ...

  6. C++虚继承(七) --- 虚继承对基类构造函数调用顺序的影响

    继承作为面向对象编程的一种基本特征,其使用频率非常高.而继承包含了虚拟继承和普通继承,在可见性上分为public.protected.private.可见性继承比较简单,而虚拟继承对学习c++的难度较 ...

  7. C++中的各种“虚“-- 虚函数、纯虚函数、虚继承、虚基类、虚析构、纯虚析构、抽象类讲解

    C++中的各种"虚" 1. 菱形继承 1.1 虚继承 && 虚基类 1.2 虚基类指针(vbptr)&& 虚基类表(vbtable) 2. 多态 2 ...

  8. 派生类继承虚基类后对象的大小----C++学习(1)

    C++中类继承虚基类对象的大小 原理 1.基类有虚函数,派生类无虚函数 2.基类有虚函数,派生类有虚函数 3.基类无虚函数,派生类无虚函数 4.基类无虚函数,派生类有虚函数 5.两个基类都有虚函数,派 ...

  9. android mvp框架基类,Android MVP架构项目搭建封装,基类封装

    综述 对于MVP (Model View Presenter)架构是从著名的MVC(Model View Controller)架构演变而来的.而对于Android应用的开发中本身可视为一种MVC架构 ...

  10. Android 获取通讯录联系人,打开通讯录获取联系人信息;整个流程封装在基类中;

    打开原生通讯录获取联系人姓名和手机号 1.获取通讯录权限: <!--访问通讯录--><uses-permission android:name="android.permi ...

最新文章

  1. Python中使用Flask、MongoDB搭建简易图片服务器
  2. UNIX网络编程——UDP回射服务器程序(初级版本)以及漏洞分析
  3. day06:02oracle体系结构_存储结构
  4. redis 流 stream的使用总结 - 如何遍历
  5. 面对疫情等群体性危机,程序员如何在家高效办公?
  6. PHP程序员最常犯的11个MySQL错误
  7. zblog修改上传服务器,【记录有用】ZBlog备份、恢复与搬家(换空间换服务器)方法...
  8. 保存的离线网页总是自动跳转怎么办???
  9. 微信自定义分享,分享至QQ和空间自定义内容正常、分享至微信时只有链接问题
  10. 屏蔽烦人的网易云音乐评论区(附防颓小技巧)
  11. bootstrap为什么不直接使用.btn-default而要使用.btn.btn-default实现同样的效果?
  12. Excel:数据分列功能分割文本
  13. 移动端 --- 解决苹果手机滑动卡顿的问题
  14. 金融信贷存量客户运营管理
  15. 神经网络和图神经网络,神经网络的图怎么画
  16. 【金猿产品展】诸葛用户数据分析平台(Insight)——聚焦业务场景数据应用价值挖掘,赋能精细化运营...
  17. dockers安装Jenkins
  18. 代购服务器哪个网站好,搭建韩国代购平台网站选择哪家服务器比较好?
  19. 网页显示自动日期(自动更新)
  20. 2020Android开发陷入饱和,移动开发者未来的出路在哪里,我们该如何让应对?

热门文章

  1. web网页设计实例作业 ——中国茶文化(6页) 茶文化网页制作作业_中国化(网页设计...
  2. a modern epidemic
  3. python seek_Python 文件 seek() 方法
  4. 大数据分析取得的成果有哪些
  5. android Launcher 自定义View 高仿hola一键清理效果
  6. 搜索引擎Autonomy
  7. Blend 混合模式
  8. 将Python程序打包成exe文件
  9. 顺丰全栈资源下的自动化运维灵魂
  10. 托管服务器ip绑定域名_如何在一台服务器上托管多个域名和项目