Builder設計模式構建NavigationBar

1. 概述


前邊我們通過兩節課來對Builder設計模式應該都有了一個了解和認識,如果對Builder設計模式還不是特別了解的,可以先去看下我前邊的兩篇文章
Builder設計模式構建自定義萬能的Dialog
從源碼角度分析AlertDialog
那么這節課我們的內容還是對Builder設計模式的一個學習,我們學習下通過Builder設計模式如何去構建NavigationBar,來實現我們的導航欄。

2. 導航欄的幾種寫法


我們在項目中的導航欄一般都會有以下幾種寫法:
1>:直接在布局文件中寫一個 layout_title,然后通過include直接引入到每個布局文件中;
2>:用系統的 ToolBar;
3>:自定義View;
當然除過以上的幾種寫法,肯定也會有其他的寫法,在這里就不一一列舉了,那么我們下邊就針對于 使用 Builder設計模式構建NavigationBar方式來分析下
一般像頂部導航欄最好添加一些MetrialDesign的一些動畫效果會比較好看一些。

3. 代碼如下

頭部的基類AbsNavigationBar代碼如下:

/**
 * Email: 2185134304@qq.com
 * Created by JackChen 2018/4/5 18:43
 * Version 1.0
 * Params:
 * Description:   頭部的基類
 *                可能在整個項目中像這樣的布局用的地方不是很多,但是必須要有,
 *                所以這里為了考慮頭部可能出現的所有情況,也把基本的布局考慮進去
*/

public abstract class AbsNavigationBar<P extends AbsNavigationBar.Builder.AbsNavigationParams> implements INavigationBar {

    private P mParams;

    private View mNavigationView;

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


    public P getParams() {
        return mParams;
    }


    /**
     * 設置文本
     * @param viewId
     * @param text
     */
    protected void setText(int viewId, String text) {
        TextView tv = findViewById(viewId);
        if(!TextUtils.isEmpty(text)){
            tv.setVisibility(View.VISIBLE);
            tv.setText(text);
        }
    }

    /**
     * 設置點擊
     * @param viewId
     * @param listener
     */
    protected void setOnClickListener(int viewId,View.OnClickListener listener){
        findViewById(viewId).setOnClickListener(listener);
    }


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

    /**
     * 綁定和創建View
     */
    private void createAndBindView() {
        // 1. 創建View

        if(mParams.mParent == null){
            // 獲取activity的根布局,View源碼
            // 方法1:android.R.id.content就是 android.R.layout.screen_simple中的一個 FrameLayout的一個id
//            ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
//                    .findViewById(android.R.id.content);

            // 方法2:.getWindow().getDecorView() 表示直接加載的就是 android.R.layout.screen_simple的根布局,而根布局就是一個 LinearLayout
            // 所以不管 activity_main中的根布局是RelativeLayout、LinearLayout都可以
            ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
                    .getWindow().getDecorView() ;

            mParams.mParent = (ViewGroup) activityRoot.getChildAt(0);
            Log.e("TAG",mParams.mParent+"");
        }

        // 處理Activity的源碼,后面再去看


        if(mParams.mParent == null){
            return;
        }

        mNavigationView = LayoutInflater.from(mParams.mContext).
                inflate(bindLayoutId(), mParams.mParent, false);// 插件換膚

        // 2.添加
        mParams.mParent.addView(mNavigationView, 0);

        applyView();
    }

    // Builder  仿照系統寫的, 套路,活  AbsNavigationBar  Builder  參數Params
    public abstract static class Builder {

        public Builder(Context context, ViewGroup parent) {

        }

        public abstract AbsNavigationBar builder();


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

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

使用默認的頭部的 DefaultNavigationBar代碼如下:

public class DefaultNavigationBar<D extends
        DefaultNavigationBar.Builder.DefaultNavigationParams> extends
        AbsNavigationBar<DefaultNavigationBar.Builder.DefaultNavigationParams> {

    public DefaultNavigationBar(DefaultNavigationBar.Builder.DefaultNavigationParams 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);

        setOnClickListener(R.id.right_text, getParams().mRightClickListener);
        // 左邊 要寫一個默認的  finishActivity
        setOnClickListener(R.id.back,getParams().mLeftClickListener);
    }


    public static class Builder extends AbsNavigationBar.Builder {

        DefaultNavigationParams P;


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

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

        @Override
        public DefaultNavigationBar builder() {
            DefaultNavigationBar navigationBar = new DefaultNavigationBar(P);
            return navigationBar;
        }

        // 1. 設置所有效果

        public DefaultNavigationBar.Builder setTitle(String title) {
            P.mTitle = title;
            return this;
        }


        public DefaultNavigationBar.Builder setRightText(String rightText) {
            P.mRightText = rightText;
            return this;
        }

        /**
         * 設置右邊的點擊事件
         */
        public DefaultNavigationBar.Builder
        setRightClickListener(View.OnClickListener rightListener) {
            P.mRightClickListener = rightListener;
            return this;
        }

        /**
         * 設置左邊的點擊事件
         */
        public DefaultNavigationBar.Builder
        setLeftClickListener(View.OnClickListener rightListener) {
            P.mLeftClickListener = rightListener;
            return this;
        }

        /**
         * 設置右邊的圖片
         */
        public DefaultNavigationBar.Builder setRightIcon(int rightRes) {

            return this;
        }

        public static class DefaultNavigationParams extends
                AbsNavigationBar.Builder.AbsNavigationParams {


            // 2.所有效果放置
            public String mTitle;

            public String mRightText;

            // 后面還有一些通用的
            public View.OnClickListener mRightClickListener;

            public View.OnClickListener mLeftClickListener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 關閉當前Activity
                    ((Activity) mContext).finish();
                }
            };

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

在MainActivity中的 initTitle()中直接 一句代碼引用即可:

public class MainActivity extends BaseSkinActivity {


    @Override
    protected void setContentView() {
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void initData() {

    }

    @Override
    protected void initView() {

    }

    @Override
    protected void initTitle() {
        DefaultNavigationBar navigationBar = new
                DefaultNavigationBar.Builder(this)
                .setTitle("投稿")
                .builder();
    }
}

注意:

在AbsNavigationBar中的這兩種方法要注意,這兩種方法代表的意思是一樣的

// 方法1:android.R.id.content就是 android.R.layout.screen_simple中的一個 FrameLayout的一個id
// ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
// .findViewById(android.R.id.content);

// 方法2:.getWindow().getDecorView() 表示直接加載的就是 android.R.layout.screen_simple的根布局,而根布局就是一個 LinearLayout
// 所以不管 activity_main中的根布局是RelativeLayout、LinearLayout都可以
 ViewGroup activityRoot = (ViewGroup) ((Activity)(mParams.mContext))
        .getWindow().getDecorView() ;

 mParams.mParent = (ViewGroup) activityRoot.getChildAt(0);
            Log.e("TAG",mParams.mParent+"");

分析方法1:

方法1中的findViewById(android.R.id.content);是直接加載的是 系統的資源文件,即就是android.R.layout.screen_simple中的一個叫做 android.R.id.content的布局,它是一個FrameLayout,也就是說我們是把我們的 setContentView的布局其實是加載到 這個資源文件中的 android.R.id.content中的,它是一個 mContentParent,是一個 FrameLayout,也就是說我們是把 setContentView的布局是加載到 mContentParent中的,android.R.layout.screen_simple的布局文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

分析方法2:

方法2其實就是 加載的是DecorView,而DecorView中加載的就是 系統的資源文件,就是 android.R.layout.screen_simple,其實就是直接加載該布局文件的根布局 LinearLayout,這樣的話,不管 我們自己的 setContentView()中根布局是 LinearLayout還是RelativeLayout,都可以直接把布局加載到第0個位置

上邊的兩種方法其實考的就是 setContentView()的加載流程,如果不是很懂的,可以去看下我前邊的文章,setContentView的加載流程分析

代碼已上傳至github:
https://github.com/shuai999/EssayJoke_day_08.git

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內容