XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2015/10/01

使用Material Theme來變更App主題與風格 Theme / Style

在Android 5.0之後,提出了 Material Theme的設計指引,提供了每個開發者有個統一規範可以遵循來設計每個App,透過這個指引,我們可以透過 Theme / Style 的XML定義檔案,不需要修正任何C#程式碼,就可以直接將您的App風格樣貌做個更換。

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式

不過,Material Theme這樣的設計方式,只存在於 Android 5.0 (API 21)或者更新版本的作業系統環境上才能夠運行,若您想要使用這樣的方式來設計您的App,並且希望讓您的App可以在比Android 5.0更舊的版本上來運行,並且享受到同樣的好處,您需要透過 v7 Support Libraries的支援,它提供了 material 設計樣式的主題 theme,可以用來配置不同的UI控制項,並且可以讓您客製化不同的顏色配置。
在看到如何使用之前,我們先看看有哪些項目是我們可以透過 Theme / Style的方式來進行替換不同的風格,在下圖中,我們可以看到不同的屬性,例如:colorPrimary, colorPrimaryDark等等,透過定義這些屬性到不同的顏色色碼,我們就可以立即將您的App更換到不同的風格樣貌。

首先,我們需要在 Resources/values目錄內styles.xml,加入一些定義,如下所示。
在 styles.xml檔案內定義我們App要用的基礎主題 MyTheme.Base,這個主題內容是從 Theme.AppCompat.Light.DarkActionBar繼承而來;  Theme.AppCompat.Light.DarkActionBar 這個主題,是在 Meterial Theme內預設定義的。  我們在自行定義的基礎主題 MyTheme.Base 中,修改了許多顏色配置,這些顏色配置將會存在於 Resources/values/colors.xml內,  也就是說,我們要變更App的顏色風格配置,只需要修改Resources/values/colors.xml內的顏色色碼即可。
想要參考更詳盡的 系統提供的 Theme / Style ,可以參考 Android API開發指南的Styles / Themes部分內容
R.Style的參考文件中,其實並不是一個很好與方便查詢各 Style和屬性的地方,因為在其官方文件上,這部分並沒有寫得相當清楚;如果您想要更進一步、完整的了解與參考這些Android內的 Style 和 Theme,可以參考底下兩個連結,在這兩個連結中,完整的列出了系統內提供的各個 Style & Theme 與其屬性定義名稱,透過這樣的查詢,您可以廓中您的App成為更多采多姿的程式。
Android Styles (styles.xml)
Android Themes (themes.xml)
在上圖中所所標示出來的 colorPrimary, colorPrimaryDark, textColorPrimary, windowbackground, navigationbarcolor 這些屬性,就可以透過地下的方式來進行擴充,若想要自訂其他的項目,可以參考與查詢上述兩個 Android Style / Android Themes 這兩份文件來做到。
  <style name="MyTheme" parent="MyTheme.Base">
  </style>

  <!--
  定義我們App要用的基礎主題 MyTheme.Base,這個主題內容是從 Theme.AppCompat.Light.DarkActionBar繼承而來
  Theme.AppCompat.Light.DarkActionBar 這個主題,是在 Meterial Theme內預設定義的
  我們在自行定義的基礎主題 MyTheme.Base 中,修改了許多顏色配置,這些顏色配置將會存在於 Resources/values/colors.xml內
  也就是說,我們要變更App的顏色風格配置,只需要修改Resources/values/colors.xml內的顏色色碼即可
  -->
  <!-- Base theme applied no matter what API -->
  <style name="MyTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowNoTitle">true</item>
    <!--We will be using the toolbar so no need to show ActionBar-->
    <item name="windowActionBar">false</item>
    <!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette-->
    <!-- colorPrimary is used for the default action bar background -->
    <item name="colorPrimary">@color/primary</item>
    <!-- colorPrimaryDark is used for the status bar -->
    <item name="colorPrimaryDark">@color/primaryDark</item>
    <!-- colorAccent is used as the default value for colorControlActivated which is used to tint widgets -->
    <item name="colorAccent">@color/accent</item>    
    <!-- You can also set colorControlNormal, colorControlActivated colorControlHighlight and colorSwitchThumbNormal. -->
    <!-- Option to hide the drop shadow here    
      <item name="actionBarStyle">@style/MyTheme.ActionBar</item>
      <item name="android:windowContentOverlay">@null</item>
    -->
  </style>

  <style name="MyTheme.ActionBar" parent="style/Widget.AppCompat.Light.ActionBar.Solid">
    <!-- remove shadow below action bar -->
    <!-- <item name="android:elevation">0dp</item> -->
    <!-- Support library compatibility -->
    <item name="elevation">0dp</item>
  </style>
</resources>

底下為自行定義擴充的顏色定義,上面的Xml定義中,標示著黃底紫色的文字的屬性,其顏色代碼,就是參考底下這個XML定義。
<?xml version="1.0" encoding="utf-8"?>
<!--專案範本預設的樣式-->
<!--Get colors from: http://www.materialpalette.com/-->
<!--<resources>
  <color name="primary">#03A9F4</color>
  <color name="primaryDark">#0288D1</color>
  <color name="accent">#FFC107</color>
  <color name="lightPrimary">#B3E5FC</color>
  <color name="textIcon">#FFFFFF</color>
  <color name="primaryText">#212121</color>
  <color name="secondaryText">#727272</color>
  <color name="divider">#B6B6B6</color>
  <color name="activated_color">#E8E8E8</color>
</resources>-->

<!--自訂顏色-->
<resources>
  <!--Get colors from: http://www.materialpalette.com/-->
  <color name="primary">#4CAF50</color>
  <color name="primaryDark">#388E3C</color>
  <color name="accent">#FFEB3B</color>
  <color name="lightPrimary">#C8E6C9</color>
  <color name="textIcon">#FFFFFF</color>
  <color name="primaryText">#212121</color>
  <color name="secondaryText">#727272</color>
  <color name="divider">#B6B6B6</color>
  <color name="activated_color">#E8E8E8</color>
</resources>

上面的 Colors.xml 檔案,我們定義了兩種情境顏色配置,下面螢幕截圖則是執行結果;下圖左邊是 [專案範本預設的樣式]定義,下圖右邊則是[自訂顏色]的執行結果

image image

image image

當然,我們也有方便的視覺快速設定方式,您可以透過 material palette 網頁,得到底下的設定

image





image

Xamarin.Android Templates Pack 範本解密3

在上一篇 [Xamarin.Android Templates Pack 範本解密2]文章中,我們剖析了專案內的 Activity & 相關的 Layout 配置,不過,在 Resources\layout 目錄下,還有兩個檔案沒有說明到
fragment1.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:text="@string/fragment1"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/textView1"
        android:gravity="center" />
</LinearLayout>

Fragment1.cs 這個檔案定義這個 Fragment 的控制相關的行為,程式碼內容如下,我們可以看到,這個類別內並沒有複雜的定義,而是在 OnCreateView 方法內,使用 LayoutInflater.Inflate方法,指定這個 Fragment要顯示的視覺配置內容。

    public class Fragment1 : Fragment
    {
        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Create your fragment here        }

        public static Fragment1 NewInstance()
        {
            var frag1 = new Fragment1 { Arguments = new Bundle() };
            return frag1;
        }


        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            var ignored = base.OnCreateView(inflater, container, savedInstanceState);
            return inflater.Inflate(Resource.Layout.fragment1, null);
        }
    }


fragment2.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:text="@string/fragment2"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/textView1"
        android:gravity="center" />
</LinearLayout>

Fragment2.cs 這個檔案定義這個 Fragment 的控制相關的行為,程式碼內容如下,我們可以看到,這個類別內並沒有複雜的定義,而是在 OnCreateView 方法內,使用 LayoutInflater.Inflate方法,指定這個 Fragment要顯示的視覺配置內容。

    public class Fragment2 : Fragment
    {
        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Create your fragment here        }

        public static Fragment2 NewInstance()
        {
            var frag2 = new Fragment2 { Arguments = new Bundle() };
            return frag2;
        }


        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            var ignored = base.OnCreateView(inflater, container, savedInstanceState);
            return inflater.Inflate(Resource.Layout.fragment2, null);
        }
    }


這兩個 .axml檔案,定義了兩個 Fragment,而當我們在 MainActivity內,有定義了當點選了導覽列中的功能表選項的話,會進行切換到不同的 Fragment

            //定義事件,當 navigation view 內的功能表選單有被選取的時候,要做何相對應的處置            navigationView.NavigationItemSelected += (sender, e) =>
            {
                e.MenuItem.SetChecked(true);

                switch (e.MenuItem.ItemId)
                {
                    case Resource.Id.nav_home_1:
                        ListItemClicked(0);
                        break;
                    case Resource.Id.nav_home_2:
                        ListItemClicked(1);
                        break;
                }

                // 這是 Google 新推出的 Metrial Design
                // 詳細說明,請參考 https://www.google.com/design/spec/material-design/introduction.html
                // 底下為產生類似 Toast 的簡短提示訊息通知效果
                Snackbar.Make(drawerLayout, "You selected: " + e.MenuItem.TitleFormatted, Snackbar.LengthLong)
                    .Show();

                // 使用動畫撥放方式,關閉所有正在開啟的 Drawer views
                drawerLayout.CloseDrawers();
            };


而 ListItemClicked方法定義如下,我們可以看到,這個方法會根據所選擇的項目,產生相對應的Fragment物件,並且透過 SuppotFragmentManager的 BeginTranscation 來進行切換到所指定的 Fragment。

Fragement是我當初學習 Android UI時候,一開始不太了解的東東,不過,把 Fragment當作是在 WPF中的 User Control,這樣的使用者控制項,是可以反覆使用的,也就是說,一旦我們定義好了之後,這些 Fragment是可以用於不同的 Activity內。
        private void ListItemClicked(int position)
        {
            //this way we don't load twice, but you might want to modify this a bit.
            if (position == oldPosition)
                return;

            oldPosition = position;

            Android.Support.V4.App.Fragment fragment = null;
            switch (position)
            {
                case 0:
                    fragment = Fragment1.NewInstance();
                    break;
                case 1:
                    fragment = Fragment2.NewInstance();
                    break;
            }

            SupportFragmentManager.BeginTransaction()
                .Replace(Resource.Id.content_frame, fragment)
                .Commit();
        }


最後,我們要來看 Resources\values 內的一些常數定義內容,首先看到的是 colors.xml 檔案,這個檔案定義了 Activity 的要顯示的顏色配色定義。

這些配色定義,將會在樣式定義檔案 styles.xml 內使用到
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <!--Get colors from: http://www.materialpalette.com/-->
  <color name="primary">#03A9F4</color>
  <color name="primaryDark">#0288D1</color>
  <color name="accent">#FFC107</color>
  <color name="lightPrimary">#B3E5FC</color>
  <color name="textIcon">#FFFFFF</color>
  <color name="primaryText">#212121</color>
  <color name="secondaryText">#727272</color>
  <color name="divider">#B6B6B6</color>
  <color name="activated_color">#E8E8E8</color>
</resources>

接下來看看格式定義檔案的內容 styles.xml,在這個檔案內,我們定義一個 MyTheme樣式,他繼承了 MyTheme.Base;而 MyTheme.Base則是繼承從 Theme.AppCompat.Light.DarkActionBar,並且做了些修正來取代系統中的定義,另外還有兩種 Theme可以來選擇,分別是 Theme.AppCompat / Theme.AppCompat.Light

例如:我們定義了Theme.AppCompat.Light.DarkActionBar內用到的 colorPrimary 顏色,將會使用 Resources\values\color.xml 內的 primary 來取代,也就是這個色碼 #03A9F4

而有些資源資料,可以從這裡取得 http://developer.android.com/intl/zh-tw/design/downloads/index.html#action-bar-icon-pack
<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <style name="MyTheme" parent="MyTheme.Base">
  </style>
  <!-- Base theme applied no matter what API -->
  <style name="MyTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowNoTitle">true</item>
    <!--We will be using the toolbar so no need to show ActionBar-->
    <item name="windowActionBar">false</item>
    <!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette-->
    <!-- colorPrimary is used for the default action bar background -->
    <item name="colorPrimary">@color/primary</item>
    <!-- colorPrimaryDark is used for the status bar -->
    <item name="colorPrimaryDark">@color/primaryDark</item>
    <!-- colorAccent is used as the default value for colorControlActivated
         which is used to tint widgets -->
    <item name="colorAccent">@color/accent</item>
    <!-- You can also set colorControlNormal, colorControlActivated
         colorControlHighlight and colorSwitchThumbNormal. -->    
    <!-- Option to hide the drop shadow here
    
      <item name="actionBarStyle">@style/MyTheme.ActionBar</item>
      <item name="android:windowContentOverlay">@null</item>
    -->
  </style>

  <style name="MyTheme.ActionBar" parent="style/Widget.AppCompat.Light.ActionBar.Solid">
    <!-- remove shadow below action bar -->
    <!-- <item name="android:elevation">0dp</item> -->
    <!-- Support library compatibility -->
    <item name="elevation">0dp</item>
  </style>
</resources>

而在 Resources/values-v21 目錄下也有個 styles.xml 檔案,內容如下;這裡定義了 API 21以上用到的Theme會用到的內容。
<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <!--
        Base application theme for API 21+. This theme completely replaces
        MyTheme from BOTH res/values/styles.xml on API 21+ devices.
    -->
  <style name="MyTheme" parent="MyTheme.Base">
   <item name="android:windowContentTransitions">true</item>
    <item name="android:windowAllowEnterTransitionOverlap">true</item>
    <item name="android:windowAllowReturnTransitionOverlap">true</item>
    <item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
    <item name="android:windowSharedElementExitTransition">@android:transition/move</item>
  </style>
</resources>

2015/09/30

Android Design Support Library


Android Design Support Library 這是這連三天才開始進行研究的內容,原本以為看過一些 Android SDK 文件中後,就可以開始進行 Xamarin.Android App開發了,沒想到,有接觸到Android 5.0 SDK,有看到了 Toolbar 這樣的視覺API,接著研究後,才知道這些都是涵蓋在 Android Design Support Library 內的咚咚,這也解除了在專案檔案中看到的一些套件,明瞭到這些套件存在的目的。

Xamarin.Android Templates Pack 範本解密2

我們有方案總管的畫面目錄結構,由上而下來開始解析,首先來了解 [BaseActivity.cs]。
當我們使用這個專案範本的時候,所有的 Activity 都是繼承了 BaseActivity,而 BaseAcitity 則是繼承了 AppCompatActivity。
[Android Support v7 AppCompat] 這個套件,讓我們可以使用 Android 5.0 Lollipop系統內的 Material Desigan 相關 UI,並且可以讓之前舊 Android 版本也可以跑這個App。當我們從 Xamarin Component Store 或者 NuGet 內安裝了 Support Library v7 AppCompat,Support v4 程式庫也會安裝進來,而我們使用的這個專案範本,已經自動幫我們加入進來了,所以,我們就可以直接使用 AppCompatActivity類別。
image
底下列出了 BaseActivity 類別的定義,我們可以看到他定義了 Toolbar 這個物件,這個物件是屬於 Toolbar 這個類別,而 Toolbar 的UI則是在 Android 5.0系統裡才會有的,如同前面所述,我們可以讓Android 5.0之前的作業系統,也可以使用這樣的物件功能。
在 OnCreate 方法內,當系統內有 Toolbar UI的時候,我們呼叫了 SetSupportActionBar這個方法,這讓 Toolbar 的行為會如同 Actionbar 一樣;SupportActionBar.SetDisplayHomeAsUpEnabled(true)則是允許讓我們移動到上一個畫面中,也就是起用了上一層按鈕功能;SupportActionBar.SetHomeButtonEnable(true)則是啟用了回首頁的功能
namespace AndroidNavigationDrawerAppCompat.Activities
{
    public abstract class BaseActivity : AppCompatActivity
    {
        public Toolbar Toolbar
        {
            get;
            set;
        }
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(LayoutResource);
            Toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);
            if (Toolbar != null)
            {
                SetSupportActionBar(Toolbar);
                SupportActionBar.SetDisplayHomeAsUpEnabled(true);
                SupportActionBar.SetHomeButtonEnabled(true);
            }
        }

        protected abstract int LayoutResource
        {
            get;
        }

        protected int ActionBarIcon
        {
            set { Toolbar.SetNavigationIcon(value); }
        }
    }
}

在底下,我們說明了 MainActivity.cs 這個檔案內的內容,您可以參考註解說明。

在 MainActivity內,定義了兩個變數 drawerLayout & navigationView,透過這兩個變數的設定,可以讓您的App輕鬆做到有滑動子視窗的預覽列功能,在預覽列中,我們可以透過功能表的定義出各項App提供的子功能,讓使用者方便選擇與使用。

另外,在OnCreate方法內,我們也看到了 Google Metrial Design的新功能 Snackbar.Make,透過 Snackbar物件,我們可以取代將之前Toast要提供功能。

在OnCreate方法內,我們則是透過了 CloseDrawers()方法,將浮動導覽列收合起來。
namespace AndroidNavigationDrawerAppCompat.Activities
{
    // 定義了該 Activity 為程式啟動的主要第一個畫面,
    // 該App的名稱定義為 Home,並且指定了該 App 的圖示為 @drawable/Icon
    [Activity(Label = "Home", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, Icon = "@drawable/Icon")]
    public class MainActivity : BaseActivity
    {
        /// <summary>
        /// 定義該畫面上的主要 DrawerLayout,並且在後面透過 Resource.Id.drawer_layout來取得該物件
        /// DrawerLayout可以讓你很方便建立左側選單
        /// 請參考 Google API 文件 https://developer.android.com/intl/zh-tw/reference/android/support/v4/widget/DrawerLayout.html
        /// </summary>        DrawerLayout drawerLayout;
        /// <summary>
        /// 定義導覽列的UI物件,當使用 NavigationView的時候,我們必須要搭配 DrawerLayout來使用,而 DrawerLayout必須是在最外層
        /// 請參考 Google API 文件 https://developer.android.com/intl/zh-tw/reference/android/support/design/widget/NavigationView.html
        /// </summary>        NavigationView navigationView;

        protected override int LayoutResource
        {
            get            {
                return Resource.Layout.main;
            }
        }

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // 取得 DrawerLayout UI 實際物件
            drawerLayout = this.FindViewById<DrawerLayout>(Resource.Id.drawer_layout);

            //設定漢堡按鈕的圖片資源            SupportActionBar.SetHomeAsUpIndicator(Resource.Drawable.ic_menu);

            //設定 navigation view            navigationView = FindViewById<NavigationView>(Resource.Id.nav_view);

            //定義事件,當 navigation view 內的功能表選單有被選取的時候,要做何相對應的處置            navigationView.NavigationItemSelected += (sender, e) =>
            {
                e.MenuItem.SetChecked(true);

                switch (e.MenuItem.ItemId)
                {
                    case Resource.Id.nav_home_1:
                        ListItemClicked(0);
                        break;
                    case Resource.Id.nav_home_2:
                        ListItemClicked(1);
                        break;
                }

                // 這是 Google 新推出的 Metrial Design
                // 詳細說明,請參考 https://www.google.com/design/spec/material-design/introduction.html
                // 底下為產生類似 Toast 的簡短提示訊息通知效果
                Snackbar.Make(drawerLayout, "You selected: " + e.MenuItem.TitleFormatted, Snackbar.LengthLong)
                    .Show();

                // 使用動畫撥放方式,關閉所有正在開啟的 Drawer views                drawerLayout.CloseDrawers();
            };


            //if first time you will want to go ahead and click first item.
            if (savedInstanceState == null)
            {
                ListItemClicked(0);
            }
        }

        int oldPosition = -1;
        private void ListItemClicked(int position)
        {
            //this way we don't load twice, but you might want to modify this a bit.
            if (position == oldPosition)
                return;

            oldPosition = position;

            Android.Support.V4.App.Fragment fragment = null;
            switch (position)
            {
                case 0:
                    fragment = Fragment1.NewInstance();
                    break;
                case 1:
                    fragment = Fragment2.NewInstance();
                    break;
            }

            SupportFragmentManager.BeginTransaction()
                .Replace(Resource.Id.content_frame, fragment)
                .Commit();
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            switch (item.ItemId)
            {
                case Android.Resource.Id.Home:
                    drawerLayout.OpenDrawer(Android.Support.V4.View.GravityCompat.Start);
                    return true;
            }
            return base.OnOptionsItemSelected(item);
        }
    }
}

接著我們來看看這個 Activity 用到的視覺定義內容,也就是 main.axml。這個視覺畫面定內容相當的簡單,我們將會看到最後面的 [android.support.design.widget.NavigationView] 這個物件,裡面的屬性 app:headerLayout 定義了該導覽列的頁首內容,這個內容定義在 nav_header.xml內,而app:menu屬性,定義了導覽列中的功能清單配置,該配置定義在 nav_menu.xml內。

另外,我們看到了裡面定義了 android.support.design.widget.AppBarLayout 這個物件,這是一個可以伸縮摺疊的Toolbar(Collapsing Toolbar)。其實,這部分的UI是屬於 Goole Support Design Library內的功能,您可以參考這個連結,得到更多詳細資訊:http://android-developers.blogspot.tw/2015/05/android-design-support-library.html
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<!-- The main content view -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.design.widget.AppBarLayout            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:id="@+id/toolbar_layout">
            <include
                android:id="@+id/toolbar"
                layout="@layout/toolbar"
                app:layout_scrollFlags="scroll|enterAlways" />
        </android.support.design.widget.AppBarLayout>
        <FrameLayout
            android:id="@+id/content_frame"
            android:layout_below="@id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </RelativeLayout>
    <android.support.design.widget.NavigationView        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_menu" />
</android.support.v4.widget.DrawerLayout>

底下為 nav_header.axml的內容,這個檔案可以從方案總館內的 Resources\layout內找到。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:background="?attr/colorPrimaryDark"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:orientation="vertical"
    android:gravity="bottom">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Username"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
</LinearLayout>
底下為 nav_menu.xml的內容,這個檔案可以從方案總館內的 Resources\menu內找到。
<?xml version="1.0" encoding="UTF-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

  <group android:checkableBehavior="single">
    <item
        android:id="@+id/nav_home_1"
        android:icon="@drawable/ic_home_1"
        android:title="Home 1" />
    <item
        android:id="@+id/nav_home_2"
        android:icon="@drawable/ic_home_2"
        android:title="Home 2" />
  </group>

  <item android:title="Sub items">
    <menu>
      <item
          android:icon="@drawable/ic_home_1"
          android:title="Sub item 1" />
      <item
          android:icon="@drawable/ic_home_2"
          android:title="Sub item 2" />
    </menu>
  </item>
</menu>

這篇文章中所提到的內容,可以參考底下動畫,您就會更清楚這個框架可以幫您做到更友善的操作介面。



ezgif.com-video-to-gif (1)

Xamarin.Android Path to long 因為專案名稱過長,產生異常錯誤

因為昨天發現到整個Xamarin.Android所在的目錄下,不能夠有任何的中文字體存在(請參考這篇文章 Xamarin 帳號突然失效不能使用了 ),想說,把之前建立的專案,重新建立一次,而又因為必須使用英文名稱作為目錄名稱,想說,在命名的時候,儘可能地把他列清楚。
第一個建立的專案,當然是 Xamarin.Android 預設的空專案 [Blank App],接著要建立的是採用 Xamarin.Android Templstes Pack 的樣板所產生的空專案,要了要更加清晰,我把全部的名稱都寫出來,不使用縮寫,所以,該專案命名為 [AndroidNavigationDrawerAppAppCompat],當然,目錄中也會有這個名稱出現;接著,就是要編譯、執行來測試看看,所產生的專案是否可以正確執行。
經過編譯後,發現到一個錯誤:
image
image
看到這樣的訊息,其實,我有點擔心,因為,我正在使用 Visual Studio 2015 + C# 來開發,理論上,不會使用到任何關於 Java 的程式開發項目與內容,此時卻出現了錯誤訊息:
嚴重性    程式碼    描述    專案    檔案    行
錯誤        "GenerateJavaStubs" 工作發生未預期的失敗。
System.IO.PathTooLongException: 指定的路徑、檔名,或是兩者都太長。完整的檔名必須少於 260 個字元,並且目錄名稱必須少於 248 個字元
   於 System.IO.PathHelper.GetFullPathName()
   於 System.IO.Path.NormalizePath(String path, Boolean fullCheck, Int32 maxPathLength, Boolean expandShortPaths)
   於 System.IO.Path.GetFullPathInternal(String path)
   於 System.IO.Path.GetFullPath(String path)
   於 Xamarin.Android.Tasks.GenerateJavaStubs.Run()
   於 Xamarin.Android.Tasks.GenerateJavaStubs.Execute()
   於 Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
   於 Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext()    AndroidNavigationDrawerAppAppCompat
上面清單為實際展該該錯誤訊息,我們可以看到,發出的異常是 System.IO.PathTooLongException ,我想說,應該是我的專案所在完整目錄超過Xamarin可以接受的長度,解法之一,就是把完整路徑的長度減少,另外一個就是把專案名稱的長度減少,以達到系統的需要,我選擇了後者。
因此,我選擇了後者,重新建立起一個新的專案,並且將專案名稱的字數減少;當專案建立完成後,立即編譯看看,哇,可以通過編譯了,接著進行執行專案,讓他在模擬器上跑看看,也可以順利執行。

Xamarin 帳號突然失效不能使用了 (Xamarin Account Invalid)

這個月獲得 Xamarin 通知,獲得一年份的開發者授權使用權利,當然,要好好利用這個免費的開發授權,畢竟,單買一年的的單一平台開發授權,需要 US$999元,這不是個小數目。
早在兩個星期前,我就已經把Xamarin提供的授權,註冊到 Visual Studio 2015 開發環境中:
image
經過一段時間的努力,閱讀了 Xamarin 開發相關書籍、Android SDK、Xamarin UI Developer Guide、Android UI Developer Guide等等文件;想說,這下應該比較弄懂了 Xamarin.Android的開發架構與應用技巧,準備要開始寫些測試專案與框架。
二話不說,當然先從 Xamarin.Android Blank App 專案樣本先建立起來,之後發現到 [Xamarin.Android Templates Pack]這個開發範本相當的實用,接著也透過 Visual Studio 2015 的 [擴充功能和更新] 把它也安裝起來,並且也建立了一個 [Android Navigation Drawer App AppCompat] 類型的專案。
以上還是昨天的事情,我都可以順利再度開啟這兩個我建立好的專案,必且在模擬器上編譯與執行;今天,我又下載了一些其他範例專案,其中有一個 Android 5.0 專案 [ThemeSwitcher],感到非常有興趣,想要研究這個專案上的一些技巧,當然,是要把它下載到電腦中來執行看看。
不知道是不是今天運氣不好,還是打開電腦前,沒有洗手,竟發現到該專案開啟,接著進行編譯,就看到底下錯誤訊息。
image
There was an internal error in the activation system. Please contact Xamarin Support with the following information.

Unhandled Activation Error
System.Exception: Could not load machine data:
at Xamarin.Components.Ide.Activation.ActivationService.GetErrorWorkflow
(LicenseSyncResult[] results, Boolean ignoreSyncErrors)
at
Xamarin.Components.Ide.Activation.ActivationService.<GenerateFullWorkflowStep
(ActivationWorkflowStep step)
at
Xamarin.Components.Ide.Activation.ActivationDialog.<>c_DisplayClass13,
<StartSpiner
TaskAndScheduleContinuation>b_12(Task t)
看到這樣的訊息,想說應該是我的授權發生了問題,所以,檢查 Xamarin Account的內容,結果看到的是底下悲慘內容。
image
想說,可能是 Xamarin 系統有些問題,我只好登出,接著重新登入,不用說,大家也知道,出現的還是一樣的錯誤訊息 [Unhandled Activation Error]。
我想說,專案打開不能用,我只好關閉所有的 VS2015,接著開啟VS2015,直接進入到 [Xamarin Account]功能中,進行登入,呵呵,可以重新取得 Business 的授權。
因為,也正在準備些開發紀錄的文件整理,也打開了昨天產生的 [Blank App] & [Android Navigation Drawer App AppCompat] 這兩個專案,也是發生一樣的悲劇:[Unhandled Activation Error]。
究竟發生了甚麼事情呢?難道好事多磨,我已經準備好要來利用 Xamarin.Android來開發App了,卻連還沒有開始開發,就無法使用了。
由於 Xamarin 提供給我的開發授權並沒有提供 Email 技術支援服務,而我只好準備透過 Xamarin 論壇 寫下我的問題,希望能夠得到解決方案;當問題寫到一半的時候,就在這個時候,我突然想到了,因為今天取得了許多參考範例專案,為了日後方便參考,我把上述與今天取得的專案都彙整到一個適當的目錄下,當然,我是取了一個中文的目錄名稱。
image
根據以往經驗,許多非國產的軟體,對於中文字判讀上,多少可能都會有些狀況,因此,我把原先存放在 [實驗與測試專案] 內的 [App1] & [ThemeSwitcher] 這兩個專案,移到一個目錄路徑內沒有中文字的環境中,很幸運的,我可以開啟這兩個專案,也可以編譯、執行。
因此,若您也遇到的[Unhandled Activation Error]的錯誤訊息,可以嘗試檢查看看,該專案所在路徑上,是否存在著中文名稱。

2015/09/29

Xamarin.Android Templates Pack 範本解密1

當您安裝了 Xamarin.Android Templates Pack後,並且使用 Visual Studio 2015 建立一個 [Android Navigation Drawer App AppCompact] 類型專案,我們可以從 Xamarin.Android Templates Pack 文章中看到這是一個相當實用的起始專案。
接下來,讓我們看看這個採用 Xamarin.Android Template Pack 建立起來的專案裡面,究竟有那些東西。
image
其中我們在 [參考] 內,有著三個 Xamarin.Android.Support.XXX 的檔案
Android Design Support Library
Android Support Library v4
Android Support Library v7 AppCompat
它們提供了Android框架API的向後兼容的版本和功能,這些功能只能通過庫API。每個支持庫是向後兼容到一個特定的Andr​​oid API級別。這樣的設計意味著你的應用程序可以使用圖書館的功能,仍然是與運行Android的老口味設備兼容。另外,可以參考這裡
接著,我們從方案總館內看到其他內容,如下圖所示:
image
這個專案有一個基礎 Activity 叫做 BaseActivity,這個 class 是繼承了 AppCompatActivity class,而真正的主畫面 Activity 叫做 MainActivity ,他繼承 BaseActivity,也就是,專案中的所有的 Activity ,都需要繼承 BaseActivity。
在這個專案模式下,相關 UI 內容,都是透過 Fragment 來呈現,因此,這裡有兩個 Fragment存放在 Fragments 目錄下。
在 layout 目錄下,當就是存放著視覺畫面的定義,這包括了兩個 Fragment、主頁面畫面、彈跳出功能表、工具列。
接下來,我們來看看剩下的檔案內容。
image
menu目錄下,當然就定義了 App 內要使用的功能表內容。
values目錄下,定義了字串陣列、使用到的顏色色碼定義、尺寸值、整數值、字串值、樣式定義

基本上,透過這樣的框架下來進行 Xamarin.Android 來開發App,所開發出來的App,可以使用最新的API,並且可以在絕大部分的舊的Android OS下來執行;而且,整體專案下,已經將許多視覺設計的內容從axml內分離出來了,也就是說,當要變更某些視覺設計,例如,變更顏色,只需要到 values 目錄下,找到適當的定義檔案,修改那些值,就可以立即改變App執行顏色,不需要修改程式碼,真是太棒了。
而我日後也會使用這個框架來進相關 Xamarin.Android App開發。