一、为什么要自定义属性
如果想使用一个属性,那么首先这个属性应该存在,所以,如果我们想在自定义View中扩展一些自定义的属性,我们就应该去创建出来这些属性,否则只能使用View的属性,因为所有的控件都继承自View
二、自定义属性的步骤
-
创建自定义View
public class LinearLayout extends ViewGroup {}
-
创建Value/attrs.xml文件,编写declare-styleable、attr等标签,创建自定义属性
<resources> …… <attr name="orientation"> <enum name="horizontal" value="0" /> <enum name="vertical" value="1" /> </attr> <declare-styleable name="LinearLayout"> <attr name="orientation" /> <attr name="gravity" /> <attr name="baselineAligned" format="boolean" /> <attr name="weightSum" format="float" /> <attr name="measureWithLargestChild" format="boolean" /> <attr name="divider" /> <attr name="showDividers"> <flag name="none" value="0" /> <flag name="beginning" value="1" /> <flag name="middle" value="2" /> <flag name="end" value="4" /> </attr> <attr name="dividerPadding" format="dimension" /> </declare-styleable> </resources>
带有
format
的是自定义的,没有的是直接使用系统的 -
xml中使用自定义View,使用自定义属性
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:pftext="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:background="@color/white" android:fitsSystemWindows="true"> </LinearLayout>
xmlns:android="http://schemas.android.com/apk/res/android"
是命名空间,这里代表是到android系统里去查找相关属性,只有引入了命名空间,XML文件才知道下面使用的属性应该去哪里查找所以,我们自定义属性也要声明命名空间:
xmlns:空间名称="http://schemas.android.com/apk/我们的包名"
这样就会到我们的应用程序包中查找相关属性现在,Android Studio会自动为我们生成命名空间:
xmlns:空间名称="http://schemas.android.com/apk/res-auto"
自动查找空间名称,随便起一个就行
-
在自定义View的构造方法中,通过TypeArray获取自定义属性值
public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); // 1. 获取TypedArray final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes); // 2. 获取相应的属性值 int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1); if (index >= 0) { setOrientation(index); } // 3. 释放TypedArray a.recycle(); }
三、declare-styleable
declare-styleable就是一个属性集的声明加分类,name一般都是用我们自定义View的名字
首先,使用这个标签方便我们自己查看,每两个declare-styleable标签之间都是一个自定义view的自定义属性,并且方便系统为我们分组,并在R文件生成每个属性对应的索引值,TypedArray正好需要通过此索引值获取属性。
public static final int[] AppBarLayout = {
0x010100d4, 0x7f01006b, 0x7f010076
};
public static final int AppBarLayout_android_background = 0;
public static final int AppBarLayout_elevation = 1;
public static final int AppBarLayout_expanded = 2;
可以不使用declare-styleable标签,那么类似上面代码这种工作就要我们自己去做了
四、AttributeSet与TypedArray
Attributeset是一个属性的集合,它内部就是一个XML解析器,帮我们将布局文件中该控件的所有属性解析出来,并以key-value的键值对形式维护起来。
Attributeset可以获取到属性值,但是,如果设置值时使用的是引用类型(例:@dimen/dp_16),AttributeSet获取到的属性值将会是类似@213456
这种字符串
如果使用AttributeSet去获得最终的属性值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程,可以直接获得属性值
五、TypedArray.recycle()
程序在运行时维护了一个TypedArray的池,程序调用时,会向该池中请求一个实例,用完之后,调用recycle()方法来释放该实例,从而使其可被其他模块复用
TypedArray使用的是池+单例模式,如果不这样的话TypedArray会随着 Activity的每一次Create而频繁的创建array,对内存和性能是一个不小的开销,如果不使用池模式,每次都让GC来回收,很可能就会造成OutOfMemory。
六、format取值类型
共有11种类型:
1. reference:资源ID
属性定义:
<attr name="layout" format="reference" />
属性使用:
android:layout="@layout/item"
2. color:颜色值
属性定义:
<attr name="colorAccent" format="color" />
属性使用:
android:colorAccent="#00FF00"
3. boolean:布尔值
属性定义:
<attr name="showTitle" format="boolean" />
属性使用:
android:showTitle="true"
4. dimension:尺寸值
属性定义:
<attr name="padding" format="dimension" />
<attr name="textSize" format="dimension" />
属性使用:
android:padding="15dp"
android:textSize="15sp"
5. float:浮点值
属性定义:
<attr name="alpha" format="float" />
属性使用:
android:alpha="0.5"
6. integer:整型值
属性定义:
<attr name="rowCount" format="integer" />
属性使用:
android:rowCount="3"
7. string:字符串
属性定义:
<attr name="hint" format="string" />
属性使用:
andrid:hint="hint"
8. fraction:百分数
属性定义:
<attr name="centerX" format="float|fraction" />
属性使用:
android:centerX="50%"
9. enum:枚举值
属性定义:
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
属性使用:
android:orientation="horizontal"
注意:枚举类型的属性在使用的过程中只能同时使用其中一个,不能 android:orientation = “horizontal|vertical"
10. flag:位或运算
属性定义:
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
<flag name = "stateHidden" value = "2" />
<flag name = "stateAlwaysHidden" value = "3" />
<flag name = "stateVisible" value = "4" />
<flag name = "stateAlwaysVisible" value = "5" />
<flag name = "adjustUnspecified" value = "0x00" />
<flag name = "adjustResize" value = "0x10" />
<flag name = "adjustPan" value = "0x20" />
<flag name = "adjustNothing" value = "0x30" />
</attr>
属性使用:
android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
注意:位运算类型的属性在使用的过程中可以使用多个值
11. 混合类型:属性定义时可以指定多种类型值
属性定义:
<attr name="centerX" format="float|fraction" />
属性使用:
android:centerX="50%"
android:centerX="0.5"
以上各种类型,通过TypeArray获取时都有对应的get方法
个人总结,水平有限,如果有错误,希望大家能给留言指正!如果对您有所帮助,可以帮忙点个赞!如果转载,希望可以标明文章出处!最后,非常感谢您的阅读!