BaseActivity 和 ToolBar 的完美结合

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

  • 写一个标题的布局,然后每个页面都去使用“ include ”
  • 自定义view,然后写到你的activity的布局文件里面去使用这个控件。

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

  • 每写一个页面,你就需要改变布局文件,然后在activity里面找到布局或定义控件,然后给标题设置,或者写点击事件,步骤繁琐;
  • 当某些页面样式改动,你原先写的布局或自定义控件的扩展性不够强,则无法满足需求,然后你必须重新画标题;

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

我的思路:

  1. 在基类BaseActivity中留出一部分布局作为标题布局A,一部分作为内容布局B,将子类的布局全部内容放到基类的内容布局B中(子类的画布局的时候完全不用管标题的部分,后面会有讲到),基类定义abstract方法让子类必须返回一个标题布局,然后将返回的标题布局放到A里面;
  2. 建立标题view工厂类,存储各种不同的标题样式,在开发中,根据不同的需求,传入type值,工厂类生产不同的标题出来,然后提供给activity,activity再将这个view提供给基类,放到上面说的标题布局A中;
  3. 通过接口回调处理标题的点击事件;

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欢迎下载和指教项目地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值