BaseActivity中封裝通用的Toolbar

Smilyyy發表於2017-04-28

之前寫過一篇關於如何使用Toolbar的文章,最近在搭建新專案時對Toolbar做了封裝。封裝的預期目標是隻在BaseActivity中引入Toolbar,然後子Activity通過繼承BaseActivity就能顯示Toolbar。接下來就看看如何實現這樣的功能。
因為我們使用toolbar作為titlebar,因此首先需要去掉Actionbar。在style檔案下修改預設Theme的parent為Theme.AppCompat.Light.NoActionBar便可以去掉預設的Actionbar,如下:

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

然後定義一個toolbar_layout的xml檔案,在toolbar中加入倆個TextView作為標題和子標題,如下:

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary"
    android:minHeight="?attr/actionBarSize">
    <!--自定義toolbar的title 和subtitle -->
    <TextView
        android:id="@+id/tv_right"
        style="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:maxLines="1"
        android:textColor="@color/white"
        android:text="right"
        android:paddingRight="10dp"
        android:layout_gravity="right" />

    <TextView
        android:id="@+id/tv_title"
        style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:lines="1"
        android:ellipsize="end"
        android:text="title"
        android:scrollHorizontally="true"
        android:textColor="@color/white"
        android:layout_gravity="center" />

</android.support.v7.widget.Toolbar>

接著在BaseActivity的佈局檔案中include進toolbar_layout,如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <include layout="@layout/toolbar_layout"/>

</LinearLayout>

這樣BaseActivity中就可以正常顯示出Toolbar了。那麼如何實現子Activity繼承BaseActivity而顯示toolbar呢?其實我們可以在BaseActivity中做文章。
首先在BaseActivity中宣告一個LinearLayout,然後把BaseActivity的佈局檔案新增到該LinearLayout中。我們知道子Activity通過setContentView()方法來關聯佈局檔案,因此我們可以在BaseActivity中去重寫setContentView()方法,在重寫的setContentView中把子類的佈局檔案也新增到事先宣告的LinearLayout中,接下來應該解決如何將這個LinearLayout與Activity關聯。查閱相關資料可以知道,可以通過 findViewById(android.R.id.content)拿到window的ViewGroup然後將剛才宣告的LinearLayout新增到這個ViewGroup中,這樣就可以在子Activity中顯示出BaseActivity中的Toolbar了。(具體原因可以查閱android.R.id.content和 DecorView)程式碼如下:

public abstract class BaseActivity extends AppCompatActivity{
     //the container of this activity layout and sub-activity layout
    private LinearLayout parentLinearLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initContentView(R.layout.activity_base);
        setContentView(getLayoutId());
        initToolBar();
        init();
    }
//  overwrite the function in sub-activity and return the layout id of sub-activity   
 protected abstract int getLayoutId();

 private void initContentView(@LayoutRes int layoutResID) {
        ViewGroup viewGroup = (ViewGroup) findViewById(android.R.id.content);
        viewGroup.removeAllViews();
        parentLinearLayout = new LinearLayout(this);
        parentLinearLayout.setOrientation(LinearLayout.VERTICAL);
        //  add parentLinearLayout in viewGroup
        viewGroup.addView(parentLinearLayout);
        //  add the layout of BaseActivity in parentLinearLayout
        LayoutInflater.from(this).inflate(layoutResID, parentLinearLayout, true);
    }
   /**
     * @param layoutResID  layout id of sub-activity
     */
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        //  added the sub-activity layout id in parentLinearLayout
        LayoutInflater.from(this).inflate(layoutResID, parentLinearLayout, true);

    }
}

上述程式碼通過initContentView()方法將BaseActivity中的佈局檔案新增到了宣告的parentLinearLayout中,通過setContentView()方法將子Activity的佈局也新增到了parentLinearLayout中,然後又將parentLinearLayout新增到了viewGroup中實現了Activity與佈局檔案的關聯。至此我們完成了第一步,使子Activity可以顯示出BaseActivity中的Toolbar佈局。
接下來,我們需要在BaseActivity中對Toolbar進行封裝,使其能夠更加方便的在子Activity中使用。比如新增toolbar回退鍵的監聽,新增是否顯示回退鍵的方法等。完整的BaseActivity如下:

public abstract class BaseActivity extends AppCompatActivity{
    //the container of this activity layout and sub-activity layout 
    private LinearLayout parentLinearLayout;
    private TextView mTvTitle;
    private TextView mTvRight;
    private Toolbar mToolbar;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initContentView(R.layout.activity_base);

        setContentView(getLayoutId());
        initToolBar();
        setBackIcon();
        init();
    }
//  overwrite the function in sub-activity and return the layout id of sub-activity 
 protected abstract int getLayoutId();

 private void initContentView(@LayoutRes int layoutResID) {
        ViewGroup viewGroup = (ViewGroup) findViewById(android.R.id.content);
        viewGroup.removeAllViews();
        parentLinearLayout = new LinearLayout(this);
        parentLinearLayout.setOrientation(LinearLayout.VERTICAL);
        viewGroup.addView(parentLinearLayout);
        //  add the layout of BaseActivity in parentLinearLayout
        LayoutInflater.from(this).inflate(layoutResID, parentLinearLayout, true);
    }
   /**
     * @param layoutResID  the layout id of sub Activity
     */
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        //  added the sub-activity layout id in parentLinearLayout
        LayoutInflater.from(this).inflate(layoutResID, parentLinearLayout, true);

    }

private void setBackIcon(){
        if (null != getToolbar() && isShowBacking()) {
            getToolbar().setNavigationIcon(R.drawable.icon_back);
            getToolbar().setNavigationOnClickListener((v) -> onBackPressed());
        }
    }

    /**
     * @return TextView in center
     */
    public TextView getToolbarTitle() {
        return mTvTitle;
    }

    /**
     * @return TextView on the right
     */
    public TextView getSubTitle() {
        return mTvRight;
    }

    /**
     * set Title
     * @param title
     */
    public void setToolBarTitle(CharSequence title) {
        if (mTvTitle != null) {
            mTvTitle.setText(title);
        } else {
            getToolbar().setTitle(title);
            setSupportActionBar(getToolbar());
        }
    }

    /**
     * the toolbar of this Activity
     * @return support.v7.widget.Toolbar.
     */
    public Toolbar getToolbar() {
        return (Toolbar) findViewById(R.id.toolbar);
    }

    /**
     * is show back icon,default is none。
     * you can override the function in subclass and return to true show the back icon
     * @return
     */
    protected boolean isShowBacking() {
        return true;
    }
}

最後我們可以在子Activity中去使用toolbar了。
我們讓MainActivity繼承BaseActivity,並在佈局檔案中新增一個button,切佈局檔案中沒有新增標題欄,佈局檔案的程式碼就不再貼出了。然後 重寫isShowBacing()方法,使其返回false,隱藏MainActivity的回退鍵。
程式碼如下:

public class MainActivity extends BaseActivity {
    @BindView(R.id.btn)
    Button mBtn1;
    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

     @Override
    protected boolean isShowBacking() {
        return false;
    }

@OnClick({R.id.btn})
    public void Onclick(View view){
        switch (view.getId()){
            case R.id.btn:
                startActivity(new Intent(this,TestActivity.class));
                break;
        }
    }
}

建立TestActivity並繼承BaseActivity,TestActivity的佈局檔案中不新增任何view。然後在TestActivity中給toolbar設定內容,如下:

public class TestActivity extends BaseActivity {

    @Override
    protected int getLayoutId() {
        return R.layout.activity_test;
    }

    @Override
    protected void init() {
        getToolbarTitle().setText("中間標題");
        getSubTitle().setText("右邊標題");
        Toolbar toolbar = getToolbar();
        toolbar.setLogo(R.mipmap.ic_launcher);
        toolbar.setNavigationIcon(R.drawable.back_white);
    }
}

效果如下圖所示

這裡寫圖片描述

相關文章