declare-styleable(Android 中 declare-styleable 和 style 的不同)
大家好,今天我将向大家分享有关declare-styleable和Android 中 declare-styleable 和 style 的不同的一些独特见解,希望能够为你们带来新的思考和启示。
Android自定义属性TypedArray详解
大家好,我是程序员双木L,后续会发专题类的文章,这是自定义控件的第一篇,之后也会陆续更新相关的文章,欢迎关注。
自定义属性在自定义控件过程中属于比较常见的操作,我们可以回想一下这样的场景:自定义view的过程中,我们需要在不同的情况下设置不同的文字大小,那么我们是不是就需要提供对外的方法来设置,这样就比较灵活操作。而我们自定义对外的方法,就是我们自定义的属性啦,那我们来分析一下其原理及作用。
下面我们根据例子来进行分析:
1、首先我们需要在res->values目录下新建attrs.xml文件,该文件就是用来声明属性名及其接受的数据格式的,如下:
attr名词解析:
name表示属性名,上面的属性名是我自己定义的。
format表示接受的输入格式,format格式集合如下:
2、自定义属性的使用,这里我们使用两种方式进行对比解析
最最最原始的使用方式
(1)、自定义文件如下:
我们可以在TestAttrsView方法的参数AttributeSet是个xml解析工具类,帮助我们从布局的xml里提取属性名和属性值。
(2)、在布局文件xml中的使用
这里使用自定义属性需要声明xml的命名空间,其中app是命名空间,用来加在自定义属性前面。
xmlns:app=" http://schemas.android.com/apk/res-auto"
声明xml命名空间,xmlns意思为“xml namespace”.冒号后面是给这个引用起的别名。
schemas是xml文档的两种约束文件其中的一种,规定了xml中有哪些元素(标签)、
元素有哪些属性及各元素的关系,当然从面向对象的角度理解schemas文件可以
认为它是被约束的xml文档的“类”或称为“模板”。
(3)、将属性名与属性值打印结果如下:
从打印结果我们可以看出,AttributeSet将布局文件xml下的属性全部打印出来了,细心的童鞋可能已经看出来:
这个属性我们设置的是一个整数尺寸,可最后打印出来的是资源编号。
那如果我们想要输出我们设置的整数尺寸,需要怎么操作呢?
这个时候就该我们这篇的主角出场了,使用TypedArray方式。
(1)、这里我们需要将attrs.xml使用“declare-styleable”标签进行改造,如下:
从改造后的attrs文件可以看出,我们将属性声明归结到TestStyleable里面,也就意味着这些属性是属于TestStyleable下的。
(2)、属性的解析:
这里我直接打印出解析结果,这里可以获取我们想要的自定义属性,而系统有的属性可以忽略。
(3)、运行结果如下
从解析的结果可以看出,尺寸的结果已经转换为实际值了:
这个时候有童鞋又问了,我设置的是15dp,为啥最后打印是41.25了呢?其实解析出来的值单位是px,所以这里输出的是转换后的值。
解析的过程中用到了这个方法:
我们来看一下这个方法的源码:
源码中我们可以看到这个方法有两个参数:
obtainStyledAttributes方法返回值类型为TypedArray。该类型记录了获取到的属性值集合,而通过数组下标索引即可找到对应的属性值。索引下标通过R.styleable.TestStyleable_xx获取,"xx"表示属性名,一般命名为"styleable名"+"_"+"属性名"。
而TypedArray提供了各种Api,如getInteger,getString,getDimension等方法来获取属性值,这些方法都需要传入对应属性名在obtainStyledAttributes中的int数组的位置索引,通过下标获取数组里属性值。
这个TypedArray的作用就是资源的映射作用,把自定义属性在xml设置值映射到class,这样怎么获取都很简单啦。
到这里就分析完啦!
android 自定义switch样式
修改后的MySwitch控件接口基本与原Switch控件一致,并且除了可支持所有SDK外,增加了2项小功能:
1.支持用Track背景图片的方式代替Texton Textoff等文字方式表现开关状态
2.支持调整控制Switch的高度
下面贴出Switch修改的关键代码:
/**
*<p>
* modified from android SDK 14(4.0) android.widget.Switch.
*<br/>
*<strong>new feature:</strong>
*<ol>
*<li>support SDK 1 or higher.</li>
*<li>you can use track drawable instead of text to display the changes of off-on state!</li>
*<li>you can control the Switch minimum height.</li>
*</ol>
*</p>
*
*@see{@link Switch}
*@author Wison
*/
public class MySwitch extends CompoundButton{
// Support track drawable instead of text
private Drawable mTrackOnDrawable;
private Drawable mTrackOffDrawable;
// Support minimum height
private int mSwitchMinHeight;
/**
* Construct a new Switch with a default style determined by the given theme attribute,
* overriding specific style attributes as requested.
*
*@param context The Context that will determine this widget's theming.
*@param attrs Specification of attributes that should deviate from the default styling.
*@param defStyle An attribute ID within the active theme containing a reference to the
* default style for this widget. e.g. android.R.attr.switchStyle.
*/
public MySwitch(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
mTextPaint= new TextPaint(Paint.ANTI_ALIAS_FLAG);
Resources res= getResources();
mTextPaint.density= res.getDisplayMetrics().density;
//float scaledDensity= res.getDisplayMetrics().scaledDensity;
//mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
TypedArray a= context.obtainStyledAttributes(attrs, R.styleable.Switch, defStyle, 0);
// off-on模式:图片模式或文字模式,图片模式是用Track背景图片表示off-on的状态,文字模式是用文字来表示off-on状态。
mTrackOnDrawable= a.getDrawable(R.styleable.Switch_trackOn);
mTrackOffDrawable= a.getDrawable(R.styleable.Switch_trackOff);
if(checkTrackOffOnDrawable()){
//如果设定图片模式,则默认显示off状态
mTrackDrawable= mTrackOffDrawable;
} else{
mTrackDrawable= a.getDrawable(R.styleable.Switch_track);
}
mThumbDrawable= a.getDrawable(R.styleable.Switch_thumb);
mTextOn= a.getText(R.styleable.Switch_textOn);
mTextOff= a.getText(R.styleable.Switch_textOff);
mThumbTextPadding= a.getDimensionPixelSize(R.styleable.Switch_thumbTextPadding, 0);
mSwitchMinWidth= a.getDimensionPixelSize(R.styleable.Switch_switchMinWidth, 0);
mSwitchMinHeight= a.getDimensionPixelSize(R.styleable.Switch_switchMinHeight, 0);
mSwitchPadding= a.getDimensionPixelSize(R.styleable.Switch_switchPadding, 0);
int appearance= a.getResourceId(R.styleable.Switch_switchTextAppearance, 0);
if(appearance!= 0){
setSwitchTextAppearance(context, appearance);
}
a.recycle();
ViewConfiguration config= ViewConfiguration.get(context);
mTouchSlop= config.getScaledTouchSlop();
mMinFlingVelocity= config.getScaledMinimumFlingVelocity();
// Refresh display with current params
refreshDrawableState();
setChecked(isChecked());
}
private boolean checkTrackOffOnDrawable(){
return mTrackOnDrawable!= null&& mTrackOffDrawable!= null;
}
@SuppressLint("NewApi")
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
if(mOnLayout== null){
mOnLayout= makeLayout(mTextOn);
}
if(mOffLayout== null){
mOffLayout= makeLayout(mTextOff);
}
mTrackDrawable.getPadding(mTempRect);
final int maxTextWidth= Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
final int switchWidth= Math.max(mSwitchMinWidth,
maxTextWidth* 2+ mThumbTextPadding* 4+ mTempRect.left+ mTempRect.right);
// final int switchHeight= mTrackDrawable.getIntrinsicHeight();
int switchHeight;
if(mSwitchMinHeight<= 0){
switchHeight= mTrackDrawable.getIntrinsicHeight();
} else{
switchHeight= Math.max(mSwitchMinHeight, mTempRect.top+ mTempRect.bottom);
}
mThumbWidth= maxTextWidth+ mThumbTextPadding* 2;
mSwitchWidth= switchWidth;
mSwitchHeight= switchHeight;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int measuredHeight= getMeasuredHeight();
if(measuredHeight< switchHeight){
if(Build.VERSION.SDK_INT>= 11){
setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
} else{
setMeasuredDimension(getMeasuredWidth(), switchHeight);
}
}
}
@Override
public void setChecked(boolean checked){
if(checkTrackOffOnDrawable()){
mTrackDrawable= checked? mTrackOnDrawable: mTrackOffDrawable;
refreshDrawableState();
}
super.setChecked(checked);
mThumbPosition= checked? getThumbScrollRange(): 0;
invalidate();
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
// Draw the switch
int switchLeft= mSwitchLeft;
int switchTop= mSwitchTop;
int switchRight= mSwitchRight;
int switchBottom= mSwitchBottom;
if(checkTrackOffOnDrawable()){
mTrackDrawable= getTargetCheckedState()? mTrackOnDrawable: mTrackOffDrawable;
refreshDrawableState();
}
mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
mTrackDrawable.draw(canvas);
canvas.save()
mTrackDrawable.getPadding(mTempRect);
int switchInnerLeft= switchLeft+ mTempRect.left;
int switchInnerTop= switchTop+ mTempRect.top;
int switchInnerRight= switchRight- mTempRect.right;
int switchInnerBottom= switchBottom- mTempRect.bottom;
canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
mThumbDrawable.getPadding(mTempRect);
final int thumbPos=(int)(mThumbPosition+ 0.5f);
int thumbLeft= switchInnerLeft- mTempRect.left+ thumbPos;
int thumbRight= switchInnerLeft+ thumbPos+ mThumbWidth+ mTempRect.right;
mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
mThumbDrawable.draw(canvas);
// mTextColors should not be null, but just in case
if(mTextColors!= null){
mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(),
mTextColors.getDefaultColor()));
}
mTextPaint.drawableState= getDrawableState();
Layout switchText= getTargetCheckedState()? mOnLayout: mOffLayout;
if(switchText!= null){
canvas.translate((thumbLeft+ thumbRight)/ 2- switchText.getWidth()/ 2,
(switchInnerTop+ switchInnerBottom)/ 2- switchText.getHeight()/ 2);
switchText.draw(canvas);
}
canvas.restore();
}
}
下面是关键属性声明:
<declare-styleable name="Switch">
<!-- Drawable to use when the switch is in the checked/"on" state.-->
<attr name="trackOn" format="reference"/>
<!-- Drawable to use when the switch is in the unchecked/"off" state.-->
<attr name="trackOff" format="reference"/>
<!-- Minimum height for the switch component-->
<attr name="switchMinHeight" format="dimension"/>
<!-- Drawable to use as the"thumb" that switches back and forth.-->
<attr name="thumb" format="reference"/>
<!-- Drawable to use as the"track" that the switch thumb slides within.-->
<attr name="track" format="reference"/>
<!-- Text to use when the switch is in the checked/"on" state.-->
<attr name="textOn" format="string"/>
<!-- Text to use when the switch is in the unchecked/"off" state.-->
<attr name="textOff" format="string"/>
<!-- Amount of padding on either side of text within the switch thumb.-->
<attr name="thumbTextPadding" format="dimension"/>
<!-- TextAppearance style for text displayed on the switch thumb.-->
<attr name="switchTextAppearance" format="reference"/>
<!-- Minimum width for the switch component-->
<attr name="switchMinWidth" format="dimension"/>
<!-- Minimum space between the switch and caption text-->
<attr name="switchPadding" format="dimension"/>
</declare-styleable>
Android 中 declare-styleable 和 style 的不同
我们注意到上文中的CodeFont的定义,有没有发现item里面的name都是android开头?因为这些属性都是在android中预先设定好的,所以我们可以随意用。但是如果我们想用自己定义的属性名呢?这时候styleable的作用就出现了。我们只需要把attr的定义包围在styleable里面,这样定义的属性名就可以在style里面用。示例如下(示例来自上文给出的stackoverflow链接):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="attrib1" format="string"/>
<declare-styleable name="blahblah">
<attr name="attrib2" format="string"/>
</declare-styleable>
在attrs.xml里面定义了两个attr,attrib1是普通的,attrib2包围在declare-styleable标签中;
<com.custom.ViewClass attrib1="xyz" attrib2="abc"/>
我们可以在layout/someactivity.xml里直接使用这些attr;
<style name="customstyle" parent="@android:style/Widget.TextView">
<item name="attrib2">text value</item>
<!-- customize other, standard attributes too:-->
<item name="android:textColor">@color/white</item>
</style>
在styles.xml中,我们就能用attrib2。(原网站这里写成了attrib1,怀疑是笔误。)
后来我验证过attrib1也能使用在style里面(我真的不确定,逻辑上应该不能才对,但是编译就是通过了。。。),那么这里就必须说明attr包不包含在styleable里面的另一个主要区别了,stackoverflow中是这么说的:
感谢您的阅读!希望本文对解决您关于declare-styleable的问题有所帮助。如果您还有其他疑问,欢迎随时向我们提问。