今天來講Android如何使用Toolbar並且加入Navigation Drawer,首先Navigation Drawer可以直接在Android Studio(以下簡稱AS)新建專案的時候,選擇 Navigation Drawer Activity範例來建立新專案。建立完成之後,AS會自動幫你產生下列檔案:

res/layout資料夾

  • activity_main.xml
  • fragment_main.xml
  • fragment_navigation_drawer.xml

java/package-name資料夾(package-name是你專案的package名稱)

  • MainActivity.java
  • NavigationDrawerFragment.java

接下來要開始修改這些檔案,加入Toolbar而且把Navigation Drawer改成Material Design,讓我們開始吧~


新增Toolbar Layout檔案

我們從Toolbar開始,先在build.gradle裡面加入

compile 'com.android.support:appcompat-v7:22.2.0'

讓gradle同步完後,建立新的layout檔案叫做toolbar.xml,裡面加入

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/Theme.AppCompat.Light.DarkActionBar"
    android:fitsSystemWindows="true"
    />
  • 記得是使用v7的Toolbar而非android.widget.Toolbar!!!
  • android:background="?attr/colorPrimary 這邊是使用@style/colorPrimary來設定背景顏色,這是Android 5.0以後的寫法。
  • android:fitsSystemWindows="true" 這是讓Toolbar可以向上延伸到狀態列,主要是用在Android 4.4可以使用透明的狀態列。

加入Toolbar到主介面

再來activity_main.xml裡面我們要把剛剛建立的toolbar.xml引入,所以用一個RelativeLayoutToolbar用include的方式和主要內容的FrameLayout包起來如下

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar"/>

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/toolbar"/>
    </RelativeLayout>

    <fragment
        android:id="@+id/navigation_drawer"
        android:name="com.moviebomber.ui.fragment.NavigationDrawerFragment"
        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        tools:layout="@layout/fragment_navigation_drawer"/>
</android.support.v4.widget.DrawerLayout>

修改style檔案

因為我們要用Toolbar取代Actionbar,所以需要把原本的styles.xml修改如下

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

改成

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/primary</item>
    <item name="colorPrimaryDark">@color/primary_dark</item>
    <item name="colorAccent">@color/accent</item>
    <item name="android:windowNoTitle">true</item>
    <item name="windowActionBar">false</item>
    <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
</style>

<style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
    <item name="spinBars">true</item>
    <item name="color">@android:color/white</item>
</style>
  • colorPrimary, colorPrimaryDak, colorAccent顏色對應關係可以見下圖,這是讓Android 5.0可以抓到對應的顏色。

  • android:windowNoTitle把視窗標題移除, windowActionBar不使用Actionbar。
  • drawerArrowStyle和下面DrawerArrowStyle是做出開啟Navigation Drawer漢堡變箭頭的動畫(如下圖)


在MainActivity.java使用Toolbar

MainActivity.java要加入Toolbar並且把Actionbar指向Toolbar,然後設定Statusbar為透明的。

public class MainActivity extends AppCompatActivity 
    implements NavigationDrawerFragment.NavigationDrawerCallbacks {

    @InjectView(R.id.toolbar)
    Toolbar mToolbar;

    /**
     * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
     */
    private NavigationDrawerFragment mNavigationDrawerFragment;

    /**
     * Used to store the last screen title. For use in {@link #restoreActionBar()}.
     */
    private CharSequence mTitle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Window w = getWindow(); // in Activity's onCreate() for instance

            w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        setContentView(R.layout.activity_main);
        ButterKnife.inject(this);
        this.setSupportActionBar(this.mToolbar);

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
        mTitle = getTitle();

        // Set up the drawer.

        mNavigationDrawerFragment.setUp(
                R.id.navigation_drawer,
                this.mToolbar,
                (DrawerLayout) findViewById(R.id.drawer_layout));
    }
}
  • 增加了mToolbar的成員,這邊是import android.support.v7.widget.Toolbar;,我們都是使用v7的Toolbar!
  • if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {那一段是把Statusbar設為透明的,讓colorPrimaryDark也可以在Android 4.4顯示出來,讓Toolbar可以延伸到Statusbar。
  • 使用setSupportActionBar()把Toolbar傳入。
  • mNavigationDrawerFragment.setUp(...)這方法要傳入Toolbar,所以下列的NavigationDrawlerFragment.java要改掉API。

NavigationDrawerFragment.java使用Toolbar

NavigationDrawerFragment.java要可以知道Toolbar的狀態,得知是否有按下漢堡,所以改動的地方如下

public void setUp(int fragmentId, Toolbar toolbar, DrawerLayout drawerLayout)  {
    mDrawerToggle = new ActionBarDrawerToggle(
                getActivity(),                    /* host Activity */
                mDrawerLayout,                    /* DrawerLayout object */
                toolbar,
                R.string.navigation_drawer_open,  /* "open drawer" description for accessibility */
                R.string.navigation_drawer_close  /* "close drawer" description for accessibility */
        )
} 
  • 原本setup()API多加了Toolbar當傳入參數。
  • ActionBarDrawerToggle要從v4換成v7的,並且把Toolbar傳入。

完成結果

  • 現在上面是Toolbar,不是Actionbar。

  • 展開功能表

完成收工!!

希望各位看完這篇文章之後,你可以勇敢的關掉Eclipse(除非你跟我一樣也有另外在寫Java就不用),下載安裝並開啟Android Studio來用!!
以下中英文摻插,用中文實在翻不出一個有感覺的字詞,就直接寫英文,然後Android Studio有時會簡寫成AS。
我盡量把截圖放上,所以圖多,目的在於讓你可以更了解。

你將會知道...

  • Part1. Android Studio是?
  • Part2. 特色功能。
  • Part3. Gradle簡單介紹。

Part1. Android Studio是?

  1. Android Studio本身是Google官方推出的Android IDE。(Eclipse Android ADT確定不再發佈更新了喔!!)
  2. 是以IntelliiJ IDEA為基礎去改造的工具。
  3. 採用Gradle build工具。
  4. 更多的介紹google一下就一堆,我就不再贅述。

Part2. 特色功能

  1. Auto Complete:比Eclipse ADT更強的(有自動學習功能,會自動辨識並且顯示最相關的提示)程式碼自動提示和自動完成功能。
  2. Build Variants: 這像功能可以幫你build不同功能版本的apk,例如:debug / release,或是你的app有分免費版和付費版,兩者功能不相同,都可以同時在build variant這功能來建置。
  3. Productivity Guild:在功能表的 [Help] -> [Productivity Guild] 可以開啟,裡面有列出一些能提高生產力的功能並且統計你使用的次數和最近一次的使用時間。


  4. 多裝置除錯:在測試App階段,允許選擇多個裝置,讓你可以一次同時測試不同的裝置。

  5. 多裝置、多theme的Layout Preview:在介面設計工具,除了有即時的預覽之外,也允許讓你同時預覽不同裝置的樣子,或是你可以直接套用不同的theme。

  6. 超多的plugins:跟Eclipse一樣擁有很多外掛可以安裝使用,安裝方法為開啟設定視窗Setting -> Plugins 來檢視。

  7. DDMS:開啟方法是左下的6: Android分頁標籤點下後會顯示,DDMS的左邊有工具列,包含了很多監測的工具,像是App截圖或錄影、記憶體使用情況、執行GC、Java Heap Dump...等。

  8. 監測記憶體使用狀況:從右下的Memory Monitor分頁標籤可以開啟記憶體監測的功能視窗,你就可以直接看到記憶體使用狀況的圖表。

  9. 建立不同裝置類型的專案:我們可以直接使用AS來開發手機、平板、TV、Wearable和Glass的專案。

  10. Code Tempate:在建立新專案或是新檔案的時候,我們可以選擇預先提供的程式碼樣板,像是不同類型的Activity、Fragment、或Service...等。

  11. Layout Tools Attributes:在AS新增的Tool Attributes主要是用在設計階段,最常出現在Layout的XML檔案裡面,像是tool:context出現在Activity的Layout.xml檔裡面,是紀錄該Layout.xml檔案是屬於某個Activity。 我覺得非常好用的還有 tools:text,我們一般如果想預覽TextView實際上套用文字的樣子,我們以前會在該TextView裡面加入android:text='我是測試文字,然後才能在Design Preview當中看到實際上的樣子,然後App要發怖時才把這個測試文字移除,現在如果直接加入`tools:text='我是測試文字',我們就可以直接在Layout Preview當中看到套用文字的介面實際樣子,但是在實際的App裡面就不會出現。記得,Tools Attributes是用在設計階段,只會在設計階段看到,完全不會影響到實際執行的樣子。

更多Tools Attributes介紹可以參考這篇的介紹。

  1. Resource Preview:在資源檔當中,我們可以直接預覽,例如:在activity_main.xml當中我們寫到@string/app_name,可以直接預覽到對應的文字是什麼,或是@color/text_current_position可以直接預覽顏色。

Part 3. Gradle簡單介紹

Gradle是一種依賴管理工具,基於Groovy語言,捨棄了基於XML繁瑣的配置方法,而採用Groovy的DSL語言。
而AS導入了Project v.s. Modules的概念,下面分別作簡單的講解:

Project:
1. 是一個完整的app專案。
2. 可以包含很多的modules。
Module:
1. 模組是一個獨立的元件,可以獨立的build、測試和除錯。
2. 在AS當中有三種模組:Java Library, Android Library, Android Application。

在AS當中project structure有些更動,和原本的eclipse專案結構有所不同,大家可以在下圖看到Project的根目錄底下就兩個Modules(粗體),頂層專案目錄下個別有build.gradlesettings.gradle兩個build files, 每個Modules也個別有一個build.gradle檔案(*紅字),下面在一一做簡單的介紹。

頂層settings.gradle

用途在於告訴gradle專案底下有哪些Modules要來build。

include ':app'
include ':iconicDroid'

如果今天你的Modules沒有在這個專案底下,而是在本機電腦的其他位置,你可以先[Import Module]加入,或是用下面語法來include進來。

project(':library1').projectDir = new File('other/place/on/the/computer')
頂層build.gradle

用途在於增加所有子專案或是所有模組的共同組態,例如你的remote repository、gradle的dependencies...等。

// Top-level build file where you can add configuration options common to all sub-projects/modules.


buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.0.0'

        // NOTE: Do not place your application dependencies here; they belong

        // in the individual module build.gradle files

    }
}

allprojects {
    repositories {
        jcenter()
    }
}
各Module的build.gradle

用途在於各模組可以增加不同的Android設定(例如:compileSDKVersion, buildType)以及dependencies。這邊記得兩件事情:

  1. 一個模組,就會有一個build.gradle
  2. 每個模組的dependencies就寫在自己模組底下的build.gradle檔。
apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.1"

    defaultConfig {
        applicationId "gov.epa"
        minSdkVersion 14
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'
    compile project(':iconicDroid')
}
Module Dependencies

模組的dependencies有分成下面三種:

  1. 相同專案底下的Module:這樣的寫法要確保該模組是在此專案底下,或是已經import進該專案。
    compile project(':library1')
    
  2. 在本機電腦的檔案目錄:可以用來加入*.jar檔案或是libs/資料夾。
    compile files('libs/gson.jar')
    compile fileTree(dir:'libs', include:['*.jar'])
    
  3. Remote Repositiory:加入非本機端的程式庫,gradle會自動幫你完成所有的import動作(例如:下載所需要的jar,管理該程式庫的dependencies)
    compile 'com.android.support:support-v4:21.0.3'
    

結語

以上大致上簡單的介紹到此,我個人的經驗是只有在從eclipse轉換成gradle的時候有些新東西要學不太習慣而已,其他都很OK,如果有使用上的問題歡迎留言問我,至於還沒用的趕緊來用,你會體驗到它的強大!!

以下多機率公式,會看得眼花撩亂,請確保自己清醒後再來看。
如果公式位置錯亂或是沒有顯示,就多重新整理幾次。

前情提要

貝式分類器,擁有幾項特點:

  1. 基於機率型分類(probabilistic classification
  2. 使用貝氏定理來做計算。
  3. 假設特徵之間事件獨立(independence)。
  4. 最常用於文件自動分類的應用上。

所建立出來簡單且有效的分類演算法,以下我們一樣使用新聞分類來作為演算法範例,主要目標為將新聞依照內容描述來自動分類為汽車(C)運動(S)科技(T)


演算法思路

貝氏定理

一切都從條件機率開始說起,我們說B事件發生的條件下發生A事件的機率公式如下:

現在把「B事件發生的條件下發生A事件的機率」和「A事件發生的條件下發生B事件的機率」公式寫在一起,然後做個公式位移:

把 (1) 和 (2) 公式調整一下

最後把P(B ∩ A)帶入置換我們一開始說的P(A|B)公式,就可以導出貝氏定理:

這個公式也可解讀成下列意思:

  • prior probability: 先驗機率,P(A)是在觀察特徵或證據「前」,不考慮任何事件B的因素,能表達事件A的機率分佈。
  • posterior probability: 後驗機率,P(A|B)是在觀察特徵或證據「後」,所得到的條件機率。

貝氏分類

回歸到貝氏分類,我們該如何應用貝氏定理還幫我們執行分類的工作呢??
我們剛剛提的「後驗機率」可以解釋成

給定一些觀察特徵值,我們能計算事物是屬於某個分類的機率。

上述的觀察特徵值我們可以把表示成一個向量,對於新聞分類這問題,我們可以把特徵關鍵字字詞頻率表示成向量:

如果你對於「特徵關鍵字字詞頻率」陌生,可以參考我寫的KNN分類演算法裡面的「前置作業」篇幅。

我們現在的問題是要把新聞自動分類,那麼「後驗機率」就可以說成

給定一些特徵關鍵字字詞頻率向量,我們能計算這篇新聞是屬於某個分類的機率。

寫成貝氏定理的公式就是

換成實際的例子就是

中文解釋就是

在出現「賓士, 寶馬, 籃球, 路跑, 手機, App」這些特徵關鍵字的情況下,該篇新聞是屬於「汽車」的機率是多少?

會等於

在「汽車」新聞當中出現「賓士, 寶馬, 籃球, 路跑, 手機, App」字詞的機率 x 
「汽車」新聞的機率 / 「賓士, 寶馬, 籃球, 路跑, 手機, App」字詞的機率。

上面字過長,滑鼠往右拉。

訓練階段

貝氏分類器的訓練階段是計算

這個算式的數值就要從訓練集合而來,我們要準備各個分類(汽車、運動、科技)的數篇新聞集合,然後做切字並且比對計算特徵關鍵字字詞頻率向量。

新聞 分類 賓士 寶馬 籃球 路跑 手機 App
C63發表會 P 15 25 0 5 8 3
BMW i8 P 35 40 1 3 3 2
林書豪 S 5 0 35 50 0 0
湖人隊 S 1 5 32 15 0 0
Android 5.0 T 10 5 7 0 2 30
iPhone6 T 5 5 5 15 8 32

這邊我已經沒有寫P(特徵關鍵字字詞頻率向量),因為比較不同分類之間的後驗機率時,分母P(特徵關鍵字字詞頻率向量)總是常數,因此可以忽略

獨立事件

實際上,即便有了各分類的新聞集合,我們也很難計算P(特徵關鍵字字詞頻率向量|分類),也就是很難計算

所以要引進貝氏分類最重要的「獨立事件」假設,所謂獨立事件就是一個事件A的結果不會影響另一個事件B發生的機率,舉個例子,給予兩個公正的硬幣,投擲硬幣兩次,那麼第一次投擲的結果不影響第二次投擲的機率。 兩個獨立事件發生的機率就會變成兩個事件機率的乘積。

回到我們的P(特徵關鍵字字詞頻率向量|分類),我們 假設每個分類下的各個特徵關鍵字出現的機率彼此獨立,所以公式可以寫成:

字詞分佈模式

這邊我們有兩個字詞分佈模式,分別為:

  • Bernouli: 只判斷字詞是否有出現,就出現就是1,沒有出現就是0。
    • P(分類) = 該分類新聞篇數 / 所有訓練集合新聞篇數
    • P(特徵關鍵字|分類) = (該分類下包含特徵關鍵字的新聞篇數 + 1) / (該分類下包含特徵關鍵字的新聞篇數 + 2)
  • Multinomial: 直接採用字詞出現頻率。
    • P(分類) = 該分類下字詞頻率總和 / 所有訓練集合字詞頻率總和
    • P(特徵關鍵字|分類) = (該分類下、該關鍵字字詞頻率總和 + 1) / (該分類下所有關鍵字字詞頻率總和 + 訓練集合關鍵字個數)

以下我們都採用Multimonimal來計算。

這邊有一個議題可以探討:不同的字詞分佈模式是否會影響我們最後分類的準度呢??


計算步驟

我們開始先訓練分類器,這邊只用「汽車」分類當作例子,其他分類計算方式類似,各個特徵關鍵字的分類機率如下

訓練階段完成,這些數值等等會使用到。
現在有一篇新的新聞,其特徵關鍵字字詞頻率

地點 分類 賓士 寶馬 籃球 路跑 手機 App
騎士隊 ? 10 2 50 56 8 5

我們要計算該篇新聞屬於「汽車」的機率

這些乘積出來的結果就是這篇新的新聞屬於「汽車」的機率。

向下溢位

如果把這個公式的數值給電腦算,應該有99.99999...%的機率算不出來,為何??因為機率小於1,越小的數字會越乘越小,這樣乘下去電腦就產生「向下溢位」的問題,這邊我們要修改一下機率的計算公式,我們把公式兩邊都取 log,指數就變成相乘,原本相乘就變成相加,算出來的就是機率的log值。

注意,這邊我們重點在於比較各分類的機率 大小關係,而非數值本身,所以所有分類機率數值都取log一樣可以比較所屬分類。


結論

  • 貝氏分類對於少量的訓練集合一樣會有不錯的分類準度,它的威力恰好在於小樣本下,專家意見或歷史經驗,也就是所謂的先驗分配,能夠補足小樣本的不足,使得推論能夠進行。
  • 適合用在資料會不斷成長的應用。

其他參考資訊

http://sebastianraschka.com/Articles/2014_naive_bayes_1.html
http://cn.soulmachine.me/blog/20100528/
http://blog.yhathq.com/posts/naive-bayes-in-python.html
http://scikit-learn.org/stable/modules/naive_bayes.html
http://machinelearningmastery.com/better-naive-bayes/