2017年11月29日 星期三

Xamarin : android : 停用按鈕聲音

正常的狀況下,按下Android APP 內的按鈕 Button,裝置都會發出『達、達』『滴、滴』等的聲音,這個聲音不是 APP 產生或是開發出來的。

這是 Android 內置的系統聲音。

如果某些情境開發下,希望能夠關閉這個聲音,可以透過下面兩種方式實現:

1.針對按鈕 Button 操作屬性
程式方式:


button.setSoundEffectsEnabled(false);

屬性變更


<Button
    ...
    android:soundEffectsEnabled="false" />



2.針對全環境(APP)操作:
在資源檔內建立 res/values/styles.xml 樣式檔




<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="AppBaseTheme" parent="android:Theme.Black.NoTitleBar">
    <!--
        Theme customizations available in newer API levels can go in
        res/values-vXX/styles.xml, while customizations related to
        backward-compatibility can go here.
    -->
</style>

<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
    <!-- All customizations that are NOT specific to a particular API-level can go here. -->
    <item name="android:soundEffectsEnabled">false</item>
</style>

</resources>



然後,在 AndroidManifest.xml 裡面,修改(不存在就增加)這一段,強制使用樣式檔


<application
    ...
    android:theme="@style/AppTheme" >



參考:
https://stackoverflow.com/questions/15293608/disable-button-click-sound-in-android
https://stackoverflow.com/questions/5023170/how-to-disable-default-sound-effects-for-all-my-application-or-activity


2017年11月27日 星期一

Xamarin : android : Animation 特效 - 按鈕動畫測試

如果版面上需要讓 View (控件) 有點特效,其實 android 有提供一些基本2D的特效可以使用,這個就是 Animation。
※先說明Animation在這裡是一種效果,並非我們認知的那種動畫,所以無法設計太多一連串的動作,因此通常比較適合『咻一下子』這種感覺。

1 . 要在要運行的 Activity 引入

Using Android.Views.Animations

2 . 然後在右邊Resource資料夾下建立一個anim資料夾(這個請勿隨意命名,因為在android套件是有關聯的,隨意命名會找不到)



3 . 然後在anim資料夾下建立你的預訂好的動畫(特效)檔案,它基本上就是一個XML檔案。


這個檔案的詳細內容寫法可以參考下面網站:
官方網站(詳細但太多太雜,很容易找不到重點):https://developer.xamarin.com/guides/android/application_fundamentals/graphics_and_animation/
極客學院(簡單易懂,但無太多詳細解釋):http://wiki.jikexueyuan.com/project/android-animation/1.html

2017年11月24日 星期五

Xamarin : Android : 自訂繼承控制項 Coustom class inherit by TextView or other

很多時候,原始的控制項功能無法滿足社計上的需要時就必須修改控制項了。

這個範例是繼承自 TextView 的自訂元件。

1.首先到專案內新增一個類別 (新增項目→C#類別),名稱為 MyTextView.cs


2.首先要引用下面這個類別,才能使用 IAttributeSet


using Android.Util;


3.然後讓這個類別繼承 View,然後要實現相關的 建構子


namespace TestAnimator
{
    //讓 MyTextView 繼承自 TextView
    public class MyTextView : TextView
    {
        //實現基本的建構子
        public MyTextView(Context context) : base(context) { }
        public MyTextView(Context context, IAttributeSet attributeSet) : base(context, attributeSet) { }
        public MyTextView(Context context, IAttributeSet attributeSet, int defaultStyle) : base(context, attributeSet, defaultStyle) { }


4.接下來,我希望這個 TextView 有一個 MeMe 的屬性,而這屬性是 int 型別,當這個 MeMe 數值改變時,我希望能夠顯示在 Text 上。

因此在 class 下再建立一個內部儲存變數 _meme 與屬性 public int MeMe,然後覆寫 Draw 事件讓它可以把變數內容寫入 Text


        //內部變數
        private int _meme = 0;
       
        //屬性設定
        public int MeMe
        {
            get { return _meme; }
            set
            {
                _meme = value;
                //Invalidate是用來觸發 Draw 功能的
                this.Invalidate();
            }
        }


        //覆寫 Draw 事件

        public override void Draw(Canvas canvas)
        {
            base.Draw(canvas);
            //顯示到 Text 上
            this.Text = _meme.ToString();
        }


5.到此,存檔,基本的繼承型自訂元件雛型就完成,然後準備把它加到我們的 Main.axml 上。


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/linearLayout2">
        <TextView
            android:text="ObjectAnimator:"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:id="@+id/label2" />
        <TestAnimator.MyTextView
            android:text="0"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/textView2" />
    </LinearLayout>
                        .
                        .
                        .


但是.....看似沒問題的時候,IDE卻發出了警告

這個大意是『找不到我們自訂的元素』,怎麼回事?

應該是這個自訂的 MyTextView.cs 是 C# 的, 而 java 並不認識它

所以,我們就利用 Android.Runtime.Register 把它導出給 java 看得到。

回到 MyTextView.cs 裡,並在最上方面引用


using Android.Runtime;


然後在 class 前面寫入 Register


namespace TestAnimator
{
    // 用 Register 導出類別
    [Register("TestAnimator.MyTextView")]
    public class MyTextView : TextView
    {
        public MyTextView(Context context) : base(context) { }
        public MyTextView(Context context, IAttributeSet attributeSet) : base(context, attributeSet) { }
        public MyTextView(Context context, IAttributeSet attributeSet, int defaultStyle) : base(context, attributeSet, defaultStyle) { }
                        .
                        .
                        .


存檔後,再回到 Main.axml 上看看,就會發現它已經可以找到我們自訂的類別元素了。

其他:
如果,這個自訂類別需要被 ObjectAnimator 呼叫並自動寫入 MeMe 屬性,通常都會失敗。

研究其原因,發現 Android 某些可以更改屬性值的函數,它在更改函數時不是使用:

object.PropertyName = newValue

而是使用 Method "方法"去改變屬性值,像這樣:

object.setPropertyName(newValue);

一個可能被其他類別操作的的屬性都會附帶 get/set 操作方法

而我這邊案例是做給 ObjectAnimator 處理的,所以我只會實做 setPropertyName 方法

回到 MyTextView.cs 裡,我們找到 MeMe 屬性的程序碼下方加入:


        public void setMeMe(int value)
        {
            _meme = value;
            // 內容變更時要刷新畫面
            this.Invalidate();
        }


此時,如果你已經準備好 ObjectAnimator  的程序片段就可以去試試看是否會成功。


            //對MyTextView的MeMe屬性設定數字 0 到100 的變化
            var tv2 = FindViewById<MyTextView>(Resource.Id.textView2);
            ObjectAnimator objectAnimator = ObjectAnimator.OfInt(tv2, "MeMe", 1, 100);
            objectAnimator.SetDuration(1000);
            objectAnimator.RepeatCount = ObjectAnimator.Infinite;
            objectAnimator.Start();


結果,又不如預期,無法執行!

為什麼?

研究發現,其原因還是跟 TestAnimator.MyTextView 無法被 Main.axml 找到一樣

需要導出!!!!!!

類別裡面的方法要導出不同於類別是使用 Export 指令。

使用前要先確定 『專案』→『參考』底下是否有 『Mono.Android.Export』,如果沒有請加入參考:

再回到 MyTextView.cs 裡,前面加入


using Java.Interop;


然後在 setMeMe(int value) 前面 Export 它(注意大小寫,java對大小寫很敏感)


        [Export("setMeMe")]
        public void SetMeMe(int value)
        {
            _meme = value;
            this.Invalidate();
        }


存檔後,再去執行 ObjectAnimator  程式碼,發現可以用了耶!!

2017年11月22日 星期三

Xamarin:Android:Animator 控制器簡單使用法

原文說明:看這裡

這篇主要介紹 Property animators 的常見用法,使用前要引用下面的 Name Space
using Android.Animation

這個 animators 有三個子類別

ValueAnimator - 數值計數器,數字更新對象必須以加載是件方式執行
ObjectAnimator - 進階版 ValueAnimator 可以直接指定物件與更新對象
AnimatorSet - 動作集合器,可以把許多 ValueAnimator 和 ObjectAnimator 掛載一起執行,也可以指定執行順序

ValueAnimator
其實這個 ValueAnimator 控制器,說穿了就是一個線性計數器而已,跟繪圖一點關係都沒有,因為它的基本動作就是:

你給予 A 到 B 區間的數字,然後要它在一定時間內跑完,然後在跑數字的時候同時去更新某個變數。

下面有個基本範例,這個範例可以讓顯示的文字從0漸增到100,然後再從100漸減到0,每次的變化需要的時間剛好就1秒:


//設定數字 0 到100 的變化
ValueAnimator valueAnimator = ValueAnimator.ofInt(0 , 100);
//並且在 1000毫秒內執行完
valueAnimator.SetDuration(1000);
//設定為無限次執行
valueAnimator.RepeatCount = ValueAnimator.Infinite;
//循環方式為『反向計數』
valueAnimator.RepeatMode = ValueAnimatorRepeatMode.Reverse;

//當數字發生變化時,更新TextView的文字顯示
valueAnimator.Update += delegate (object sender,ValueAnimator.AnimatorUpdateEventArgs e)
{
        //取得變化數值
        var newValue = (int)e.Animation.AnimatedValue;
        var tv1 = FindViewById<TextView>(Resource.Id.textView1);
        tv1.Text = newValue.ToString();
}

//啟動 animator
valueAnimator.Start();


ObjectAnimator
如果不想使用『事件』去更新對象,可以使用 ObjectAnimator 直接指定要更新的對象物件與變數,這是針對比較單純的目的去處理。

底下範例效果同上面的程式碼:


//對MyTextView的MeMe屬性設定數字 0 到100 的變化
var tv2 = FindViewById<MyTextView>(Resource.Id.textView2);
ObjectAnimator objectAnimator = ObjectAnimator.OfInt(tv2, "MeMe", 1, 100);
objectAnimator.SetDuration(1000);
objectAnimator.RepeatCount = ObjectAnimator.Infinite;
//底下因為沒有ObjectAnimatorRepeatMode這種列舉,所以直接使用ValueAnimatorRepeatMode
objectAnimator.RepeatMode = ValueAnimatorRepeatMode.Restart;
//啟動 animator
objectAnimator.Start();


AnimatorSet
這是個可以把一堆 ValueAnimator 和 ObjectAnimator 整合在一起執行的動作集合器
我們可以用以下的範例讓兩個 TextView的文字 交互執行遞增和遞減的動作

先建立 activity 層級變數, 因為在底下做 ObjectAnimation 參考的 tv4 變數會因為 離開onCreate而被消滅,導至無法執行。


public class MainActivity : Activity
    {
        public MyTextView  tv4 = FindViewById<MyTextView>(Resource.Id.textView4);
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
                        .
                        .
                        .



//建立一個升值 ValueAnimator
ValueAnimator vUpAnimator = ValueAnimator.OfInt(1, 100);
vUpAnimator.SetDuration(1000);
vUpAnimator.Update += delegate (object sender, ValueAnimator.AnimatorUpdateEventArgs e)
{
        //設定更新對象
        var newValue = (int)e.Animation.AnimatedValue;
        var tv3 = FindViewById<TextView>(Resource.Id.textView3);
        tv3.Text = newValue.ToString();
};
//建立一個降值 ValueAnimator
ValueAnimator vDnAnimation = ValueAnimator.OfInt(100, 1);
vDnAnimation.SetDuration(1000);
vDnAnimation.Update += delegate (object sender, ValueAnimator.AnimatorUpdateEventArgs e)
{
        //設定更新對象
        var newValue = (int)e.Animation.AnimatedValue;
        var tv3 = FindViewById<TextView>(Resource.Id.textView3);
        tv3.Text = newValue.ToString();
};
//建立一個升值 ObjectAnimator
ObjectAnimator oUptAnimator = ObjectAnimator.OfInt(tv4, "MeMe", 1, 100);
oUptAnimator.SetDuration(1000);
//建立一個降值 ValueAnimator
ObjectAnimator oDntAnimator = ObjectAnimator.OfInt(tv4, "MeMe", 100, 1);
oDntAnimator.SetDuration(1000);

//建立一個動作整合器
AnimatorSet animatorSet = new AnimatorSet();
//設定動作順序
animatorSet.PlaySequentially(vUpAnimator, oUptAnimator, vDnAnimation, oDntAnimator);
//啟動動作整合器
animatorSet.Start();



重複的 AnimatorSet :
因為 AnimatorSet 沒有 RepeatCount 屬性,所以必須自己處理,處理方式不難,就是利用 AnimatorSet 結束時的事件通知,再讓 AnimatorSet 執行即可。


//獲取 AnimatorSet 結束事件
animatorSet.AnimationEnd += delegate
{
        animatorSet.Start();
}


如果想執行指定的次數,只要在 AnimationEnd 事件內弄個共享變數加個判斷即可。

其他注意事項:
1.當使用 animatorSet 的循序執行 PlaySequentially 去執行 ValueAnimator /ObjectAnimator (以下通稱 animator) 時 ,animator 的 RepeatCount 不可以設定為 Infinite 狀態,否則在指令內遇到 Infinite 的 animator 後,下一個 animator 動作就永遠不會被執行了。
2. animator 也可以執行浮點 ofFloat 、顏色 ofArgb 與物件 ofObject 的計數執行。
3. AnimatorSet 除了循序執行 PlaySequentially 也有同時執行 PlayTogether ,在這個同時執行模式下就不必擔心 animator 設定為 Infinite 執行狀態,因為不會互相干擾。
4.範例中用的 MyTextView 是繼承型的自訂 TextView,所以才有 MeMe 屬性。

Animator 這個類別命名很容易讓人以為是做為『動畫』的,但實際上畫面不是它的主要操作對象,反而比較像動畫中的『時間』與『方向』元素的操作,不過確實有了它支援,不同時間屬性與方向性的動畫物件才得以容易實現。




2017年11月10日 星期五

AngularJS 使用 $routeProvider 時輸出路徑 "/" 變更成 "%2F" 解決方式

按照 AngularJS 官方網站實做了 Wire up a Backend 的範例發現了一些問題,主要是在 routeProvider 變更瀏覽器 URL時,路徑上的 "/" 被改成了 "!/" 與指定路徑 "/new" 被換成 "%2Fnew",導製後續動作都無法解析問題:


按照它的範例執行一開始路徑就怪怪的,當按下『Add』時,沒有切換到 detail.html 的畫面,原因在於路徑出現了非預期的字元。


這個原因出在 routeProvider 回傳時把一些特殊字元進行轉換造成的問題,因此必須修改它的 config 對 routeProvider 處理程式碼片段:
(原程式碼)

.config(function($routeProvider) {
  var resolveProjects = {
    projects: function (Projects) {
      return Projects.fetch();
    }
  };

  $routeProvider
    .when('/', {
      controller:'ProjectListController as projectList',
      templateUrl:'list.html',
      resolve: resolveProjects
    })
    .when('/edit/:projectId', {
      controller:'EditProjectController as editProject',
      templateUrl:'detail.html',
      resolve: resolveProjects
    })
    .when('/new', {
      controller:'NewProjectController as editProject',
      templateUrl:'detail.html',
      resolve: resolveProjects
    })
    .otherwise({
      redirectTo:'/'
    });
})

修改:增加 $locationProvider.hashPrefix(''); 可以讓顯式的URL正確顯示
(修改後)

.config(function($routeProvider,$locationProvider) {
  var resolveProjects = {
    projects: function (Projects) {
      return Projects.fetch();
    }
  };
  $locationProvider.hashPrefix('');
  $routeProvider
    .when('/', {
      controller:'ProjectListController as projectList',
      templateUrl:'list.html',
      resolve: resolveProjects
    })
    .when('/edit/:projectId', {
      controller:'EditProjectController as editProject',
      templateUrl:'detail.html',
      resolve: resolveProjects
    })
    .when('/new', {
      controller:'NewProjectController as editProject',
      templateUrl:'detail.html',
      resolve: resolveProjects
    })
    .otherwise({
      redirectTo:'/'
    });
})




比較弔詭的是 hashPrefix 是用來設定 URL上#後面的前綴字元用的

理論上沒有指定  hashPrefix('!') 理論上URL不應該出現  /#!/  這樣的狀況

而且 / 被 URI 編譯成 %2F 的問題也不知為何?

當然強制 hashPrefix('') 不是問題,但卻一併處理了 URI 編碼問題?怎麼看都像是個BUG吧!!

關於hashPrefix的用法可以參考這邊 http://code-beginner.logdown.com/posts/334029-start-angularjs-theme-routing

官方網站的範例卻可以在官方網站正常執行,在我的網站卻不行正常有點奇怪,而且我也嘗試過各種瀏覽器都是一樣的狀況。










2017年11月9日 星期四

OA辦公椅越坐越下沉問題

一般的公司辦公室的辦公椅大概都是長成這樣子的:


搭配的辦公桌通常是固定的高度,大家都一樣高,

因此只能調整座椅的高度,而OA辦公椅因為要符合各種人身高需求,

通常但是這種椅子的結構很多種,但它的升降機構但最常見(也最便宜)的就是像螺絲一樣利用旋轉支柱來改變高低的。


但這種旋轉調整高低的椅子卻有一個很糟糕的問題,因為大部分坐OA椅的人會音工作需要旋轉椅子改變方向,但是如果沒有將這個螺旋固定住,久會導致椅子越坐越低,不知不覺坐姿改變,導致肩膀痠痛或是腰酸背痛。

總之,這種升降機結構就是一個『爛東西』,但有的廠商會稍微貼心點在旋轉柱上加上一個固定用螺絲,可以用來鎖住調整好的位置。

如果,你的公司只有這種OA椅給你用,然後也沒得選擇,你可以用點小技巧來固定椅子的高度。

只要利用束帶就可以了,找兩條(一條固定不太住)足夠長的束帶,調整好椅子高度後再稍微調高 一 ~ 二 格螺紋高度後再把束帶綁入螺紋的溝槽內(一定要綁入溝槽內,不然會滑脫),然後用老虎鉗子把束帶拉緊(如果只用手的力量束帶綁不太緊),如下:

使用一段時間後,位置只會有一點往上跑,只時再坐一次調整(再綁入一條束帶)固定住,就不會跑掉了。

2017年10月30日 星期一

Xamarin : Android : Vibrate 使用裝置震動

在 Xamarin 下開發 Android 裝置需要振動程式碼如下


void button_vibrator_Click(object sender , EventArgs ea)
{
    // 建立振動服務
    var vibrator = (Vibrator)GetSystemService(Android.Content.Context.VibratorService);
    int iDuration = 500; // 500毫秒,振動持續時間
 
    // 執行
    vibrator.Vibrate(iDuration);
}


這個 Vibrator 需要引用


using Android.OS;



這個服務屬於非同步機制,發出震動後不會等待振動時間完畢,而會繼續執行程式。

另外,還需要賦予適當的使用權限,可以在專案屬性調整


或是到 AndroidManifest.xml 裡增加


<uses-permission android:name="android.permission.VIBRATE" />


建議使用專案屬性調整會比較安全,比較不會打錯字