您的位置:首页 > 其它

BaseActivity 和 ToolBar 的完美结合

2017-03-10 15:59 330 查看
在项目中,很多页面拥有类似的标题,我们只需要改变一些文字和样式就可以做到重用,所以一般的情况都是 :

写一个标题的布局,然后每个页面都去使用“ include ”

自定义view,然后写到你的activity的布局文件里面去使用这个控件。

这样是的确是一种简化开发的方法;但你可能会遇到一下问题:

每写一个页面,你就需要改变布局文件,然后在activity里面找到布局或定义控件,然后给标题设置,或者写点击事件,步骤繁琐;

当某些页面样式改动,你原先写的布局或自定义控件的扩展性不够强,则无法满足需求,然后你必须重新画标题;

画标题是项目开发中最简单的时,但同时也很繁琐,作为一个喜欢偷懒的程序员,为何不试着去改变呢!

我的思路:

在基类BaseActivity中留出一部分布局作为标题布局A,一部分作为内容布局B,将子类的布局全部内容放到基类的内容布局B中(子类的画布局的时候完全不用管标题的部分,后面会有讲到),基类定义abstract方法让子类必须返回一个标题布局,然后将返回的标题布局放到A里面;

建立标题view工厂类,存储各种不同的标题样式,在开发中,根据不同的需求,传入type值,工厂类生产不同的标题出来,然后提供给activity,activity再将这个view提供给基类,放到上面说的标题布局A中;

通过接口回调处理标题的点击事件;

talking is cheap show me the code

具体实现步骤

**1.定义基类BaseActivity**

布局文件


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">

<!--标题布局-->
<FrameLayout
android:id="@+id/fl_base_top"
android:layout_width="match_parent"
android:layout_height="48dp">
</FrameLayout>

<!--内容布局-->
<FrameLayout
android:id="@+id/fl_base_content"
android:layout_width="match_parent"
android:background="@color/white"
android:layout_height="0dp"
android:layout_weight="1">

</FrameLayout>
</LinearLayout>


基类代码

public abstract class BaseActivity extends AppCompatActivity {

private FrameLayout flBaseTop;//头布局
private FrameLayout flBaseContent;//内容布局

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//去掉系统的TitleBar
this.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
//初始化content的View
initContentView(R.layout.activity_base);

}

/**
* 子类必须实现,返回一个写好了的 topbar,如果不需要标题,实现方法后返回null就行,
* @return
*/
abstract protected View getTopBar();

private void initContentView(@LayoutRes int layoutResID) {

ViewGroup group = (ViewGroup) findViewById(android.R.id.content);   //得到窗口的根布局
group.removeAllViews();                                             //首先先移除在根布局上的组件
LayoutInflater.from(this).inflate(layoutResID, group, true);        //group,true的意思表示添加上去
}

/**
* 这句的意思表示将MainActivity的布局又加到parentLinearLayout的content上
*/
@Override
public void setContentView(@LayoutRes int layoutContentID) {
// super.setContentView(layoutResID);//一定不能调用这句话,不然之前做的添加的布局都被覆盖了
flBaseTop = (FrameLayout) findViewById(R.id.fl_base_top);
flBaseContent = (FrameLayout) findViewById(R.id.fl_base_content);

flBaseStatus = (RelativeLayout) findViewById(R.id.fl_base_status);
evBaseStatus = (EmptyView) findViewById(R.id.ev_base_status);

//将子类的内容布局添加到基类的内容布局中
LayoutInflater.from(this).inflate(layoutContentID, flBaseContent, true);

//将子类实现的标题,添加到基类的标题布局当中
if(getTopBar() != null){
flBaseTop.addView(getTopBar());
flBaseTop.setVisibility(View.VISIBLE);
}else {
flBaseTop.setVisibility(View.GONE);
}

}

}


重要的几个方法:

/**
* 子类必须实现,返回一个写好了的 topbar,如果不需要标题,实现方法后返回null就行,
* @return
*/
abstract protected View getTopBar();


这个方法就是子类必须实现,然后就返回一个写好的,配置好了的标题 view

@Override
public void setContentView(@LayoutRes int layoutContentID) {
// super.setContentView(layoutResID);//一定不能调用这句话,不然之前做的添加的布局都被覆盖了
flBaseTop = (FrameLayout) findViewById(R.id.fl_base_top);
flBaseContent = (FrameLayout) findViewById(R.id.fl_base_content);

flBaseStatus = (RelativeLayout) findViewById(R.id.fl_base_status);
evBaseStatus = (EmptyView) findViewById(R.id.ev_base_status);

//将子类的内容布局添加到基类的内容布局中
LayoutInflater.from(this).inflate(layoutContentID, flBaseContent, true);

//将子类实现的标题,添加到基类的标题布局当中
if(getTopBar() != null){
flBaseTop.addView(getTopBar());
flBaseTop.setVisibility(View.VISIBLE);
}else {
flBaseTop.setVisibility(View.GONE);
}

}


重写setContentView方法,将子类的内容和标题添加到基类里面的布局里面去

if(getTopBar() != null){
flBaseTop.addView(getTopBar());
flBaseTop.setVisibility(View.VISIBLE);
}else {
flBaseTop.setVisibility(View.GONE);
}


上面这行就是将主要的步骤,调用上面定义好的abstract方法getTopBar();添加标题布局;

子类继承基类BaseActivity

public class MainActivity extends BaseActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

@Override
protected View getTopBar() {
return new T_Style_3_Factory.Builder(this)
.setTab1Str("123")
.setTab2Str("456")
.setCallBack(new Style_3_Callback() {
@Override
public void leftClick() {

}

@Override
public void tab1Click() {

}

@Override
public void tab2Click() {

}
})
.build()
.getTitleView();
}

}


从使用方法可以看到,建立一个复杂的标题就是一个方法,几行代码的事,其实主要的功劳是这个factory,

这个工厂类主要运用了工厂模式,build模式,策略模式来实现。

下面如何讲解建立这个工厂类

1.先定义几个接口和抽象类

工厂抽象类:

public abstract class T_Factoryable {

///////////////////////////////////////////////////////////////////////////

// 样式 1     +--------------------------------+
//            | < 返回        标题             |
//            +--------------------------------+

//            +--------------------------------+
//            | <             标题             |
//            +--------------------------------+
//

// 样式 2     +--------------------------------+
//            | < 返回       标题       文字★ |
//            +--------------------------------+

//            +--------------------------------+
//            | < 返回       标题           ★ |
//            +--------------------------------+

//            +--------------------------------+
//            | <            标题           ★ |
//            +--------------------------------+

// 样式 3     +--------------------------------+
//            | <       (tab1 | tab2)        |
//            +--------------------------------+

//
///////////////////////////////////////////////////////////////////////////

abstract public View getTitleView();

}


定义点击事件的接口(虽然是空的,但是为了扩展新强,才用的)

public interface StyleCallBack {
}


建立具体工程类

public class T_Style_3_Factory  extends T_Factoryable{

// 样式 3     +--------------------------------+
//            | <       (tab1 | tab2)        |
//            +--------------------------------+

private Context context;

//左边文字
private String leftString="";
//中间标题
private String centerString="";
//右边文字
private String rightString="";

//左边图片资源
private int leftImgRes;
//右边图片资源
private int rightImgRes;

//点击事件的回调
private StyleCallBack styleCallback;

//tab1 文字
private String tab1Str;
//tab2 文字
private String tab2Str;

Style_3_Callback style_3_Callback;

@Override
public View getTitleView() {

RelativeLayout view;

//得到布局
view = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.topbar_style3, null);
//左边文字
//        TextView tv_left= (TextView) view.findViewById(R.id.tv_left2);
//tab 1 文字
final TextView tv_tab1= (TextView) view.findViewById(R.id.tv_tab1);
//tab 2 文字
final TextView tv_tab2= (TextView) view.findViewById(R.id.tv_tab2);
//左边图标  一般情况下不需要动
ImageView img_left1= (ImageView) view.findViewById(R.id.img_left1);

//textView赋值
//        tv_left.setText(leftString);
//        tv_center.setText(centerString);
//        tv_right1.setText(rightString);

//在没赋值的情况下 左边、右边图标不变
if(-1 != leftImgRes){
img_left1.setImageResource(leftImgRes);
}
if(-1 != rightImgRes){
img_left1.setImageResource(rightImgRes);
}

//策略模式
style_3_Callback= (Style_3_Callback) styleCallback;

tv_tab1.setText(tab1Str);
tv_tab2.setText(tab2Str);

//设置点击事件
img_left1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(style_3_Callback != null){
style_3_Callback.leftClick();
}
}
});
tv_tab1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(style_3_Callback != null){
style_3_Callback.tab1Click();

tv_tab1.setSelected(true);//选择充值
tv_tab2.setSelected(false);//不选中充值卡充值
tv_tab1.setTextColor(context.getResources().getColor(R.color.white));
tv_tab2.setTextColor(context.getResources().getColor(R.color.style_3_color));
tv_tab1.setBackground(context.getResources().getDrawable(R.drawable.shape_style3_left));
tv_tab2.setBackground(context.getResources().getDrawable(R.drawable.shape_style3_right));
}
}
});
tv_tab2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(style_3_Callback != null){
style_3_Callback.tab2Click();

tv_tab1.setSelected(false);//选择充值
tv_tab2.setSelected(true);//不选中充值卡充值
tv_tab1.setTextColor(context.getResources().getColor(R.color.style_3_color));
tv_tab2.setTextColor(context.getResources().getColor(R.color.white));
tv_tab1.setBackground(context.getResources().getDrawable(R.drawable.shape_style3_right1));
tv_tab2.setBackground(context.getResources().getDrawable(R.drawable.shape_style3_left1));
}
}
});

return view;
}

/**
* Build 模式,负责ConstantView的构建
* 使类的 构建 与 使用 分离,能让你 暴露出的接口使用起来更加方便清晰
*/
public static class Builder{
//左边文字
private String leftString="";

//右边文字
private String rightString="";

//左边图片资源
private int leftImgRes=-1;
//右边图片资源
private int rightImgRes=-1;

//上下文
private Context context;
//回调
private StyleCallBack styleCallBack;

//tab1 文字
private String tab1Str;
//tab2 文字
private String tab2Str;

private Builder(){}

public Builder(Context context){
this.context=context;
}

public Builder setLeftString(String leftString){
this.leftString=leftString;
return this;
}
public Builder setRightString(String rightString){
this.rightString=rightString;
return this;
}
public Builder setLeftImgRes(int leftImgRes){
this.leftImgRes=leftImgRes;
return this;
}
public Builder setRightImgRes(int rightImgRes){
this.rightImgRes=rightImgRes;
return this;
}

public Builder setCallBack(StyleCallBack styleCallBack){
this.styleCallBack=styleCallBack;
return this;
}
public Builder setTab1Str(String tab1Str){
this.tab1Str=tab1Str;
return this;
}

public Builder setTab2Str(String tab2Str){
this.tab2Str=tab2Str;
return this;
}

/**
* 完成 build
* @return
*/
public T_Style_3_Factory build(){
T_Style_3_Factory titleFactory=new T_Style_3_Factory();

apply(titleFactory);

return titleFactory;
}

/**
* 将build中的值赋值到  constantView类中
* @param titleFactory
*/
private void apply(T_Style_3_Factory titleFactory){
titleFactory.leftString=this.leftString;
titleFactory.rightString=this.rightString;

titleFactory.leftImgRes=this.leftImgRes;
titleFactory.rightImgRes=this.rightImgRes;

//防止内存泄漏
titleFactory.context=this.context.getApplicationContext();

titleFactory.styleCallback=this.styleCallBack;

titleFactory.tab1Str=this.tab1Str;
titleFactory.tab2Str=this.tab2Str;
}
}


建立标题的具体点击事件接口

public interface Style_3_Callback extends StyleCallBack {
void leftClick();
void tab1Click();
void tab2Click();
}


工厂类的具体职责就是生产配置好的标题view,关于标题的配置其实是一个比较复杂的过程,这里用Builder类来辅助标题的建立,将构建与使用分离;

样式定义好后,将点击事件通过style_3_Callback接口,将点击事件传递出去,

//设置点击事件
img_left1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(style_3_Callback != null){
style_3_Callback.leftClick();
}
}
});


于是使用起来就相当简洁易用

就像这样

@Override
protected View getTopBar() {
return new T_Style_3_Factory.Builder(this)
.setTab1Str("123")
.setTab2Str("456")
.setCallBack(new Style_3_Callback() {
@Override
public void leftClick() {

}

@Override
public void tab1Click() {

}

@Override
public void tab2Click() {

}
})
.build()
.getTitleView();
}


项目以上传github欢迎下载和指教项目地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  布局 控件