1. 概述
前边我们通过两节课来对Builder设计模式应该都有了一个了解和认识,如果对Builder设计模式还不是特别了解的,可以先去看下我前边的两篇文章
Builder设计模式构建自定义万能的Dialog
从源码角度分析AlertDialog,
那么这节课我们的内容还是对Builder设计模式的一个学习,我们学习下通过Builder设计模式如何去构建NavigationBar,来实现我们的导航栏。
2. 导航栏的几种写法
我们在项目中的导航栏一般都会有以下几种写法:
1>:直接在布局文件中写一个 layout_title,然后通过include直接引入到每个布局文件中;
2>:用系统的 ToolBar;
3>:自定义View;
当然除过以上的几种写法,肯定也会有其他的写法,在这里就不一一列举了,那么我们下边就针对于 使用 Builder设计模式构建NavigationBar方式来分析下
一般像顶部导航栏最好添加一些MetrialDesign的一些动画效果会比较好看一些。
3. 代码如下
头部的基类AbsNavigationBar代码如下:
/**
* Email: [email protected]
* Created by JackChen 2018/4/5 18:43
* Version 1.0
* Params:
* Description: 头部的基类
* 可能在整个项目中像这样的布局用的地方不是很多,但是必须要有,
* 所以这里为了考虑头部可能出现的所有情况,也把基本的布局考虑进去
*/
public abstract class AbsNavigationBar implements INavigationBar {
private P mParams;
private View mNavigationView;
public AbsNavigationBar(P params) {
this.mParams = params;
createAndBindView();
}
public P getParams() {
return mParams;
}
/**
* 设置文本
* @param viewId
* @param text
*/
protected void setText(int viewId, String text) {
TextView tv = findViewById(viewId);
if(!TextUtils.isEmpty(text)){
tv.setVisibility(View.VISIBLE);
tv.setText(text);
}
}
/**
* 设置点击
* @param viewId
* @param listener
*/
protected void setOnClickListener(int viewId,View.OnClickListener listener){
findViewById(viewId).setOnClickListener(listener);
}
public T findViewById(int viewId){
return (T)mNavigationView.findViewById(viewId);
}
/**
* 绑定和创建View
*/
private void createAndBindView() {
// 1. 创建View
if(mParams.mParent == null){
// 获取activity的根布局,View源码
// 方法1:android.R.id.content就是 android.R.layout.screen_simple中的一个 FrameLayout的一个id
// ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
// .findViewById(android.R.id.content);
// 方法2:.getWindow().getDecorView() 表示直接加载的就是 android.R.layout.screen_simple的根布局,而根布局就是一个 LinearLayout
// 所以不管 activity_main中的根布局是RelativeLayout、LinearLayout都可以
ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
.getWindow().getDecorView() ;
mParams.mParent = (ViewGroup) activityRoot.getChildAt(0);
Log.e("TAG",mParams.mParent+"");
}
// 处理Activity的源码,后面再去看
if(mParams.mParent == null){
return;
}
mNavigationView = LayoutInflater.from(mParams.mContext).
inflate(bindLayoutId(), mParams.mParent, false);// 插件换肤
// 2.添加
mParams.mParent.addView(mNavigationView, 0);
applyView();
}
// Builder 仿照系统写的, 套路,活 AbsNavigationBar Builder 参数Params
public abstract static class Builder {
public Builder(Context context, ViewGroup parent) {
}
public abstract AbsNavigationBar builder();
public static class AbsNavigationParams {
public Context mContext;
public ViewGroup mParent;
public AbsNavigationParams(Context context, ViewGroup parent) {
this.mContext = context;
this.mParent = parent;
}
}
}
}
使用默认的头部的 DefaultNavigationBar代码如下:
public class DefaultNavigationBar extends
AbsNavigationBar {
public DefaultNavigationBar(DefaultNavigationBar.Builder.DefaultNavigationParams params) {
super(params);
}
@Override
public int bindLayoutId() {
return R.layout.title_bar;
}
@Override
public void applyView() {
// 绑定效果
setText(R.id.title, getParams().mTitle);
setText(R.id.right_text, getParams().mRightText);
setOnClickListener(R.id.right_text, getParams().mRightClickListener);
// 左边 要写一个默认的 finishActivity
setOnClickListener(R.id.back,getParams().mLeftClickListener);
}
public static class Builder extends AbsNavigationBar.Builder {
DefaultNavigationParams P;
public Builder(Context context, ViewGroup parent) {
super(context, parent);
P = new DefaultNavigationParams(context, parent);
}
public Builder(Context context) {
super(context, null);
P = new DefaultNavigationParams(context, null);
}
@Override
public DefaultNavigationBar builder() {
DefaultNavigationBar navigationBar = new DefaultNavigationBar(P);
return navigationBar;
}
// 1. 设置所有效果
public DefaultNavigationBar.Builder setTitle(String title) {
P.mTitle = title;
return this;
}
public DefaultNavigationBar.Builder setRightText(String rightText) {
P.mRightText = rightText;
return this;
}
/**
* 设置右边的点击事件
*/
public DefaultNavigationBar.Builder
setRightClickListener(View.OnClickListener rightListener) {
P.mRightClickListener = rightListener;
return this;
}
/**
* 设置左边的点击事件
*/
public DefaultNavigationBar.Builder
setLeftClickListener(View.OnClickListener rightListener) {
P.mLeftClickListener = rightListener;
return this;
}
/**
* 设置右边的图片
*/
public DefaultNavigationBar.Builder setRightIcon(int rightRes) {
return this;
}
public static class DefaultNavigationParams extends
AbsNavigationBar.Builder.AbsNavigationParams {
// 2.所有效果放置
public String mTitle;
public String mRightText;
// 后面还有一些通用的
public View.OnClickListener mRightClickListener;
public View.OnClickListener mLeftClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
// 关闭当前Activity
((Activity) mContext).finish();
}
};
public DefaultNavigationParams(Context context, ViewGroup parent) {
super(context, parent);
}
}
}
}
在MainActivity中的 initTitle()中直接 一句代码引用即可:
public class MainActivity extends BaseSkinActivity {
@Override
protected void setContentView() {
setContentView(R.layout.activity_main);
}
@Override
protected void initData() {
}
@Override
protected void initView() {
}
@Override
protected void initTitle() {
DefaultNavigationBar navigationBar = new
DefaultNavigationBar.Builder(this)
.setTitle("投稿")
.builder();
}
}
注意:
在AbsNavigationBar中的这两种方法要注意,这两种方法代表的意思是一样的
// 方法1:android.R.id.content就是 android.R.layout.screen_simple中的一个 FrameLayout的一个id
// ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
// .findViewById(android.R.id.content);
// 方法2:.getWindow().getDecorView() 表示直接加载的就是 android.R.layout.screen_simple的根布局,而根布局就是一个 LinearLayout
// 所以不管 activity_main中的根布局是RelativeLayout、LinearLayout都可以
ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
.getWindow().getDecorView() ;
mParams.mParent = (ViewGroup) activityRoot.getChildAt(0);
Log.e("TAG",mParams.mParent+"");
分析方法1:
方法1中的findViewById(android.R.id.content);是直接加载的是 系统的资源文件,即就是android.R.layout.screen_simple中的一个叫做 android.R.id.content的布局,它是一个FrameLayout,也就是说我们是把我们的 setContentView的布局其实是加载到 这个资源文件中的 android.R.id.content中的,它是一个 mContentParent,是一个 FrameLayout,也就是说我们是把 setContentView的布局是加载到 mContentParent中的,android.R.layout.screen_simple的布局文件如下:
分析方法2:
方法2其实就是 加载的是DecorView,而DecorView中加载的就是 系统的资源文件,就是 android.R.layout.screen_simple,其实就是直接加载该布局文件的根布局 LinearLayout,这样的话,不管 我们自己的 setContentView()中根布局是 LinearLayout还是RelativeLayout,都可以直接把布局加载到第0个位置
上边的两种方法其实考的就是 setContentView()的加载流程,如果不是很懂的,可以去看下我前边的文章,setContentView的加载流程分析
代码已上传至github:
https://github.com/shuai999/EssayJoke_day_08.git