欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android View 高级框架一 Builder模式打造通用TitleBar

程序员文章站 2024-03-14 12:54:16
...

1 简介

我们在开发中往往都有一个TitleBar在APP最上面。例如下图
Android View 高级框架一 Builder模式打造通用TitleBarAndroid View 高级框架一 Builder模式打造通用TitleBar
这个TitleBar如果每次都写在布局中,则每个Activity都要在布局中展现。会增加布局的复杂度,另外也不便于维护。另外,对于不同的界面,有时候这个TitleBar又会有所差别。因此怎么定制化一个可以复用又方便维护的TitleBar呢?

当然,我们也可以单独写一个布局文件,然后每个Activity的界面include进去,但是这样每个Activity界面都需要要单独的findViewById。后期的维护性其实也不是很强。

2 TitleBar框架

设计的TitleBar框架如下图
Android View 高级框架一 Builder模式打造通用TitleBar
可以看到,我们采用了Builder设计模式,来构造我们的TitleBar的各种参数。另外,我们在基类中指定了布局的文件及bindView的方法。从而使我们的应用能绑定到Activity的根布局上。
具体的逻辑如下:

    /**
     * 绑定View到ActivityRoot上
     */
    private void bindViewToActivityRoot() {
        ViewGroup activityRoot = null;
        //创建View
        if (mParams.mActivityRoot == null || activityRoot == null){
            //获取Activity的根布局
            activityRoot = (ViewGroup) ((Activity)mParams.mContext).findViewById(android.R.id.content);
            //获取我们在Activity中设置ContentView的View
            mParams.mActivityRoot = (ViewGroup)activityRoot.getChildAt(0);
            LogManager.i(TAG,"activityRoot size : " +activityRoot.getChildCount() + " mParams.mActivityRoot:" + mParams.mActivityRoot);
        }
        if (mParams.mActivityRoot == null){
            return;
        }

        mNavigationView = LayoutInflater.from(mParams.mContext)
                .inflate(bindLayoutId(),activityRoot,false);

        if (mParams.mActivityRoot instanceof RelativeLayout){
            //相对布局
            //先构造一个线性布局,指定垂直排列
            LinearLayout newActivityRoot = new LinearLayout(mParams.mContext);
            newActivityRoot.setOrientation(LinearLayout.VERTICAL);

            //移除原有的activityRoot的parent,否则会报"The specified child already has a parent. " +
            //"You must call removeView() on the child's parent first. 异常
            ViewGroup viewParent = (ViewGroup)mParams.mActivityRoot.getParent();
            viewParent.removeAllViews();

            //将titleBar添加为第一个child,原来的activityRoot为第二个
            newActivityRoot.addView(mNavigationView,0);
            newActivityRoot.addView(mParams.mActivityRoot,1);
            //将新的activityRoot添加到android.R.id.content中
            activityRoot.addView(newActivityRoot,0);

            LogManager.i(TAG,"mParams.mActivityRoot,child at 0 view:");

        }else {
            //添加View到mParams.mParent中
            mParams.mActivityRoot.addView(mNavigationView,0);
        }
        LogManager.i(TAG,"mParams.mActivityRoot,child:" + mParams.mActivityRoot.getChildCount());
        //绑定View
        bindView();
    }

这里有一点技巧就是,我们的Activity的布局最终是添加到id为android.R.id.content的一个FrameLayout
布局中的,然后获取我们的Activity界面的根布局,将TitleBar布局添加到根布局的第一个位置处。这样就完成了我们TitleBar布局的添加,省去了在xml中添加了。

一个具体的TitleBar需要继承BaseTitleBar和继承BaseTitleParams,然后实现bindLayoutId()和bindView()。

3 CommonTitleBar

CommonTitleBar是一个具体的例子,效果图如下:
Android View 高级框架一 Builder模式打造通用TitleBar
我们先来看它的UML类图
Android View 高级框架一 Builder模式打造通用TitleBar
可以看到,CommonTitleBar主要继承自BaseTitleBar。它的源码如下:

/**
 * Email: 1273482124@qq.com
 * Created by qiyei2015 on 2017/5/14.
 * Version: 1.0
 * Description:
 */
public class CommonTitleBar extends BaseTitleBar<CommonTitleParams> {

    protected CommonTitleBar(CommonTitleParams params) {
        super(params);
    }

    /**
     * 布局文件
     * @return
     */
    @Override
    public int bindLayoutId() {
        return R.layout.title_bar;
    }

    @Override
    public void bindView() {
        setText(R.id.title,mParams.mTitle);
        setText(R.id.right_text,mParams.mRightText);

        setOnClickListener(R.id.right_text,mParams.mRightClickListener);
        // 左边 要写一个默认的  finishActivity
        setOnClickListener(R.id.back,mParams.mLeftClickListener);
    }

    /**
     * 设置Tiltle
     * @param title
     */
    public void setTitle(String title){
        mParams.mTitle = title;
        setText(R.id.title,mParams.mTitle);
    }

    /**
     * 设置右侧文字
     * @param text
     * @return
     */
    public void setRightText(String text){
        mParams.mRightText = text;
        setText(R.id.right_text,mParams.mRightText);
    }

    /**
     * Builder模式设置各种效果
     */
    public static class Builder extends BaseTitleBar.Builder{
        /**
         * 所有的效果参数
         */
        CommonTitleParams mBarParams;

        public Builder(Context context) {
            super(context);
            mBarParams = new CommonTitleParams(context,null);
        }

        /**
         * 设置Title
         * @param title
         * @return
         */
        public Builder setTitle(String title){
            mBarParams.mTitle = title;
            return this;
        }

        /**
         * 设置右侧文字
         * @param text
         * @return
         */
        public Builder setRightText(String text){
            mBarParams.mRightText = text;
            return this;
        }

        /**
         * 设置右侧点击事件
         * @param listener
         * @return
         */
        public Builder setRightClickListener(View.OnClickListener listener){
            mBarParams.mRightClickListener = listener;
            return this;
        }

        /**
         * 创建CommonNavigationBar
         * @return
         */
        @Override
        public CommonTitleBar build() {
            return new CommonTitleBar(mBarParams);
        }
    }
}

CommonTitleBar主要封装了一些公共的返回按钮图标及响应的点击事件,右侧的文字,标题等内容。可根据项目的实际情况进行自定义其他的TitleBar
可以看到这是一个典型的Builder模式,主要的参数都存放在CommonTitleParams这个类中。如果项目有多个类型风格不同的TitleBar,则可以定义不同的TitleBar

详细的源码请参考
github https://github.com/qiyei2015/EssayJoke 中SDK 下的titlebar目录