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" />


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

2017年10月28日 星期六

Xamarin : Android : playback a tone 發出警告聲(簡單聲音)

在 Xamarin 下開發 Android 讓裝置發出 警告聲音 或是 提示聲音


void button_beep_Click(object sender , EventArgs ea)
{
    int iVolume = 100; // 0 ~ 100 音量,但不是裝置實際音量,而是產生音波的音量
    int iDuration = 500; // 500毫秒,聲音持續時間
    // 建立執行物件
    var tonGen = new ToneGenerator(Android.Media.Stream.Music, iVolume);

    //播放聲音
    //tonGen.StartTone(Android.Media.Tone.CdmaAlertAutoredialLite,1000);
    //tonGen.StartTone(Android.Media.Tone.CdmaAlertCallGuard, 200);
    //tonGen.StartTone(Android.Media.Tone.CdmaAlertIncallLite, iDuration );
    //tonGen.StartTone(Android.Media.Tone.CdmaAlertNetworkLite, iDuration );
    tonGen.StartTone(Android.Media.Tone.PropBeep, iDuration );
}


Android.Media.Tone 底下列舉不少基本 DTMF 的音調,可以自行選擇測試
這個元件比較適合播放訊息聲音(比較簡單的音調)。

如果要播放音樂建議使用 AudioTrack 物件。

注意這個播放是屬於非同步的,而且會以最後一次呼叫StartTone的聲音為主,如果同時播放多個聲音檔,則只會聽到最後一個聲音。


2017年10月27日 星期五

Xamarin : Android : Lock Screen(View) Orientation 禁止螢幕(畫面)旋轉

在 Xamarin 開發 Android 的時候如果要禁止畫面旋轉 要在 Activity 的程式碼使用下面方式(黃色部分)宣告


[Activity(Label = "ZxingTest", MainLauncher = true, Icon = "@drawable/icon" ,ScreenOrientation =Android.Content.PM.ScreenOrientation.Portrait)]


Android.Content.PM.ScreenOrientation 裡面有些列舉屬性,像是:
Android.Content.PM.ScreenOrientation.Portrait (豎屏;直向畫面)
Android.Content.PM.ScreenOrientation.Landscape (橫屏;橫向畫面)

而大部分網路上說修改 AndroidManifest.xml 裡面更改
<activity> 屬性增加 android:screenOrientation="portrait"等這類方式都是
 Android Studio 的開發方式 不是 Ms Visual Studio 的開發方式,所以特別紀錄一下。


為什麼要禁止螢幕旋轉:

除了特定版面排版特性之外,當每次旋轉螢幕時 Activity 和 View都會被重載 (Reload/Redraw) ,此時畫面上所有物件都會被 摧毀並釋放記憶體空間,由於這個特性,在畫面上的資料如果沒被儲存就會消失的一乾二淨。

當然可以利用 OnSaveInstanceState 和 OnRestoreInstanceState 事件覆寫來處理資料儲存和還原,但是如果不想做太複雜,就直接設定禁止畫面旋轉就可以了。

2017年10月25日 星期三

Xamarin : Android : Using ZXing.Net.Mobile Scan Barcode 掃描讀取條碼/QR碼

網路上大部份文章在講 Xamarin 開發都用上了 CPL類型的專案,
對於純粹使用 Android 專案使用者要理解真是有點不方便,

這裡是記錄了在 Xamarin 下的 Android 專案如何開發一個讀取 條碼或是 QR碼的功能

個功能是基於 ZXing.Net.Mobile 套件開發的,可以自行把這個套件應用你的專案中

1、假設你經開啟了一個 Android blank Project (空白專案)

2、使用 NuGet 管理員去新增 ZXing.Net.Mobile 套件,並把它安裝到專案中


3、然後到你要呼叫使用 ZXing 操作的 Activity 去新增引用(在這案例裡我是放在 MainActivity.cs 內)

using ZXing;
using ZXing.Mobile;


4、在顯示畫面(Main.axml)上設計兩個 TextView 和一個 Button,用來啟動掃描和接收掃描的結果


5、然後就是把操作 ZXing 的程式碼加入,在這案例中,我只簡單全部做在 MainActivity 的 OnCreate 內,實際應用上你可以依照需要放置。

using Android.App;
using Android.Widget;
using Android.OS;
using ZXing;
using ZXing.Mobile;

namespace ZxingTest
{
    [Activity(Label = "ZxingTest", MainLauncher = true, Icon = "@drawable/icon")]

    public class MainActivity : Activity
    {
        //建立操作物件指標
        private TextView _barcodeFormat, _barcodeData;

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

            // 設定顯示畫面
            SetContentView (Resource.Layout.Main);

            //掃描元件初始化
            MobileBarcodeScanner.Initialize(Application);

            //簡化操作的位置(變數指標)
            _barcodeFormat = FindViewById<TextView>(Resource.Id.barcode_format);
            _barcodeData = FindViewById<TextView>(Resource.Id.barcode_data);

            //建立按鈕 Click 事件
            var button = FindViewById<Button>(Resource.Id.button_scan);
            button.Click += async (Senders, args) =>
            {
                //設定掃描元件操作
                var opts = new MobileBarcodeScanningOptions
                {
                    //限定(啟用)可掃描識別種類
                    PossibleFormats = new System.Collections.Generic.List<BarcodeFormat>
                    {
                        BarcodeFormat.CODE_128,
                        BarcodeFormat.CODE_39,
                        BarcodeFormat.EAN_13,
                        BarcodeFormat.EAN_8,
                        BarcodeFormat.QR_CODE
                    }
                };
                //建立可執行化實例
                var scanner = new MobileBarcodeScanner();
                //利用 await 執行掃描,等待回應
                var result = await scanner.Scan(opts);

                //將回傳結果輸出到 TextView
                _barcodeFormat.Text = result?.BarcodeFormat.ToString() ?? string.Empty;
                _barcodeData.Text = result?.Text ?? string.Empty;
            };
        }
    }
}


6、實際操作
主畫面


掃描


返回掃描結果


參考文章來源:
Xamarin Android 二维码扫描示例– Design based .NET