引言
在自定义View中可以定义、获取并设置属性。那么同样的Button在安卓不同版本下面拥有不同的风格,这是怎样实现的呢?在本文将会对一下几点进行探索。
1.资源文件declare-styleable标签的详解
2.如何获取自定义属性
3.如何针对同一view设置不同主题
declare-styleable标签详解
首先,我自定义一个StarView继承自TextView
public class StarView extends TextView{
public StarView(Context context) {
super(context);
}
public StarView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public StarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
然后,在res/values/attr.xml中添加以下代码:
<declare-styleable name="StarView">
<attr name="name" format="string"></attr><!-- 名称 -->
<attr name="speed" format="float"></attr><!-- 速度 -->
<attr name="type">
<flag name="planet" value="0"/><!-- 行星 -->
<flag name="star" value="1"/><!-- 恒星 -->
<flag name="satellite" value="2"/><!-- 卫星 -->
</attr>
<attr name="textSize" format="dimension"/>
</declare-styleable>
标签declare-styleable的name属性
必须是类名,不可为其他。在其中,通过attr标签的name可以定义属性名称,format可指定属性类型。属性类型有
<attr name="tag1" format="boolean"></attr><!-- 布尔值 -->
<attr name="tag2" format="integer"></attr><!-- 整型值 -->
<attr name="tag3" format="string"></attr><!-- 字符串 -->
<attr name="tag4" format="color"></attr><!-- 颜色值 -->
<attr name="tag5" format="dimension"></attr><!-- 尺寸 -->
<attr name="tag6" format="enum"></attr><!-- 枚举值 -->
<attr name="tag7" format="flag"></attr><!-- 标签值 -->
<attr name="tag8" format="fraction"></attr><!-- 百分值 -->
<attr name="tag9" format="reference"></attr><!--参考某一资源ID 一旦设置为reference,值的引用就必须用资源引用的方式来设置,否则会报错 -->
<attr name="tag10" format="float"></attr><!-- 浮点值 -->
设置完属性后,如果需要应用的话,那么则需要在引用该view的layout中加入
xmlns:starattrs="http://schemas.android.com/apk/res-auto"
其中starattrs是自定义标签头,一般将该语句加入到根布局中,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:starattrs="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.dls.myview.widget.StarView
starattrs:name="太阳"
starattrs:type="star"
starattrs:speed="123.5"
starattrs:textSize="@dimen/y14"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
获取属性值
public class StarView extends TextView{
public StarView(Context context) {
super(context);
}
public StarView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StarView);
String name = typedArray.getString(R.styleable.StarView_name);//获取名称
String speed = typedArray.getFloat(R.styleable.StarView_speed,0f)+"km/s";//获取速度
int type = typedArray.getInt(R.styleable.StarView_type,0);//获取类型
float textSize = typedArray.getDimensionPixelSize(R.styleable.StarView_textSize,10);//获取尺寸大小,默认会转换成px
typedArray.recycle();//回收资源
setTextSize(textSize);
setText(name+"\n"+speed+"\n"+getType(type)+"\n");
}
public StarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public StarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public String getType(int type){
String typeStr = "";
switch (type){
case 0:
typeStr= "行星";
break;
case 1:
typeStr= "恒星";
break;
case 2:
typeStr= "卫星";
break;
default:
typeStr = "行星";
break;
}
return typeStr;
}
}
这样,一运行后,就会将获得到的属性值通过setText()显示到UI中。
看了一下View的源码,发现源码中也会用到switch来获取属性,如下:
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
background = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.View_padding:
padding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = padding;
mUserPaddingRightInitial = padding;
leftPaddingDefined = true;
rightPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingLeft:
leftPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = leftPadding;
leftPaddingDefined = true;
break;
针对同一view设置不同主题
获取属性的构造方法如下,主要看看参数defStyleAttr和defStyleRes
public final TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
}
将实现三种不同的效果:太阳设置为红色的,火星设置为黄色的,地球设置为蓝色的。
defStyleAttr的注释如下:
@param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
显而易见,defStyleAttr是针对当前主题提供默认样式的引用。可以为0.
为了使用defStyleAttr,首先在attr.xml中定义
<attr name="sunStyle" format="reference"/>
再为StarView添加一条颜色属性
<attr name="textColor" format="color"/>
在style.xml中添加一条颜色style
<style name="SunStyle">
<item name="textColor">#ff0000</item>
</style>
然后在AppTheme中加入上面SunStyle
<item name="sunStyle">@style/SunStyle</item>
在获取属性的时候传入参数:
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StarView,R.attr.sunStyle,0);
此时,StarView的颜色就变成上面定义好的颜色了。
defStyleRes的注释如下
A resource identifier of a style resource that
* supplies default values for the view, used only if
* defStyleAttr is 0 or can not be found in the theme. Can be 0
* to not look for defaults.
以上,表明defStyleRes是在非主题引用的时候加载的style资源引用。而且只有当defStyleAttr为0或者是无法找到的时候,才会去调用defStyleRes。则表明,defStyleRes的优先级要低于defStyleAttr。当两者同时设置时,默认先调用defStyleAttr.
使用defStyleRes就更简单了,只需要在style.xml中自定义一个style然后在获取属性的时候传入该style就可以了:
<style name="SunSpStyle">
<item name="textColor">#ff0000</item>
<item name="name">太阳</item>
<item name="speed">10</item>
<item name="type">star</item>
<item name="textSize">@dimen/y12</item>
</style>
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StarView,0,R.style.SunSpStyle);
以上,就是针对View设置主题的两种不同方式。