标题栏这种东西,每个App都会有,但是不是每个设计师都会为一个App设计Android和IOS两种风格,所以苦了Android开发的。不能用系统的,所以我们来自定义好了。(该篇文章主要提供布局文件在Acitivity使用时,标题和内容的解耦方案)
需求分析
- 标题栏出现的位置可选
- 标题栏与内容相互之间的位置可选
- 标题栏支持Toolbar调用
要优雅
- 基类实现方式,继承配置即可使用
- 不影响Super Activity的正常使用
- 良好的容错率
- 可配置标题栏位置及使用方式
- 支持自定义rootView及TitleStyle
OK,我们来写代码
导入依赖库
// 这里注意,如果只有一个app的module,可以使用implementation
implementation 'com.android.support:appcompat-v7:26.1.0'
// 如果BaseActivity的代码作为lib库,使用api
api 'com.android.support:appcompat-v7:26.1.0'
创建TitleActivity类
/**
* 自定义标题的BaseActivity
* 使用时需配置style Theme.AppCompat.NoActionBar
* setTitleView 设置标题
* setContentView 设置内容
* 复写titleStyle 设置标题风格
*/
public class TitleActivity extends AppCompatActivity {
// 获取自身实力的引用,防止代码里出现类似BaseActivity.this这种调用方式(个人习惯,查找类在哪里被引用的时候不乱)
protected TitleActivity thisActivity;
private LayoutInflater inflater;
}
预定义TitleStyle
protected static final int TITLE_NONE = 0;// 无标题
protected static final int TITLE_ABOVE_CONTENT = 1;// 内容在标题下方
protected static final int TITLE_BELOW_CONTENT = 2;// 标题在底部,内容在上方
protected static final int TITLE_FLOAT_TOP = 3;// 内容铺满全局,标题悬浮在内容上方
protected static final int TITLE_FLOAT_BOTTOM = 3;// 内容铺满全局,标题悬浮在内容下方
protected static final int TITLE_CONTENT_SCROLL = 4;// 标题随内容滚动
/**
* 标题栏风格
*/
protected int titleStyle() {
return TITLE_ABOVE_CONTENT;
}
添加标题的设置方法
protected void setTitleView(int layoutResID) {
if (titleStyle() == TITLE_NONE) {
titleView = inflater.inflate(layoutResID, null);
} else {
titleView = inflater.inflate(layoutResID, rootView, false);
notifyTitle();
}
}
private void notifyTitle() {
switch (titleStyle()) {
case TITLE_ABOVE_CONTENT:
default:
rootView.removeAllViews();
rootView.addView(titleView);
if (contentView != null) {
rootView.addView(contentView);
}
}
}
复写setContentView
@Override
public void setContentView(int layoutResID) {
if (titleStyle() == TITLE_NONE) {
super.setContentView(layoutResID);
} else {
contentView = inflater.inflate(layoutResID, rootView, false);
notifyContent(contentView.getLayoutParams());
}
}
@Override
public void setContentView(View view) {
if (titleStyle() == TITLE_NONE) {
super.setContentView(view);
} else {
contentView = view;
notifyContent(view.getLayoutParams());
}
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (titleStyle() == TITLE_NONE) {
super.setContentView(view, params);
} else {
contentView = view;
notifyContent(params);
}
}
private void notifyContent(ViewGroup.LayoutParams params) {
switch (titleStyle()) {
case TITLE_ABOVE_CONTENT:
default:
// content置空
int childCount = rootView.getChildCount();
if (childCount > 2) {
rootView.removeViewAt(childCount - 1);
}
if (contentView != null) {// 添加新的content
rootView.addView(contentView, params);
}
}
}
在onCreate中初始化rootView
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thisActivity = this;
inflater = LayoutInflater.from(thisActivity);
if (titleStyle() != TITLE_NONE) {
buildRootViewByStyle();
}
}
// 初始化rootView
private void buildRootViewByStyle() {
switch (titleStyle()) {
case TITLE_ABOVE_CONTENT:
default:
LinearLayout ll = new LinearLayout(thisActivity);
ll.setOrientation(LinearLayout.VERTICAL);
rootView = ll;
}
super.setContentView(rootView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
是否开启lib id
<!-- 在values中创建ids.xml文件 -->
<item name="TitleActivity_id_Toolbar" type="id"/>
<item name="TitleActivity_id_back" type="id"/>
<item name="TitleActivity_id_title" type="id"/>
/**
* @return true 自动检索相关ids并做响应操作 false 不做固定id检索
*/
protected boolean libTitleIdsEnable() {
return false;
}
/* 在标题初始化后 */
// init toolbar
if (libTitleIdsEnable()) {
try {
tv_title = findViewById(R.id.TitleActivity_id_title);
setCustomTitle();
} catch (ClassCastException e) {
throw new RuntimeException("title must instanceof TextView");
}
btn_back = findViewById(R.id.TitleActivity_id_back);
if (btn_back != null) {
if (backListener != null) {
btn_back.setOnClickListener(backListener);
} else {
btn_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
}
}
}
对外提供修改方法
protected Toolbar getToolbar() {
return toolbar;
}
private CharSequence titleCharSequence;
private int titleResId = -1;
protected void setCustomTitle(CharSequence title) {
this.titleCharSequence = title;
this.titleResId = -1;
setCustomTitle();
}
protected void setCustomTitle(int title) {
this.titleResId = title;
this.titleCharSequence = null;
setCustomTitle();
}
private void setCustomTitle() {
if (tv_title != null) {
if (titleCharSequence != null) {
tv_title.setText(titleCharSequence);
} else if (titleResId != -1) {
tv_title.setText(titleResId);
}
}
}
private View.OnClickListener backListener;
protected void setOnBackClickListener(View.OnClickListener listener) {
backListener = listener;
if (btn_back != null) {
btn_back.setOnClickListener(listener);
}
}
使用方法
<! -- 在布局文件声明lib 中的id -->
android:id="@id/TitleActivity_id_title"
public class TestTitleActivity extends TitleActivity {
@Override
protected boolean libTitleIdsEnable() {
return true;
}
@Override
protected int titleStyle() {
return TITLE_ABOVE_CONTENT;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitleView(R.layout.layout_title);
setContentView(R.layout.activity_title_test);
// TODO: do other
setCustomTitle("HaHaHa");// 设置title
}
}
让TitleStyle优雅起来
为TitleStyle添加抽象工厂模式
创建接口
public interface ITitleStyle {
boolean hasTitle();// 是否包括标题
boolean bindSystemActionBar();// Toolbar是否绑定系统ActionBar
ViewGroup onBuildRootViewByStyle(Context context);// 创建rootView
void onTitleLayoutSet(ViewGroup rootView, View titleView, View contentView);// 放置标题布局
void onContentLayoutSet(ViewGroup rootView, View titleView, View contentView, ViewGroup.LayoutParams params);// 放置内容布局位置
}
创建默认Style并实现接口
/**
* 线性布局
* 内容在标题下方
* 这里的逻辑是从BaseAcitivity中抽离的
*/
public class DefaultTitleStyle implements ITitleStyle {
@Override
public boolean hasTitle() {
return true;
}
@Override
public boolean bindSystemActionBar() {
return true;
}
@Override
public void onTitleLayoutSet(ViewGroup rootView, View titleView, View contentView) {
rootView.removeAllViews();
rootView.addView(titleView);
if (contentView != null) {
rootView.addView(contentView);
}
}
@Override
public void onContentLayoutSet(ViewGroup rootView, View titleView, View contentView, ViewGroup.LayoutParams params) {
// content置空
int childCount = rootView.getChildCount();
if (childCount > 2) {
rootView.removeViewAt(childCount - 1);
}
if (contentView != null) {// 添加新的content
rootView.addView(contentView, params);
}
}
@Override
public ViewGroup onBuildRootViewByStyle(Context context) {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.VERTICAL);
return ll;
}
}
于是,BaseAcitivity中的实现变为
/**
* 自定义标题的BaseActivity
* 使用时需配置style Theme.AppCompat.NoActionBar
* setTitleView 设置标题
* setContentView 设置内容
* 复写titleStyle 设置标题风格
*/
public class TitleActivity extends AppCompatActivity {
……
private ViewGroup rootView;
private View titleView;
private Toolbar toolbar;
private View contentView;
……
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
……
titleStyle = titleStyle();
if (titleStyle.hasTitle()) {
rootView = titleStyle.onBuildRootViewByStyle(thisActivity);
super.setContentView(rootView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
protected void setTitleView(int layoutResID) {
if (!titleStyle.hasTitle()) {
titleView = inflater.inflate(layoutResID, null);
} else {
titleView = inflater.inflate(layoutResID, rootView, false);
titleStyle.onTitleLayoutSet(rootView, titleView, contentView);
}
// init toolbar
if (titleView instanceof Toolbar) {
toolbar = (Toolbar) titleView;
if (titleStyle.bindSystemActionBar()) {
setSupportActionBar(toolbar);
}
} else if (libTitleIdsEnable()) {
toolbar = findViewById(R.id.TitleActivity_id_Toolbar);
if (titleStyle.bindSystemActionBar()) {
setSupportActionBar(toolbar);
}
}
// init title and back button
……
}
@Override
public void setContentView(int layoutResID) {
if (!titleStyle.hasTitle()) {
super.setContentView(layoutResID);
} else {
contentView = inflater.inflate(layoutResID, rootView, false);
titleStyle.onContentLayoutSet(rootView, titleView, contentView, contentView.getLayoutParams());
}
}
……
/**
* 标题风格
*/
protected ITitleStyle titleStyle() {
return new DefaultTitleStyle();
}
……
}
与Activity解耦
那么,当使用时,如果遇到以存在BaseAcitivity,无法继承怎么办?哼,一点也不优雅
封装LayoutHandler
public class LayoutHandler {
protected AppCompatActivity thisActivity;
private LayoutInflater inflater;
private ITitleStyle titleStyle;
// protected static final int TITLE_BELOW_CONTENT = 2;// 标题在底部,内容在上方
// protected static final int TITLE_FLOAT_TOP = 3;// 内容铺满全局,标题悬浮在内容上方
// protected static final int TITLE_FLOAT_BOTTOM = 3;// 内容铺满全局,标题悬浮在内容下方
// protected static final int TITLE_CONTENT_SCROLL = 4;// 标题随内容滚动
private boolean libTitleIdsEnable;
private ViewGroup rootView;
private View titleView;
private Toolbar toolbar;
private View contentView;
private TextView tv_title;
private View.OnClickListener backListener;
private CharSequence titleCharSequence;
private int titleResId = -1;
private View btn_back;
private LayoutHandler() {
}
public boolean onCreate() {
if (titleStyle.hasTitle()) {
rootView = titleStyle.onBuildRootViewByStyle(thisActivity);
return true;
}
return false;
}
public ViewGroup getRootView() {
return rootView;
}
protected Toolbar getToolbar() {
return toolbar;
}
public void setTitleView(int layoutResID) {
if (!titleStyle.hasTitle()) {
titleView = inflater.inflate(layoutResID, null);
} else {
titleView = inflater.inflate(layoutResID, rootView, false);
titleStyle.onTitleLayoutSet(rootView, titleView, contentView);
}
// init toolbar
if (titleView instanceof Toolbar) {
toolbar = (Toolbar) titleView;
if (titleStyle.bindSystemActionBar()) {
thisActivity.setSupportActionBar(toolbar);
}
} else if (libTitleIdsEnable) {
toolbar = thisActivity.findViewById(R.id.TitleActivity_id_Toolbar);
if (titleStyle.bindSystemActionBar()) {
thisActivity.setSupportActionBar(toolbar);
}
}
// init title and back button
if (libTitleIdsEnable) {
try {
tv_title = thisActivity.findViewById(R.id.TitleActivity_id_title);
setCustomTitle();
} catch (ClassCastException e) {
throw new RuntimeException("title must instanceof TextView");
}
btn_back = thisActivity.findViewById(R.id.TitleActivity_id_back);
if (btn_back != null) {
if (backListener != null) {
btn_back.setOnClickListener(backListener);
} else {
btn_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
thisActivity.onBackPressed();
}
});
}
}
}
}
public boolean setContentView(View view, ViewGroup.LayoutParams params) {
if (titleStyle.hasTitle()) {
contentView = view;
titleStyle.onContentLayoutSet(rootView, titleView, contentView, params);
return true;
}
return false;
}
public boolean setContentView(View view) {
if (titleStyle.hasTitle()) {
contentView = view;
titleStyle.onContentLayoutSet(rootView, titleView, contentView, contentView.getLayoutParams());
return true;
}
return false;
}
public boolean setContentView(int layoutResID) {
if (titleStyle.hasTitle()) {
contentView = inflater.inflate(layoutResID, rootView, false);
titleStyle.onContentLayoutSet(rootView, titleView, contentView, contentView.getLayoutParams());
return true;
}
return false;
}
protected void setCustomTitle(CharSequence title) {
this.titleCharSequence = title;
this.titleResId = -1;
setCustomTitle();
}
protected void setCustomTitle(int title) {
this.titleResId = title;
this.titleCharSequence = null;
setCustomTitle();
}
private void setCustomTitle() {
if (tv_title != null) {
if (titleCharSequence != null) {
tv_title.setText(titleCharSequence);
} else if (titleResId != -1) {
tv_title.setText(titleResId);
}
}
}
public void setOnBackClickListener(View.OnClickListener listener) {
this.backListener = listener;
if (btn_back != null) {
btn_back.setOnClickListener(listener);
}
}
}
为LayoutHandler添加建造者模式
public static class Builder {
LayoutHandler handler;
public Builder() {
handler = new LayoutHandler();
}
public Builder setActivity(AppCompatActivity act) {
handler.thisActivity = act;
handler.inflater = LayoutInflater.from(act);
return this;
}
public Builder setTitleStyle(ITitleStyle titleStyle) {
handler.titleStyle = titleStyle;
return this;
}
public Builder setLibTitleIdsEnable(boolean libTitleIdsEnable) {
handler.libTitleIdsEnable = libTitleIdsEnable;
return this;
}
public LayoutHandler build() {
return handler;
}
}
于是BaseActivity简化为
/**
* 自定义标题的BaseActivity
* 使用时需配置style Theme.AppCompat.NoActionBar
* setTitleView 设置标题
* setContentView 设置内容
* 复写titleStyle 设置标题风格
*/
public class TitleActivity extends AppCompatActivity {
// 获取自身实力的引用,防止代码里出现类似BaseActivity.this这种调用方式(个人习惯,查找类在哪里被引用的时候不乱)
protected TitleActivity thisActivity;
private LayoutHandler layoutHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thisActivity = this;
layoutHandler = new LayoutHandler.Builder()
.setActivity(thisActivity)
.setTitleStyle(titleStyle())
.setLibTitleIdsEnable(libTitleIdsEnable())
.build();
if (layoutHandler.onCreate()) {
super.setContentView(layoutHandler.getRootView(), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
@Override
public void setContentView(int layoutResID) {
if (!layoutHandler.setContentView(layoutResID)) {
super.setContentView(layoutResID);
}
}
@Override
public void setContentView(View view) {
if (!layoutHandler.setContentView(view)) {
super.setContentView(view);
}
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (!layoutHandler.setContentView(view, params)) {
super.setContentView(view, params);
}
}
/**
* 标题风格
*/
protected ITitleStyle titleStyle() {
return new DefaultTitleStyle();
}
/**
* @return true 自动检索相关ids并做响应操作 false 不做固定id检索
*/
protected boolean libTitleIdsEnable() {
return false;
}
/**
* 设置标题布局
*/
protected void setTitleView(int layoutResID) {
layoutHandler.setTitleView(layoutResID);
}
protected Toolbar getToolbar() {
return layoutHandler.getToolbar();
}
/**
* 设置标题
*/
protected void setCustomTitle(CharSequence title) {
layoutHandler.setCustomTitle(title);
}
/**
* 设置标题
*/
protected void setCustomTitle(int title) {
layoutHandler.setCustomTitle(title);
}
/**
* id为TitleActivity_id_back的view被点击的回调
*/
protected void setOnBackClickListener(View.OnClickListener listener) {
layoutHandler.setOnBackClickListener(listener);
}
}
网友评论