背景

在大多数应用的页面顶部,都会有标题栏,这种业务相关性不大,通用性很强的视图,第一直觉,是要把它抽取,做成通用的。


最先想到的是使用google推荐的ActionBar 和 ToolBar。但要标题文字居中就特别麻烦,ActionBar得使用自定义布局setCustomView(),设置后,它提供的其它api就相当于废弃了,原有的api无法操作自定义的布局,相当于只能用它当作容器,view的操作还得自己写;ToolBar更奇怪,得用TextView覆盖在ToolBar视图之上,再给ToolBar 的标题文字清空,它的setTitle函数这就算是废掉了,,后续都操作自己加入的title TextView。唉,标题居中都这么麻烦,原谅我我不擅长使用这个轮子,… 那就按照项目中的要求自己造一个吧。

方法一 include 通用型布局

这是早期项目中的比较原始的一般做法

实现

  1. 将标题栏写成通用型的布局文件,在主布局文件中inclide
    新建titlebar_view.xml 大致代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="@dimen/titlebar_height"android:background="#CCCCCC"android:orientation="vertical"><LinearLayout
        android:layout_width="wrap_content"android:layout_height="match_parent"android:background="#AAAAAA"android:gravity="center_vertical"android:orientation="horizontal"><ImageView
            android:layout_width="36dp"android:layout_height="36dp"android:src="@android:drawable/ic_menu_compass"/><TextView
            android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="首页"/></LinearLayout><TextView
        android:id="@+id/titlebar_title_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="title text"/><LinearLayout
        android:layout_width="wrap_content"android:layout_height="match_parent"android:background="#AAAAAA"android:layout_alignParentRight="true"android:gravity="center_vertical"android:orientation="horizontal"><TextView
            android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="重设"/><ImageView
            android:layout_width="36dp"android:layout_height="36dp"android:src="@android:drawable/ic_menu_add"/></LinearLayout>
</RelativeLayout>

  1. 在主布局文件activity_main.xml 中include titlebar_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><include layout="@layout/titlebar_view"/><TextView
        android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!" />
</RelativeLayout>

使用

Activity中对view进行操作。设置标题,控制右侧操作图标的展示隐藏等

public void setTitle(CharSequence title) {((TextView)findViewById(R.id.titlebar_title_tv)).setText(title);}

缺陷

每个页面的布局得include这个文件,很繁琐,Activity中直接操作titlebar的子视图id,学习成本过高,而且团队开发中不易维护。

能否在xml中不用include 标题栏布局,只关注自己特有的一些布局。而且也不希望调用者关注标题栏中的各子视图的具体id

方法二 在根布局中动态添加标栏视图

自定义标题栏View 和 根容器View,在根容器中动态的把标题栏视图添加进去,这样,布局中就不需要标题栏了。

实现

  1. 自定义titleBarView
    对外提供操作子元素方法 如 setTitle();
public class TitleBarView extends FrameLayout {public TitleBarView(Context context) {super(context);init();}private void init() {this.setId(R.id.titlebar_view);this.removeAllViewsInLayout();LayoutInflater layoutInflater = LayoutInflater.from(getContext());View view = layoutInflater.inflate(R.layout.titlebar_view, this, false);addView(view, new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));}/*** 设置标题栏文字*/public void setTitle(CharSequence text){((TextView)findViewById(R.id.titlebar_title_tv)).setText(text);}
}
  1. 自定义根布局RootLinearLayout
    初始化的时候,把TitleBarView添加到第一个元素的位子
public class RootLinearLayout extends LinearLayout {public RootLinearLayout(Context context, AttributeSet attrs) {super(context, attrs);init();}public RootLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public RootLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}private void init() {addView(new TitleBarView(getContext()), 0, new LinearLayoutCompat.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, getResources().getDimensionPixelSize(R.dimen.titlebar_height)));}
}

3.在布局文件中使用RootLinearLayout 做为跟布局就可以了

<?xml version="1.0" encoding="utf-8"?>
<miao.t5.RootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextView
        android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!" />
</miao.t5.RootLinearLayout>

使用

public TitleBarView getTitleBar(){return (TitleBarView)findViewById(R.id.titlebar_view);}public void setTitle(CharSequence title) {getTitleBar().setText(title);}

缺陷

这种方法对于方法一有了很大进步,也解决了繁琐的include和需要知道标题栏子视图viewId的问题
但根布局必须是自定的RootLinearLayout,实际使用有局限性

方法三 根视图中加入自定义的xml布局

将通用型视图抽离(rootview + titlebarView + innerLoadingView + emptyView…)作为容器,各个页面的布局作为子视图被rootView动态 include,这样,各页面只需要处理自有的主业务布局了。类似于android窗口的构建方式。


图片来源于http://blog.csdn.net/cauchyweierstrass/article/details/44303657

我们在上图id/content-Fragment 里,局部再构建类似于DecroWindow的视图结构

实现

  1. 新建根视图布局layout_root.xml,作为根容器,被所有Activity加载
?xml version="1.0" encoding="utf-8"?>
<miao.t5.RootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><!-- setContentView()的容器,将xml填充其中 --><FrameLayout
        android:id="@+id/root_content_fl"android:layout_width="match_parent"android:layout_height="match_parent"></FrameLayout>
</miao.t5.RootLinearLayout>

2.重写BaseActivity的setContentView( )函数,将activity_main.xml作为子视图add进root_content_fl 中

@Overridepublic void setContentView(@LayoutRes int layoutResID) {super.setContentView(R.layout.layout_root);LayoutInflater.from(this).inflate(layoutResID, (ViewGroup) findViewById(R.id.root_content_fl), true);}

3.activity_main.xml就不需要关心titlebar了,正常编写自有的布局即可

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextView
        android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!" />
</LinearLayout>

使用方式和图二一致

优化

对外TitleBar接口可见

方法二和方法三中,标题栏是以自定义组合控件 TitleBarView 的方式进行封装的,继承自FrameLayout,也就继承了所有父类的非私有函数。调用者可对其removeView,setVisable对外的安全性也降低了,使用起来也不方便。

实现

  1. 创建接口TitleBar 来规范我们的标题栏能够支持哪些操作
public interface TitleBar {void setNavigationContentDescription(CharSequence navigationContentDescription);void setTitle(CharSequence title);void setBackground(@DrawableRes int backgroundRes);...
}

2.TitleBar来实现这些接口

public class TitleBarView extends FrameLayout implements TitleBar{public TitleBarView(Context context) {super(context);init();}private void init() {this.setId(R.id.titlebar_view);this.removeAllViewsInLayout();LayoutInflater layoutInflater = LayoutInflater.from(getContext());View view = layoutInflater.inflate(R.layout.titlebar_view, this, false);addView(view, new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.WRAP_CONTENT));}@Overridepublic void setNavigationContentDescription(CharSequence navigationContentDescription) {((TextView)findViewById(R.id.titlebar_back_tv)).setText(navigationContentDescription);}@Overridepublic void setTitle(CharSequence title) {((TextView)findViewById(R.id.titlebar_title_tv)).setText(title);}@Overridepublic void setBackground(@DrawableRes int backgroundRes) {this.setBackgroundResource(backgroundRes);}
}

在BaseActivity中得到TitleBar接口的实例TitleBarView,子Activity只对接口可见

public class BaseActivity extends AppCompatActivity {private TitleBar titleBar;@Overridepublic void setContentView(@LayoutRes int layoutResID) {super.setContentView(R.layout.layout_root);titleBar = (TitleBarView)findViewById(R.id.titlebar_view);LayoutInflater.from(this).inflate(layoutResID, (ViewGroup) findViewById(R.id.root_content_fl), true);}protected TitleBar getTitleBar(){return titleBar;}
}

调用也方便了许多

部分界面不需要标题栏

1.使用java注解的方式

在对类进行声明是否需要标题栏,然后加载不同的layout_root视图,注解的简单使用介绍,注意:自定义的运行时注解,在获取值时会用到反射

2.通过不同的theme加载不同的根视图

类似源码中AppCompatDelegateImplV7.java#createSubDecor()函数实现

private ViewGroup createSubDecor() {TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);//...//得到theme中windowIsFloating 的值mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);a.recycle();final LayoutInflater inflater = LayoutInflater.from(mContext);ViewGroup subDecor = null;//判断不同的style属性值加载不同的根布局if (!mWindowNoTitle) {if (mIsFloating) {// If we're floating, inflate the dialog title decorsubDecor = (ViewGroup) inflater.inflate(R.layout.abc_dialog_title_material, null);} else if (mHasActionBar) {subDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null);} else {if (mOverlayActionMode) {subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);} else {subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);}//...         }//
}

实现

  1. 自定义attrs来标注是否需要titleBarView

    <declare-styleable name="CustomThemeAttrs"><attr name="NoTitleBar" format="boolean" /></declare-styleable>
  2. 自定义theme

    <!-- Base application theme. --><style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"></style><!--没有标题栏的theme --><style name="AppTheme.NoTitleBar"><item name="NoTitleBar">false</item></style>
    1. 在BaseActivity的setContentView函数中处理
    @Overridepublic void setContentView(@LayoutRes int layoutResID) {//读取当前activity中的NoTitleBar属性值TypedArray a = obtainStyledAttributes(R.styleable.CustomThemeAttrs);boolean hasTitleBar = a.getBoolean(R.styleable.CustomThemeAttrs_NoTitleBar, false);a.recycle();//加载不同的根布局if(hasTitleBar) {super.setContentView(R.layout.layout_root);titleBar = (TitleBarView)findViewById(R.id.titlebar_view);}else{super.setContentView(R.layout.layout_root_no_titlebar);}//将页面的布局加载根布局的root_content_fl容器中
    LayoutInflater.from(this).inflate(layoutResID, (ViewGroup) findViewById(R.id.root_content_fl), true);}

延展

结合上图中Android的窗口结构,我们再看看ActionBar在这个结构中的位置

ActionBar是插入DectorView下面,和我们contentView平级

最近看了标题栏和状态栏一体化/沉浸式状态栏 在Android 4.0~5.0上的解决方案
也是在DectorView中添加空的View占据statusbar

那么我们完全可以将titleBarView add进DectorView 中,和contentView平级,构造和上图一样的视图结构,这样 contentView 和 titeBarView完全隔离,可以达到和ActionBar同样的效果,我们就自己做了一个ActionBar!哈哈…

Android 自定义标题栏相关推荐

  1. android 刷新标题栏,Android 自定义标题栏的实例详解

    Android 自定义标题栏的实例详解 开发 Android APP 经常会用到自定义标题栏,而有多级页面的情况下还需要给自定义标题栏传递数据. 本文要点: 自定义标题填充不完整 自定义标题栏返回按钮 ...

  2. android分享的主标题,Android 自定义标题栏(title栏)

    近日 需要在android的标题栏上添加按钮,所以对android的标题栏进行了一下简单的研究- 第一步,向实现自定义标题栏,需要在onCreate方法里这样写 requestWindowFeatur ...

  3. android 标题栏进度圈使用方法,Android 自定义标题栏 显示网页加载进度的方法实例...

    这阵子在做Lephone的适配,测试组提交一个bug:标题栏的文字较长时没有显示完全,其实这并不能算个bug,并且这个问题在以前其他机器也没有出现,只是说在Lephone的这个平台上显示得不怎么美观, ...

  4. (转自ztp800201) Android - 自定义标题栏(在标题栏中增加按钮和文本居中)

    现在很多的Android程序都在标题栏上都显示了一些按钮和标题,如下图: 下面通过实例来看一下如何实现. 1.在layout下创建一个titlebtn.xml文件,内容如下: 1 <?xml v ...

  5. Android学习之自定义标题栏

    一.如今好多的Android APP都在界面的顶部放置一个标题栏,标题栏里通常会有一两个按钮可用于返回和其它操作,虽然Android系统中已经给每个Activity提供了标题栏功能,但是这里介绍一下我 ...

  6. android标题栏上增加按钮,Android:向自定义标题栏添加按钮

    我创建了一个自定义标题栏,如本例所示 "一个自定义标题栏" – 一半下来. 在某些活动中,我想在标题栏的右侧放置一个按钮(与facebook app相同).我试图按如下方式向视图添 ...

  7. Android 安卓 自定义标题栏+沉浸式状态栏

    Android 安卓 自定义标题栏+沉浸式状态栏 由于Android原生的标题栏单调,简单,并不是很好看,自定义个性化的标题栏可以让app更加美观. Android 4.4以后增加了沉浸式透明状态栏. ...

  8. android fragment中引入自定义view_厉害了,用Android自定义View实现八大行星绕太阳3D旋转效果...

    作者:史蒂芬诺夫斯基 链接:https://www.jianshu.com/p/2954f2ef8ea5 好久没写View了,最近恰巧遇到一个八大行星绕太阳旋转的假3D效果,写完之后感觉效果还不错.能 ...

  9. 安卓学习随笔 -- 自定义标题栏

    在安卓中不喜欢系统默认的标题栏,那么如何让自定义一个自己的标题栏呢. 自定义后的标题栏如下: 首先这里需要定义一个自定义的标题栏布局 title.xml文件 (里边需要两个图片这个很简单) <R ...

  10. 解决android自定义标题充满的问题

    一篇写的很好的文章,转自:http://www.iteye.com/topic/760314 一个接着一个的activity,写啊写,调啊调,后来,终于发觉,activity的标题栏好难看,好单调啊. ...

最新文章

  1. python内建作用域_为什么 Python 的类不构成作用域(scope)?
  2. 虚幻引擎C++编程游戏开发基础
  3. 点击右侧导航栏,实现iframe嵌入子页面中div,滑动到最上面
  4. uboot源码——gd_t和bd_t数据结构
  5. 计算机硬件结构控制信息,计算机硬件的基本结构
  6. l4d2服务器修改武器伤害,辐射4武器伤害及护甲修改图文教程_快吧单机游戏
  7. python异步爬虫_Python实现基于协程的异步爬虫
  8. android 关闭线程句柄,android进程与线程详解三:AsyncTask
  9. python graphviz_Python中Graphviz的输出问题
  10. matlab定义对角块矩阵,Matlab中的扩展块对角矩阵
  11. python中5个json库的速度对比 1
  12. 将白色背景图片变透明
  13. NHibernate之旅(9):探索父子关系(一对多关系)
  14. 管理系统里用户角色与权限的设计
  15. DataPipeline选择MQ模式的理由
  16. 相亲角、地摊,暗访小县城的夜市
  17. jquery图片轮播思路
  18. STM32堆栈溢出的主要的问题和现象
  19. Multisim基础 变压器 简单示例
  20. MSDN(VS2010版)使用问题小记

热门文章

  1. 易语言新手入门教程第十五课 - QQ自动登录器第三部分
  2. RDS - 远程桌面服务
  3. java的datasource_JAVA创建DataSource
  4. 分享一个好的清理系统垃圾软件
  5. 正则表达式 '^[a-zA-Z0-9''-'\s]{1,30}$' 代表什么意思?
  6. Software_Reporter_Tool.exe
  7. 2021年茶艺师(中级)考试试卷及茶艺师(中级)模拟试题
  8. VUE打包图片,icon图标不显示解决方案
  9. 蓝牙变成“未知USB设备”的解决方法
  10. c语言输出实心心矩形,c语言打印空白星号矩形