一直有个问题就是,Android中是如何通过布局文件,就能实现控件效果的不同呢?比如在布局文件中,我设置了一个TextView,给它设置了textColor,它就能够改变这个TextView的文本的颜色。这是如何做到的呢?我们分3个部分来看这个问题1.attrs.xml 2.styles.xml 3.看组件的源码。

1.attrs.xml:

我们知道Android的源码中有attrs.xml这个文件,这个文件实际上定义了所有的控件的属性,就是我们在布局文件中设置的各类属性

你可以找到attrs.xml这个文件,打开它,全选,右键->Show In->OutLine。可以看到整个文件的解构

我们大概可以看出里面是Android中的各种属性的声明,比如textStyle这个属性是这样定义的:

Java代码
  1. <!--Defaulttexttypefacestyle.-->
  2. <attrname="textStyle">
  3. <flagname="normal"value="0"/>
  4. <flagname="bold"value="1"/>
  5. <flagname="italic"value="2"/>
  6. </attr>

那么现在你知道,我们在写android:textStyle的时候为什么会出现normal,bold和italic这3个东西了吧,就是定义在这个地方。

再看看textColor:

Java代码
  1. <!--Coloroftext(usuallysameascolorForeground).-->
  2. <attrname="textColor"format="reference|color"/>

format的意思是说:这个textColor可以以两种方式设置,要么是关联一个值,要么是直接设置一个颜色的RGB值,这个不难理解,因为我们可以平时也这样做过。

也就是说我们平时在布局文件中所使用的各类控件的属性都定义在这里面,那么这个文件,除了定义这些属性外还定义了各种具体的组件,比如TextView,Button,SeekBar等所具有的各种特有的属性

比如SeekBar:

Java代码
  1. <declare-styleablename="SeekBar">
  2. <!--Drawsthethumbonaseekbar.-->
  3. <attrname="thumb"format="reference"/>
  4. <!--Anoffsetforthethumbthatallowsittoextendoutoftherangeofthetrack.-->
  5. <attrname="thumbOffset"format="dimension"/>
  6. </declare-styleable>

也许你会问SeekBar的background,等属性怎么没有看到?这是因为Android中几乎所有的组件都是从View中继承下来的,SeekBar自然也不例外,而background这个属性几乎每个控件都有,因此被定义到了View中,你可以在declare-styleable:View中找到它。

总结下,也就是说attrs.xml这个文件定义了布局文件中的各种属性attr:***,以及每种控件特有的属性declare-styleable:***

2.styles.xml:

刚才的attrs.xml定义的是组件的属性,现在要说的style则是针对这些属性所设置的值,一些默认的值。


这个是SeekBar的样式,我们可以看到,这里面设置了一个SeekBar的默认的样式,即为attrs.xml文件中的各种属性设置初始值

Java代码
  1. <stylename="Widget.SeekBar">
  2. <itemname="android:indeterminateOnly">false</item>
  3. <itemname="android:progressDrawable">@android:drawable/progress_horizontal</item>
  4. <itemname="android:indeterminateDrawable">@android:drawable/progress_horizontal</item>
  5. <itemname="android:minHeight">20dip</item>
  6. <itemname="android:maxHeight">20dip</item>
  7. <itemname="android:thumb">@android:drawable/seek_thumb</item>
  8. <itemname="android:thumbOffset">8dip</item>
  9. <itemname="android:focusable">true</item>
  10. </style>

这个是Button的样式:

Java代码
  1. <stylename="Widget.Button">
  2. <itemname="android:background">@android:drawable/btn_default</item>
  3. <itemname="android:focusable">true</item>
  4. <itemname="android:clickable">true</item>
  5. <itemname="android:textAppearance">?android:attr/textAppearanceSmallInverse</item>
  6. <itemname="android:textColor">@android:color/primary_text_light</item>
  7. <itemname="android:gravity">center_vertical|center_horizontal</item>
  8. </style>

有了属性和值,但是这些东西是如何关联到一起的呢?它们如何被android的framework层所识别呢?

3.组件的源码

我们看下TextView的源码:

Java代码
  1. publicTextView(Contextcontext){
  2. this(context,null);
  3. }//这个构造器用来给用户调用,比如newTextView(this);
  4. publicTextView(Contextcontext,
  5. AttributeSetattrs){
  6. this(context,attrs,com.android.internal.R.attr.textViewStyle);
  7. }
  8. publicTextView(Contextcontext,
  9. AttributeSetattrs,
  10. intdefStyle){
  11. super(context,attrs,defStyle);//为用户自定义的TextView设置默认的style
  12. mText="";
  13. //设置画笔
  14. mTextPaint=newTextPaint(Paint.ANTI_ALIAS_FLAG);
  15. mTextPaint.density=getResources().getDisplayMetrics().density;
  16. mTextPaint.setCompatibilityScaling(
  17. getResources().getCompatibilityInfo().applicationScale);
  18. mHighlightPaint=newPaint(Paint.ANTI_ALIAS_FLAG);
  19. mHighlightPaint.setCompatibilityScaling(
  20. getResources().getCompatibilityInfo().applicationScale);
  21. mMovement=getDefaultMovementMethod();
  22. mTransformation=null;
  23. //attrs中包含了这个TextView控件在布局文件中定义的属性,比如android:background,android:layout_width等
  24. //com.android.internal.R.styleable.TextView中包含了TextView中的针对attrs中的属性的默认的值
  25. //也就是说这个地方能够将布局文件中设置的属性获取出来,保存到一个TypeArray中,为这个控件初始化各个属性
  26. TypedArraya=
  27. context.obtainStyledAttributes(
  28. attrs,com.android.internal.R.styleable.TextView,defStyle,0);
  29. inttextColorHighlight=0;
  30. ColorStateListtextColor=null;
  31. ColorStateListtextColorHint=null;
  32. ColorStateListtextColorLink=null;
  33. inttextSize=15;
  34. inttypefaceIndex=-1;
  35. intstyleIndex=-1;
  36. /*
  37. *Looktheappearanceupwithoutcheckingfirstifitexistsbecause
  38. *almosteveryTextViewhasoneanditgreatlysimplifiesthelogic
  39. *tobeabletoparsetheappearancefirstandthenletspecifictags
  40. *forthisViewoverrideit.
  41. */
  42. TypedArrayappearance=null;
  43. //TextView_textAppearance不太了解为什么要这样做?难道是为了设置TextView的一些默认的属性?
  44. intap=a.getResourceId(com.android.internal.R.styleable.TextView_textAppearance,-1);
  45. if(ap!=-1){
  46. appearance=context.obtainStyledAttributes(ap,
  47. com.android.internal.R.styleable.
  48. TextAppearance);
  49. }
  50. if(appearance!=null){
  51. intn=appearance.getIndexCount();
  52. for(inti=0;i<n;i++){
  53. intattr=appearance.getIndex(i);
  54. switch(attr){
  55. casecom.android.internal.R.styleable.TextAppearance_textColorHighlight:
  56. textColorHighlight=appearance.getColor(attr,textColorHighlight);
  57. break;
  58. casecom.android.internal.R.styleable.TextAppearance_textColor:
  59. textColor=appearance.getColorStateList(attr);
  60. break;
  61. casecom.android.internal.R.styleable.TextAppearance_textColorHint:
  62. textColorHint=appearance.getColorStateList(attr);
  63. break;
  64. casecom.android.internal.R.styleable.TextAppearance_textColorLink:
  65. textColorLink=appearance.getColorStateList(attr);
  66. break;
  67. casecom.android.internal.R.styleable.TextAppearance_textSize:
  68. textSize=appearance.getDimensionPixelSize(attr,textSize);
  69. break;
  70. casecom.android.internal.R.styleable.TextAppearance_typeface:
  71. typefaceIndex=appearance.getInt(attr,-1);
  72. break;
  73. casecom.android.internal.R.styleable.TextAppearance_textStyle:
  74. styleIndex=appearance.getInt(attr,-1);
  75. break;
  76. }
  77. }
  78. appearance.recycle();
  79. }
  80. //各类属性
  81. booleaneditable=getDefaultEditable();
  82. CharSequenceinputMethod=null;
  83. intnumeric=0;
  84. CharSequencedigits=null;
  85. booleanphone=false;
  86. booleanautotext=false;
  87. intautocap=-1;
  88. intbuffertype=0;
  89. booleanselectallonfocus=false;
  90. DrawabledrawableLeft=null,drawableTop=null,drawableRight=null,
  91. drawableBottom=null;
  92. intdrawablePadding=0;
  93. intellipsize=-1;
  94. booleansingleLine=false;
  95. intmaxlength=-1;
  96. CharSequencetext="";
  97. CharSequencehint=null;
  98. intshadowcolor=0;
  99. floatdx=0,dy=0,r=0;
  100. booleanpassword=false;
  101. intinputType=EditorInfo.TYPE_NULL;
  102. intn=a.getIndexCount();
  103. for(inti=0;i<n;i++){
  104. intattr=a.getIndex(i);
  105. //通过switch语句将用户设置的,以及默认的属性读取出来并初始化
  106. switch(attr){
  107. casecom.android.internal.R.styleable.TextView_editable:
  108. editable=a.getBoolean(attr,editable);
  109. break;
  110. casecom.android.internal.R.styleable.TextView_inputMethod:
  111. inputMethod=a.getText(attr);
  112. break;
  113. casecom.android.internal.R.styleable.TextView_numeric:
  114. numeric=a.getInt(attr,numeric);
  115. break;
  116. //更多的case语句...
  117. casecom.android.internal.R.styleable.TextView_textSize:
  118. textSize=a.getDimensionPixelSize(attr,textSize);//设置当前用户所设置的字体大小
  119. break;
  120. casecom.android.internal.R.styleable.TextView_typeface:
  121. typefaceIndex=a.getInt(attr,typefaceIndex);
  122. break;
  123. //更多的case语句...
  124. }

通过上面的代码大概可以知道,每个组件基本都有3个构造器,其中只传递一个Context上下文的那个构造器一般用来在java代码中实例化使用。

比如你可以

Java代码
  1. TextViewtv=newTextView(context);

来实例化一个组件。

最终调用的是第3个构造器

Java代码
  1. publicTextView(Contextcontext,
  2. AttributeSetattrs,
  3. intdefStyle)

在这个构造器中为你设置了默认的属性attrs和值styles。关键不在这里,而是后面通过使用下面的代码

Java代码
  1. TypedArraya=
  2. context.obtainStyledAttributes(
  3. attrs,com.android.internal.R.styleable.TextView,defStyle,0);

来将属性和值获取出来,放到一个TypeArray中,然后再利用一个switch语句将里面的值取出来。再利用这些值来初始化各个属性。这个View最终利用这些属性将这个控件绘制出来。

如果你在布局文件中定义的一个View的话,那么你定义的值,会被传递给构造器中的attrs和styles。也是利用同样的方式来获取出你定义的值,并根据你定义的值来绘制你想要的控件。

再比如其实Button和EditText都是继承自TextView。看上去两个控件似乎差异很大,其实不然。Button的源码其实相比TextView变化的只是style而已:

Java代码
  1. publicclassButtonextendsTextView{
  2. publicButton(Contextcontext){
  3. this(context,null);
  4. }
  5. publicButton(Contextcontext,AttributeSetattrs){
  6. this(context,attrs,com.android.internal.R.attr.buttonStyle);
  7. }
  8. publicButton(Contextcontext,AttributeSetattrs,intdefStyle){
  9. super(context,attrs,defStyle);
  10. }
  11. }

再看看EditText:

Java代码
  1. publicclassEditTextextendsTextView{
  2. publicEditText(Contextcontext){
  3. this(context,null);
  4. }
  5. publicEditText(Contextcontext,AttributeSetattrs){
  6. this(context,attrs,com.android.internal.R.attr.editTextStyle);
  7. }
  8. publicEditText(Contextcontext,AttributeSetattrs,intdefStyle){
  9. super(context,attrs,defStyle);
  10. }
  11. @Override
  12. protectedbooleangetDefaultEditable(){
  13. returntrue;
  14. }
  15. @Override
  16. protectedMovementMethodgetDefaultMovementMethod(){
  17. returnArrowKeyMovementMethod.getInstance();
  18. }
  19. @Override
  20. publicEditablegetText(){
  21. return(Editable)super.getText();
  22. }
  23. @Override
  24. publicvoidsetText(CharSequencetext,BufferTypetype){
  25. super.setText(text,BufferType.EDITABLE);
  26. }
  27. /**
  28. *Conveniencefor{@linkSelection#setSelection(Spannable,int,int)}.
  29. */
  30. publicvoidsetSelection(intstart,intstop){
  31. Selection.setSelection(getText(),start,stop);
  32. }
  33. /**
  34. *Conveniencefor{@linkSelection#setSelection(Spannable,int)}.
  35. */
  36. publicvoidsetSelection(intindex){
  37. Selection.setSelection(getText(),index);
  38. }
  39. /**
  40. *Conveniencefor{@linkSelection#selectAll}.
  41. */
  42. publicvoidselectAll(){
  43. Selection.selectAll(getText());
  44. }
  45. /**
  46. *Conveniencefor{@linkSelection#extendSelection}.
  47. */
  48. publicvoidextendSelection(intindex){
  49. Selection.extendSelection(getText(),index);
  50. }
  51. @Override
  52. publicvoidsetEllipsize(TextUtils.TruncateAtellipsis){
  53. if(ellipsis==TextUtils.TruncateAt.MARQUEE){
  54. thrownewIllegalArgumentException("EditTextcannotusetheellipsizemode"
  55. +"TextUtils.TruncateAt.MARQUEE");
  56. }
  57. super.setEllipsize(ellipsis);
  58. }
  59. }

不知道你是不是和我一样感到意外呢?

不得不说这种方式非常的好。最大程度地利用了继承,并且可以让控件之间的属性可以很方便的被开发者使用。也利用以后的扩展,实际上,不同的style就可以得到不同的UI,这也是MVC的一种体现。

比如用户想自定义某个控件,只要覆盖父类的style就可以很轻松的实现,可以参考我的一篇博文,就是使用style自定义ProgressBar。

Android中的主题theme也是使用的style。当用户在Activity中设置一个style的时候那么会影响到整个Activity,如果为Application设置style的话,则会影响所有的Activity,所以,如果你在开发一个应用的时候

可以考虑将应用的Activity的背景颜色等一类的属性放到一个style中去,在Application中调用,这种做法会比较方便。

themes.xml:

Java代码
  1. <!--Variantofthedefault(dark)themewithnotitlebar-->
  2. <stylename="Theme.NoTitleBar">
  3. <itemname="android:windowNoTitle">true</item>
  4. </style>
  5. <!--Variantofthedefault(dark)themethathasnotitlebarand
  6. fillstheentirescreen-->
  7. <stylename="Theme.NoTitleBar.Fullscreen">
  8. <itemname="android:windowFullscreen">true</item>
  9. <itemname="android:windowContentOverlay">@null</item>
  10. </style>

我们平时使用的主题实际上就定义在这个文件中。也是一个style。

更多相关文章

  1. 一款常用的 Squid 日志分析工具
  2. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  3. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  4. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  5. [Android]读取自身安装包信息
  6. 安卓安全——读书笔记
  7. Android(安卓)通过JNI C++进行MD5加密
  8. 【Tensorflow】Tensorflow移植Android上的过程和坑
  9. React Native Android从源码看WebView 没有OverrideUrl解决办法,

随机推荐

  1. 在Ubuntu16.04上下载并编译Android内核源
  2. Android访问WCF服务(上篇)-服务端开发
  3. Android自学笔记-1-android运行时Dalvik
  4. android中ListView背景设置问题(转)
  5. android 中使用java aes加密算法,报错信息
  6. Android系统input系统(1)
  7. 菜鸟与月薪10万大神的差距都在这 : Andro
  8. Android中系统状态栏的隐藏和显示
  9. Android 高手进阶教程(十四)之----Androi
  10. Android SystemUI (导入到Eclipse,可以正