大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
一直有个问题就是,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这个属性是这样定义的:
那么现在你知道,我们在写android:textStyle的时候为什么会出现normal,bold和italic这3个东西了吧,就是定义在这个地方。
再看看textColor:
format的意思是说:这个textColor可以以两种方式设置,要么是关联一个值,要么是直接设置一个颜色的RGB值,这个不难理解,因为我们可以平时也这样做过。
也就是说我们平时在布局文件中所使用的各类控件的属性都定义在这里面,那么这个文件,除了定义这些属性外还定义了各种具体的组件,比如TextView,Button,SeekBar等所具有的各种特有的属性
比如SeekBar:
也许你会问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文件中的各种属性设置初始值
false
@android:drawable/progress_horizontal
@android:drawable/progress_horizontal
20dip
20dip
@android:drawable/seek_thumb
8dip
true
这个是Button的样式:
@android:drawable/btn_default
true
true
?android:attr/textAppearanceSmallInverse
@android:color/primary_text_light
center_vertical|center_horizontal
有了属性和值,但是这些东西是如何关联到一起的呢?它们如何被android的framework层所识别呢?
3.组件的源码
我们看下TextView的源码:
publicTextView(Context context) {this(context, null);
}//这个构造器用来给用户调用,比如new TextView(this);
publicTextView(Context context,
AttributeSet attrs) {this(context, attrs, com.android.internal.R.attr.textViewStyle);
}publicTextView(Context context,
AttributeSet attrs,intdefStyle) {
super(context, attrs, defStyle);//为用户自定义的TextView设置默认的style
mText = “”;//设置画笔
mTextPaint = newTextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.density=getResources().getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling(
getResources().getCompatibilityInfo().applicationScale);
mHighlightPaint= newPaint(Paint.ANTI_ALIAS_FLAG);
mHighlightPaint.setCompatibilityScaling(
getResources().getCompatibilityInfo().applicationScale);
mMovement=getDefaultMovementMethod();
mTransformation= null;//attrs中包含了这个TextView控件在布局文件中定义的属性,比如android:background,android:layout_width等//com.android.internal.R.styleable.TextView中包含了TextView中的针对attrs中的属性的默认的值//也就是说这个地方能够将布局文件中设置的属性获取出来,保存到一个TypeArray中,为这个控件初始化各个属性
TypedArray a =context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.TextView, defStyle, 0);int textColorHighlight = 0;
ColorStateList textColor= null;
ColorStateList textColorHint= null;
ColorStateList textColorLink= null;int textSize = 15;int typefaceIndex = -1;int styleIndex = -1;/** Look the appearance up without checking first if it exists because
* almost every TextView has one and it greatly simplifies the logic
* to be able to parse the appearance first and then let specific tags
* for this View override it.*/TypedArray appearance= null;//TextView_textAppearance不太了解为什么要这样做?难道是为了设置TextView的一些默认的属性?
int ap = a.getResourceId(com.android.internal.R.styleable.TextView_textAppearance, -1);if (ap != -1) {
appearance=context.obtainStyledAttributes(ap,
com.android.internal.R.styleable.
TextAppearance);
}if (appearance != null) {int n =appearance.getIndexCount();for (int i = 0; i < n; i++) {int attr =appearance.getIndex(i);switch(attr) {case com.android.internal.R.styleable.TextAppearance_textColorHighlight:
textColorHighlight=appearance.getColor(attr, textColorHighlight);break;case com.android.internal.R.styleable.TextAppearance_textColor:
textColor=appearance.getColorStateList(attr);break;case com.android.internal.R.styleable.TextAppearance_textColorHint:
textColorHint=appearance.getColorStateList(attr);break;case com.android.internal.R.styleable.TextAppearance_textColorLink:
textColorLink=appearance.getColorStateList(attr);break;case com.android.internal.R.styleable.TextAppearance_textSize:
textSize=appearance.getDimensionPixelSize(attr, textSize);break;case com.android.internal.R.styleable.TextAppearance_typeface:
typefaceIndex= appearance.getInt(attr, -1);break;case com.android.internal.R.styleable.TextAppearance_textStyle:
styleIndex= appearance.getInt(attr, -1);break;
}
}
appearance.recycle();
}//各类属性
boolean editable =getDefaultEditable();
CharSequence inputMethod= null;int numeric = 0;
CharSequence digits= null;
boolean phone= false;
boolean autotext= false;int autocap = -1;int buffertype = 0;
boolean selectallοnfοcus= false;
Drawable drawableLeft= null, drawableTop = null, drawableRight = null,
drawableBottom= null;int drawablePadding = 0;int ellipsize = -1;
boolean singleLine= false;int maxlength = -1;
CharSequence text= “”;
CharSequence hint= null;int shadowcolor = 0;float dx = 0, dy = 0, r = 0;
boolean password= false;int inputType =EditorInfo.TYPE_NULL;int n =a.getIndexCount();for (int i = 0; i < n; i++) {int attr =a.getIndex(i);//通过switch语句将用户设置的,以及默认的属性读取出来并初始化
switch(attr) {case com.android.internal.R.styleable.TextView_editable:
editable=a.getBoolean(attr, editable);break;case com.android.internal.R.styleable.TextView_inputMethod:
inputMethod=a.getText(attr);break;case com.android.internal.R.styleable.TextView_numeric:
numeric=a.getInt(attr, numeric);break;//更多的case语句…
case com.android.internal.R.styleable.TextView_textSize:
textSize= a.getDimensionPixelSize(attr, textSize);//设置当前用户所设置的字体大小
break;case com.android.internal.R.styleable.TextView_typeface:
typefaceIndex=a.getInt(attr, typefaceIndex);break;//更多的case语句…
}
通过上面的代码大概可以知道,每个组件基本都有3个构造器,其中只传递一个Context上下文的那个构造器一般用来在java代码中实例化使用。
比如你可以
TextView tv = new TextView(context);
来实例化一个组件。
最终调用的是第3个构造器
publicTextView(Context context,
AttributeSet attrs,int defStyle)
在这个构造器中为你设置了默认的属性attrs和值styles。关键不在这里,而是后面通过使用下面的代码
TypedArray a =context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.TextView, defStyle, 0);
来将属性和值获取出来,放到一个TypeArray中,然后再利用一个switch语句将里面的值取出来。再利用这些值来初始化各个属性。这个View最终利用这些属性将这个控件绘制出来。
如果你在布局文件中定义的一个View的话,那么你定义的值,会被传递给构造器中的attrs和styles。也是利用同样的方式来获取出你定义的值,并根据你定义的值来绘制你想要的控件。
再比如其实Button和EditText都是继承自TextView。看上去两个控件似乎差异很大,其实不然。Button的源码其实相比TextView变化的只是style而已:
public classButton extends TextView {publicButton(Context context) {this(context, null);
}publicButton(Context context, AttributeSet attrs) {this(context, attrs, com.android.internal.R.attr.buttonStyle);
}public Button(Context context, AttributeSet attrs, intdefStyle) {
super(context, attrs, defStyle);
}
}
再看看EditText:
public classEditText extends TextView {publicEditText(Context context) {this(context, null);
}publicEditText(Context context, AttributeSet attrs) {this(context, attrs, com.android.internal.R.attr.editTextStyle);
}public EditText(Context context, AttributeSet attrs, intdefStyle) {
super(context, attrs, defStyle);
}
@Overrideprotectedboolean getDefaultEditable() {return true;
}
@OverrideprotectedMovementMethod getDefaultMovementMethod() {returnArrowKeyMovementMethod.getInstance();
}
@OverridepublicEditable getText() {return(Editable) super.getText();
}
@Overridepublic voidsetText(CharSequence text, BufferType type) {
super.setText(text, BufferType.EDITABLE);
}/**
* Convenience for {@link Selection#setSelection(Spannable, int, int)}.*/
public void setSelection(int start, intstop) {
Selection.setSelection(getText(), start, stop);
}/**
* Convenience for {@link Selection#setSelection(Spannable, int)}.*/
public void setSelection(intindex) {
Selection.setSelection(getText(), index);
}/**
* Convenience for {@link Selection#selectAll}.*/
public voidselectAll() {
Selection.selectAll(getText());
}/**
* Convenience for {@link Selection#extendSelection}.*/
public void extendSelection(intindex) {
Selection.extendSelection(getText(), index);
}
@Overridepublic voidsetEllipsize(TextUtils.TruncateAt ellipsis) {if (ellipsis ==TextUtils.TruncateAt.MARQUEE) {throw new IllegalArgumentException(“EditText cannot use the ellipsize mode”
+ “TextUtils.TruncateAt.MARQUEE”);
}
super.setEllipsize(ellipsis);
}
}
不知道你是不是和我一样感到意外呢?
不得不说这种方式非常的好。最大程度地利用了继承,并且可以让控件之间的属性可以很方便的被开发者使用。也利用以后的扩展,实际上,不同的style就可以得到不同的UI,这也是MVC的一种体现。
比如用户想自定义某个控件,只要覆盖父类的style就可以很轻松的实现,可以参考我的一篇博文,就是使用style自定义ProgressBar。
Android中的主题theme也是使用的style。当用户在Activity中设置一个style的时候那么会影响到整个Activity,如果为Application设置style的话,则会影响所有的Activity,所以,如果你在开发一个应用的时候
可以考虑将应用的Activity的背景颜色等一类的属性放到一个style中去,在Application中调用,这种做法会比较方便。
themes.xml:
true
true
@null
我们平时使用的主题实际上就定义在这个文件中。也是一个style。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/180772.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...