Builder设计模式构建通用标题栏

目录

一、基本框架搭建

二、构建通用型标题栏

2.1、布局编写

2.2、逻辑代码编写

三、业务层调用

四、效果展示


在上一次的分享中我使用构建者模式打造了一个通用的Dialog——《Builder设计模式构建通用型Dialog》,受此启发,我决定本篇再次通过构建者模式来实现一个通用型的标题栏,也就是我们的TitleBar。我们平时开发中很多时候都是自定义一个TitleBar或者ToolBar,然后封装在BaseActivity中来使用的,不知道你是不是也是这样的,最近通过学习,有了一丝的感悟,可以使用Builder模式来封装,这种方式其实更加灵活,当你需要使用的时候,直接调用Builder构建出一个TitleBar即可,当你有特殊的标题栏需求时,也可以及时的单独处理,不至于那么死板,并且这样做还有一个好处,看过我上一篇文章的哥们肯定都知道,我是把它封装在了一个Android Library中,这样做可以方便我们后期的项目架构成模块化和组件化的工程结构,跟业务层分离,当然你也可以直接单独将其封装成一个组件来使用都是OK的,那废话不多说了,一起来开始今天的内容吧!

一、基本框架搭建

先说明一下,当我们使用Builder模式构建完成TitleBar之后,我们就不用再在xml文件中去引入布局了,或者这样说我们是在代码中引入,实际上是通过代码将我们的标题栏插入到根布局的第一个位置了,防止后面有些小伙伴在xml文件中找不到然后有点懵。那下面就开始代码实战了,同样的我们第一步还是先来搭建基本的框架。

首先我们先来给我们的标题栏制定一个规范,所以先来定义一个接口,内部两个方法,一个用来绑定标题栏布局,一个用来绑定标题栏所需的参数:

package com.jarchie.lib_common.titlebar;

/**
 * 作者: 乔布奇
 * 日期: 2020-05-06 21:53
 * 邮箱: [email protected]
 * 描述: 头部标题栏规范
 */
public interface ITitleBar {

    //绑定头部视图
    public int bindLayoutId();

    //绑定头部参数
    public void applyView();

}

然后我们为了这个组件的通用性,考虑到产品可能存在的不确定性,因为整个App中可能80%的标题栏样式都是通用的,比如:左侧一个返回按钮,中间一个标题,右侧一个文本或者功能按钮,但是你保不准也有某些特殊的,为了后面对于特殊情况的处理尽可能的复用我们的这个组件,那我们必须考虑自身代码的可拓展性,所以在这里我们先来写一个最基本的TitleBar。

因为是构建者模式,所以首先要了解构建者模式,不了解的可以先去百度一下或者直接看一下我上一篇关于构建Dialog的文章,地址本篇开头已经给出了,里面有相关知识的介绍。其实对于设计模式它都是一些套路的东西(个人感觉写代码就是套路,主要还是思想和架构),比如构建者模式,我们肯定要有的BaseBar、Builder、Params等几个类。因为现在要写的是一个最基本的类,所以我将这个类定义成一个抽象类,将Params定义成泛型,后续子类继承我们这个基类,方便通用型的或者特殊场景下的标题栏单独做定制化实现。现在来看一下这个类的代码实现,因为有了上一篇的构建Dialog的经验,我想如果你看这一篇的内容应该很容易理解的:

/**
 * 作者: 乔布奇
 * 日期: 2020-05-06 21:54
 * 邮箱: [email protected]
 * 描述: 头部标题基类
 */
public abstract class AbsTitleBar<P extends AbsTitleBar.Builder.AbsTitleParams> implements ITitleBar {

    private P mParams;
    private View mTitleView;

    public AbsTitleBar(P params) {
        this.mParams = params;
        createAndBindView();
    }

    public P getParams() {
        return mParams;
    }

    public void setText(int viewId, String text) {
        TextView textView = findViewById(viewId);
        if (!TextUtils.isEmpty(text)) {
            textView.setVisibility(View.VISIBLE);
            textView.setText(text);
        }
    }

    public void setIcon(int viewId,int resId){
        ImageView imageView = findViewById(viewId);
        imageView.setImageResource(resId);
    }

    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        findViewById(viewId).setOnClickListener(listener);
    }

    public <T extends View> T findViewById(int viewId) {
        return (T) mTitleView.findViewById(viewId);
    }

    //绑定和创建View
    private void createAndBindView() {
        //处理无id情况,AppCompatActivity可以这样设置
        if (mParams.mParent == null) {
            //获取Activity的根布局
            ViewGroup activityRoot = (ViewGroup) ((Activity) mParams.mContext).findViewById(android.R.id.content);
            mParams.mParent = (ViewGroup) activityRoot.getChildAt(0);
        }
        if (mParams.mParent == null) return;
        //创建View
        mTitleView = LayoutInflater.from(mParams.mContext).inflate(bindLayoutId(), mParams.mParent, false);
        //添加,添加到第0个位置
        mParams.mParent.addView(mTitleView, 0);
        applyView();
    }

    public abstract static class Builder {

        public Builder(Context context, ViewGroup parent) {
        }

        public abstract AbsTitleBar build();

        public static class AbsTitleParams {
            public Context mContext;
            public ViewGroup mParent;

            public AbsTitleParams(Context context, ViewGroup parent) {
                this.mContext = context;
                this.mParent = parent;
            }
        }
    }
}

这里需要注意的点我提一下:当我们将TitleBar添加到根布局的最上方显示的时候,你可以传入你的根布局的id,通过id我们找到对应的布局进行添加显示,当然你也可以不传,这里如果你不传,我们默认帮你获取一次Android系统中AppCompatActivity的id为content的根布局。其它的就是一些很简单的代码了,设置文本设置图片资源设置点击事件这些,相信你肯定能看的懂!

二、构建通用型标题栏

在实际项目中,这一部分就是属于和公司业务相关的内容了,所以理论上来说应该再次封装一个介于app module和base module之间的business module,然后将与业务相关的通用性的东西放在这一层,我这里偷个懒,都放在commonLib中了,但是你需要了解这个分层的思想。

2.1、布局编写

先来写一个通用型标题栏的布局,因为我们一会要用到,内容也很简单,左侧一个返回按钮,中间一个文本标题,右侧文本或者Icon资源,来看代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="45dp"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="#e5e5e5">
    <ImageView
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/back"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_marginLeft="15dp"/>
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textColor="#09cb97"
        tools:text="标题"
        android:layout_centerInParent="true"/>
    <TextView
        android:id="@+id/right_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:textSize="16sp"
        android:textColor="#09cb97"
        tools:text="工具栏"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp"/>
    <ImageView
        android:id="@+id/right_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp"/>
</RelativeLayout>

2.2、逻辑代码编写

这里我们新建一个通用型标题栏的类,让它继承自上面的AbsTitleBar,然后实现父类中的方法:

/**
 * 作者: 乔布奇
 * 日期: 2020-05-06 22:12
 * 邮箱: [email protected]
 * 描述: 通用型标题栏,默认标题栏样式
 */
public class DefaultTitleBar extends AbsTitleBar<DefaultTitleBar.Builder.DefaultTitleParams> {

    public DefaultTitleBar(DefaultTitleBar.Builder.DefaultTitleParams params) {
        super(params);
    }

    @Override
    public int bindLayoutId() {
        return R.layout.title_bar;
    }

    @Override
    public void applyView() {
        //绑定效果
        setText(R.id.title, getParams().mTitle);
        setText(R.id.right_text, getParams().mRightText);
        setIcon(R.id.right_icon,getParams().mRightIcon);
        setOnClickListener(R.id.back,getParams().mLeftClickListener);
        setOnClickListener(R.id.right_text, getParams().mRightClickListener);
        setOnClickListener(R.id.right_icon, getParams().mRightClickListener);
    }

    public static class Builder extends AbsTitleBar.Builder {
        DefaultTitleParams P;

        public Builder(Context context) {
            super(context, null);
            P = new DefaultTitleParams(context, null);
        }

        public Builder(Context context, ViewGroup parent) {
            super(context, parent);
            P = new DefaultTitleParams(context, parent);
        }

        @Override
        public DefaultTitleBar build() {
            DefaultTitleBar titleBar = new DefaultTitleBar(P);
            return titleBar;
        }

        //设置标题
        public DefaultTitleBar.Builder setTitle(String title) {
            P.mTitle = title;
            return this;
        }

        //设置右侧文本
        public DefaultTitleBar.Builder setRightText(String rightText) {
            P.mRightText = rightText;
            return this;
        }

        //设置右侧图片
        public DefaultTitleBar.Builder setRightIcon(int rightIcon) {
            P.mRightIcon = rightIcon;
            return this;
        }

        //左侧点击事件
        public DefaultTitleBar.Builder setLeftClickListener(View.OnClickListener leftListener) {
            P.mLeftClickListener = leftListener;
            return this;
        }

        //右侧点击事件
        public DefaultTitleBar.Builder setRightClickListener(View.OnClickListener rightListener) {
            P.mRightClickListener = rightListener;
            return this;
        }

        public static class DefaultTitleParams extends DefaultTitleBar.Builder.AbsTitleParams {

            //放置所有效果
            public String mTitle;
            public String mRightText;
            public int mRightIcon;
            public View.OnClickListener mLeftClickListener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //关闭当前的Activity
                    ((Activity) mContext).finish();
                }
            };
            public View.OnClickListener mRightClickListener;

            public DefaultTitleParams(Context context, ViewGroup parent) {
                super(context, parent);
            }
        }
    }
}

这里也说几点需要注意的点吧:

第一个:Builder的构造方法我们给了两个,一个单参构造,一个双参构造,单参构造就是AbsTitleBar中默认获取了AppCompatActivity根布局的那种情况,这种情况你无需给页面根布局指定id。

第二个:左侧Icon的点击事件一般情况下都是关闭页面,所以这里再DefaultTitleParams类中定义这个变量的时候默认给出了关闭页面的这种实现,如果你需要处理别的业务逻辑,则在调用方使用的时候重新new OnClickListener就行了。

三、业务层调用

在Activity页面的java代码中添加我们的TitleBar,因为我们使用了Builder模式进行了封装,所以这里的代码就相当简单了,一句话一条链路下来即可搞定:

new DefaultTitleBar.Builder(this)
                .setTitle("玩安卓")
                .setRightIcon(R.drawable.share)
                .setRightClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this,"鸿洋牛逼",Toast.LENGTH_LONG).show();
                    }
                })
                .build();

看到这里,是不是感觉很简单呢,写起来是不是有点爽!

四、效果展示

来看下最后实现的效果,如下图所示:

OK,今天的内容不多,理清套路,及时上车!我的废话,也不再多!各位,咱们下次见!

猜你喜欢

转载自blog.csdn.net/JArchie520/article/details/106086039