android xml view自定义view 怎么获取xml属性

Android 中自定义View的属性(attr.xml,TypedArray)
我的图书馆
Android 中自定义View的属性(attr.xml,TypedArray)
今天我们的教程是根据前面一节扩展进行的,如果你没有看,请点击
查看第三课,这样跟容易方便你的理解!
在xml 文件里定义控件的属性,我们已经习惯了android:attrs="" ,那么我们能不能定义自己的属性能,比如:test:attrs="" 呢?答案是肯定的.
好了我就不卖关子了,直接进入主题。大致以下步骤:
一、 在res/values 文件下定义一个attrs.xml 文件.代码如下:
view plaincopy to clipboardprint?一、在res/values文件下定义一个attrs.xml文件.代码如下:&& &
&version="1.0"&encoding="utf-8"&&& &
&&&&&name="MyView"&&& &
&&&&&&&&&name="textColor"&format="color"&&&& &
&&&&&&&&&name="textSize"&format="dimension"&&&& &
一、在res/values文件下定义一个attrs.xml文件.代码如下: &
&version="1.0"&encoding="utf-8"&
&&&&&name="MyView"&
&&&&&&&&&name="textColor"&format="color"&&
&&&&&&&&&name="textSize"&format="dimension"&&
二、 我们在MyView.java 代码修改如下,其中下面的构造方法是重点,我们获取定义的属性我们R.sytleable.MyView_textColor, 获取方法中后面通常设定默认值(float textSize = a.getDimension(R.styleable.MyView_textSize, 36 ); ), 防止我们在xml 文件中没有定义.从而使用默认值!
获取,MyView 就是定义在&declare-styleable name="MyView "&&/declare-styleable& 里的名字,获取里面属性用 名字_ 属性 连接起来就可以.TypedArray 通常最后调用 .recycle() 方法,为了保持以后使用该属性一致性!
view plaincopy to clipboardprint?&
public&MyView(Context&context,AttributeSet&attrs)&&& &
&&&&{&&& &
&&&&&&&&super(context,attrs);&&& &
&&&&&&&&mPaint&=&new&Paint();&&& &
&&&&&&&&&&& &
&&&&&&&&TypedArray&a&=&context.obtainStyledAttributes(attrs,&&& &
&&&&&&&&&&&&&&&&R.styleable.MyView);&&& &
&&&&&&&&&&& &
&&&&&&&&int&textColor&=&a.getColor(R.styleable.MyView_textColor,&&& &
&&&&&&&&&&&&&&&&0XFFFFFFFF);&&& &
&&&&&&&&float&textSize&=&a.getDimension(R.styleable.MyView_textSize,&36);&&& &
&&&&&&&&&&& &
&&&&&&&&mPaint.setTextSize(textSize);&&& &
&&&&&&&&mPaint.setColor(textColor);&&& &
&&&&&&&&&&& &
&&&&&&&&a.recycle();&&& &
public&MyView(Context&context,AttributeSet&attrs) &
&&super(context,attrs); &
&&mPaint&=&new&Paint(); &
&&TypedArray&a&=&context.obtainStyledAttributes(attrs, &
&&&&R.styleable.MyView); &
&&int&textColor&=&a.getColor(R.styleable.MyView_textColor, &
&&&&0XFFFFFFFF); &
&&float&textSize&=&a.getDimension(R.styleable.MyView_textSize,&36); &
&&mPaint.setTextSize(textSize); &
&&mPaint.setColor(textColor); &
&&a.recycle(); &
MyView.java 全部代码如下:
view plaincopy to clipboardprint?&
package&com.android.&&& &
import&android.content.C&&& &
import&android.content.res.TypedA&&& &
import&android.graphics.C&&& &
import&android.graphics.C&&& &
import&android.graphics.P&&& &
import&android.graphics.R&&& &
import&android.graphics.Paint.S&&& &
import&android.util.AttributeS&&& &
import&android.view.V&&& &
public&class&MyView&extends&View&{&&& &
&&&&private&Paint&mP&&& &
&&&&private&Context&mC&&& &
&&&&private&static&final&String&mString&=&"Welcome&to&Mr&Wei's&blog";&&& &
&&&&public&MyView(Context&context)&{&&& &
&&&&&&&&super(context);&&& &
&&&&&&&&mPaint&=&new&Paint();&&& &
&&&&}&&& &
&&&&public&MyView(Context&context,AttributeSet&attrs)&&& &
&&&&{&&& &
&&&&&&&&super(context,attrs);&&& &
&&&&&&&&mPaint&=&new&Paint();&&& &
&&&&&&&&&&& &
&&&&&&&&TypedArray&a&=&context.obtainStyledAttributes(attrs,&&& &
&&&&&&&&&&&&&&&&R.styleable.MyView);&&& &
&&&&&&&&&&& &
&&&&&&&&int&textColor&=&a.getColor(R.styleable.MyView_textColor,&&& &
&&&&&&&&&&&&&&&&0XFFFFFFFF);&&& &
&&&&&&&&float&textSize&=&a.getDimension(R.styleable.MyView_textSize,&36);&&& &
&&&&&&&&&&& &
&&&&&&&&mPaint.setTextSize(textSize);&&& &
&&&&&&&&mPaint.setColor(textColor);&&& &
&&&&&&&&&&& &
&&&&&&&&a.recycle();&&& &
&&&&}&&& &
&&&&@Override&& &
&&&&protected&void&onDraw(Canvas&canvas)&{&&& &
&&&&&&&&super.onDraw(canvas);&&& &
&&&&&&&&mPaint.setStyle(Style.FILL);&&& &
&&&&&&&&&&& &
&&&&&&&&canvas.drawRect(new&Rect(10,&10,&100,&100),&mPaint);&&& &
&&&&&&&&&&& &
&&&&&&&&mPaint.setColor(Color.BLUE);&&& &
&&&&&&&&canvas.drawText(mString,&10,&110,&mPaint);&&& &
&&&&}&&& &
package&com.android. &
import&android.content.C &
import&android.content.res.TypedA &
import&android.graphics.C &
import&android.graphics.C &
import&android.graphics.P &
import&android.graphics.R &
import&android.graphics.Paint.S &
import&android.util.AttributeS &
import&android.view.V &
public&class&MyView&extends&View&{ &
&private&Paint&mP &
&private&Context&mC &
&private&static&final&String&mString&=&"Welcome&to&Mr&Wei's&blog"; &
&public&MyView(Context&context)&{ &
&&super(context); &
&&mPaint&=&new&Paint(); &
&public&MyView(Context&context,AttributeSet&attrs) &
&&super(context,attrs); &
&&mPaint&=&new&Paint(); &
&&TypedArray&a&=&context.obtainStyledAttributes(attrs, &
&&&&R.styleable.MyView); &
&&int&textColor&=&a.getColor(R.styleable.MyView_textColor, &
&&&&0XFFFFFFFF); &
&&float&textSize&=&a.getDimension(R.styleable.MyView_textSize,&36); &
&&mPaint.setTextSize(textSize); &
&&mPaint.setColor(textColor); &
&&a.recycle(); &
&@Override&
&protected&void&onDraw(Canvas&canvas)&{ &
&&super.onDraw(canvas); &
&&mPaint.setStyle(Style.FILL); &
&&canvas.drawRect(new&Rect(10,&10,&100,&100),&mPaint); &
&&mPaint.setColor(Color.BLUE); &
&&canvas.drawText(mString,&10,&110,&mPaint); &
三、将我们自定义的MyView 加入布局main.xml 文件中,平且使用自定义属性,自定义属性必须加上:
&&&&& xmlns:test =" "蓝色 是自定义属性的前缀,红色 是我们包名.
main.xml 全部代码如下:
view plaincopy to clipboardprint?&?xml&&& version="1.0" encoding="utf-8"?&&& &LinearLayout&&& xmlns:android=""& &&&&&&&&&&&&&&&& xmlns:test=""& &&& android:orientation="vertical"& &&& android:layout_width="fill_parent"& &&& android:layout_height="fill_parent"& &&& &&& &TextView&&&& &&& android:layout_width="fill_parent"&&& &&& android:layout_height="wrap_content"&&& &&& android:text="@string/hello"& &&& /&&& &com.android.tutor.MyView&& &&& android:layout_width="fill_parent"&&& &&& android:layout_height="fill_parent"&&& &&& test:textSize="20px"& &&& test:textColor="#fff"& /&&& &/LinearLayout&& &?xml version="1.0" encoding="utf-8"?&&LinearLayout xmlns:android=""&&&&&&&&&&&&& xmlns:test=""&&& android:orientation="vertical"&&& android:layout_width="fill_parent"&&& android:layout_height="fill_parent"&&& &&TextView& &&& android:layout_width="fill_parent" &&& android:layout_height="wrap_content" &&& android:text="@string/hello"&&& /&&com.android.tutor.MyView&android:layout_width="fill_parent" &&& android:layout_height="fill_parent" &&& test:textSize="20px"&&& test:textColor="#fff"/&&/LinearLayout&&
四、运行之效果如下图:
今天就到此结束,大家有什么疑问的,请留言,我会及时答复大家!谢谢~
TA的最新馆藏
喜欢该文的人也喜欢温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
阅读(252)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'自定义view调用attrs.xml中属性提示找不到该属性',
blogAbstract:'xmlns:unit_view=\"/apk/res/com.siecom.videocall\"后面的包名是AndroidManifest.xml中manifest标签下package的值,并不是自定义view所在的包名全路径',
blogTag:'',
blogUrl:'blog/static/66',
isPublished:1,
istop:false,
modifyTime:9,
publishTime:6,
permalink:'blog/static/66',
commentCount:0,
mainCommentCount:0,
recommendCount:1,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}Android中View自定义XML属性详解以及R.attr与R.styleable的区别
为View添加自定义XML属性
Android中的各种Widget都提供了很多XML属性,我们可以利用这些XML属性在layout文件中为Widget的属性赋值。
如下所示:
我们可以通过TextView所提供的XML属性android:text为TextVie
为View添加自定义XML属性
中的各种Widget都提供了很多XML属性,我们可以利用这些XML属性在layout文件中为Widget的属性赋值。
如下所示:
我们可以通过TextView所提供的XML属性android:text为TextView的文本赋值。
我们在自定义View的时候也会经常需要自定义View的XML属性。
假设我们有一个自定义的View,其类名是com.ispring.attr.MyView,其中com.ispring.attr是应用程序的包名。
要想自定义XML属性,总的来说包括三步:
在xml资源文件中定义各种attr,指定attr的数据类型。 在自定义View的构造函数中解析这些从XML中定义的属性值,将其存放到View对应的成员变量中。 在layout文件中为自定义View的XML属性赋值。
首先,我们在res/values目录下新建了一个名为attrs_my_views.xml文件,文件名是什么不重要,只要是xml文件就行。我们在该文件中定义MyView所支持的XML属性。该文件的根结点是,我们在节点下可以添加多个节点,在节点中通过name指定XML属性名称,通过format指定XML属性值的类型,如下图所示:
由上图我们可知,format支持的类型有enum、boolean、color、dimension、flag、float、fraction、integer、reference、string。
当我们指定了XML属性的名称和属性值的类型之后,我们就可以在layout文件中通过XML属性为其赋值了。如下图所示:
我们通过在layout中引入了MyView,为了能够使用自定义属性,我们通常要指定一个自定义的命名空间以区别于Android的命名空间xmlns:android,我们自定义命名空间的名字可以是任意的,通常我一般用xmlns:app。我的App的命名空间是com.ispring.attr,如果用Eclipse开发,那么可以这样定义命名空间xmlns:app=http:/喎"/kf/ware/vc/" target="_blank" class="keylink">vc2NoZW1hcy5hbmRyb2lkLmNvbS9hcGsvcmVzL2NvbS5pc3ByaW5nLmF0dHI8L2NvZGU+o6y1q8rH1NpBbmRyb2lkIFN0dWRpb9bQ1eLR+bao0uXD/MP7v9W85Lvh09DOyszioaNBbmRyb2lkIFN0dWRpb8q508NHcmFkbGW9+NDQYnVpbGSjrLb4R3JhZGxlsrvUytDt19S2qNLltcTD/MP7v9W85NLUsPzD+73hzrKjrNTaQW5kcm9pZCBTdHVkaW/W0L/J0tTV4tH5tqjS5cP8w/u/1bzkPGNvZGU+eG1sbnM6YXBwPSZxdW90O2h0dHA6Ly9zY2hlbWFzLmFuZHJvaWQuY29tL2Fway9yZXMtYXV0byZxdW90OzwvY29kZT6jrNXi0fm2qNLltcTD/MP7v9W85NfUtq/WuM/ytbHHsEFwcLXEw/zD+7/VvOShozwvcD4NCjxwPtTa1f3It7ao0uVhcHC1xMP8w/u/1bzk1q6686OsztLDx77Nv8nS1NPDPGNvZGU+YXBwOmN1c3RvbUF0dHI8L2NvZGU+zqpNeVZpZXe1xGN1c3RvbUF0dHLK9NDUuLPWtcHLoaPI57n7ztLDx72rY3VzdG9tQXR0crXEZm9ybWF0tqjS5c6qYm9vbGVhbrXEo6zEx8O0tMu0pr7N1rvE3Mzu0LR0cnVlu/LV32ZhbHNlo6zM7tC0xuTL+8Dg0M21xNa1u+GxqLTtoaM8L3A+DQo8cD7PwsPm1Nm21GF0dHK1xGZvcm1hdLXEwODQzb340NDSu8/Cy7XD96GjPC9wPg0KPHA+Ym9vbGVhbjxiciAvPg0KYm9vbGVhbrHtyr5hdHRyyseyvLb7wODQzbXE1rWjrMih1rXWu8Tcysd0cnVlu/JmYWxzZaGjPC9wPg0KPHA+c3RyaW5nPGJyIC8+DQpzdHJpbmex7cq+YXR0csrH19a3+7SuwODQzaGjPC9wPg0KPHA+aW50ZWdlcjxiciAvPg0KaW50ZWdlcrHtyr5hdHRyysfV+8r9wODQzaOsyKHWtda7xNzKx9X7yv2jrLK7xNzKx7ihtePK/aGjPC9wPg0KPHA+ZmxvYXQ8YnIgLz4NCmZsb2F0se3KvmF0dHLKx7ihtePK/cDg0M2jrMih1rXWu8Tcyse4obXjyv278tX7yv2hozwvcD4NCjxwPmZyYWN0aW9uPGJyIC8+DQpmcmFjdGlvbrHtyr5hdHRyysew2bfWyv3A4NDNo6zIoda11rvE3NLUJb3hzrKjrMD9yOczMCWhojEyMC41JbXIoaM8L3A+DQo8cD5jb2xvcjxiciAvPg0KY29sb3Kx7cq+YXR0csrH0dXJq8Dg0M2jrMD9yOcjZmYwMDAwo6zSsr/J0tTKudPD0ru49ta4z/JDb2xvcrXE18rUtKOsscjI50BhbmRyb2lkOmNvbG9yL2JhY2tncm91bmRfZGFya6OstavKx7K7xNzTwzB4ZmZmZjAwMDDV4tH5tcTWtaGjPC9wPg0KPHA+ZGltZW5zaW9uPGJyIC8+DQpkaW1lbnNpb26x7cq+YXR0csrHs9+058Dg0M2jrMD9yOfIoda1MTZweKGiMTZkcKOs0rK/ydLUyrnTw9K7uPbWuM/yPGNvZGU+PGRpbWVuPjwvZGltZW4+PC9jb2RlPsDg0M21xNfK1LSjrLHIyOc8Y29kZT5AYW5kcm9pZDpkaW1lbi9hcHBfaWNvbl9zaXplPC9jb2RlPqGjPC9wPg0KPHA+cmVmZXJlbmNlPGJyIC8+DQpyZWZlcmVuY2Wx7cq+YXR0crXE1rXWu8Tc1rjP8sSz0rvXytS0tcRJRKOswP3I58ih1rU8Y29kZT5AaWQvdGV4dFZpZXc8L2NvZGU+oaM8L3A+DQo8cD5lbnVtPGJyIC8+DQplbnVtse3KvmF0dHLKx8O2vtnA4NDNo6zU2rao0uVlbnVtwODQzbXEYXR0csqxo6y/ydLUvathdHRytcRmb3JtYXTJ6NbDzqplbnVto6zSsr/J0tSyu9PDyejWw2F0dHK1xGZvcm1hdMr00NSjrLWrysex2NDr1NphdHRyvdq148/Cw+bM7bzT0ru49rvytuC49mVudW292rXjoaPI58/Cy/nKvqO6PC9wPg0KPHByZSBjbGFzcz0="brush:">
&code class=" hljs cs"&&attr name="customAttr"&
&enum name="man" value="0"&
&enum name="woman" value="1"&
&/enum&&/enum&&/attr&&/code&
这样attr的属性值只能取man或woman了。
flag表示attr是bit位标记,flag与enum有相似之处,定义了flag的attr,在设置值时,可以通过|设置多个值,而且每个值都对应一个bit位,这样通过按位或操作符|可以将多个值合成一个值,我们一般在用flag表示某个字段支持多个特性,需要注意的是,要想使用flag类型,不能在attr上设置format为flag,不要设置attr的format的属性,直接在attr节点下面添加flag节点即可。如下所示:
在节点下通过定义多个表示其支持的值,value的值一般是0或者是2的N次方(N为大于等于0的整数),对于上面的例子我们在实际设置值是可以设置单独的值,如none、bold、italic、underline,也可以通过|设置多个值,例如app:customAttr="italic|underline"。
MyView直接继承自View,我想让MyView可以显示文本,即我传递文本给MyView,MyView能画出来,就相当于非常简单的TextView。
因此,我的attrs_my_view.xml如下所示:
&code class=" hljs xml"&&resources&
&attr name="customText" format="string"&
&attr name="customColor" format="color"&
&/attr&&/attr&&/resources&&/code&
我们定义了两个XML属性,customText是一个string类型,表示MyView要显示的文本,customColor是color类型,表示文本的颜色。
对项目进行编译之后会生成R.java文件,R.java文件对应着R类。如果是Android Studio项目,那么R文件的目录是app\build\generated\source\r\debug\com\ispring\attr\R.java,在该文件中有内部类public static final class attr,在R.attr中会发现有customText和customColor,如下图所示:
R.attr.customText和R.attr.customColor分别是属性customText和customColor的资源ID。
在使用MyView时,我们可以在layout文件中为MyView设置customText和customColor两个XML属性。layout文件如下所示:
&code class=" hljs xml"&&!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%2D%2D%3E--&
&relativelayout xmlns:android="/apk/res/android" xmlns:tools="/tools" xmlns:app="/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ispring.attr.MainActivity"&
&com.ispring.attr.myview android:layout_width="match_parent" android:layout_height="match_parent" app:customtext="Hello World!" app:customcolor="#FF0000FF"&
&/com.ispring.attr.myview&&/relativelayout&&/code&
运行效果如下所示:
可以看出在界面上显示了蓝色的“Hello World!”文本,说明MyView的自定义属性起作用了。
我们看一下MyView的具体实现,MyView的代码如下所示:
package com.ispring.
import android.content.C
import android.graphics.C
import android.graphics.P
import android.text.TextP
import android.util.AttributeS
import android.view.V
public class MyView extends View {
//存储要显示的文本
private String mCustomT
//存储文本的显示颜色
private int mCustomColor = 0xFF000000;
private TextPaint mTextP
//字体大小
private float fontSize = getResources().getDimension(R.dimen.fontSize);
public MyView(Context context) {
super(context);
init(null, 0);
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
private void init(AttributeSet attrs, int defStyle) {
//首先判断attrs是否为null
if(attrs != null){
//获取AttributeSet中所有的XML属性的数量
int count = attrs.getAttributeCount();
//遍历AttributeSet中的XML属性
for(int i = 0; i & i++){
//获取attr的资源ID
int attrResId = attrs.getAttributeNameResource(i);
switch (attrResId){
case R.attr.customText:
//customText属性
mCustomText = attrs.getAttributeValue(i);
case R.attr.customColor:
//customColor属性
//如果读取不到对应的颜色值,那么就用黑色作为默认颜色
mCustomColor = attrs.getAttributeIntValue(i, 0xFF000000);
//初始化画笔
mTextPaint = new TextPaint();
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(fontSize);
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mCustomText != null && !mCustomText.equals("")){
mTextPaint.setColor(mCustomColor);
//将文本绘制显示出来
canvas.drawText(mCustomText, 0, fontSize, mTextPaint);
我们在MyView中定义了两个成员变量mCustomText和mCustomColor。MyView的几个构造函数都会调用init方法,我们重点看一下init方法。
传递给init方法的是一个AttributeSet对象,可以把它看成一个索引数组,这个数组里面存储着属性的索引,通过索引可以得到XML属性名和属性值。
通过调用AttributeSet的getAttributeCount()方法可以获得XML属性的数量,然后我们就可以在for循环中通过索引遍历AttributeSet的属性名和属性值。AttributeSet中有很多getXXX方法,一般必须的参数都是索引号,说几个常用的方法:
通过AttributeSet的public abstract String getAttributeName (int index)方法可以得到对应索引的XML属性名。
通过AttributeSet的public abstract int getAttributeNameResource (int index)方法可以得到对应索引的XML属性在R.attr中的资源ID,例如R.attr.customText、R.attr.customColor。
如果index对应的XML属性的format是string,那么通过AttributeSet的public abstract String getAttributeName (int index)方法,可以得到对应索引的XML属性的值,该方法返回的是String。除此之外,AttributeSet还有getAttributeIntValue、getAttributeFloatValue、getAttributeListValue等方法,返回不同类型的属性值。
我们通过attrs.getAttributeNameResource(i)得到获取attr的资源ID,然后对attrResId进行switch判断:
如果是R.attr.customText,表示当前属性是customText,我们通过attrs.getAttributeValue(i)读取customText属性值,并将其赋值给成员变量mCustomText。
如果是R.attr.customColor,表示当前属性是customColor,由于Android中用一个4字节的int型整数表示颜色,所以我们通过attrs.getAttributeIntValue(i, 0xFF000000)读取了customColor的颜色值,并将其赋值给成员变量mCustomColor。
我们重写了MyView的onDraw方法,通过执行mTextPaint.setColor(mCustomColor)把画笔设置成mCustomColor的颜色,通过执行canvas.drawText(mCustomText, 0, fontSize, mTextPaint)将mCustomText绘制到界面上。这样,MyView就使用了customText和customColor这两个XML属性。
使用和obtainStyledAttributes方法
我们上面定义的customText和customColor这两个属性都是直接在节点下定义的,这样定义属性存在一个问题:不能想通过style或theme设置这两个属性的值。
要想能够通过style或theme设置XML属性的值,需要在节点下添加节点,并在节点下定义,如下所示:
需要给设置name属性,一般name设置为自定义View的名字,我们此处设置为MyView。
在下面定义的属性与直接在定义的属性其实本质上没有太大区别,无论哪种方式定义,都会在R.attr类中定义R.attr.customText和R.attr.customColor。不同的是,节点会在R.styleable这个内部类中有如下定义:
R.styleable.MyView是一个int数组,其值为0x7f010038和 0x7fx7f010038就是属性R.attr.customText,0x7f010039就是属性R.attr.customColor。也就是R.styleable.MyView等价于数组[R.attr.customText, R.attr.customColor]。R.styleable.MyView的作用会在下面介绍。
我们同样可以在R.styleable中发现R.styleable.MyView_customColor和R.styleable.MyView_customText这两个ID。中的name加上里面的属性的name就组成了R.styleable中的MyView_customColor和MyView_customText,中间以下划线连接。如下图所示:
其中R.styleable.MyView_customColor对应R.attr.customColor,R.styleable.MyView_customText对应R.attr.customText。MyView_customColor和MyView_customText的作用在下面介绍。
在中定义的在MyView中需要通过调用theme的obtainStyledAttributes方法来读取解析属性值。obtainStyledAttributes有三个重载方法,签名分别如下所示:
public TypedArray obtainStyledAttributes (int[] attrs)
public TypedArray obtainStyledAttributes (int resid, int[] attrs)
public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
我们先看第三个最复杂的方法,在MyView中使用方法如下所示:
private void init(AttributeSet attributeSet, int defStyle) {
//首先判断attributeSet是否为null
if(attributeSet != null){
//获取当前MyView所在的Activity的theme
Resources.Theme theme = getContext().getTheme();
//通过theme的obtainStyledAttributes方法获取TypedArray对象
TypedArray typedArray = theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, 0, 0);
//获取typedArray的长度
int count = typedArray.getIndexCount();
//通过for循环遍历typedArray
for(int i = 0; i & i++){
//通过typedArray的getIndex方法获取指向R.styleable中对应的属性ID
int styledAttr = typedArray.getIndex(i);
switch (styledAttr){
case R.styleable.MyView_customText:
//如果是R.styleable.MyView_customText,表示属性是customText
//通过typedArray的getString方法获取字符串值
mCustomText = typedArray.getString(i);
case R.styleable.MyView_customColor:
//如果是R.styleable.MyView_customColor,表示属性是customColor
//通过typedArray的getColor方法获取整数类型的颜色值
mCustomColor = typedArray.getColor(i, 0xFF000000);
//在使用完typedArray之后,要调用recycle方法回收资源
typedArray.recycle();
我们在res/valeus/styles.xml文件中定义了如下style:
然后我们在layout文件中将MyView的style属性设置为上面的style,如下所示:
运行效果如下所示:
我们虽然没有直接设置MyView的customText和customColor两个属性,但是通过设置style属性之后,在效果上RedStyle中所定义的属性值应用到了MyView上了。
下面我们就来解释一下init方法:
首先我们通过getContext().getTheme()获取到了当前MyView所在的Activity的theme。
然后调用方法theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, 0, 0),该方法返回一个TypedArray对象。TypedArray是一个数组,通过该数组可以获取应用了style和theme的XML属性值。上面这个方法有四个参数,后面两个参数都是0,大家暂且忽略不计,后面会介绍。第一个参数还是AttributeSet对象,第二个参数是一个int类型的数组,该数组表示想要获取的属性值的属性的R.attr中的ID,此处我们传入的是R.styleable.MyView,在上面我们已经提到其值等价于[R.attr.customText, R.attr.customColor],表示我们此处想获取customText和customColor这两个属性的值。
如果在layout文件中直接为MyView设置了某些XML属性,那么这些XML属性及其属性值就会出现在AttributeSet中,那么Android就会直接使用AttributeSet中该XML属性值作为theme.obtainStyledAttributes()的返回值,比如在上面的例子中,我们通过app:customText="customText in AttributeSet"设置了MyView的XML属性,最终运行的效果显示的也是文本”customText in AttributeSet”。
如果在layout文件中没有为MyView设置某个XML属性,但是给MyView设置了style属性,例如style="@style/RedStyle",并且在style中指定了相应的XML属性,那么Android就会用style属性所对应的style资源中的XML属性值作为theme.obtainStyledAttributes()的返回值。比如在上面的例子中,我们在layout文件中没有设置app:customColor的值,但是在其style属性所对应的RedStyle资源中将customColor设置成了红色#FFFF0000,最终文本也是以红色显示在界面上的。
通过以上描述,我们可以知道,View的style属性对应的style资源中定义的XML属性值其实是View直接在layou文件中定义XML属性值的替补值,是用于补漏的,AttributeSet(即在layout中直接定义XML属性)的优先级高于style属性中资源所定义的属性值。
obtainStyledAttributes方法之defStyleAttr
我们再看一下方法obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)中的第三个参数defStyleAttr,这个参数表示的是一个
我们添加了HelloWorldStyle和GreenStyle,其中HelloWorldStyle只定义了customText的属性值,而GreenStyle同时定义了customText和customColor的值,其中customColor定义为绿色。
在AppTheme中,我们设置了myViewStyle这个属性的值,如下所示:
@style/GreenStyle
myViewStyle这个属性是在values/attrs_my_view.xml中定义的,如下所示:
myViewStyle被定义为一个reference格式,即其值指向一个资源类型,我们在AppTheme中将其赋值为@style/GreenStyle,即在AppTheme中,myViewStyle的就是GreenStyle,其指向了一个style资源。
我们更改MyView代码,如下所示:
//通过theme的obtainStyledAttributes方法获取TypedArray对象
TypedArray typedArray = theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, R.attr.myViewStyle, 0);
注意,上面obtainStyledAttributes方法最后一个参数还是为0,可以忽略,但是第三个参数的值不再是0,而是R.attr.myViewStyle。
然后我们更新layout文件,如下所示:
我们为MyView设置了style属性,其值为HelloWorldStyle。
程序运行效果如下所示:
我们发现结果是绿色的“Hello World!”,我们解释一下原因。
由于这次我们没有通过layout文件直接设置MyView的XML属性的值,所以AttributeSet本身是没有XML属性值的,我们直接忽略掉AttributeSet。
我们通过style="@style/HelloWorldStyle"为MyView设置了style为HelloWorldStyle,HelloWorldStyle中定义customText的属性值为”Hello World!”,所以最终customText的值就是”Hello World!”,在界面上显示的也是该值。
HelloWorldStyle中并没有定义customColor属性值。我们将theme.obtainStyledAttributes()方法的第三个参数设置为R.attr.myViewStyle,此处的theme就是我们上面提到的AppTheme,Android会去AppTheme中查找属性为myViewStyle的值,我们之前提到了,它的值就是@style/GreenStyle,即GreenStyle,由于该值是个style资源,Android就会去该资源中查找customColor的值,GreenStyle定义了customColor的颜色为绿色,所以MyView最终所使用的customColor的值就是绿色。
综上,我们发现,此处的第三个参数的作用是:当在AttributeSet和style属性中都没有找到属性值时,就去Theme的某个属性(即第三个参数)中查看其值是否是style资源,如果是style资源再去这个style资源中查找XML属性值作为替补值。
obtainStyledAttributes方法之defStyleRes
最后我们看一下方法obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)中的第四个参数defStyleRes。与defStyleAttr类似,defStyleRes是前面几项的替补值,defStyleRes的优先级最低。与defStyleAttr不同的是,defStyleRes本身直接表示一个style资源,而theme要通过属性defStyleAttr间接找到style资源。
我们添加了BlueStyle这个style,如下所示:
并将layout文件改为如下所示:
我们更改MyView代码如下所示:
TypedArray typedArray = theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, 0, R.style.BlueStyle);
第三个参数设置为0,第四个参数不再是0,而是R.style.BlueStyle。运行界面如下所示:
只有第三个参数defStyleAttr为0或者该属性在theme中找不到时,才会使用第四个参数defStyleRes。如果第三个参数defStyleAttr不为0,但是theme的defStyleAttr所对应的属性值中的style没有定义任何XML属性值,那么第四个参数也不会defStyleRes被使用。
可以不通过节点定义XML属性,不过还是建议将XML属性定义在节点下,因为这样Android会在R.styleable下面帮我们生成很多有用的常量供我们直接使用。
obtainStyledAttributes方法中,优先级从高到低依次是:直接在layout中设置View的XML属性值(AttributeSet) & 设置View的style属性 & defStyleAttr & defStyleRes
希望本文对大家理解与使用自定义XML属性有所帮助!

我要回帖

更多关于 android自定义xml属性 的文章

 

随机推荐