# ActionBarEx **Repository Path**: goweii/ActionBarEx ## Basic Information - **Project Name**: ActionBarEx - **Description**: 高拓展高自定义性ActionBar,完美替代Android系统默认 - **Primary Language**: Java - **License**: LGPL-3.0 - **Default Branch**: master - **Homepage**: https://github.com/goweii/ActionBarEx - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2018-10-20 - **Last Updated**: 2024-04-02 ## Categories & Tags **Categories**: android-modules **Tags**: None ## README # [求职](https://github.com/goweii/job-wanted) # ActionBarEx **高拓展高自定义性ActionBar,完美替代Android系统默认。** - 支持自定义标题栏布局,支持XML中直接以子布局编写 - 支持自定义前景布局,如显示Loading效果 - 支持自定义背景布局,如图片等 - 支持自定义底部分割线,可以投影效果显示在外部 - 支持自适应状态栏暗亮色图标模式,可实时动态刷新 - 状态栏暗亮色图标模式已适配常见国产ROM,详见[StatusBarCompat](https://github.com/goweii/StatusBarCompat) - 已封装3种通用样式,可按需引入 [GitHub主页](https://github.com/goweii/ActionBarEx) [Demo下载](https://github.com/goweii/ActionBarEx/raw/master/app/release/app-release.apk) ## 项目简介 ## 为什么要写一个ActionBar?官方的和网上开源的那么多。 第一,先说说官方的。官方的在不同系统上的风格并不统一,自定义项太少,不能满足开发需求,所以才涌现了这么多的开源项目。 第二,网上开源的。网上的已有的,我发现的,用过的大部分都是直接封装一个固定样式的布局,或控制控件的显示隐藏,或动态添加需要的控件。但也仅限于自定义左右图标的和中间标题的个数和样式。如果要自定义ActionBar的背景呢?要让背景显示一个图片呢?要让图片延伸到状态栏下面呢?要让背景实现半透明高斯模糊效果呢?要限定按钮图标的大小呢?要自定义中间标题的背景呢?要显示一个搜索框呢?要让搜索框带有清空和搜索图标呢?要自定义搜索框样式呢?要在网络请求时显示一个Loading效果呢?等等这些需求都是无法满足的。而且我相信这样的需求不在少数。 **也可能有可以实现的框架,只是我还没发现。** ## GitHub上Star那么多的框架都无法实现,这个就可以? 要说这个绝对可以实现,肯定是不可能的,但我相信可以实现市面上99%以上的需求。 **哪里来的这么大的自信?还是先看看整个项目的层级结构吧。** 整个ActionBarEx分为3个基本层级,具体负责功能和命名约定如下: | 根层级 | 子层级 | 说明 | 备注 | | ------------------- | ---------- | ------ | :----------- | | **BackgroundLayer** | | 背景层 | 可自定义布局 | | **ActionBarLayer** | | 主体层 | 垂直线性布局 | | | StatusBar | 状态栏 | | | | TitleBar | 标题栏 | 可自定义布局 | | | BottomLine | 分割线 | | | **ForegroundLayer** | | 前景层 | 可自定义布局 | - **BackgroundLayer** 背景层,负责整个ActionBar背景的绘制,如实现半透明背景或图片背景或高斯模糊背景 - **ActionBarLayer** 主体层,是以下3个不分的统称: - StatusBar 系统状态栏占位,可控制单独显示一个背景色,可实现状态栏图片暗色和亮色模式切换 - TitleBar 导航栏占位,用于显示标题图标按钮等,支持自定义布局样式,以实现复杂需求 - BottomLine 底部分割线,可控制分割线颜色和高度 - **ForegroundLayer** 前景层,用于显示一些特殊的状态,如加载状态LoadingBar,支持自定义布局 ## 别整这么多虚的,先看看疗效 ![](https://github.com/goweii/ActionBarEx/blob/master/picture/action_bar_demo.gif?raw=true) ![](https://github.com/goweii/ActionBarEx/blob/master/picture/action_bar_1.png?raw=true) ![](https://github.com/goweii/ActionBarEx/blob/master/picture/action_bar_2.png?raw=true) ![](https://github.com/goweii/ActionBarEx/blob/master/picture/action_bar_3.png?raw=true) ## 呦吼,看着还不错呀,快告诉我怎么用? 别急呀,我再说说实现细节。 好好,别动手,有话好好说,你把这个规则长方体放下。。。 要说使用方式,还得再明确几个类 - ActionBarEx 这个就是上面说的封装好的分3个层级的基类 - ActionBarCommon 继承自ActionBarEx,标题栏布局为【图标-文字-标题文字-文字-图标】 - ActionBarSearch 继承自ActionBarEx,标题栏布局为【图标-文字-搜索框-文字-图标】 - ActionBarSuper 继承自ActionBarEx - 支持自定义主标题和副标题显示 - 支持自定义标题位置居中或居左 - 左右分别支持最多5个按钮 - 支持精确自定义每个按钮的边距 # 集成方式 ## 添加依赖 1. 在Project的**build.gradle**添加仓库地址 ``` allprojects { repositories { ... maven { url 'https://www.jitpack.io' } } } ``` 2. 在Model:app的**build.gradle**添加框架依赖 最新版本是多少,看下[Releases](https://github.com/goweii/ActionBarEx/releases) 从2.2.9以后版本去除版本号前的v,引用时需要注意下。 从3.0.0版本开始分离了ActionBarEx和ActionBarCommon/Search。 从3.1.0版本新增ActionBarSuper ```groovy dependencies { // 完全引入 implementation 'com.github.goweii:ActionBarEx:3.2.2' // 只引入ActionBarEx implementation 'com.github.goweii.ActionBarEx:actionbarex:3.2.2' // 引入ActionBarCommon/Search/Super,依赖于ActionBarEx implementation 'com.github.goweii.ActionBarEx:actionbarex-common:3.2.2' } ``` ## 简单需求只需要下面2步就可以了 这里用ActionBarCommon举个例子 1. 布局文件引用 ```xml ``` 2. 代码中绑定事件 ```java actionBarCommon.setOnLeftIconClickListener(new OnActionBarChildClickListener() { @Override public void onClick() { Toast.makeText(context, "onLeftIconClick", Toast.LENGTH_SHORT).show() } }); ``` ## 复杂一点的需求,需要自定义导航栏布局 使用起来是这个样子的 1. 自定义布局文件 title_bar_custom.xml ```xml ``` 2. 引用布局 ```xml ``` 3. 代码中绑定事件 ```java actionBarEx.getView(R.id.iv_back).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); ``` ## 更加复杂的需求,可能需要自定义子类了 自定义子类需要下面三步,复写2个方法 1. **initAttrs(AttributeSet attrs)**:获取子类的自定义属性,注意不要忘了调用**super.initAttrs(attrs);** 2. **inflateTitleBar()**:初始化TitleBar导航栏布局 3. 自定义点击事件回调 举个例子吧 ```java @Override protected void initAttrs(AttributeSet attrs) { super.initAttrs(attrs); TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ActionBarCommon); float titleTextMaxWidthDef = getContext().getResources().getDimension(R.dimen.title_bar_title_text_max_width_def); float iconPaddingDef = getContext().getResources().getDimension(R.dimen.title_bar_icon_padding_def); float textSizeDef = getContext().getResources().getDimension(R.dimen.title_bar_text_size_def); float textPaddingLeftDef = getContext().getResources().getDimension(R.dimen.title_bar_text_padding_left_def); float textPaddingRightDef = getContext().getResources().getDimension(R.dimen.title_bar_text_padding_right_def); float titleTextSizeDef = getContext().getResources().getDimension(R.dimen.title_bar_title_text_size_def); int iconColorDef = ContextCompat.getColor(getContext(), R.color.title_bar_icon_color_def); int textColorDef = ContextCompat.getColor(getContext(), R.color.title_bar_text_color_def); int titleTextColorDef = ContextCompat.getColor(getContext(), R.color.title_bar_title_text_color_def); leftTextClickToFinish = typedArray.getBoolean(R.styleable.ActionBarCommon_abc_leftTextClickToFinish, false); leftIconClickToFinish = typedArray.getBoolean(R.styleable.ActionBarCommon_abc_leftIconClickToFinish, false); leftText = typedArray.getString(R.styleable.ActionBarCommon_abc_leftText); leftTextSize = typedArray.getDimension(R.styleable.ActionBarCommon_abc_leftTextSize, textSizeDef); leftTextColor = typedArray.getColor(R.styleable.ActionBarCommon_abc_leftTextColor, textColorDef); leftTextPaddingLeft = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_leftTextPaddingLeft, textPaddingLeftDef); leftTextPaddingRight = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_leftTextPaddingRight, textPaddingRightDef); leftIconRes = typedArray.getResourceId(R.styleable.ActionBarCommon_abc_leftIconRes, 0); leftIconColor = typedArray.getColor(R.styleable.ActionBarCommon_abc_leftIconColor, iconColorDef); leftIconPadding = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_leftIconPadding, iconPaddingDef); leftIconMarginLeft = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_leftIconMarginLeft, 0); rightText = typedArray.getString(R.styleable.ActionBarCommon_abc_rightText); rightTextSize = typedArray.getDimension(R.styleable.ActionBarCommon_abc_rightTextSize, textSizeDef); rightTextColor = typedArray.getColor(R.styleable.ActionBarCommon_abc_rightTextColor, textColorDef); rightTextPaddingLeft = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_rightTextPaddingLeft, textPaddingLeftDef); rightTextPaddingRight = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_rightTextPaddingRight, textPaddingRightDef); rightIconRes = typedArray.getResourceId(R.styleable.ActionBarCommon_abc_rightIconRes, 0); rightIconColor = typedArray.getColor(R.styleable.ActionBarCommon_abc_rightIconColor, iconColorDef); rightIconPadding = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_rightIconPadding, iconPaddingDef); rightIconMarginRight = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_rightIconMarginRight, 0); titleText = typedArray.getString(R.styleable.ActionBarCommon_abc_titleText); titleTextSize = typedArray.getDimension(R.styleable.ActionBarCommon_abc_titleTextSize, titleTextSizeDef); titleTextColor = typedArray.getColor(R.styleable.ActionBarCommon_abc_titleTextColor, titleTextColorDef); titleTextMaxWidth = (int) typedArray.getDimension(R.styleable.ActionBarCommon_abc_titleTextMaxWidth, titleTextMaxWidthDef); typedArray.recycle(); } @Override protected View inflateTitleBar() { titleBarChild = (RelativeLayout) inflate(getContext(), R.layout.action_bar_title_bar_common, null); leftIconView = titleBarChild.findViewById(R.id.iv_left); leftTextView = titleBarChild.findViewById(R.id.tv_left); titleTextView = titleBarChild.findViewById(R.id.tv_title); rightTextView = titleBarChild.findViewById(R.id.tv_right); rightIconView = titleBarChild.findViewById(R.id.iv_right); LinearLayout.LayoutParams leftIconViewParams = (LinearLayout.LayoutParams) leftIconView.getLayoutParams(); leftIconViewParams.leftMargin = leftIconMarginLeft; leftIconView.setLayoutParams(leftIconViewParams); if (leftIconRes > 0) { leftIconView.setVisibility(VISIBLE); leftIconView.setPadding(leftIconPadding, leftIconPadding, leftIconPadding, leftIconPadding); leftIconView.setImageResource(leftIconRes); leftIconView.setColorFilter(leftIconColor); if (leftIconClickToFinish) { leftIconView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finishActivity(); } }); } } else { leftIconView.setVisibility(GONE); } if (!TextUtils.isEmpty(leftText)) { leftTextView.setVisibility(VISIBLE); leftTextView.setText(leftText); leftTextView.setTextColor(leftTextColor); leftTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, leftTextSize); leftTextView.setPadding(leftTextPaddingLeft, 0, leftTextPaddingRight, 0); if (leftTextClickToFinish) { leftTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finishActivity(); } }); } } else { leftTextView.setVisibility(GONE); } titleTextView.setVisibility(VISIBLE); titleTextView.setText(titleText); titleTextView.setTextColor(titleTextColor); titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextSize); titleTextView.setMaxWidth(titleTextMaxWidth); LinearLayout.LayoutParams rightIconViewParams = (LinearLayout.LayoutParams) rightIconView.getLayoutParams(); rightIconViewParams.rightMargin = rightIconMarginRight; rightIconView.setLayoutParams(rightIconViewParams); if (rightIconRes > 0) { rightIconView.setVisibility(VISIBLE); rightIconView.setPadding(rightIconPadding, rightIconPadding, rightIconPadding, rightIconPadding); rightIconView.setImageResource(rightIconRes); rightIconView.setColorFilter(rightIconColor); } else { rightIconView.setVisibility(GONE); } if (!TextUtils.isEmpty(rightText)) { rightTextView.setVisibility(VISIBLE); rightTextView.setText(rightText); rightTextView.setTextColor(rightTextColor); rightTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, rightTextSize); rightTextView.setPadding(rightTextPaddingLeft, 0, rightTextPaddingRight, 0); } else { rightTextView.setVisibility(GONE); } return titleBarChild; } ``` 好吧我承认,我只是复制了下ActionBarCommon的实现代码,不过流程就是这么个流程,还是很简单的。 ## 哎,等等,前景层怎么用呢? 厉害了,想跳过去,都被你发现了。 看下前面自定义导航栏布局中是不是有这么一句 ```xml app:ab_foregroundLayerLayout="@layout/loading_bar" ``` 那这个布局长什么样子呢? ```xml ``` 这个其实就是一个1dp高的ProgressBar,用来显示加载中效果 在代码中这样使用就可以了 ```java actionBarEx.getForegroundLayer().setVisibility(View.VISIBLE); actionBarEx.getForegroundLayer().setVisibility(View.GONE); ``` 是不是很简单? ## 既然都说到这了,就把一些常用方法和属性都说了吧 ### 常用方法 #### ActionBarEx - **getBackgroundLayer()**:获取背景层布局 - **getActionBar()**:获取主体层布局 - **getForegroundLayer()**:获取前景层布局 - **getStatusBar()**:获取状态栏视图 - **getTitleBar()**:获取导航栏布局 - **getTitleBarChild()**:获取导航栏子视图 - **getBottomLine()**:获取底部分割线视图 - **getView(@IdRes int id)**:获取子控件 - **getActionBarHeight()**:获取整个ActionBar的高度 - **getStatusBarHeight()**:获取状态栏高度 - **getTitleBarHeight()**:获取标题栏高度 - **getBottomHeight()**:获取分割线高度 - **initAttrs(AttributeSet attrs)**:获取布局属性 - **inflateTitleBar()**:初始化标题栏布局 #### ActionBarCommon - **getLeftIconView()**:获取左侧图标视图 - **getLeftTextView()**:获取左侧文本视图 - **getTitleTextView()**:获取中间标题视图 - **getRightTextView()**:获取右侧文本视图 - **getRighIconView()**:获取右侧图标视图 - **setOnLeftIconClickListener(final OnActionBarChildClickListener onLeftIconClickListener)**:左侧图标点击事件 - **setOnLeftTextClickListener(final OnActionBarChildClickListener onLeftTextClickListener)**:左侧文本点击事件 - **setOnRightTextClickListener(final OnActionBarChildClickListener onRightTextClickListener)**:右侧文本点击事件 - **setOnRightIconClickListener(final OnActionBarChildClickListener onRightIconClickListener)**:右侧图标点击事件 #### ActionBarSearch - **getLeftIconView()**:获取左侧图标视图 - **getLeftTextView()**:获取左侧文本视图 - **getEditTextView()**:获取中间输入框视图 - **getRightTextView()**:获取右侧文本视图 - **getRightIconView()**:获取右侧图标视图 - **setOnLeftIconClickListener(final OnActionBarChildClickListener onLeftIconClickListener)**:左侧图标点击事件 - **setOnLeftTextClickListener(final OnActionBarChildClickListener onLeftTextClickListener)**:左侧文本点击事件 - **setOnRightTextClickListener(final OnActionBarChildClickListener onRightTextClickListener)**:右侧文本点击事件 - **setOnRightIconClickListener(final OnActionBarChildClickListener onRightIconClickListener)**:右侧图标点击事件 #### ActionBarSuper - **getTitleTextView()**:获取主标题 - **getSubtitleTextView()**:获取副标题 - **getLeftActionViews()**:获取左侧按钮集合 - **getRightActionViews()**:获取右侧按钮集合 - **getLeftActionView(int index)**:获取指定位置的左侧按钮(由0开始,0为XML中序号为1的) - **getRightActionView(int index)**:获取指定位置的右侧按钮(由0开始,0为XML中序号为1的) ### 属性介绍 #### ActionBarEx ```xml ``` #### ActionBarCommon ```xml ``` #### ActionBarSearch ```xml ``` #### ActionBarSuper 针对下面的属性有几点要强调下 - 不带有left/right的属性会同时应用到左右两侧 - 带有left/right的属性会覆盖不带有left/right的属性 - 不带有数字序号的属性会同时应用到该侧的全部序号 - 带有数字序号的属性会覆盖不不带有数字序号的属性 ```xml ``` # 实现细节 好了,下面上干货。还是从整个层级划分开始说起吧。 ### 先从布局结构重新看下层级划分 - FrameLayout(ActionBarEx本身,继承自FrameLayout) - ImageView或者自定义布局(背景层BackgroundLayer) - LinearLayout(主体层ActionBar) - View(状态栏StatusBar) - FrameLayout(标题栏TitleBar) - View(底部分割线BottomLine) - 自定义布局(前景层ForegroundLayer) 这样是不是对整个结构有了一个更清晰的认识。 ### 从代码上看主要的两个功能 - ActionBarEx(自定义控价,负责显示布局) - StatusBarUtils(负责系统状态栏的沉浸和图标亮色暗色模式切换) ### 来先看下ActionBarEx的自定义过程 #### 先看下构造方法都干了那些事 ```java public ActionBarEx(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; mDisplayInfoUtils = DisplayInfoUtils.getInstance(context); mStatusBarHeight = mDisplayInfoUtils.getStatusBarHeight(); initAttrs(attrs); makeImmersion(); initView(); } ``` 可以看到初始化了一个DisplayInfoUtils对象,获取了下系统状态栏的高度。 然后调用了3个方法,下面一个一个看下: #### initAttrs(AttributeSet attrs) 主要为获取了自定义属性,设置默认值。这都是自定义View的常规操作。 ```java protected void initAttrs(AttributeSet attrs) { TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.ActionBarEx); float titleBarHeightDef = mContext.getResources().getDimension(R.dimen.title_bar_height_def); float bottomLineHeightDef = mContext.getResources().getDimension(R.dimen.bottom_line_height_def); int bottomLineColorDef = ContextCompat.getColor(mContext, R.color.bottom_line_color_def); int statusBarColorDef = ContextCompat.getColor(mContext, R.color.status_bar_color_def); mAutoImmersion = typedArray.getBoolean(R.styleable.ActionBarEx_ab_auto_immersion, true); mBackgroundLayerLayoutRes = typedArray.getResourceId(R.styleable.ActionBarEx_ab_background_layer_layout, 0); mBackgroundLayerImageRes = typedArray.getResourceId(R.styleable.ActionBarEx_ab_background_layer_image_res, 0); mStatusBarVisible = typedArray.getBoolean(R.styleable.ActionBarEx_ab_status_bar_visible, true); mStatusBarDarkMode = typedArray.getInt(R.styleable.ActionBarEx_ab_status_bar_mode, STATUS_BAR_MODE_LIGHT) == STATUS_BAR_MODE_DARK; mStatusBarColor = typedArray.getColor(R.styleable.ActionBarEx_ab_status_bar_color, statusBarColorDef); mTitleBarLayoutRes = typedArray.getResourceId(R.styleable.ActionBarEx_ab_title_bar_layout, 0); mTitleBarHeight = (int) typedArray.getDimension(R.styleable.ActionBarEx_ab_title_bar_height, titleBarHeightDef); mBottomLineHeight = (int) typedArray.getDimension(R.styleable.ActionBarEx_ab_bottom_line_height, bottomLineHeightDef); mBottomLineColor = typedArray.getColor(R.styleable.ActionBarEx_ab_bottom_line_color, bottomLineColorDef); mForegroundLayerLayoutRes = typedArray.getResourceId(R.styleable.ActionBarEx_ab_foreground_layer_layout, 0); typedArray.recycle(); } ``` #### makeImmersion() 看名字可以知道是设置沉浸模式,先贴代码: ```java private void makeImmersion() { if (!mAutoImmersion) { return; } Activity activity = getActivity(); if (activity == null) { return; } setSystemStatusBar(activity); hintSystemActionBar(activity); } ``` 看最下面2行,分别设置了系统沉浸状态栏和隐藏一同自带的导航栏。 **具体如何实现状态栏沉浸的,我就不多介绍了,都是百度的一些零零散散的方法,和看GitHub上一些大神的代码,针对小米魅族VIVO做了适配。并不能保证所有系统都有效。如果你有更好的适配方案,欢迎讨论,或者你可以让mAutoImmersion=false来关闭自动适配,去Activity实现自己的方法** #### initView() 这个方法是本项目的重点,但很简单,没什么难的。 ```java private void initView() { // 1 初始化BackgroundLayer // 1.1 初始化背景图 if (mBackgroundLayerLayoutRes > 0){ mBackgroundLayer = inflate(getContext(), mBackgroundLayerLayoutRes, null); mBackgroundLayer.setLayoutParams(makeLayerLayoutParams()); addView(mBackgroundLayer); } else { if (mBackgroundLayerImageRes > 0) { ImageView actionBarImageView = new ImageView(mContext); actionBarImageView.setLayoutParams(makeLayerLayoutParams()); actionBarImageView.setImageResource(mBackgroundLayerImageRes); actionBarImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); addView(actionBarImageView); } } // 2 初始ContentLayer(ActionBar) mActionBar = (LinearLayout) inflate(getContext(), R.layout.action_bar, null); mActionBar.setLayoutParams(makeLayoutParamsWithHeight(getActionBarHeight())); // 2.1 初始StatusBar mStatusBar = mActionBar.findViewById(R.id.status_bar); mStatusBar.setLayoutParams(makeLayoutParamsWithHeight(mStatusBarHeight)); mStatusBar.setBackgroundColor(mStatusBarColor); mStatusBar.setVisibility(mStatusBarVisible ? VISIBLE : GONE); // 2.2 初始TitleBar mTitleBar = mActionBar.findViewById(R.id.title_bar); mTitleBar.setClickable(true); mTitleBar.setFocusable(true); mTitleBar.setFocusableInTouchMode(true); mTitleBar.setLayoutParams(makeLayoutParamsWithHeight(mTitleBarHeight)); mTitleBarChild = inflateTitleBar(); if (mTitleBarChild != null) { mTitleBar.addView(mTitleBarChild); } // 2.3 初始BottomLine mBottomLine = mActionBar.findViewById(R.id.bottom_line); mBottomLine.setLayoutParams(makeLayoutParamsWithHeight(mBottomLineHeight)); mBottomLine.setBackgroundColor(mBottomLineColor); addView(mActionBar); // 3 初始ForegroundLayer if (mForegroundLayerLayoutRes > 0) { mForegroundLayer = inflate(getContext(), mForegroundLayerLayoutRes, null); mForegroundLayer.setLayoutParams(makeLayerLayoutParams()); addView(mForegroundLayer); } } ``` 这里又有一个inflateTitleBar()方法,就是为了继承自定义而加的,它是这样实现的 ```java protected View inflateTitleBar() { if (mTitleBarLayoutRes > 0) { return inflate(getContext(), mTitleBarLayoutRes, null); } return null; } ``` #### 然后是。?还有然后?哦,没了 一共就干了这么点事,没有实现任何效果,完全是一个空架子。是的,任何效果都需要自定义。 这个ActionBarEx只是划分了下导航栏的层级,限定了下各层级的功能。 当然每个项目肯定都有一个固定风格的设计,不可能每次使用都在布局中添加一个ActionBarEx,再引用一个自定义布局,再在代码中getView拿到控件初始化和绑定事件。稍微复杂点的肯定都是要自定义子类封装的,具体可以参考上面的自定义子类或者直接参考SimpleActionBar或者SearchActionBar的实现。 # 写在最后 这个完全是个人项目,有几点有必要说明下: 1. 觉得好用的欢迎给个Star([GitHub](https://github.com/goweii/ActionBarEx)) 2. 发现任何BUG欢迎留言或者留个Issues([Issues](https://github.com/goweii/ActionBarEx/issues)) 3. 任何转载请注明出处 4. 还没想到,未完待续 # 如果你觉得App还不错,就请我喝杯咖啡吧~ | ![wx_qrcode](https://gitee.com/goweii/WanAndroidServer/raw/master/about/wx_qrcode.png) | |---|