Android(安卓)button原理 转载
在Android中Button是非常常用的一个View控件, 原本以为Button实现的代码肯定很多,但是看了原来着实吃了一惊.Button的源码几乎仅仅对继承的TextView类做了一个小小的修改, 仅仅是加了一个Style. 一个Style就能够实现Button的显示效果样式么?Android的Style机制真的很强大.
首先来看一下ButtonView的实现代码:
- *<p><strong>XMLattributes</strong></p>
- *<p>
- *See{@linkandroid.R.styleable#ButtonButtonAttributes},
- *{@linkandroid.R.styleable#TextViewTextViewAttributes},
- *{@linkandroid.R.styleable#ViewViewAttributes}
- *</p>
- */
- ublicclassButtonextendsTextView{
- publicButton(Contextcontext){
- this(context,null);
- }
- publicButton(Contextcontext,AttributeSetattrs){
- this(context,attrs,com.android.internal.R.attr.buttonStyle);
- }
- publicButton(Contextcontext,AttributeSetattrs,intdefStyle){
- super(context,attrs,defStyle);
- }
可以看到,Button继承了TextView之后,仅仅是添加了一个默认的Style —
com.android.internal.R.attr.buttonStyle
我们知道,button其实在TextView的基础之上增加了按钮的背景效果以及按钮按下去的press效果。这么一个Style文件可以搞定这件事情么?顺着这个style找下去,在Android的源码中找到style.xml,并找到相关的定义:
- <stylename="Widget.Button">
- <itemname="android:background">@android:drawable/btn_default</item>
- <itemname="android:focusable">true</item>
- <itemname="android:clickable">true</item>
- <itemname="android:textSize">20sp</item>
- <itemname="android:textStyle">normal</item>
- <itemname="android:textColor">@android:color/button_text</item>
- <itemname="android:gravity">center_vertical|center_horizontal</item>
- </style>
这里定义了好多style相关的属性,其他的属性都好理解,这个backgroud属性难道仅仅是一个drawable图片?如果仅仅是一个图片的化,怎么能够实现button各种状态下表现出不同背景的功能呢?还是来看看这个drawable到底是什么东西。
到drwable目录中发现这个btn_default原来也是一个xml文件,内容如下:
- <selectorxmlns:android="http://schemas.android.com/apk/res/android">
- ?<itemandroid:state_window_focused="false"android:state_enabled="true"
- android:drawable="@drawable/btn_default_normal"/>
- ?<itemandroid:state_window_focused="false"android:state_enabled="false"
- android:drawable="@drawable/btn_default_normal_disable"/>
- <itemandroid:state_pressed="true"
- android:drawable="@drawable/btn_default_pressed"/>
- <itemandroid:state_focused="true"android:state_enabled="true"
- android:drawable="@drawable/btn_default_selected"/>
- <itemandroid:state_enabled="true"
- android:drawable="@drawable/btn_default_normal"/>
- <itemandroid:state_focused="true"
- android:drawable="@drawable/btn_default_normal_disable_focused"/>
- <item
- android:drawable="@drawable/btn_default_normal_disable"/>
- </selector>
其实drawable在Android中有很多种,最普通的就是一个图片。而这里用到的是StateListDrawable。当Android的解析器解析到上面的xml时,会自动转化成一个StateListDrawable类的实例。这个类的一些核心代码如下:
- publicclassStateListDrawableextendsDrawableContainer{
- publicStateListDrawable()
- {
- this(null);
- }
- /**
- *Addanewimage/stringIDtothesetofimages.
- *@paramstateSet-AnarrayofresourceIdstoassociatewiththeimage.
- *SwitchtothisimagebycallingsetState().
- *@paramdrawable-Theimagetoshow.
- */
- publicvoidaddState(int[]stateSet,Drawabledrawable){
- if(drawable!=null){
- mStateListState.addStateSet(stateSet,drawable);
- //incasethenewstatematchesourcurrentstate...
- onStateChange(getState());
- }
- }
- ...
- }
xml里面每一个Item就对应一种状态,而每一个有state_的属性就是一个状态的描述,drawable则是真正的drawable图片(这个其实也可以是其他类型的Drawable实例)。
当把这个实例付给View作为Background的时候,View会根据不同的state来切换不同状态的图片,从而实现了Press等诸多效果。简单看一下View中有关状态切换的代码如下:
各种View的状态列表:
- /**
- *Theorderhereisveryimportantto{@link#getDrawableState()}
- */
- privatestaticfinalint[][]VIEW_STATE_SETS={
- EMPTY_STATE_SET,//00000
- WINDOW_FOCUSED_STATE_SET,//00001
- SELECTED_STATE_SET,//00010
- SELECTED_WINDOW_FOCUSED_STATE_SET,//00011
- FOCUSED_STATE_SET,//00100
- FOCUSED_WINDOW_FOCUSED_STATE_SET,//00101
- FOCUSED_SELECTED_STATE_SET,//00110
- FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,//00111
- ENABLED_STATE_SET,//01000
- ENABLED_WINDOW_FOCUSED_STATE_SET,//01001
- ENABLED_SELECTED_STATE_SET,//01010
- ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,//01011
- ENABLED_FOCUSED_STATE_SET,//01100
- ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,//01101
- ENABLED_FOCUSED_SELECTED_STATE_SET,//01110
- ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,//01111
- PRESSED_STATE_SET,//10000
- PRESSED_WINDOW_FOCUSED_STATE_SET,//10001
- PRESSED_SELECTED_STATE_SET,//10010
- PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET,//10011
- PRESSED_FOCUSED_STATE_SET,//10100
- PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET,//10101
- PRESSED_FOCUSED_SELECTED_STATE_SET,//10110
- PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,//10111
- PRESSED_ENABLED_STATE_SET,//11000
- PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET,//11001
- PRESSED_ENABLED_SELECTED_STATE_SET,//11010
- PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,//11011
- PRESSED_ENABLED_FOCUSED_STATE_SET,//11100
- PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,//11101
- PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET,//11110
- PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,//11111
- };
设置background的代码:
- /**
- *SetthebackgroundtoagivenDrawable,orremovethebackground.Ifthe
- *backgroundhaspadding,thisView'spaddingissettothebackground's
- *padding.However,whenabackgroundisremoved,thisView'spaddingisn't
- *touched.Ifsettingthepaddingisdesired,pleaseuse
- *{@link#setPadding(int,int,int,int)}.
- *
- *@paramdTheDrawabletouseasthebackground,ornulltoremovethe
- *background
- */
- publicvoidsetBackgroundDrawable(Drawabled){
- ...
- if(d.isStateful()){
- d.setState(getDrawableState());
- }
- d.setVisible(getVisibility()==VISIBLE,false);
- mBGDrawable=d;
- ...
- invalidate();
- }
红色的部分就是首先判断这个Drawable对象是否支持state切换,当然我们这里的drawable是支持的。然后设置状态,达到切换图片的效果。
所以,以后作一些图片需要根据状态切换不同的效果可以用这个方法啦。。。
最近转了很多别人的东西 有空我自己也会写一些。每个人多进步一点点 加起来就是一个大大的步子。
更多相关文章
- Android中控件的显示和隐藏以及EditText的可编辑和不可编辑状态
- Android横竖屏切换总结
- Activity生命周期详解
- android 获取屏幕高度,宽度,状态栏高度
- android 学习八 android selector的使用
- [Android] Android开发规范
- 【Android(安卓)4.0】Android(安卓)Icon Set的使用
- 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Fedora镜像
- 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Debian镜像