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  程式碼,發現可以用了耶!!

留言

這個網誌中的熱門文章

【研究】列印的條碼為什麼很難刷(掃描)

統一發票列印小程式

C# 使用 Process.Start 執行外部程式