Android View 高级框架一 Builder模式打造通用TitleBar
1 简介
我们在开发中往往都有一个TitleBar在APP最上面。例如下图
这个TitleBar如果每次都写在布局中,则每个Activity都要在布局中展现。会增加布局的复杂度,另外也不便于维护。另外,对于不同的界面,有时候这个TitleBar又会有所差别。因此怎么定制化一个可以复用又方便维护的TitleBar呢?
当然,我们也可以单独写一个布局文件,然后每个Activity的界面include进去,但是这样每个Activity界面都需要要单独的findViewById。后期的维护性其实也不是很强。
2 TitleBar框架
设计的TitleBar框架如下图
可以看到,我们采用了Builder设计模式,来构造我们的TitleBar的各种参数。另外,我们在基类中指定了布局的文件及bindView的方法。从而使我们的应用能绑定到Activity的根布局上。
具体的逻辑如下:
/**
* 绑定View到ActivityRoot上
*/
private void bindViewToActivityRoot() {
ViewGroup activityRoot = null;
//创建View
if (mParams.mActivityRoot == null || activityRoot == null){
//获取Activity的根布局
activityRoot = (ViewGroup) ((Activity)mParams.mContext).findViewById(android.R.id.content);
//获取我们在Activity中设置ContentView的View
mParams.mActivityRoot = (ViewGroup)activityRoot.getChildAt(0);
LogManager.i(TAG,"activityRoot size : " +activityRoot.getChildCount() + " mParams.mActivityRoot:" + mParams.mActivityRoot);
}
if (mParams.mActivityRoot == null){
return;
}
mNavigationView = LayoutInflater.from(mParams.mContext)
.inflate(bindLayoutId(),activityRoot,false);
if (mParams.mActivityRoot instanceof RelativeLayout){
//相对布局
//先构造一个线性布局,指定垂直排列
LinearLayout newActivityRoot = new LinearLayout(mParams.mContext);
newActivityRoot.setOrientation(LinearLayout.VERTICAL);
//移除原有的activityRoot的parent,否则会报"The specified child already has a parent. " +
//"You must call removeView() on the child's parent first. 异常
ViewGroup viewParent = (ViewGroup)mParams.mActivityRoot.getParent();
viewParent.removeAllViews();
//将titleBar添加为第一个child,原来的activityRoot为第二个
newActivityRoot.addView(mNavigationView,0);
newActivityRoot.addView(mParams.mActivityRoot,1);
//将新的activityRoot添加到android.R.id.content中
activityRoot.addView(newActivityRoot,0);
LogManager.i(TAG,"mParams.mActivityRoot,child at 0 view:");
}else {
//添加View到mParams.mParent中
mParams.mActivityRoot.addView(mNavigationView,0);
}
LogManager.i(TAG,"mParams.mActivityRoot,child:" + mParams.mActivityRoot.getChildCount());
//绑定View
bindView();
}
这里有一点技巧就是,我们的Activity的布局最终是添加到id为android.R.id.content的一个FrameLayout
布局中的,然后获取我们的Activity界面的根布局,将TitleBar布局添加到根布局的第一个位置处。这样就完成了我们TitleBar布局的添加,省去了在xml中添加了。
一个具体的TitleBar需要继承BaseTitleBar和继承BaseTitleParams,然后实现bindLayoutId()和bindView()。
3 CommonTitleBar
CommonTitleBar是一个具体的例子,效果图如下:
我们先来看它的UML类图
可以看到,CommonTitleBar主要继承自BaseTitleBar。它的源码如下:
/**
* Email: 1273482124@qq.com
* Created by qiyei2015 on 2017/5/14.
* Version: 1.0
* Description:
*/
public class CommonTitleBar extends BaseTitleBar<CommonTitleParams> {
protected CommonTitleBar(CommonTitleParams params) {
super(params);
}
/**
* 布局文件
* @return
*/
@Override
public int bindLayoutId() {
return R.layout.title_bar;
}
@Override
public void bindView() {
setText(R.id.title,mParams.mTitle);
setText(R.id.right_text,mParams.mRightText);
setOnClickListener(R.id.right_text,mParams.mRightClickListener);
// 左边 要写一个默认的 finishActivity
setOnClickListener(R.id.back,mParams.mLeftClickListener);
}
/**
* 设置Tiltle
* @param title
*/
public void setTitle(String title){
mParams.mTitle = title;
setText(R.id.title,mParams.mTitle);
}
/**
* 设置右侧文字
* @param text
* @return
*/
public void setRightText(String text){
mParams.mRightText = text;
setText(R.id.right_text,mParams.mRightText);
}
/**
* Builder模式设置各种效果
*/
public static class Builder extends BaseTitleBar.Builder{
/**
* 所有的效果参数
*/
CommonTitleParams mBarParams;
public Builder(Context context) {
super(context);
mBarParams = new CommonTitleParams(context,null);
}
/**
* 设置Title
* @param title
* @return
*/
public Builder setTitle(String title){
mBarParams.mTitle = title;
return this;
}
/**
* 设置右侧文字
* @param text
* @return
*/
public Builder setRightText(String text){
mBarParams.mRightText = text;
return this;
}
/**
* 设置右侧点击事件
* @param listener
* @return
*/
public Builder setRightClickListener(View.OnClickListener listener){
mBarParams.mRightClickListener = listener;
return this;
}
/**
* 创建CommonNavigationBar
* @return
*/
@Override
public CommonTitleBar build() {
return new CommonTitleBar(mBarParams);
}
}
}
CommonTitleBar主要封装了一些公共的返回按钮图标及响应的点击事件,右侧的文字,标题等内容。可根据项目的实际情况进行自定义其他的TitleBar
可以看到这是一个典型的Builder模式,主要的参数都存放在CommonTitleParams这个类中。如果项目有多个类型风格不同的TitleBar,则可以定义不同的TitleBar
详细的源码请参考
github https://github.com/qiyei2015/EssayJoke 中SDK 下的titlebar目录