欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android自定義View構造函數詳解

 更新時間:2016年10月13日 15:26:55   作者:低調小一  
這篇文章主要為大家詳細介紹了Android自定義View構造函數,具有一定的參考價值,感興趣的小伙伴們可以參考一下

初始Custom View的構造函數

之前寫過一篇實現(xiàn)圓形進度條的博客(自定義圓形進度條),通常我們在實現(xiàn)Custom View的時候,都會先繼承View并實現(xiàn)View的三個構造函數,例如:

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

public class MyCustomView extends View {
 /**
  * 第一個構造函數
  */
 public MyCustomView(Context context) {
  this(context, null);
 }

 /**
  * 第二個構造函數
  */
 public MyCustomView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 /**
  * 第三個構造函數
  */
 public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  // TODO:獲取自定義屬性
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
 }
}

網上有很多關于三個構造函數使用時機的說法,但是說法正確的卻沒有幾家,這里正式的給大家科普一下:

在代碼中直接new一個Custom View實例的時候,會調用第一個構造函數.這個沒有任何爭議.
在xml布局文件中調用Custom View的時候,會調用第二個構造函數.這個也沒有爭議.
在xml布局文件中調用Custom View,并且Custom View標簽中還有自定義屬性時,這里調用的還是第二個構造函數.
也就是說,系統(tǒng)默認只會調用Custom View的前兩個構造函數,至于第三個構造函數的調用,通常是我們自己在構造函數中主動調用的(例如,在第二個構造函數中調用第三個構造函數).

至于自定義屬性的獲取,通常是在構造函數中通過obtainStyledAttributes函數實現(xiàn)的.這里先介紹一下如何生成Custom View的自定義屬性.

生成Custom View的自定義屬性

Custom View添加自定義屬性主要是通過declare-styleable標簽為其配置自定義屬性,具體做法是: 在res/values/目錄下增加一個resources xml文件,示例如下(res/values/attrs_my_custom_view.xml):

<resources>
 <declare-styleable name="MyCustomView">
  <attr name="custom_attr1" format="string" />
  <attr name="custom_attr2" format="string" />
  <attr name="custom_attr3" format="string" />
  <attr name="custom_attr4" format="string" />
 </declare-styleable>
 <attr name="custom_attr5" format="string" />
</resources>

在上述xml文件中,我們聲明了一個自定義屬性集MyCustomView,其中包含了custom_attr1,custom_att2,custom_attr3,custom_attr4四個屬性.同時,我們還聲明了一個獨立的屬性custom_attr5.

所有resources文件中聲明的屬性都會在R.attr類中生成對應的成員變量:

public final class R {
 public static final class attr {
  public static final int custom_attr1=0x7f010038;
  public static final int custom_attr2=0x7f010039;
  public static final int custom_attr3=0x7f01003a;
  public static final int custom_attr4=0x7f01003b;
  public static final int custom_attr5=0x7f010000;
 }
}

但是聲明在標簽中的屬性,系統(tǒng)還會在R.styleable類中生成相關的成員變量:

public static final class styleable {
  public static final int[] MyCustomView = {
   0x7f010038, 0x7f010039, 0x7f01003a, 0x7f01003b
  };
  public static final int MyCustomView_custom_attr1 = 0;
  public static final int MyCustomView_custom_attr2 = 1;
  public static final int MyCustomView_custom_attr3 = 2;
  public static final int MyCustomView_custom_attr4 = 3;
}

可以看出,R.styleable.MyCustomView是一個數組,其中的元素值恰好就是R.attr.custom_attr1~R.attr.custom_attr4的值.而下面的MyCustomView_custom_attr1~MyCustomView_custom_attr4正好就是其對應的索引.

知道了這些之后,我們就可以來學習一下,如何在Custom View的構造函數中獲取自定義屬性的值了.

在Custom View的構造函數中獲取自定義屬性

在第三個構造函數中獲取自定義屬性的代碼如下:

public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);

 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);

 String attr1 = ta.getString(R.styleable.MyCustomView_custom_attr1);
 String attr2 = ta.getString(R.styleable.MyCustomView_custom_attr2);
 String attr3 = ta.getString(R.styleable.MyCustomView_custom_attr3);
 String attr4 = ta.getString(R.styleable.MyCustomView_custom_attr4);

 Log.e("customview", "attr1=" + attr1);
 Log.e("customview", "attr2=" + attr2);
 Log.e("customview", "attr3=" + attr3);
 Log.e("customview", "attr4=" + attr4);
 ta.recycle();
 }

關于自定義屬性的獲取,我們主要是調用了context.obtainStyledAttributes這個函數,相信這個函數大家自定義View的時候都用的很熟練了.我們來看一下這個函數的源碼實現(xiàn):

public final TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs) {
 return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
}

通過對源碼的追蹤,我們發(fā)現(xiàn)context的兩個參數的obtainStyledAttributes方法最終是調用了Theme的4個參數的obtainStyledAttributes方法.我們來看一下這個函數的源碼實現(xiàn):

public TypedArray obtainStyledAttributes(AttributeSet set,
  @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
 final int len = attrs.length;
 final TypedArray array = TypedArray.obtain(Resources.this, len);

 // XXX note that for now we only work with compiled XML files.
 // To support generic XML files we will need to manually parse
 // out the attributes from the XML file (applying type information
 // contained in the resources and such).
 final XmlBlock.Parser parser = (XmlBlock.Parser)set;
 AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
   parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices);

 array.mTheme = this;
 array.mXml = parser;

 if (false) {
  int[] data = array.mData;

  System.out.println("Attributes:");
  String s = " Attrs:";
  int i;
  for (i=0; i<set.getAttributeCount(); i++) {
   s = s + " " + set.getAttributeName(i);
   int id = set.getAttributeNameResource(i);
   if (id != 0) {
    s = s + "(0x" + Integer.toHexString(id) + ")";
   }
   s = s + "=" + set.getAttributeValue(i);
  }
  System.out.println(s);
  s = " Found:";
  TypedValue value = new TypedValue();
  for (i=0; i<attrs.length; i++) {
   int d = i*AssetManager.STYLE_NUM_ENTRIES;
   value.type = data[d+AssetManager.STYLE_TYPE];
   value.data = data[d+AssetManager.STYLE_DATA];
   value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
   value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
   s = s + " 0x" + Integer.toHexString(attrs[i])
    + "=" + value;
  }
  System.out.println(s);
 }

 return array;
}

這里就不做過多的源碼講解,而是把這四個參數的含義解釋給大家:

AttributeSet set: 屬性值的集合.

int[] attrs: 我們自定義屬性集合在R類中生成的int型數組.這個數組中包含了自定義屬性的資源ID.
int defStyleAttr: 這是當前Theme中的包含的一個指向style的引用.當我們沒有給自定義View設置declare-styleable資源集合時,默認從這個集合里面查找布局文件中配置屬性值.傳入0表示不像該defStyleAttr中查找默認值.
int defStyleRes: 這個也是一個指向Style的資源ID,但是僅在defStyleAttr為0或者defStyleAttr不為0但Theme中沒有為defStyleAttr屬性賦值時起作用.

由于一個屬性可以在很多地方對其進行賦值,包括: XML布局文件中、decalare-styleable、theme中等,它們之間是有優(yōu)先級次序的,按照優(yōu)先級從高到低排序如下:

屬性賦值優(yōu)先級次序表:

在布局xml中直接定義 > 在布局xml中通過style定義 > 自定義View所在的Activity的Theme中指定style引用 > 構造函數中defStyleRes指定的默認值

為了讓大家有更清楚更直觀的了解,再接下來設置自定義屬性的章節(jié)中,我將對custom_attr1~4這4個屬性分別在上述四個地方進行定義,然后在Custom View的構造函數中獲取它的值,從而看一下,優(yōu)先級順序是否和我們預期的一樣.

設置自定義屬性值

以下的第幾個參數均是針對Resources.Theme類的obtainStyledAttributes四參數構造方法來說明的.

第二個參數——在布局xml文件中為屬性賦值

在設置自定義屬性之前,我們首先要在主Activity的布局文件中調用我們的Custom View,并且為其設置特定的屬性.

主布局文件內容如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:custom="http://schemas.android.com/apk/res-auto"
 android:id="@+id/container"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <com.kevintan.eventbussample.view.MyCustomView
  android:id="@+id/id_custom_view"
  android:layout_width="400dp"
  android:layout_height="400dp"
  custom:custom_attr1="attr1_xml"
  style="@style/TestCustomView"/>

</FrameLayout>

示例結果:

05-28 17:19:56.542 23575-23575/? E/customview: attr1=attr1_xml
05-28 17:19:56.542 23575-23575/? E/customview: attr2=null
05-28 17:19:56.542 23575-23575/? E/customview: attr3=null
05-28 17:19:56.542 23575-23575/? E/customview: attr4=null

注意:

在給自定義屬性賦值時,首先需要增加自定義屬性的命名空間,例如: xmlns:custom=”http://schemas.Android.com/apk/res-auto”,Android Studio推薦使用res-auto,在Eclipse中需要使用Custom View所在的包名: xmlns:cv=”http://schemas.android.com/apk/com.kevintan.eventbussample.view”
這里,在布局文件中我們?yōu)閏ustom_attr1賦值為: attr1_xml.自定義View中獲取該屬性值對應了getTheme().obtainStyledAttributes方法中的第二個參數@StyleableRes int[] attrs

第二個參數——在style中為屬性賦值

其次,自定義屬性還可以在Style中進行賦值.

首先,我們在xml布局文件中還為MyCustomView增加一個自定義的style,style代碼如下:

<style name="TestCustomView">
 <item name="custom_attr1">attr1_style</item>
 <item name="custom_attr2">attr2_style</item>
</style>

然后,我們修改布局文件,增加style字段:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:custom="http://schemas.android.com/apk/res-auto"
 android:id="@+id/container"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <com.kevintan.eventbussample.view.MyCustomView
  android:id="@+id/id_custom_view"
  android:layout_width="400dp"
  android:layout_height="400dp"
  custom:custom_attr1="attr1_xml"
  style="@style/TestCustomView"/>

</FrameLayout>

示例結果:

05-28 17:19:56.542 23575-23575/? E/customview: attr1=attr1_xml
05-28 17:19:56.542 23575-23575/? E/customview: attr2=attr2_style
05-28 17:19:56.542 23575-23575/? E/customview: attr3=null
05-28 17:19:56.542 23575-23575/? E/customview: attr4=null

這里我們再次對custom_attr1屬性進行了賦值,同時我們對custom_attr2也進行了賦值.

小提示:

聰明的同學肯定都猜到我這樣賦值的作用了,但是還是要簡述一下:
對于custom_attr1,我們在xml布局文件、style、defStyle和theme中均進行賦值,那最終得到的結果必然能證實誰的優(yōu)先級最高.
對于custom_attr2,我們在style、defStyle和theme中進行賦值,通過得到的結果我們能知道誰的優(yōu)先級第二高.
對于custom_attr3和custom_attr4的賦值情況我就不多解釋了,我相信大家都懂得?。?br /> 同時,還需要大家注意的是,只要是layout布局文件中,無論是通過namespace直接為屬性賦值,還是通過style為屬性賦值,在構造函數獲取時都對應了getTheme().obtainStyledAttributes方法中的第二個參數@StyleableRes int[] attrs

第三個參數defStyleAttr

這個參數的意思是:

原文: An attribute in the current theme that contains areference to a style resource that supplies defaults values for the TypedArray. Can be 0 to not look for defaults.
翻譯: 這是當前Theme中的包含的一個指向style的引用.當我們沒有給自定義View設置declare-styleable資源集合時,默認從這個集合里面查找布局文件中配置屬性值.傳入0表示不向該defStyleAttr中查找默認值.
為了測試該參數的作用和優(yōu)先級,我們需要進行如下操作.

首先, 我們先聲明一個refrence格式的屬性, 用于表示style的引用.聲明在之前的res/values/attrs_my_custom_view.xml文件里即可:

<attr name="MyCustomViewDefStyleAttr" format="reference"/>

然后,需要到AndroidManifest.xml中查看包含該自定義View的Activity所使用的主題是:

<activity>
 android:name="com.kevintan.eventbussample.MainActivity"
 android:theme="@style/AppTheme"
 android:label="@string/app_name" >
 <intent-filter>
  <action android:name="android.intent.action.MAIN" />
  <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
</activity>

最后,在style.xml中的AppTheme主題下增加MyCustomViewDefStyleAttr的引用實現(xiàn).

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
 <!-- Customize your theme here. -->
 <item name="MyCustomViewDefStyleAttr">@style/MyCustomViewDefStyleAttrImpl</item>
</style>

<style name="MyCustomViewDefStyleAttrImpl">
 <item name="custom_attr1">attr1_defStyleAttr</item>
 <item name="custom_attr2">attr2_defStyleAttr</item>
 <item name="custom_attr3">attr3_defStyleAttr</item>
</style>

代碼驗證,記住如果要使用obtainStyledAttributes方法的第三個參數, 就需要在第三個構造函數中顯示的調用getTheme()的obtainStyledAttributes方法.

public MyCustomView(Context context, AttributeSet attrs) {
 // 為defStyleAttr進行賦值
 this(context, attrs, R.attr.MyCustomViewDefStyleAttr);
}

public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);

 TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyleAttr, 0);

 String attr1 = ta.getString(R.styleable.MyCustomView_custom_attr1);
 String attr2 = ta.getString(R.styleable.MyCustomView_custom_attr2);
 String attr3 = ta.getString(R.styleable.MyCustomView_custom_attr3);
 String attr4 = ta.getString(R.styleable.MyCustomView_custom_attr4);

 Log.e("customview", "attr1=" + attr1);
 Log.e("customview", "attr2=" + attr2);
 Log.e("customview", "attr3=" + attr3);
 Log.e("customview", "attr4=" + attr4);
 ta.recycle();
}

代碼中,有兩點需要大家注意:

我在第二個構造參數中已經對defStyleAttr進行了賦值,第三個構造參數直接使用傳入參數即可.
第三個構造參數中,我使用了getTheme()的obtainStyledAttributes方法來代替context的2個參數的obtainStyledAttributes構造方法.
示例結果:

05-28 17:19:56.542 23575-23575/? E/customview: attr1=attr1_xml
05-28 17:19:56.542 23575-23575/? E/customview: attr2=attr2_style
05-28 17:19:56.542 23575-23575/? E/customview: attr3=attr3_defStyleAttr
05-28 17:19:56.542 23575-23575/? E/customview: attr4=null

從結果可以看出,在主題中指定style引用的優(yōu)先級是低于在xml中直接賦值和使用style字段的.

同時,我們還需要了解一點:
在Android系統(tǒng)中的控件,很多都在構造參數中使用了第三個參數,例如Button.這樣做的好處是: 當我們切換不同的主題時,Button的樣式也能隨之進行改變.

第四個參數——通過defStyleRes為屬性賦值

這個參數的意思是:

原文: A resource identifier of a style resource that supplies default values for the TypedArray, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults.
翻譯: 這是一個指向Style的資源ID,但是僅在defStyleAttr為0或者defStyleAttr不為0但Theme中沒有為defStyleAttr屬性賦值時起作用.
通過翻譯,我們可以明確兩點:

1.defStyleRes: 指向一個style引用.
2.defStyleRes的優(yōu)先級低于defStyleAttr.
為了驗證,我們先在theme.xml文件中定義一個style:

<style name="MyCustomViewDefStyleRes">
 <item name="custom_attr1">attr1_defStyleRes</item>
 <item name="custom_attr2">attr2_defStyleRes</item>
 <item name="custom_attr3">attr3_defStyleRes</item>
 <item name="custom_attr4">attr4_defStyleRes</item>
</style>

然后,我們在自定義View的第三個構造函數中的obtainStyledAttributes函數中進行賦值,具體方法如下:

public MyCustomView(Context context, AttributeSet attrs) {
 this(context, attrs, R.attr.MyCustomViewDefStyleAttr);
}

public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);

 // 為defStyleRes進行賦值
 TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyleAttr, R.style.MyCustomViewDefStyleRes);

 String attr1 = ta.getString(R.styleable.MyCustomView_custom_attr1);
 String attr2 = ta.getString(R.styleable.MyCustomView_custom_attr2);
 String attr3 = ta.getString(R.styleable.MyCustomView_custom_attr3);
 String attr4 = ta.getString(R.styleable.MyCustomView_custom_attr4);

 Log.e("customview", "attr1=" + attr1);
 Log.e("customview", "attr2=" + attr2);
 Log.e("customview", "attr3=" + attr3);
 Log.e("customview", "attr4=" + attr4);
 ta.recycle();
}

測試結果:

05-28 17:44:09.282 3137-3137/? E/customview: attr1=attr1_xml
05-28 17:44:09.282 3137-3137/? E/customview: attr2=attr2_style
05-28 17:44:09.282 3137-3137/? E/customview: attr3=attr3_defStyleAttr
05-28 17:44:09.282 3137-3137/? E/customview: attr4=null

重點:
如果大家認真的看實驗結果,肯定會被上面的結果感到奇怪,明明指定了defStyleRes,為什么attr4的值還是null?
是因為之前講過defStyleRes的使用優(yōu)先級:只有當defStyleAttr為0或者當前Theme中沒有給defStyleAttr屬性賦值時才起作用.

所以,這里我們需要修改構造函數,將defStyleAttr設置為0.

public MyCustomView(Context context, AttributeSet attrs) {
 // 為了驗證defStyleRes的作用,將defStyleAttr設置為0
 this(context, attrs, 0);
}

public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);

 TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyleAttr, R.style.MyCustomViewDefStyleRes);

 String attr1 = ta.getString(R.styleable.MyCustomView_custom_attr1);
 String attr2 = ta.getString(R.styleable.MyCustomView_custom_attr2);
 String attr3 = ta.getString(R.styleable.MyCustomView_custom_attr3);
 String attr4 = ta.getString(R.styleable.MyCustomView_custom_attr4);

 Log.e("customview", "attr1=" + attr1);
 Log.e("customview", "attr2=" + attr2);
 Log.e("customview", "attr3=" + attr3);
 Log.e("customview", "attr4=" + attr4);
 ta.recycle();
}

最終結果:

05-28 17:49:03.707 5772-5772/? E/customview: attr1=attr1_xml
05-28 17:49:03.707 5772-5772/? E/customview: attr2=attr2_style
05-28 17:49:03.707 5772-5772/? E/customview: attr3=attr3_defStyleRes
05-28 17:49:03.707 5772-5772/? E/customview: attr4=attr4_defStyleRes

后記

在文章結尾, 我們再次總結一下自定義屬性的屬性賦值優(yōu)先級:

在布局xml中直接定義 > 在布局xml中通過style定義 > 自定義View所在的Activity的Theme中指定style引用 > 構造函數中defStyleRes指定的默認值.

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • Android中Bitmap、File與Uri之間的簡單記錄

    Android中Bitmap、File與Uri之間的簡單記錄

    這篇文章主要給大家介紹了關于Android中Bitmap、File與Uri之間的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-02-02
  • 詳解Android廣播Broadcast的啟動流程

    詳解Android廣播Broadcast的啟動流程

    這篇文章主要為大家介紹了Android廣播Broadcast啟動流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • Android實現(xiàn)獲取應用程序相關信息列表的方法

    Android實現(xiàn)獲取應用程序相關信息列表的方法

    這篇文章主要介紹了Android實現(xiàn)獲取應用程序相關信息列表的方法,是應用管理器常用的功能,需要的朋友可以參考下
    2014-07-07
  • Android自定義PasswordInputView密碼輸入

    Android自定義PasswordInputView密碼輸入

    這篇文章主要為大家詳細介紹了Android自定義PasswordInputView密碼輸入功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • 深入淺出學習Android ListView基礎

    深入淺出學習Android ListView基礎

    這篇文章主要介紹了深入淺出的帶領大家學習Android ListView基礎,ListView是安卓里常用的控件,本文介紹一下常用用法,以及優(yōu)化等方法,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android實現(xiàn)倒計時效果

    Android實現(xiàn)倒計時效果

    這篇文章主要為大家詳細介紹了Android實現(xiàn)倒計時效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • Android中bindService基本使用方法概述

    Android中bindService基本使用方法概述

    這篇文章主要介紹了Android中bindService基本使用方法,詳細解釋了bindService的基本使用概述及其生命周期,需要的朋友可以參考下
    2015-12-12
  • 利用adt-bundle輕松搭建Android開發(fā)環(huán)境與Hello world(Windows)

    利用adt-bundle輕松搭建Android開發(fā)環(huán)境與Hello world(Windows)

    這篇文章主要介紹了利用adt-bundle在Windows下輕松搭建Android開發(fā)環(huán)境與Hello world,感興趣的小伙伴們可以參考一下
    2016-07-07
  • 淺談Android LruCache的緩存策略

    淺談Android LruCache的緩存策略

    這篇文章主要介紹了淺談Android LruCache的緩存策略,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Android窗口小部件基礎編寫代碼實例

    Android窗口小部件基礎編寫代碼實例

    這篇文章主要介紹了Android窗口小部件基礎編寫代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11

最新評論