BaseActivity自定义多样化标题布局

标题栏这种东西,每个App都会有,但是不是每个设计师都会为一个App设计Android和IOS两种风格,所以苦了Android开发的。不能用系统的,所以我们来自定义好了。(该篇文章主要提供布局文件在Acitivity使用时,标题和内容的解耦方案)

需求分析

  1. 标题栏出现的位置可选
  2. 标题栏与内容相互之间的位置可选
  3. 标题栏支持Toolbar调用

要优雅

  1. 基类实现方式,继承配置即可使用
  2. 不影响Super Activity的正常使用
  3. 良好的容错率
  4. 可配置标题栏位置及使用方式
  5. 支持自定义rootView及TitleStyle

OK,我们来写代码

导入依赖库

    // 这里注意,如果只有一个app的module,可以使用implementation
    implementation 'com.android.support:appcompat-v7:26.1.0'
    // 如果BaseActivity的代码作为lib库,使用api
    api 'com.android.support:appcompat-v7:26.1.0'

创建TitleActivity类

    /**
     * 自定义标题的BaseActivity
     * 使用时需配置style Theme.AppCompat.NoActionBar
     * setTitleView 设置标题
     * setContentView 设置内容
     * 复写titleStyle 设置标题风格
     */
    public class TitleActivity extends AppCompatActivity {
        // 获取自身实力的引用,防止代码里出现类似BaseActivity.this这种调用方式(个人习惯,查找类在哪里被引用的时候不乱)
        protected TitleActivity thisActivity;
        private LayoutInflater inflater;
    }

预定义TitleStyle

    protected static final int TITLE_NONE = 0;// 无标题
    protected static final int TITLE_ABOVE_CONTENT = 1;// 内容在标题下方
    protected static final int TITLE_BELOW_CONTENT = 2;// 标题在底部,内容在上方
    protected static final int TITLE_FLOAT_TOP = 3;// 内容铺满全局,标题悬浮在内容上方
    protected static final int TITLE_FLOAT_BOTTOM = 3;// 内容铺满全局,标题悬浮在内容下方
    protected static final int TITLE_CONTENT_SCROLL = 4;// 标题随内容滚动
    /**
     * 标题栏风格
     */
    protected int titleStyle() {
        return TITLE_ABOVE_CONTENT;
    }

添加标题的设置方法

    protected void setTitleView(int layoutResID) {
        if (titleStyle() == TITLE_NONE) {
            titleView = inflater.inflate(layoutResID, null);
        } else {
            titleView = inflater.inflate(layoutResID, rootView, false);
            notifyTitle();
        }
    }
    
    private void notifyTitle() {
        switch (titleStyle()) {
            case TITLE_ABOVE_CONTENT:
            default:
                rootView.removeAllViews();
                rootView.addView(titleView);
                if (contentView != null) {
                    rootView.addView(contentView);
                }
        }
    }

复写setContentView

    @Override
    public void setContentView(int layoutResID) {
        if (titleStyle() == TITLE_NONE) {
            super.setContentView(layoutResID);
        } else {
            contentView = inflater.inflate(layoutResID, rootView, false);
            notifyContent(contentView.getLayoutParams());
        }
    }

    @Override
    public void setContentView(View view) {
        if (titleStyle() == TITLE_NONE) {
            super.setContentView(view);
        } else {
            contentView = view;
            notifyContent(view.getLayoutParams());
        }
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (titleStyle() == TITLE_NONE) {
            super.setContentView(view, params);
        } else {
            contentView = view;
            notifyContent(params);
        }
    }
    
    private void notifyContent(ViewGroup.LayoutParams params) {
        switch (titleStyle()) {
            case TITLE_ABOVE_CONTENT:
            default:
                // content置空
                int childCount = rootView.getChildCount();
                if (childCount > 2) {
                    rootView.removeViewAt(childCount - 1);
                }
                if (contentView != null) {// 添加新的content
                    rootView.addView(contentView, params);
                }
        }
    }

在onCreate中初始化rootView

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        thisActivity = this;
        inflater = LayoutInflater.from(thisActivity);
        if (titleStyle() != TITLE_NONE) {
            buildRootViewByStyle();
        }
    }

    // 初始化rootView
    private void buildRootViewByStyle() {
        switch (titleStyle()) {
            case TITLE_ABOVE_CONTENT:
            default:
                LinearLayout ll = new LinearLayout(thisActivity);
                ll.setOrientation(LinearLayout.VERTICAL);
                rootView = ll;
        }
        super.setContentView(rootView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    }

是否开启lib id

    
    
    
    
    /**
     * @return true 自动检索相关ids并做响应操作 false 不做固定id检索
     */
    protected boolean libTitleIdsEnable() {
        return false;
    }
    /* 在标题初始化后 */
    // init toolbar
    if (libTitleIdsEnable()) {
            try {
                tv_title = findViewById(R.id.TitleActivity_id_title);
                setCustomTitle();
            } catch (ClassCastException e) {
                throw new RuntimeException("title must instanceof TextView");
            }
            btn_back = findViewById(R.id.TitleActivity_id_back);
            if (btn_back != null) {
                if (backListener != null) {
                    btn_back.setOnClickListener(backListener);
                } else {
                    btn_back.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            onBackPressed();
                        }
                    });
                }
            }
        }

对外提供修改方法

    protected Toolbar getToolbar() {
        return toolbar;
    }

    private CharSequence titleCharSequence;
    private int titleResId = -1;

    protected void setCustomTitle(CharSequence title) {
        this.titleCharSequence = title;
        this.titleResId = -1;
        setCustomTitle();
    }

    protected void setCustomTitle(int title) {
        this.titleResId = title;
        this.titleCharSequence = null;
        setCustomTitle();
    }

    private void setCustomTitle() {
        if (tv_title != null) {
            if (titleCharSequence != null) {
                tv_title.setText(titleCharSequence);
            } else if (titleResId != -1) {
                tv_title.setText(titleResId);
            }
        }
    }

    private View.OnClickListener backListener;

    protected void setOnBackClickListener(View.OnClickListener listener) {
        backListener = listener;
        if (btn_back != null) {
            btn_back.setOnClickListener(listener);
        }
    }

使用方法

    
    android:id="@id/TitleActivity_id_title"
public class TestTitleActivity extends TitleActivity {

    @Override
    protected boolean libTitleIdsEnable() {
        return true;
    }
    
    @Override
    protected int titleStyle() {
        return TITLE_ABOVE_CONTENT;
    }


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setTitleView(R.layout.layout_title);
        setContentView(R.layout.activity_title_test);
         // TODO: do other
         setCustomTitle("HaHaHa");// 设置title
    }
}

让TitleStyle优雅起来

为TitleStyle添加抽象工厂模式

创建接口

public interface ITitleStyle {

    boolean hasTitle();// 是否包括标题
    boolean bindSystemActionBar();// Toolbar是否绑定系统ActionBar
    ViewGroup onBuildRootViewByStyle(Context context);// 创建rootView
    void onTitleLayoutSet(ViewGroup rootView, View titleView, View contentView);// 放置标题布局
    void onContentLayoutSet(ViewGroup rootView, View titleView, View contentView, ViewGroup.LayoutParams params);// 放置内容布局位置
    
}

创建默认Style并实现接口

/**
 * 线性布局
 * 内容在标题下方
 * 这里的逻辑是从BaseAcitivity中抽离的
 */
public class DefaultTitleStyle implements ITitleStyle {

    @Override
    public boolean hasTitle() {
        return true;
    }

    @Override
    public boolean bindSystemActionBar() {
        return true;
    }

    @Override
    public void onTitleLayoutSet(ViewGroup rootView, View titleView, View contentView) {
        rootView.removeAllViews();
        rootView.addView(titleView);
        if (contentView != null) {
            rootView.addView(contentView);
        }
    }

    @Override
    public void onContentLayoutSet(ViewGroup rootView, View titleView, View contentView, ViewGroup.LayoutParams params) {
// content置空
        int childCount = rootView.getChildCount();
        if (childCount > 2) {
            rootView.removeViewAt(childCount - 1);
        }
        if (contentView != null) {// 添加新的content
            rootView.addView(contentView, params);
        }
    }

    @Override
    public ViewGroup onBuildRootViewByStyle(Context context) {
        LinearLayout ll = new LinearLayout(context);
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }

}

于是,BaseAcitivity中的实现变为

/**
 * 自定义标题的BaseActivity
 * 使用时需配置style Theme.AppCompat.NoActionBar
 * setTitleView 设置标题
 * setContentView 设置内容
 * 复写titleStyle 设置标题风格
 */
public class TitleActivity extends AppCompatActivity {
    ……
    private ViewGroup rootView;
    private View titleView;
    private Toolbar toolbar;
    private View contentView;
    ……

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ……
        titleStyle = titleStyle();
        if (titleStyle.hasTitle()) {
            rootView = titleStyle.onBuildRootViewByStyle(thisActivity);
            super.setContentView(rootView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        }
    }

    protected void setTitleView(int layoutResID) {
        if (!titleStyle.hasTitle()) {
            titleView = inflater.inflate(layoutResID, null);
        } else {
            titleView = inflater.inflate(layoutResID, rootView, false);
            titleStyle.onTitleLayoutSet(rootView, titleView, contentView);
        }
        // init toolbar
        if (titleView instanceof Toolbar) {
            toolbar = (Toolbar) titleView;
            if (titleStyle.bindSystemActionBar()) {
                setSupportActionBar(toolbar);
            }
        } else if (libTitleIdsEnable()) {
            toolbar = findViewById(R.id.TitleActivity_id_Toolbar);
            if (titleStyle.bindSystemActionBar()) {
                setSupportActionBar(toolbar);
            }
        }
        // init title and back button
        ……
    }

    @Override
    public void setContentView(int layoutResID) {
        if (!titleStyle.hasTitle()) {
            super.setContentView(layoutResID);
        } else {
            contentView = inflater.inflate(layoutResID, rootView, false);
            titleStyle.onContentLayoutSet(rootView, titleView, contentView, contentView.getLayoutParams());
        }
    }
    
    ……

    /**
     * 标题风格
     */
    protected ITitleStyle titleStyle() {
        return new DefaultTitleStyle();
    }
    
    ……

}

与Activity解耦

那么,当使用时,如果遇到以存在BaseAcitivity,无法继承怎么办?哼,一点也不优雅

封装LayoutHandler

public class LayoutHandler {

    protected AppCompatActivity thisActivity;
    private LayoutInflater inflater;

    private ITitleStyle titleStyle;
//    protected static final int TITLE_BELOW_CONTENT = 2;// 标题在底部,内容在上方
//    protected static final int TITLE_FLOAT_TOP = 3;// 内容铺满全局,标题悬浮在内容上方
//    protected static final int TITLE_FLOAT_BOTTOM = 3;// 内容铺满全局,标题悬浮在内容下方
//    protected static final int TITLE_CONTENT_SCROLL = 4;// 标题随内容滚动
    private boolean libTitleIdsEnable;

    private ViewGroup rootView;
    private View titleView;
    private Toolbar toolbar;
    private View contentView;
    private TextView tv_title;
    private View.OnClickListener backListener;

    private CharSequence titleCharSequence;
    private int titleResId = -1;
    
    private View btn_back;


    private LayoutHandler() {
    }

    public boolean onCreate() {
        if (titleStyle.hasTitle()) {
            rootView = titleStyle.onBuildRootViewByStyle(thisActivity);
            return true;
        }
        return false;
    }

    public ViewGroup getRootView() {
        return rootView;
    }

    protected Toolbar getToolbar() {
        return toolbar;
    }

    public void setTitleView(int layoutResID) {
        if (!titleStyle.hasTitle()) {
            titleView = inflater.inflate(layoutResID, null);
        } else {
            titleView = inflater.inflate(layoutResID, rootView, false);
            titleStyle.onTitleLayoutSet(rootView, titleView, contentView);
        }
        // init toolbar
        if (titleView instanceof Toolbar) {
            toolbar = (Toolbar) titleView;
            if (titleStyle.bindSystemActionBar()) {
                thisActivity.setSupportActionBar(toolbar);
            }
        } else if (libTitleIdsEnable) {
            toolbar = thisActivity.findViewById(R.id.TitleActivity_id_Toolbar);
            if (titleStyle.bindSystemActionBar()) {
                thisActivity.setSupportActionBar(toolbar);
            }
        }
        // init title and back button
        if (libTitleIdsEnable) {
            try {
                tv_title = thisActivity.findViewById(R.id.TitleActivity_id_title);
                setCustomTitle();
            } catch (ClassCastException e) {
                throw new RuntimeException("title must instanceof TextView");
            }
            btn_back = thisActivity.findViewById(R.id.TitleActivity_id_back);
            if (btn_back != null) {
                if (backListener != null) {
                    btn_back.setOnClickListener(backListener);
                } else {
                    btn_back.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            thisActivity.onBackPressed();
                        }
                    });
                }
            }
        }
    }

    public boolean setContentView(View view, ViewGroup.LayoutParams params) {
        if (titleStyle.hasTitle()) {
            contentView = view;
            titleStyle.onContentLayoutSet(rootView, titleView, contentView, params);
            return true;
        }
        return false;
    }

    public boolean setContentView(View view) {
        if (titleStyle.hasTitle()) {
            contentView = view;
            titleStyle.onContentLayoutSet(rootView, titleView, contentView, contentView.getLayoutParams());
            return true;
        }
        return false;
    }

    public boolean setContentView(int layoutResID) {
        if (titleStyle.hasTitle()) {
            contentView = inflater.inflate(layoutResID, rootView, false);
            titleStyle.onContentLayoutSet(rootView, titleView, contentView, contentView.getLayoutParams());
            return true;
        }
        return false;
    }

    protected void setCustomTitle(CharSequence title) {
        this.titleCharSequence = title;
        this.titleResId = -1;
        setCustomTitle();
    }

    protected void setCustomTitle(int title) {
        this.titleResId = title;
        this.titleCharSequence = null;
        setCustomTitle();
    }

    private void setCustomTitle() {
        if (tv_title != null) {
            if (titleCharSequence != null) {
                tv_title.setText(titleCharSequence);
            } else if (titleResId != -1) {
                tv_title.setText(titleResId);
            }
        }
    }
    
    public void setOnBackClickListener(View.OnClickListener listener) {
        this.backListener = listener;
        if (btn_back != null) {
            btn_back.setOnClickListener(listener);
        }
    }

}

为LayoutHandler添加建造者模式

    public static class Builder {

        LayoutHandler handler;

        public Builder() {
            handler = new LayoutHandler();
        }

        public Builder setActivity(AppCompatActivity act) {
            handler.thisActivity = act;
            handler.inflater = LayoutInflater.from(act);
            return this;
        }

        public Builder setTitleStyle(ITitleStyle titleStyle) {
            handler.titleStyle = titleStyle;
            return this;
        }

        public Builder setLibTitleIdsEnable(boolean libTitleIdsEnable) {
            handler.libTitleIdsEnable  = libTitleIdsEnable;
            return this;
        }

        public LayoutHandler build() {
            return handler;
        }

    }

于是BaseActivity简化为

/**
 * 自定义标题的BaseActivity
 * 使用时需配置style Theme.AppCompat.NoActionBar
 * setTitleView 设置标题
 * setContentView 设置内容
 * 复写titleStyle 设置标题风格
 */
public class TitleActivity extends AppCompatActivity {
    // 获取自身实力的引用,防止代码里出现类似BaseActivity.this这种调用方式(个人习惯,查找类在哪里被引用的时候不乱)
    protected TitleActivity thisActivity;
    private LayoutHandler layoutHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        thisActivity = this;
        layoutHandler = new LayoutHandler.Builder()
                .setActivity(thisActivity)
                .setTitleStyle(titleStyle())
                .setLibTitleIdsEnable(libTitleIdsEnable())
                .build();
        if (layoutHandler.onCreate()) {
            super.setContentView(layoutHandler.getRootView(), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        }
    }

    @Override
    public void setContentView(int layoutResID) {
        if (!layoutHandler.setContentView(layoutResID)) {
            super.setContentView(layoutResID);
        }
    }

    @Override
    public void setContentView(View view) {
        if (!layoutHandler.setContentView(view)) {
            super.setContentView(view);
        }
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (!layoutHandler.setContentView(view, params)) {
            super.setContentView(view, params);
        }
    }

    /**
     * 标题风格
     */
    protected ITitleStyle titleStyle() {
        return new DefaultTitleStyle();
    }

    /**
     * @return true 自动检索相关ids并做响应操作 false 不做固定id检索
     */
    protected boolean libTitleIdsEnable() {
        return false;
    }
    /**
     * 设置标题布局
     */
    protected void setTitleView(int layoutResID) {
        layoutHandler.setTitleView(layoutResID);
    }

    protected Toolbar getToolbar() {
        return layoutHandler.getToolbar();
    }

    /**
     * 设置标题
     */
    protected void setCustomTitle(CharSequence title) {
        layoutHandler.setCustomTitle(title);
    }
    /**
     * 设置标题
     */
    protected void setCustomTitle(int title) {
        layoutHandler.setCustomTitle(title);
    }

    /**
     * id为TitleActivity_id_back的view被点击的回调
     */
    protected void setOnBackClickListener(View.OnClickListener listener) {
        layoutHandler.setOnBackClickListener(listener);
    }
    
}

你可能感兴趣的:(BaseActivity自定义多样化标题布局)