Android 創(chuàng)建自定義的View類

2018-08-02 18:13 更新

編寫:kesenhoo - 原文:http://developer.android.com/training/custom-views/create-view.html

設(shè)計良好的類總是相似的。它使用一個好用的接口來封裝一個特定的功能,它有效的使用CPU與內(nèi)存,等等。為了成為一個設(shè)計良好的類,自定義的view應(yīng)該:

  • 遵守Android標(biāo)準(zhǔn)規(guī)則。
  • 提供自定義的風(fēng)格屬性值并能夠被Android XML Layout所識別。
  • 發(fā)出可訪問的事件。
  • 能夠兼容Android的不同平臺。

Android的framework提供了許多基類與XML標(biāo)簽用來幫助你創(chuàng)建一個符合上面要求的View。這節(jié)課會介紹如何使用Android framework來創(chuàng)建一個view的核心功能。

繼承一個View

Android framework里面定義的view類都繼承自View。你自定義的view也可以直接繼承View,或者你可以通過繼承既有的一個子類(例如Button)來節(jié)約一點時間。

為了讓Android Developer Tools能夠識別你的view,你必須至少提供一個constructor,它包含一個Contenx與一個AttributeSet對象作為參數(shù)。這個constructor允許layout editor創(chuàng)建并編輯你的view的實例。

class PieChart extends View {
    public PieChart(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

定義自定義屬性

為了添加一個內(nèi)置的View到你的UI上,你需要通過XML屬性來指定它的樣式與行為。良好的自定義views可以通過XML添加和改變樣式,為了讓你的自定義的view也有如此的行為,你應(yīng)該:

  • 為你的view在資源標(biāo)簽下定義自設(shè)的屬性
  • 在你的XML layout中指定屬性值
  • 在運行時獲取屬性值
  • 把獲取到的屬性值應(yīng)用在你的view上

這一節(jié)討論如何定義自定義屬性以及指定屬性值,下一節(jié)將會實現(xiàn)在運行時獲取屬性值并將它應(yīng)用。

為了定義自設(shè)的屬性,添加 資源到你的項目中。放置于res/values/attrs.xml文件中。下面是一個attrs.xml文件的示例:

<resources>
   <declare-styleable name="PieChart">
       <attr name="showText" format="boolean" />
       <attr name="labelPosition" format="enum">
           <enum name="left" value="0"/>
           <enum name="right" value="1"/>
       </attr>
   </declare-styleable>
</resources>

上面的代碼聲明了2個自設(shè)的屬性,showTextlabelPosition,它們都歸屬于PieChart的項目下的styleable實例。styleable實例的名字,通常與自定義的view名字一致。盡管這并沒有嚴(yán)格規(guī)定要遵守這個convention,但是許多流行的代碼編輯器都依靠這個命名規(guī)則來提供statement completion。

一旦你定義了自設(shè)的屬性,你可以在layout XML文件中使用它們,就像內(nèi)置屬性一樣。唯一不同的是你自設(shè)的屬性是歸屬于不同的命名空間。不是屬于http://schemas.android.com/apk/res/android的命名空間,它們歸屬于http://schemas.android.com/apk/res/[your package name]。例如,下面演示了如何為PieChart使用上面定義的屬性:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
 <com.example.customviews.charting.PieChart
     custom:showText="true"
     custom:labelPosition="left" />
</LinearLayout>

為了避免輸入長串的namespace名字,示例上面使用了xmlns指令,這個指令可以指派custom作為http://schemas.android.com/apk/res/com.example.customviewsnamespace的別名。你也可以選擇其他的別名作為你的namespace。

請注意,如果你的view是一個inner class,你必須指定這個view的outer class。同樣的,如果PieChart有一個inner class叫做PieView。為了使用這個類中自設(shè)的屬性,你應(yīng)該使用com.example.customviews.charting.PieChart$PieView.

應(yīng)用自定義屬性

當(dāng)view從XML layout被創(chuàng)建的時候,在xml標(biāo)簽下的屬性值都是從resource下讀取出來并傳遞到view的constructor作為一個AttributeSet參數(shù)。盡管可以從AttributeSet中直接讀取數(shù)值,可是這樣做有些弊端:

  • 擁有屬性的資源并沒有經(jīng)過解析
  • Styles并沒有運用上

翻譯注:通過 attrs 的方法是可以直接獲取到屬性值的,但是不能確定值類型,如:

String title = attrs.getAttributeValue(null, "title");
int resId = attrs.getAttributeResourceValue(null, "title", 0);
title = context.getText(resId));

都能獲取到 "title" 屬性,但你不知道值是字符串還是resId,處理起來就容易出問題,下面的方法則能在編譯時就發(fā)現(xiàn)問題

取而代之的是,通過obtainStyledAttributes()來獲取屬性值。這個方法會傳遞一個TypedArray對象,它是間接referenced并且styled的。

Android資源編譯器幫你做了許多工作來使調(diào)用obtainStyledAttributes())更簡單。對res目錄里的每一個<declare-styleable>資源,自動生成的R.java文件定義了存放屬性ID的數(shù)組和常量,常量用來索引數(shù)組中每個屬性。你可以使用這些預(yù)先定義的常量來從TypedArray中讀取屬性。這里就是PieChart類如何讀取它的屬性:

public PieChart(Context context, AttributeSet attrs) {
   super(context, attrs);
   TypedArray a = context.getTheme().obtainStyledAttributes(
        attrs,
        R.styleable.PieChart,
        0, 0);

   try {
       mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
       mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
   } finally {
       a.recycle();
   }
}

清注意TypedArray對象是一個共享資源,必須被在使用后進行回收。

添加屬性和事件

Attributes是一個強大的控制view的行為與外觀的方法,但是他們僅僅能夠在view被初始化的時候被讀取到。為了提供一個動態(tài)的行為,需要暴露出一些合適的getter 與setter方法。下面的代碼演示了如何使用這個技巧:

public boolean isShowText() {
   return mShowText;
}

public void setShowText(boolean showText) {
   mShowText = showText;
   invalidate();
   requestLayout();
}

請注意,在setShowText方法里面有調(diào)用invalidate()) and requestLayout()). 這兩個調(diào)用是確保穩(wěn)定運行的關(guān)鍵。當(dāng)view的某些內(nèi)容發(fā)生變化的時候,需要調(diào)用invalidate來通知系統(tǒng)對這個view進行redraw,當(dāng)某些元素變化會引起組件大小變化時,需要調(diào)用requestLayout方法。調(diào)用時若忘了這兩個方法,將會導(dǎo)致hard-to-find bugs。

自定義的view也需要能夠支持響應(yīng)事件的監(jiān)聽器。例如,PieChart暴露了一個自定義的事件OnCurrentItemChanged來通知監(jiān)聽器,用戶已經(jīng)切換了焦點到一個新的組件上。

我們很容易忘記了暴露屬性與事件,特別是當(dāng)你是這個view的唯一用戶時。請花費一些時間來仔細定義你的view的交互。一個好的規(guī)則是總是暴露任何屬性與事件。

設(shè)計可訪問性

自定義view應(yīng)該支持廣泛的用戶群體,包含一些不能看到或使用觸屏的殘障人士。為了支持殘障人士,我們應(yīng)該:

  • 使用android:contentDescription屬性標(biāo)記輸入字段。
  • 在適當(dāng)?shù)臅r候通過調(diào)用sendAccessibilityEvent() 發(fā)送訪問事件。
  • 支持備用控制器,如方向鍵(D-pad)和軌跡球(trackball)等。

對于創(chuàng)建使用的 views的更多消息, 請參見Android Developers Guide中的 Making Applications Accessible 。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號