Android 5.x Theme 與 ToolBar 實戰

轉載請標明出處:
http://blog.csdn.net/lmj623565791/article/details/45303349
本文出自:【張鴻洋的博客】css

一、概述

隨着Material Design的逐漸的普及,業內也有不少具備分享精神的夥伴翻譯了material design specification ,中文翻譯地址:Material Design 中文版。So,咱們也開始Android 5.x相關的blog,那麼首先了解的固然是其主題的風格以及app bar。html

固然,5.x普及可能還須要一段時間,因此咱們仍是儘量的去使用兼容包支持低版本的設備。java

ps:本博客使用:android

  • compileSdkVersion 22
  • buildToolsVersion 「22.0.1」
  • compile ‘com.android.support:appcompat-v7:22.1.1’
  • 突然發現ActionBarActivity被棄用了,推薦使用AppCompatActivity,相關blog地址:Android Support Library 22.1

二、Material Design的Theme

md的主題有:git

  • @android:style/Theme.Material (dark version)
  • @android:style/Theme.Material.Light (light version)
  • @android:style/Theme.Material.Light.DarkActionBar

與之對應的Compat Theme:github

  • Theme.AppCompat
  • Theme.AppCompat.Light
  • Theme.AppCompat.Light.DarkActionBar

(1)個性化 Color Palette

咱們能夠根據咱們的app的風格,去定製Color Palette(調色板),重點有如下幾個屬性:微信

<resources>
    <!-- Base application theme. -->
    <style name="AppBaseTheme" parent="Theme.AppCompat"> <!-- customize the color palette --> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style>
</resources>
  • colorPrimary 對應ActionBar的顏色。
  • colorPrimaryDark對應狀態欄的顏色
  • colorAccent 對應EditText編輯時、RadioButton選中、CheckBox等選中時的顏色。

與之對應的圖:markdown

metarial design的theme容許咱們去設置status bar的顏色,若是你項目的最小支持版本爲5.0,那麼你可使用android:Theme.Material,設置android:statusBarColor。固然了這種狀況目前來講比較少,因此咱們多數使用的是Theme.AppCompat,經過設置android:colorPrimaryDark.來設置status bar顏色。(ps:默認狀況下,android:statusBarColor的值繼承自android:colorPrimaryDark).app

對於5.0如下的設備,目前colorPrimaryDark沒法去個性化狀態欄的顏色;底部的navagationBar可能也不同,更別說設置顏色了。ide

下面寫個簡單的Demo去測試下。

(2)測試效果

values/styles.xml

<resources>
    <!-- Base application theme. -->

    <style name="AppTheme" parent="AppBaseTheme"> </style>

    <style name="AppBaseTheme" parent="Theme.AppCompat.Light"> <!-- customize the color palette --> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style>


</resources>

values-v21/styles.xml

<resources>

    <style name="AppTheme" parent="AppBaseTheme"> <item name="android:statusBarColor">@color/material_blue_700</item> </style>


</resources>

values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="material_blue_500">#009688</color>
    <color name="material_blue_700">#00796B</color>
    <color name="material_green_A200">#FD87A9</color>
</resources>

能夠看到:colorAccent也就是圖中的粉色,EditText正在輸入時,RadioButton選中時的顏色。ps:5.0如下設備,狀態欄顏色不會變化。

三、ToolBar的使用

衆所周知,在使用ActionBar的時候,一堆的問題:這個文字能不能定製,位置能不能改變,圖標的間距怎麼控制神馬的,由此暴露出了ActionBar設計的不靈活。爲此官方提供了ToolBar,而且提供了supprot library用於向下兼容。Toolbar之因此靈活,是由於它其實就是一個ViewGroup,咱們在使用的時候和普通的組件同樣,在佈局文件中聲明。

(1)ToolBar的引入

既然準備用ToolBar,首先看看如何將其引入到app中。

1)隱藏本來的ActionBar

隱藏能夠經過修改咱們繼承的主題爲:Theme.AppCompat.Light.NoActionBar,固然也能夠經過設置如下屬性完成:

<item name="windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>

咱們這裏選擇前者:

<style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- customize the color palette --> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style>
2)在佈局文件中聲明
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto">

    <android.support.v7.widget.Toolbar  android:id="@+id/id_toolbar" android:layout_height="wrap_content" android:layout_width="match_parent" />

    <android.support.v7.widget.GridLayout  android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" app:useDefaultMargins="true" app:columnCount="3">


        <TextView  android:text="First Name:" app:layout_gravity="right" />

        <EditText  android:ems="10" app:layout_columnSpan="2" />

        <TextView  android:text="Last Name:" app:layout_column="0" app:layout_gravity="right" />

        <EditText  android:ems="10" app:layout_columnSpan="2" />


        <TextView  android:text="Visit Type:" app:layout_column="0" app:layout_gravity="right" />

        <RadioGroup app:layout_columnSpan="2">

            <RadioButton  android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Business" />


            <RadioButton  android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Social" />

        </RadioGroup>

        <Button  android:text="Ok" app:layout_column="1" />

        <Button  android:text="Cancel" app:layout_column="2" />

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

</LinearLayout>

ok,這裏咱們也貼出來上面圖片的效果的xml,使用GridLayout實現的,有興趣的能夠研究下。能夠看到咱們在佈局文件中定義了ToolBar。

3)代碼中設定
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);
        setSupportActionBar(toolbar);
    }

ok,基本就是先隱藏ActionBar,而後在佈局文件中聲明,最後代碼中設定一下。如今看一下效果圖:

能夠看到咱們的ToolBar顯示出來了,默認的Title爲ToolBar,可是這個樣式實在是不敢恭維,下面看咱們如何定製它。

(2)定製ToolBar

首先給它一個nice的背景色,還記得前面的colorPrimary麼,用於控制ActionBar的背景色的。固然這裏咱們的ToolBar就是一個普通的ViewGroup在佈局中,因此咱們直接使用background就好,值能夠爲:?attr/colorPrimary使用主題中定義的值。

ToolBar中包含Nav Icon , Logo , Title , Sub Title , Menu Items 。

咱們能夠經過代碼設置上述ToolBar中的控件:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);

        // App Logo
        toolbar.setLogo(R.mipmap.ic_launcher);
        // Title
        toolbar.setTitle("App Title");
        // Sub Title
        toolbar.setSubtitle("Sub title");

        setSupportActionBar(toolbar);
        //Navigation Icon
        toolbar.setNavigationIcon(R.drawable.ic_toc_white_24dp);
    }

可選方案固然若是你喜歡,也能夠在佈局文件中去設置部分屬性:

<android.support.v7.widget.Toolbar  android:id="@+id/id_toolbar" app:title="App Title" app:subtitle="Sub Title" app:navigationIcon="@drawable/ic_toc_white_24dp" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" android:layout_width="match_parent" android:background="?attr/colorPrimary"/>

至於Menu Item,依然支持在menu/menu_main.xml去聲明,而後複寫onCreateOptionsMenuonOptionsItemSelected便可。

可選方案也能夠經過toolbar.setOnMenuItemClickListener實現點擊MenuItem的回調。

toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                return false;
            }
        });

效果圖:

關於字體的樣式,能夠在佈局文件設置屬性app:titleTextAppearanceapp:subtitleTextAppearance或者代碼setTitleTextAppearancesetSubTitleTextAppearance設置。

四、實戰

簡單介紹了Toolbar之後呢,咱們決定作點有意思的事,整合ToolBar,DrawerLayout,ActionBarDrawerToggle寫個實用的例子,效果圖以下:

ok,簡單處理了下橫縱屏幕的切換。接下來看代碼實現。

  • 大體思路

總體實現仍是比較容易的,首先須要引入DrawerLayout(若是你對DrawerLayout不瞭解,能夠參考
Android DrawerLayout 高仿QQ5.2雙向側滑菜單),而後去初始化mActionBarDrawerToggle,mActionBarDrawerToggle其實是個DrawerListener,設置mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);就已經可以實現上面點擊Nav Icon切換效果了。固然了細節仍是挺多的。

咱們的效果圖,左側菜單爲Fragment,內容區域爲Fragment,點擊左側菜單切換內容區域的Fragment便可。關於Fragment的知識,能夠查看:Android Fragment 你應該知道的一切

  • 佈局文件
    activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent" android:background="#ffffffff" xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--app:subtitle="Sub Title"-->
    <android.support.v7.widget.Toolbar  android:id="@+id/id_toolbar" app:title="App Title" app:navigationIcon="@drawable/ic_toc_white_24dp" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" android:layout_width="match_parent" android:background="?attr/colorPrimary" />

    <android.support.v4.widget.DrawerLayout  android:id="@+id/id_drawerlayout" android:layout_width="match_parent" android:layout_height="match_parent">

        <FrameLayout  android:id="@+id/id_content_container" android:layout_width="match_parent" android:layout_height="match_parent"></FrameLayout>

        <FrameLayout  android:id="@+id/id_left_menu_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="left" android:background="#ffffffff"></FrameLayout>


    </android.support.v4.widget.DrawerLayout>


</LinearLayout>

DrawerLayout中包含兩個FrameLayout,分別放內容區域和左側菜單的Fragment。

  • LeftMenuFragment
package com.zhy.toolbar;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

/** * Created by zhy on 15/4/26. */
public class LeftMenuFragment extends ListFragment {

    private static final int SIZE_MENU_ITEM = 3;

    private MenuItem[] mItems = new MenuItem[SIZE_MENU_ITEM];

    private LeftMenuAdapter mAdapter;

    private LayoutInflater mInflater;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mInflater = LayoutInflater.from(getActivity());

        MenuItem menuItem = null;
        for (int i = 0; i < SIZE_MENU_ITEM; i++) {
            menuItem = new MenuItem(getResources().getStringArray(R.array.array_left_menu)[i], false, R.drawable.music_36px, R.drawable.music_36px_light);
            mItems[i] = menuItem;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        setListAdapter(mAdapter = new LeftMenuAdapter(getActivity(), mItems));

    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        if (mMenuItemSelectedListener != null) {
            mMenuItemSelectedListener.menuItemSelected(((MenuItem) getListAdapter().getItem(position)).text);
        }

        mAdapter.setSelected(position);

    }


    //選擇回調的接口
    public interface OnMenuItemSelectedListener {
        void menuItemSelected(String title);
    }
    private OnMenuItemSelectedListener mMenuItemSelectedListener;

    public void setOnMenuItemSelectedListener(OnMenuItemSelectedListener menuItemSelectedListener) {
        this.mMenuItemSelectedListener = menuItemSelectedListener;
    }



}

繼承自ListFragment,主要用於展現各個Item,提供了一個選擇Item的回調,這個須要在Activity中去註冊處理。

  • LeftMenuAdapter
package com.zhy.toolbar;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

/** * Created by zhy on 15/4/26. */
public class LeftMenuAdapter extends ArrayAdapter<MenuItem> {


    private LayoutInflater mInflater;

    private int mSelected;


    public LeftMenuAdapter(Context context, MenuItem[] objects) {
        super(context, -1, objects);

        mInflater = LayoutInflater.from(context);

    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {


        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item_left_menu, parent, false);
        }

        ImageView iv = (ImageView) convertView.findViewById(R.id.id_item_icon);
        TextView title = (TextView) convertView.findViewById(R.id.id_item_title);
        title.setText(getItem(position).text);
        iv.setImageResource(getItem(position).icon);
        convertView.setBackgroundColor(Color.TRANSPARENT);

        if (position == mSelected) {
            iv.setImageResource(getItem(position).iconSelected);
            convertView.setBackgroundColor(getContext().getResources().getColor(R.color.state_menu_item_selected));
        }

        return convertView;
    }

    public void setSelected(int position) {
        this.mSelected = position;
        notifyDataSetChanged();
    }


}


package com.zhy.toolbar;

public class MenuItem {

    public MenuItem(String text, boolean isSelected, int icon, int iconSelected) {
        this.text = text;
        this.isSelected = isSelected;
        this.icon = icon;
        this.iconSelected = iconSelected;
    }

    boolean isSelected;
    String text;
    int icon;
    int iconSelected;
}

Adapter沒撒說的~~提供了一個setSection方法用於設置選中Item的樣式什麼的。
接下來看ContentFragment,僅僅只是一個TextView而已,因此代碼也比較easy。

package com.zhy.toolbar;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/** * Created by zhy on 15/4/26. */
public class ContentFragment extends Fragment {

    public static final String KEY_TITLE = "key_title";
    private String mTitle;

    public static ContentFragment newInstance(String title) {
        ContentFragment fragment = new ContentFragment();
        Bundle bundle = new Bundle();
        bundle.putString(KEY_TITLE, title);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {

        TextView tv = new TextView(getActivity());
        String title = (String) getArguments().get(KEY_TITLE);
        if (!TextUtils.isEmpty(title))
        {
            tv.setGravity(Gravity.CENTER);
            tv.setTextSize(40);
            tv.setText(title);
        }

        return tv;
    }
}

提供newInstance接收一個title參數去實例化它。

最後就是咱們的MainActivity了,負責管理各類Fragment。

  • MainActivity
package com.zhy.toolbar;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Gravity;

import java.util.List;


public class MainActivity extends AppCompatActivity {

    private ActionBarDrawerToggle mActionBarDrawerToggle;

    private DrawerLayout mDrawerLayout;

    private Toolbar mToolbar;

    private LeftMenuFragment mLeftMenuFragment;
    private ContentFragment mCurrentFragment;

    private String mTitle;

    private static final String TAG = "com.zhy.toolbar";
    private static final String KEY_TITLLE = "key_title";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initToolBar();
        initViews();

        //恢復title
        restoreTitle(savedInstanceState);

        FragmentManager fm = getSupportFragmentManager();
        //查找當前顯示的Fragment
        mCurrentFragment = (ContentFragment) fm.findFragmentByTag(mTitle);

        if (mCurrentFragment == null) {
            mCurrentFragment = ContentFragment.newInstance(mTitle);
            fm.beginTransaction().add(R.id.id_content_container, mCurrentFragment, mTitle).commit();
        }

        mLeftMenuFragment = (LeftMenuFragment) fm.findFragmentById(R.id.id_left_menu_container);
        if (mLeftMenuFragment == null) {
            mLeftMenuFragment = new LeftMenuFragment();
            fm.beginTransaction().add(R.id.id_left_menu_container, mLeftMenuFragment).commit();
        }

        //隱藏別的Fragment,若是存在的話
        List<Fragment> fragments = fm.getFragments();
        if (fragments != null)

            for (Fragment fragment : fragments) {
                if (fragment == mCurrentFragment || fragment == mLeftMenuFragment) continue;
                fm.beginTransaction().hide(fragment).commit();
            }

        //設置MenuItem的選擇回調
        mLeftMenuFragment.setOnMenuItemSelectedListener(new LeftMenuFragment.OnMenuItemSelectedListener() {
            @Override
            public void menuItemSelected(String title) {

                FragmentManager fm = getSupportFragmentManager();
                ContentFragment fragment = (ContentFragment) getSupportFragmentManager().findFragmentByTag(title);
                if (fragment == mCurrentFragment) {
                    mDrawerLayout.closeDrawer(Gravity.LEFT);
                    return;
                }

                FragmentTransaction transaction = fm.beginTransaction();
                transaction.hide(mCurrentFragment);

                if (fragment == null) {
                    fragment = ContentFragment.newInstance(title);
                    transaction.add(R.id.id_content_container, fragment, title);
                } else {
                    transaction.show(fragment);
                }
                transaction.commit();

                mCurrentFragment = fragment;
                mTitle = title;
                mToolbar.setTitle(mTitle);
                mDrawerLayout.closeDrawer(Gravity.LEFT);


            }
        });

    }

    private void restoreTitle(Bundle savedInstanceState) {
        if (savedInstanceState != null)
            mTitle = savedInstanceState.getString(KEY_TITLLE);

        if (TextUtils.isEmpty(mTitle)) {
            mTitle = getResources().getStringArray(
                    R.array.array_left_menu)[0];
        }

        mToolbar.setTitle(mTitle);
    }


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(KEY_TITLLE, mTitle);
    }

    private void initToolBar() {

        Toolbar toolbar = mToolbar = (Toolbar) findViewById(R.id.id_toolbar);
        // App Logo
        // toolbar.setLogo(R.mipmap.ic_launcher);
        // Title
        toolbar.setTitle(getResources().getStringArray(R.array.array_left_menu)[0]);
        // Sub Title
        // toolbar.setSubtitle("Sub title");

// toolbar.setTitleTextAppearance();


        setSupportActionBar(toolbar);


        //Navigation Icon
        toolbar.setNavigationIcon(R.drawable.ic_toc_white_24dp);
        /* toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { return false; } });*/

    }

    private void initViews() {

        mDrawerLayout = (DrawerLayout) findViewById(R.id.id_drawerlayout);

        mActionBarDrawerToggle = new ActionBarDrawerToggle(this,
                mDrawerLayout, mToolbar, R.string.open, R.string.close);
        mActionBarDrawerToggle.syncState();
        mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);


    }
}

內容區域的切換是經過Fragment hide和show實現的,畢竟若是用replace,若是Fragment的view結構比較複雜,可能會有卡頓。固然了,注意每一個Fragment佔據的內存狀況,若是內存不足,可能須要改變實現方式。
對於旋轉屏幕或者應用長時間置於後臺,Activity重建的問題,作了簡單的處理。

對了,寫佈局的時候,能夠儘量的去考慮 Material design 的規範。

五、參考資料

源碼下載
羣號: 264950424
微信公衆號:hongyangAndroid
(歡迎關注,第一時間推送博文信息)

相關文章
相關標籤/搜索