在应用程序中肯定会有标题栏,一般就是左边一个返回,一个标题,右边一个图片或者文字,不过有时候也会碰到中间是搜索框的情况,实现方式也很多,自定view、include、直接在xml布局中通过findViewById写等方式;这里是采用Builder设计模式打造一个NavigationBar,不需要在xml布局中去写,在activity初始化标题或者控件的地方实例一个NavigationBar对象并设置相应的参数就可以了。
Builder设计模式是一种比较常见的设计模式,Dialog等源码,Okhttp等第三方框架中都有使用到;
Builder设计模式又称为建造者模式,将建造过程与表示过程相分离,让(参数)构建过程变得更加简单和直观。
在Builder设计模式中普遍会采用链式调用的方式,但是需要注意,链式调用并不是Builder设计模式,链式调用这是一种调用方式;链式调用有一个体现,就是在调用方法的时候返回自身对象,Builder 一般也有一种体现,就是一般都会出现 Builder 对象。
创建一个接口,接口提供一个创建NavigationBar、绑定参数、添加到布局中的方法,让构造类去实现它;
public interface INavigation {
/**
* 创建navigationbar
*/
void createNavigationBar();
/**
* 绑定navigationbar参数
*/
void attachNavigationParmas();
/**
* 将navigationBar添加到父布局中
* @param navigationView
* @param parent
*/
void attachParent(View navigationView, ViewGroup parent);
}
public class AbsNavigationBar implements INavigation {
private T mBuilder;
private View mNavigationView;
protected AbsNavigationBar(T builder) {
this.mBuilder = builder;
createNavigationBar();
}
@Override
public void createNavigationBar() {
mNavigationView = LayoutInflater.from(mBuilder.mContext).inflate(mBuilder.mLayoutId, mBuilder.mParent, false);
//添加
attachParent(mNavigationView, mBuilder.mParent);
//添加参数
attachNavigationParmas();
}
@Override
public void attachNavigationParmas() {
Map textMap = mBuilder.textMap;
for(Map.Entry texts:textMap.entrySet()){
TextView textView = findViewById(texts.getKey());
textView.setText(texts.getValue());
}
Map clicks = mBuilder.clicks;
for(Map.Entry click:clicks.entrySet()){
View view = findViewById(click.getKey());
view.setOnClickListener(click.getValue());
}
}
public T findViewById(int viewId) {
return (T) mNavigationView.findViewById(viewId);
}
/**
* 返回 Builder
*
* @return
*/
public T getBuilder() {
return mBuilder;
}
@Override
public void attachParent(View navigationView, ViewGroup parent) {
parent.addView(navigationView, 0);
}
public static abstract class Builder {
public Context mContext;
public int mLayoutId;
public ViewGroup mParent;
//存储对应id的文本
public Map textMap;
//存储对应id的点击事件
public Map clicks;
public Builder(Context context, int layoutId) {
this.mContext = context;
this.mLayoutId = layoutId;
Activity activity = (Activity) context;
ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();
this.mParent = (ViewGroup) viewGroup.getChildAt(0);
textMap = new HashMap<>();
clicks = new HashMap<>();
}
/**
* 设置左边文字
*
* @param leftId
* @param leftText
* @return
*/
public T setLeftText(int leftId, String leftText) {
textMap.put(leftId, leftText);
return (T) this;
}
/**
* 设置中间文字
*
* @param middleId
* @param middleText
* @return
*/
public T setMiddleText(int middleId, String middleText) {
textMap.put(middleId, middleText);
return (T) this;
}
/**
* 设置右边文字
*
* @param rightId
* @param rightText
* @return
*/
public T setRightText(int rightId, String rightText) {
textMap.put(rightId, rightText);
return (T) this;
}
/**
* 左边点击事件
*
* @param leftId
* @param listener
* @return
*/
public T setLeftClick(int leftId, View.OnClickListener listener) {
clicks.put(leftId, listener);
return (T) this;
}
/**
* 右边点击事件
*
* @param rightId
* @param listener
* @return
*/
public T setRightClick(int rightId, View.OnClickListener listener) {
clicks.put(rightId, listener);
return (T) this;
}
/**
* 用来创建 NavigationBar
*
* @return
*/
public abstract AbsNavigationBar create();
}
}
在Builder构造方法中通过上下文强转成Activity,通过当前的Activity获取到DecorView将NavigationBar添加到DecorView布局容器中,这样就不需要在使用Activity中取进行添加;Builder中提供一些设置参数的方法,在attachNavigationParmas方法中进行参数的绑定;因为Builder是一个静态抽象类,所以在使用的时候要定义一个具体的NavigationBar继承自AbsNavigationBar;
public class NavigationBar extends AbsNavigationBar{
protected NavigationBar(Builder builder) {
super(builder);
}
public static class Builder extends AbsNavigationBar.Builder{
public Builder(Context context, int layoutId) {
super(context, layoutId);
}
@Override
public NavigationBar create() {
return new NavigationBar(this);
}
}
}
这样就构建好了,实例化一个NavigationBar对象并设置相应的参数;
NavigationBar navigationBar=new NavigationBar.Builder(this,R.layout.navigation_layout)
.setLeftText(R.id.tv_left,"返回")
.setMiddleText(R.id.tv_title,"首页")
.setRightText(R.id.tv_right,"保存")
.setLeftClick(R.id.tv_left, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"返回",Toast.LENGTH_LONG).show();
}
})
.setRightClick(R.id.tv_right, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"保存",Toast.LENGTH_LONG).show();
}
})
.create();
这样就创建了一个NavigationBar,但是在每次设置参数的时候都需要传入一个对应的控件id,还是蛮繁琐的,可以根据项目的需要创建一个公用的NavigationBar,如果差异比较大的话,根据当前页面的需求有选择的创建对应的NavigationBar;
public class DefaultNavigationBar extends AbsNavigationBar{
protected DefaultNavigationBar(Builder builder) {
super(builder);
}
@Override
public void attachNavigationParmas() {
super.attachNavigationParmas();
//绑定参数
}
public static class Builder extends AbsNavigationBar.Builder{
public Builder(Context context) {
super(context, R.layout.navigation_layout);
}
@Override
public DefaultNavigationBar create() {
return new DefaultNavigationBar(this);
}
/**
* 设置左边文字
*
* @param leftText
* @return
*/
public Builder setLeftText(String leftText) {
if(leftText==null||leftText.length()==0){
leftText="返回";
}
setLeftText(R.id.tv_left,leftText);
return this;
}
/**
* 设置中间文字
*
* @param middleText
* @return
*/
public Builder setMiddleText(String middleText) {
setMiddleText(R.id.tv_title,middleText);
return this;
}
/**
* 设置右边文字
*
* @param rightText
* @return
*/
public Builder setRightText(String rightText) {
setRightText(R.id.tv_right,rightText);
return this;
}
/**
* 左边点击事件
*
* @param listener
* @return
*/
public Builder setLeftClick(View.OnClickListener listener) {
setLeftClick(R.id.tv_left,listener);
return this;
}
/**
* 右边点击事件
*
* @param listener
* @return
*/
public Builder setRightClick(View.OnClickListener listener) {
setRightClick(R.id.tv_right,listener);
return this;
}
}
}
这样子实例化NavigationBar时就不需要每次都传入一个控件id;
DefaultNavigationBar navigationBar=new DefaultNavigationBar.Builder(this)
.setMiddleText("首页")
.setRightText("保存")
.setLeftClick(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"返回",Toast.LENGTH_LONG).show();
}
})
.setRightClick(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"保存",Toast.LENGTH_LONG).show();
}
})
.create();
如果需要对NavigationBar中的一些特殊的控件做处理,可以通过实例化好的NavigationBar.findViewById()获取对应的控件进行处理;
TextView leftView = (TextView) navigationBar.findViewById(R.id.tv_left);
leftView.setText("首页返回");
源码