自定義View相關(guān)屬性細(xì)節(jié)

參考:
http://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650820236&idx=1&sn=6dec4ff1efeda3224b5a40fdad862404&mpshare=1&scene=23&srcid=0228jLwl3fR8II6i7V1zzMxK#rd

自定義View的流程##

  1. 一般我們新建一個View,繼承自現(xiàn)有View,或直接繼承自View/ViewGroup,為了讓自定義的View有更多屬性可配置,類似于textview 的textSize,textColor等屬性,我們會定義一個attrs文件中,新建一個declare-styleable,用來定義該控件的自定義屬性,如
<declare-styleable name="DivideTextView">
        <attr name="dt_drawable" format="reference|color" />
        <attr name="dt_dividerSize" format="dimension" />
        <!-- 分割線padding,左右上下padding,上下左右padding -->
        <attr name="dt_dividerPadding" format="dimension" />
        <attr name="dt_divideGravity">
            <flag name="none" value="0x01" />
            <flag name="left" value="0x02" />
            <flag name="top" value="0x04" />
            <flag name="right" value="0x08" />
            <flag name="bottom" value="0x10" />
        </attr>
    </declare-styleable>
  1. 在layout文件中,使用自定義屬性,如:
<ui.better.DivideTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text="開始放大招了"
        android:textSize="20sp"
        app:dt_divideGravity="top|right|bottom|left"
        app:dt_dividerPadding="2dp"
        app:dt_dividerSize="1dp"
        app:dt_drawable="#22bbcc" />
  1. 在自定義View類中,通過obtainStyledAttributes來獲取自定義屬性值:
public DivideTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);

        // 初始化屬性
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, defStyleAttr, 0);
        try {
            setDivideDrawable(a.getDrawable(R.styleable.DivideTextView_dt_drawable));
            setDivideGravityInner(a.getInt(R.styleable.DivideTextView_dt_divideGravity, NONE));
            setDivideSize((int) a.getDimension(R.styleable.DivideTextView_dt_dividerSize, 0f));
            setDividePadding((int) a.getDimension(R.styleable.DivideTextView_dt_dividerPadding, 0f));
        } finally {
            if (a != null) {
                a.recycle();
            }
        }
    }

obtainStyledAttributes說明##

自定義View中通過 obtainStyledAttributes來獲取自定義屬性的值;
她有4個重載方法,如圖,

obtainStyledAttributes

最終都會調(diào)用第一個,也就是4個參數(shù)的那個方法;

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

參數(shù)的意思是:

第4個參數(shù):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,即默認(rèn)的style,意思是:如果沒有在layout中設(shè)置屬性,就從style中獲取屬性的默認(rèn)值;
比如在style文件中,新建style如下:

<style name="Def_Style_DividerTextView">
        <item name="dt_divideGravity">none</item>
        <item name="dt_dividerPadding">0dp</item>
        <item name="dt_dividerSize">1dp</item>
        <item name="dt_drawable">#88cc00</item>
</style>

修改 獲取屬性的代碼 如下:

final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, defStyleAttr,
         R.style.Def_Style_DividerTextView);
 Log.e("better", "dividerPadding: " + padding);
 Log.e("better", "dividerSize: " + size);
 Log.e("better", "Drawable: " + a.getColor(R.styleable.DivideTextView_dt_drawable, 0));

輸出如下:

defStyleRes

第3個參數(shù):defStyleAttr:####

英文說明:

An attribute in the current theme that contains a reference to a style resource that supplies defaults values for the TypedArray. Can be 0 to not look for defaults.

說明它是一個引用類型的屬性,指向一個style,并且在當(dāng)前的theme中進(jìn)行設(shè)置;

使用步驟:

  1. 在attrs.xml中,聲明一個attr,創(chuàng)建一個新的引用格式屬性‘Def_Style_Attr_DividerTextView’, 如:
<!-- 指向style的引入類型 -->
<attr name="Def_Style_Attr_DividerTextView" format="reference" />
  1. 在styles.xml文件里面,找到我們所使用的theme,添加一條item:
 <!-- 在當(dāng)前使用的主題中,配置 Def_Style_Attr_DividerTextView -->
 <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

        <!-- 在當(dāng)前使用的主題中,配置 Def_Style_Attr_DividerTextView -->
        <item name="Def_Style_Attr_DividerTextView">@style/Def_Style_DividerTextView</item>
    </style>
  1. 修改 獲取屬性的代碼 如下
 // final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, defStyleAttr, R.style.Def_Style_DividerTextView);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, R.attr.Def_Style_Attr_DividerTextView, 0);
Log.e("better", "dividerPadding: " + padding);
Log.e("better", "dividerSize: " + size);
Log.e("better", "Drawable: " + a.getColor(R.styleable.DivideTextView_dt_drawable, 0));

輸出:

defStyleAttr

對于第三個參數(shù):defStyleAttr
系統(tǒng)中的Button,EdiText,他們會在構(gòu)造函數(shù)中,指定第3個參數(shù):

public Button(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.buttonStyle);
}

通過在Theme里,設(shè)置參數(shù)的樣式,比如:background、textColor等,當(dāng)切換不同的主題時,控件的樣式會發(fā)生變化,這是因?yàn)椋煌闹黝}設(shè)置了不同的style;

** 系統(tǒng)的button defStyleAttr目錄:**
默認(rèn)Theme:

D:\workTools\sdk\platforms\android-25\data\res\values\attrs.xml, 有如下:

    <!-- Normal Button style. -->
    <attr name="buttonStyle" format="reference" />

D:\workTools\sdk\platforms\android-25\data\res\values\theme.xml, 有如下:

    <!-- Button styles -->
    <item name="buttonStyle">@style/Widget.Button</item>

D:\workTools\sdk\platforms\android-25\data\res\values\styles.xml, 有如下:

<!---->
<style name="Widget.Button">
    <item name="background">@drawable/btn_default</item>
    <item name="focusable">true</item>
    <item name="clickable">true</item>
    <item name="textAppearance">?attr/textAppearanceSmallInverse</item>
    <item name="textColor">@color/primary_text_light</item>
    <item name="gravity">center_vertical|center_horizontal</item>
</style>

Holo_Theme:

D:\workTools\sdk\platforms\android-25\data\res\values\themes_holo.xml, 有如下:

    <!-- Button styles -->
    <item name="buttonStyle">@style/Widget.Holo.Button</item>

D:\workTools\sdk\platforms\android-25\data\res\values\styles_holo.xml, 有如下:

    <!--可看到繼承自:Widget.Button-->
    <style name="Widget.Holo.Button" parent="Widget.Button">
        <item name="background">@drawable/btn_default_holo_dark</item>
        <item name="textAppearance">?attr/textAppearanceMedium</item>
        <item name="textColor">@color/primary_text_holo_dark</item>
        <item name="minHeight">48dip</item>
        <item name="minWidth">64dip</item>
     </style>

** 參考系統(tǒng)的實(shí)現(xiàn),如果我們自定義的View,的自定義屬性非常多,也可以去提供默認(rèn)的style,然后讓用戶去設(shè)置到theme里面即可**

自定義View的構(gòu)造方法##

  1. 第一種:
  public DivideTextView(Context context) {
        this(context, null);
  }
  public DivideTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
 }
 public DivideTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
  1. 第二種:
 public DivideTextView(Context context) {
       super(context);
       init();
 }
 public DivideTextView(Context context, AttributeSet attrs) {
       super(context, attrs);
       init();
}
public DivideTextView(Context context, AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
       init();
}
private void init() {
  
}

使用場景的區(qū)別:

  1. 如果需要設(shè)置 obtainStyledAttributes 的第3個參數(shù),‘defStyleAttr’,使用第一種方式,沒有這個需求,2種沒區(qū)別;
  2. 繼承系統(tǒng)已有控件去定義View時,使用第一種方式,會覆蓋掉系統(tǒng)的在theme里面設(shè)置的style,如上面的 button的構(gòu)造函數(shù)的第3個參數(shù),這樣的話,使用第二種,會繼續(xù)使用系統(tǒng)提供的style,如果非要用第一種,也可以,將系統(tǒng)的 defStyleAttr copy進(jìn)來即可,如下示例代碼:
 public ClearEditText(Context context) {
        super(context);
        this(context, null);
    }
    public ClearEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
         // 注意這里,將系統(tǒng)的樣式拿過來,如果傳遞0,將丟失樣式,切記?。。?        this(context, attrs, android.R.attr.editTextStyle); 
    }
    public ClearEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

獲取自定義屬性的2種寫法##

  1. 第一種方式,如上:
// 這種形式
setDivideDrawable(a.getDrawable(R.styleable.DivideTextView_dt_drawable));

2.第二種形式:

       // 第二種獲取自定義屬性的方式,通過循環(huán)遍歷所有屬性:
        final int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            if (i == R.styleable.DivideTextView_dt_drawable) {
            } else if (i == R.styleable.DivideTextView_dt_divideGravity) {
                setDivideDrawable(a.getDrawable(i));
            } else if (i == R.styleable.DivideTextView_dt_dividerSize) {
                setDivideGravityInner(a.getInt(i, NONE));
            } else if (i == R.styleable.DivideTextView_dt_dividerPadding) {
                setDividePadding((int) a.getDimension(i, 0f));
            }
        }

區(qū)別

第一種寫法,不管有沒有在布局文件中使用該屬性,都會執(zhí)行g(shù)etXXX方法賦值;
第二種寫法,只有在布局文件中,設(shè)置該屬性后,才去調(diào)用getXXX方法(indexCount);
注意
父view已經(jīng)對某個屬性賦值了,如果采用第一種方式,可能用戶根本沒有設(shè)置值,但是會執(zhí)行g(shù)etXXX方法,可能就把父View的賦值給覆蓋了;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容