BaseActivity自定义多样化标题布局
标题栏这种东西,每个App都会有,但是不是每个设计师都会为一个App设计Android和IOS两种风格,所以苦了Android开发的。不能用系统的,所以我们来自定义好了。(该篇文章主要提供布局文件在Acitivity使用时,标题和内容的解耦方案)
需求分析
- 标题栏出现的位置可选
- 标题栏与内容相互之间的位置可选
- 标题栏支持Toolbar调用
- 基类实现方式,继承配置即可使用
- 不影响Super Activity的正常使用
- 良好的容错率
- 可配置标题栏位置及使用方式
- 支持自定义rootView及TitleStyle
// 这里注意,如果只有一个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
/**
* @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);
}
}
使用方法
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简化为
/**
* 自定义标题的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);
}}
推荐阅读
- SpringBoot调用公共模块的自定义注解失效的解决
- python自定义封装带颜色的logging模块
- 列出所有自定义的function和view
- Spring|Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件
- 自定义MyAdapter
- Android自定义view实现圆环进度条效果
- Flutter自定义view|Flutter自定义view —— 闯关进度条
- js保留自定义小数点
- django|django 自定义.save()方法
- 如何在Kubernetes|如何在Kubernetes 里添加自定义的 API 对象(一)