我須要實現本身的屬性,例如com.android.R.attr
java
在官方文檔中找不到任何內容,所以我須要有關如何定義這些屬性以及如何從個人代碼中使用它們的信息。 android
Qberticus的回答很好,可是缺乏一個有用的細節。 若是要在庫中實現這些,請替換: app
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
與: 框架
xmlns:whatever="http://schemas.android.com/apk/res-auto"
不然,使用該庫的應用程序將出現運行時錯誤。 ide
上面的答案涵蓋了全部細節,除了兩點。 函數
首先,若是沒有樣式,則將使用(Context context, AttributeSet attrs)
方法簽名實例化首選項。 在這種狀況下,只需使用context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
便可獲取TypedArray。 佈局
其次,它沒有涵蓋如何處理資源(數量字符串)。 這些不能使用TypedArray處理。 這是個人SeekBarPreference的代碼片斷,用於設置首選項摘要,並根據首選項的值將其值格式化。 若是首選項的xml將android:summary設置爲文本字符串或字符串源,則將首選項的值格式化爲字符串(應該在其中包含%d,以獲取該值)。 若是android:summary設置爲plaurals資源,則用於格式化結果。 ui
// Use your own name space if not using an android resource. final static private String ANDROID_NS = "http://schemas.android.com/apk/res/android"; private int pluralResource; private Resources resources; private String summary; public SeekBarPreference(Context context, AttributeSet attrs) { // ... TypedArray attributes = context.obtainStyledAttributes( attrs, R.styleable.SeekBarPreference); pluralResource = attrs.getAttributeResourceValue(ANDROID_NS, "summary", 0); if (pluralResource != 0) { if (! resources.getResourceTypeName(pluralResource).equals("plurals")) { pluralResource = 0; } } if (pluralResource == 0) { summary = attributes.getString( R.styleable.SeekBarPreference_android_summary); } attributes.recycle(); } @Override public CharSequence getSummary() { int value = getPersistedInt(defaultValue); if (pluralResource != 0) { return resources.getQuantityString(pluralResource, value, value); } return (summary == null) ? null : String.format(summary, value); }
onDialogClosed
方法中調用notifyChanged()
。 當前,最好的文檔是源。 您能夠在這裏(attrs.xml)進行查看。 this
您能夠在頂部<resources>
元素中或<declare-styleable>
元素內部定義屬性。 若是要在多個位置使用attr,請將其放在根元素中。 注意,全部屬性共享相同的全局名稱空間。 這意味着,即便您在<declare-styleable>
元素內建立一個新屬性,也能夠在其外部使用它,而且您不能建立具備相同名稱,不一樣類型的另外一個屬性。 spa
<attr>
元素具備兩個xml屬性name
和format
。 name
使您能夠稱呼它,這就是您最終在代碼中引用它的方式,例如R.attr.my_attribute
。 format
屬性能夠具備不一樣的值,具體取決於所需屬性的「類型」。
您可使用|
將格式設置爲多種類型|
,例如format="reference|color"
。
enum
屬性能夠定義以下:
<attr name="my_enum_attr"> <enum name="value1" value="1" /> <enum name="value2" value="2" /> </attr>
flag
屬性是類似的,除了須要定義值,以即可以將它們位在一塊兒:
<attr name="my_flag_attr"> <flag name="fuzzy" value="0x01" /> <flag name="cold" value="0x02" /> </attr>
除了屬性以外,還有<declare-styleable>
元素。 這使您能夠定義自定義視圖可使用的屬性。 您能夠經過指定<attr>
元素來執行此操做,若是先前已定義該元素,則不指定format
。 若是您想重用android attr,例如android:gravity,則可使用name
,以下所示。
自定義視圖<declare-styleable>
:
<declare-styleable name="MyCustomView"> <attr name="my_custom_attribute" /> <attr name="android:gravity" /> </declare-styleable>
在自定義視圖上以XML定義自定義屬性時,您須要作一些事情。 首先,您必須聲明一個名稱空間以找到您的屬性。 您能夠在根佈局元素上執行此操做。 一般只有xmlns:android="http://schemas.android.com/apk/res/android"
。 如今,您還必須添加xmlns:whatever="http://schemas.android.com/apk/res-auto"
。
例:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:whatever="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <org.example.mypackage.MyCustomView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" whatever:my_custom_attribute="Hello, world!" /> </LinearLayout>
最後,要訪問該自定義屬性,一般能夠在自定義視圖的構造函數中進行以下操做。
public MyCustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0); String str = a.getString(R.styleable.MyCustomView_my_custom_attribute); //do something with str a.recycle(); }
結束。 :)
傳統方法充滿了樣板代碼和笨拙的資源處理。 這就是爲何我製做了Spyglass框架 。 爲了演示其工做原理,下面的示例顯示瞭如何製做顯示String標題的自定義視圖。
步驟1:建立一個自定義視圖類。
public class CustomView extends FrameLayout { private TextView titleView; public CustomView(Context context) { super(context); init(null, 0, 0); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0, 0); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs, defStyleAttr, 0); } @RequiresApi(21) public CustomView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(attrs, defStyleAttr, defStyleRes); } public void setTitle(String title) { titleView.setText(title); } private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) { inflate(getContext(), R.layout.custom_view, this); titleView = findViewById(R.id.title_view); } }
步驟2:在values/attrs.xml
資源文件中定義一個字符串屬性:
<resources> <declare-styleable name="CustomView"> <attr name="title" format="string"/> </declare-styleable> </resources>
步驟3:將@StringHandler
批註應用於setTitle
方法,以告知Spyglass框架在視圖放大時將屬性值路由到此方法。
@HandlesString(attributeId = R.styleable.CustomView_title) public void setTitle(String title) { titleView.setText(title); }
如今您的類具備Spyglass批註,Spyglass框架將在編譯時對其進行檢測並自動生成CustomView_SpyglassCompanion
類。
步驟4:在自定義視圖的init
方法中使用生成的類:
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) { inflate(getContext(), R.layout.custom_view, this); titleView = findViewById(R.id.title_view); CustomView_SpyglassCompanion .builder() .withTarget(this) .withContext(getContext()) .withAttributeSet(attrs) .withDefaultStyleAttribute(defStyleAttr) .withDefaultStyleResource(defStyleRes) .build() .callTargetMethodsNow(); }
而已。 如今,當您從XML實例化類時,Spyglass伴隨程序將解釋屬性並進行所需的方法調用。 例如,若是咱們增長如下佈局,則setTitle
將被調用"Hello, World!"
做爲論點。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:width="match_parent" android:height="match_parent"> <com.example.CustomView android:width="match_parent" android:height="match_parent" app:title="Hello, World!"/> </FrameLayout>
該框架不只限於字符串資源,還具備許多用於處理其餘資源類型的不一樣註釋。 它還具備用於定義默認值和用於在方法具備多個參數的狀況下傳遞佔位符值的註釋。
有關更多信息和示例,請查看Github存儲庫。
若是您省略了attr
元素的format
屬性,則可使用它來引用XML佈局中的類。
Refactor > Rename
做品 Find Usages
有效 不要在... / src / main / res / values / attrs.xml中指定format
屬性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyCustomView"> .... <attr name="give_me_a_class"/> .... </declare-styleable> </resources>
在某些佈局文件中使用它... / src / main / res / layout / activity__main_menu.xml
<?xml version="1.0" encoding="utf-8"?> <SomeLayout xmlns:app="http://schemas.android.com/apk/res-auto"> <!-- make sure to use $ dollar signs for nested classes --> <MyCustomView app:give_me_a_class="class.type.name.Outer$Nested/> <MyCustomView app:give_me_a_class="class.type.name.AnotherClass/> </SomeLayout>
在您的視圖初始化代碼中解析類... / src / main / java /.../ MyCustomView.kt
class MyCustomView( context:Context, attrs:AttributeSet) :View(context,attrs) { // parse XML attributes .... private val giveMeAClass:SomeCustomInterface init { context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply() { try { // very important to use the class loader from the passed-in context giveMeAClass = context::class.java.classLoader!! .loadClass(getString(R.styleable.MyCustomView_give_me_a_class)) .newInstance() // instantiate using 0-args constructor .let {it as SomeCustomInterface} } finally { recycle() } } }