打造通用AppBar,解放你的时间.
原因
相信不少小伙伴在开发过程中都要用到ToolBar.但是SDK提供的ToolBar在很大程度上无法达到理想的扩展程度.我们不得不自己写ToolBar.
思路
每个页面的xml写一份AppBar肯定是不理想的做法.所以大部分小伙伴可能采用include的方式.确实这样开发效率和代码阅读能力都有了一定的提高.但是.能不能在此基础上再做优化呢?答案是肯定的啦.
我们可以在include布局的基础上,把我们自定义ToolBar的控件获取、资源设置、事件处理全部封装起来,交给父类来处理.然后继承该父类(父类是我们自己写的,SDK没有哟,各位注意).
这样我们的xml通过include进行了处理,activity中的大量逻辑也被父类进行了处理.我们只需要进行初始化方法的调用以及事件处理即可.
先上效果图吧(看起来没什么,但是代码量极小哟)
效果
image解释
我这边主要做了两种类型的封装.
一:标准样式的(左边返回icon,中间标题,右边的可点击文本)
二:自定义样式的(左边两个可点击icon,中间标题,右边两个可点击icon)
不仅仅如此,背景、文本颜色都可自行修改,也可指定某些文本、icon是否显示(内部处理了statusBar颜色高度等问题).
实现
一:我们需要添加布局文件,通过include布局实现
//标准样式:
<include layout="@layout/common_appbar"/>
//自定义样式
<include layout="@layout/custom_appbar"/>
二:接着继承AppBarActivity(标准样式)或者CustomAppBarActivity(自定义样式),里面封装了所有逻辑.但是它们是抽象的类,我们需要实现相关方法.
//继承AppBarActivity要实现的方法
//设置标题
@Override
protected String setAppBarTitle() {
return "标准3";
}
//设置右边文本
@Override
protected String setAppBarRightTitle() {
return "点我";
}
//设置返回键点击
@Override
protected void onAppBarBackClick() {
finish();
}
//设置右边文本点击
@Override
protected void onAppBarRightClick() {
Toast.makeText(this, "点击", Toast.LENGTH_SHORT).show();
}
------------------------------分割------------------------------
//继承CustomAppBarActivity要实现的方法
//设置标题
@Override
protected String setAppBarTitle() {
return "自定义2";
}
//设置icon资源(不显示的返回-1即可)
@Override
protected int[] setAppBarDrawableRes() {
return new int[]{R.drawable.left_arrow, R.drawable.edit, R.drawable.no_star, R.drawable.share};
}
//四个icon的点击事件
@Override
protected void onAppBarClick(int position) {
switch (position) {
case 0:
finish();
break;
case 1:
Toast.makeText(this, "编辑", Toast.LENGTH_SHORT).show();
break;
case 2:
if (mIsStared) {
Toast.makeText(this, "取消收藏", Toast.LENGTH_SHORT).show();
setThreeIVDrawable(R.drawable.no_star);
} else {
Toast.makeText(this, "收藏", Toast.LENGTH_SHORT).show();
setThreeIVDrawable(R.drawable.star);
}
mIsStared = !mIsStared;
break;
case 3:
Toast.makeText(this, "分享", Toast.LENGTH_SHORT).show();
break;
}
}
三:最后调用initAppBar() 进行最后的初始化.指定了颜色、是否显示等状态(需在setContentView()方法之后.
注:initAppBar是个多参数重载方法,实现具体功能.
四:父类实现代码(Demo下载即可,非重点了解对象)
///标准样式父类实现
public abstract class AppBarActivity extends AppCompatActivity {
protected void initAppBar() {
initAppBar(true, false, -1, -1);
}
protected void initAppBar(boolean isBack) {
initAppBar(isBack, false, -1, -1);
}
protected void initAppBar(boolean isBack, boolean isRightText) {
initAppBar(isBack, isRightText, -1, -1);
}
protected void initAppBar(boolean isBack, boolean isRightText, @ColorRes int bgColor, @ColorRes int textColor) {
RelativeLayout layout = (RelativeLayout) findViewById(R.id.common_appbar_rl);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.common_appbar_ll);
if (layout == null) {
return;
}
invadeStatusBar();
if (bgColor != -1) {
linearLayout.setBackgroundResource(bgColor);
}
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) layout.getLayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
layoutParams.setMargins(0, getStatusBarHeight(), 0, 0);
} else {
layoutParams.setMargins(0, 0, 0, 0);
}
layout.setLayoutParams(layoutParams);
ImageView iconIV = (ImageView) findViewById(R.id.common_appbar_iv);
if (!isBack) {
iconIV.setVisibility(View.GONE);
} else {
iconIV.setVisibility(View.VISIBLE);
iconIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAppBarBackClick();
}
});
}
TextView centerTV = (TextView) findViewById(R.id.common_appbar_center_tv);
if (textColor != -1) {
centerTV.setTextColor(getResources().getColor(textColor));
}
centerTV.setText(TypeUtil.isBlank(setAppBarTitle()) ? "" : setAppBarTitle());
TextView rightTV = (TextView) findViewById(R.id.common_appbar_right_tv);
if (!isRightText) {
rightTV.setVisibility(View.GONE);
} else {
rightTV.setVisibility(View.VISIBLE);
if (textColor != -1) {
centerTV.setTextColor(getResources().getColor(textColor));
}
rightTV.setText(TypeUtil.isBlank(setAppBarRightTitle()) ? "" : setAppBarRightTitle());
rightTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAppBarRightClick();
}
});
}
}
protected abstract String setAppBarTitle();
protected abstract String setAppBarRightTitle();
protected abstract void onAppBarBackClick();
protected abstract void onAppBarRightClick();
protected void invadeStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}
}
protected int getStatusBarHeight() {
Resources resources = getResources();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
int height = resources.getDimensionPixelSize(resourceId);
Log.i("theHeight", height + "");
if (height <= 10) {
height = DensityUtil.dp2px(this, 16);
}
return height;
}
}
------------------------------分割----------------------------------------------
//自定义样式父类实现
public abstract class CustomAppBarActivity extends AppCompatActivity {
private int[] mResArray;
private ImageView mThreeIV;
protected void initAppBar(boolean isOne, boolean isTwo, boolean isThree, boolean isFour) {
initAppBar(isOne, isTwo, isThree, isFour, -1, -1);
}
protected void initAppBar(boolean isOne, boolean isTwo, boolean isThree, boolean isFour, @ColorRes int bgColor, @ColorRes int textColor) {
RelativeLayout layout = (RelativeLayout) findViewById(R.id.custom_appbar_rl);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.custom_appbar_ll);
if (layout == null) {
return;
}
invadeStatusBar();
if (bgColor != -1) {
linearLayout.setBackgroundResource(bgColor);
}
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) layout.getLayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
layoutParams.setMargins(0, getStatusBarHeight(), 0, 0);
} else {
layoutParams.setMargins(0, 0, 0, 0);
}
layout.setLayoutParams(layoutParams);
mResArray = setAppBarDrawableRes();
TextView titleTV = (TextView) findViewById(R.id.custom_appbar_center_tv);
if (textColor != -1) {
titleTV.setTextColor(getResources().getColor(textColor));
}
titleTV.setText(setAppBarTitle() == null ? "默认标题" : setAppBarTitle());
ImageView oneIV = (ImageView) findViewById(R.id.custom_appbar_one_iv);
ImageView twoIV = (ImageView) findViewById(R.id.custom_appbar_two_iv);
mThreeIV = (ImageView) findViewById(R.id.custom_appbar_three_iv);
ImageView fourIV = (ImageView) findViewById(R.id.custom_appbar_four_iv);
if (!isOne || mResArray.length < 1) {
oneIV.setVisibility(View.GONE);
} else {
oneIV.setImageDrawable(getResources().getDrawable(mResArray[0]));
oneIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAppBarClick(0);
}
});
}
if (!isTwo || mResArray.length < 2) {
twoIV.setVisibility(View.GONE);
} else {
twoIV.setImageDrawable(getResources().getDrawable(mResArray[1]));
twoIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAppBarClick(1);
}
});
}
if (!isThree || mResArray.length < 3) {
mThreeIV.setVisibility(View.GONE);
} else {
mThreeIV.setImageDrawable(getResources().getDrawable(mResArray[2]));
mThreeIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAppBarClick(2);
}
});
}
if (!isFour || mResArray.length < 4) {
fourIV.setVisibility(View.GONE);
} else {
fourIV.setImageDrawable(getResources().getDrawable(mResArray[3]));
fourIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAppBarClick(3);
}
});
}
}
protected abstract String setAppBarTitle();
protected abstract int[] setAppBarDrawableRes();
protected abstract void onAppBarClick(int position);
protected void setThreeIVDrawable(@DrawableRes int drawable) {
if (mThreeIV == null) {
return;
}
mThreeIV.setImageDrawable(getResources().getDrawable(drawable));
}
protected void invadeStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}
}
private int getStatusBarHeight() {
Resources resources = getResources();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
int height = resources.getDimensionPixelSize(resourceId);
Log.i("theHeight", height + "");
if (height <= 10) {
height = DensityUtil.dp2px(this, 16);
}
return height;
}
}
通过以上设置我们处理了布局加载、控件初始化、事件处理.三大块处理完毕了.相信逻辑上大家都明白了.具体实现细节是在AppBarActivity以及CustomAppBarActivity父类里面进行了封装处理.大家可以根据自己具体业务选择继承父类.或者再此基础上面进行修改调整.使其符合自己项目业务需要.
扩展
大家有没有想过,这样重复的在每个xml布局include头布局也是挺烦人的。能不能优化呢?答案是肯定的,但是会有微小问题。
代码实现
下面注释的三行便是动态加载头布局的实现了,首先获取我们activity的最外层布局,然后将具体的头文件布局添加到最外层布局的第0个index位置。
//在父类完成,子类无需重复操作
//ViewGroup outView = (ViewGroup) ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
// View inflate = LayoutInflater.from(this).inflate(R.layout.custom_appbar, null);
//outView.addView(inflate,0);
RelativeLayout layout = (RelativeLayout) findViewById(R.id.custom_appbar_rl);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.custom_appbar_ll);
动态加载布局问题
该动态加载方法的唯一弊端,我们在写布局的时候,design预览页面会出现导航栏空白,如果是xml的include头布局方式则不会有这个问题。毕竟是代码动态加载布局的,此时未载入。当然这个也没有太大影响,使用方式因人而异吧。我会在仓库加入相关注释,大家可切换尝试。
总结
主要是思路的处理,以及细节封装.我这边有已经做好的Demo,可供大家交流以及直接使用.
地址: https://github.com/HoldMyOwn/AppBar
网友评论