1.引言
小菜鸟写博客,因为刚刚开始写,写的感觉很乱,如果你能看下去,表示感谢。身份小菜鸟,这篇文章看了鸿洋大神的博客,还有其他博客自己做的一个小总结。所以有什么错误请多多指正。
2.初始自定义View的构造函数
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* @description 这个最小API需要21,所以基本不实现这个方法
*/
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
根据以上构造函数先将以下几点基本的
- 代码里new一个View的时候,调用的是第一个构造函数
- 在XML布局中调用的时候,调用的是第二个构造函数
- 在XML布局中调用并且有自定义属性也是调用第二个构造函数
总结:说明系统只会调用前两个构造函数,第三个构造函数通常是我们在构造函数中自己调用(比如在第二个构造函数中调用第三个 this(context, attrs, 0)
)。具体如何调用得看你自己的需求
3.CustomView的自定义属性
1.declare-styleable 标签的作用
正常的自定义属性(我都是这样子的。。)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyView">
<attr name="titleText" format="string"/>
<attr name="titleTextColor" format="color"/>
<attr name="titleTextSize" format="dimension"/>
</declare-styleable>
</resources>
有人说那我不要declare-styleable标签可以不?
答案是当然可以,但是有什么不同呢,那就请看下去
<declare-styleable name="MyView">
<attr name="titleText" format="string" />
<attr name="titleTextColor" format="color" />
<attr name="titleTextSize" format="dimension" />
</declare-styleable>
调用方法:
TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.MyView);
自定义属性
<attr name="titleText" format="string"/>
<attr name="titleTextColor" format="color"/>
<attr name="titleTextSize" format="dimension"/>
调用方法:
int[] attr=new int[]{R.attr.titleText,R.attr.titleTextColor,R.attr.titleTextSize};
TypedArray typedArray = context.obtainStyledAttributes(attrs, attr);
接着往下看
所有resources文件中声明的属性都会在R.attr类中生成对应的成员变量:
我们在attr类中找到我们定义的:
public final class R{
public static final class attr{
public static final int titleText=0x7f01010d;
public static final int titleTextColor=0x7f01010e;
public static final int titleTextSize=0x7f01010f;
}
}
但是声明在标签中的属性,系统还会在R.styleable类中生成相关的成员变量:
public static final class styleable{
public static final int[] MyView = {
0x7f01010d, 0x7f01010e, 0x7f01010f
};
public static final int MyView_titleText = 0;
public static final int MyView_titleTextColor = 1;
public static final int MyView_titleTextSize = 2;
}
可以看出R.styleable.MyView是一个数组,期中的元素就是他们的值,下面的MyView_titleText等就是它的索引
总结:根据这个例子就很直观的知道了,这个标签就是为我们提供一个属性数组。分组更加的直观。所以推荐使用该标签
2.使用系统定义的属性
根据之前定义的属性,我们在布局里是使用如下
<com.magicsoft.demoone.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:titleText="我我我"
app:titleTextColor="@color/colorPrimary"
app:titleTextSize="16dp"
/>
看布局可知我们有一个属性叫做app:titleText="我我我"
但是系统有一个属性叫android:text
。
问题:这在系统中是一个语义比较明确的属性,可以直接使用吗?
答案当然是可以,如下直接在attrs中使用
<declare-styleable name="MyView">
<attr name="android:text"/>
<attr name="titleTextColor" format="color"/>
<attr name="titleTextSize" format="dimension"/>
</declare-styleable>
注意,这里我们是使用已经定义好的属性,不需要去添加format属性(注意声明和使用的区别,差别就是有没有format)。
然后在类中这么获取:titleText = typedArray.getString(R.styleable.MyView_android_text);
布局文件中:android:text="我我我"
4.自定义属性详解
obtainStyledAttributes一共有四个参数分别如下
1.AttributeSet
用处:是所有属性的集合
举个例子:我们通过AttributeSet打印出所有的属性
布局文件:
<com.magicsoft.demoone.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我我我我我"
app:titleTextSize="16dp"
/>
代码打印:
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String name = attrs.getAttributeName(i);
String value = attrs.getAttributeValue(i);
Log.e(TAG, "attrName = " + name + " , attrVal = " + value);
}
输出:
MyView: attrName = layout_width , attrVal = -1
MyView: attrName = layout_height , attrVal = -1
MyView: attrName = text , attrVal = 我我我我我
MyView: attrName = titleTextSize , attrVal = 16.0dip
很神奇对不对,所有的属性都有咧。那是不是不用TypedArray了呢。
当然不是咯。 不是都能打印吗? 咋还要用这个什么鬼TypedArray呢。
那接着往下看 我稍微修改下布局
布局:
<com.magicsoft.demoone.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/me"
app:titleTextSize="@dimen/textSize"
/>
打印代码:
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String name = attrs.getAttributeName(i);
String value = attrs.getAttributeValue(i);
Log.e(TAG, "attrName = " + name + " , attrVal = " + value);
} TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView,R.attr.myView1,0);
titleText = typedArray.getString(R.styleable.MyView_android_text);
titleTextSize = typedArray.getDimension(R.styleable.MyView_titleTextSize, 16);
Log.e(TAG, "titleText = " + titleText + " , titleTextSize = " + titleTextSize);
输出
MyView: attrName = layout_width , attrVal = -1
MyView: attrName = layout_height , attrVal = -1
MyView: attrName = text , attrVal = @2131099682
MyView: attrName = titleTextSize , attrVal = @2131165282
MyView: titleText = 我我我我我 , titleTextSize = 144.0
对比发现了什么?是不是AttributeSet
获取到的值打印出来都是@+一大串数字,再看typedArray打印出来的值
是不是懂了。
TypedArray其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/textSize),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。
2.int[] attrs
这个参数其实前面讲过了,就是你自定义属性的集合,就是在R类中生成的int型数组
3.int defStyleAttr
当在AttributeSet和style属性中都没有找到属性值时,就去Theme的某个属性(即第三个参数)中查看其值是否是style资源,如果是style资源再去这个style资源中查找XML属性值作为替补值。
看的太绕了,那就看以下例子
先看styles.xml,如图所示
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
//在AppTheme中,我们设置了defStyleAttrView这个属性的值
<item name="defStyleAttrView">@style/TextViewStyle</item>
</style>
//颜色设置绿色
<style name="TextViewStyle">
<item name="android:text">我是defStyleAttr</item>
<item name="titleTextColor">@color/green</item>
<item name="titleTextSize">@dimen/textSize</item>
</style>
首先, 我们先声明一个refrence格式的属性, 用于表示style的引用.声明在之前的res/values/attrs_my_view.xml文件里即可:
<attr name="defStyleAttrView" format="reference"/>
接下来看布局文件和MyView代码
<com.magicsoft.demoone.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我不是defStyleAttrView,略略略略"
app:titleTextSize="@dimen/textSize"
/>
布局中改变text内容,不设置颜色。
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView,R.attr.defStyleAttrView,0);
上面的第三个参数已经不是0而是R.attr.defStyleAttrView,但是第四个还是0。接着我们运行看效果
我们发现字是我不是defStyleAttrView,略略略略
,颜色是绿色对不对。为啥呢?
- 文字是这个
我不是defStyleAttrView,略略略略
可以理解吧,因为我直接在XMl里设置了属性。 - 颜色是绿色的,是因为XML和style属性中都没有找到颜色的属性值时,而我们在代码里填写了第三个参数
R.attr.defStyleAttrView
,那么Android会去AppTheme中查找属性为defStyleAttrView的值,他的值是TextViewStyle,由于该值是个style资源,Android就会去该资源中查找titleTextColor的值,TextViewStyle定义了titleTextColor的颜色为绿色,所以MyView最终所使用的titleTextColor的值就是绿色。
总结,看了例子,再看这句话当在AttributeSet和style属性中都没有找到属性值时,就去Theme的某个属性(即第三个参数)中查看其值是否是style资源,如果是style资源再去这个style资源中查找XML属性值作为替补值。
是不是就好理解一点了咧。
4. int defStyleRes
当第三个参数defStyleAttr为0或该属性在theme中找不到时,才会使用第四个参数defStyleRes.如果第三个参数defStyleAttr不为0,该属性在theme中找到,但是所对应的style没有定义任何XML的属性值,那么第四个参数也不会被使用。
defStyleRes本身直接表示一个style资源,这个就很方便
直接看例子咯
先看styles.xml和布局,给他设置个高贵紫。
<style name="defStyleResStyle">
<item name="android:text">我是defStyleResView</item>
<item name="titleTextColor">@color/purple</item>
<item name="titleTextSize">@dimen/textSize</item>
</style>
布局
<com.magicsoft.demoone.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
再看MyView,第三个参数是0了,第四个参数设置为R.style.defStyleResStyle
。
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView,0,R.style.defStyleResStyle);
看效果图
看到了吧,显示的是style里定义的文字和颜色吧
这里有个特别注意点:
如果第三个参数defStyleAttr不为0,该属性在theme中找到,但是所对应的style没有定义任何XML的属性值,那么第四个参数也不会被使用。