Android Button原理

来源:互联网 发布:js跨域cookie 编辑:程序博客网 时间:2024/04/30 12:44
        在Android中Button是非常常用的一个View控件, 原本以为Button实现的代码肯定很多,但是看了原来着实吃了一惊.Button的源码几乎仅仅对继承的TextView类做了一个小小的修改, 仅仅是加了一个Style. 一个Style就能够实现Button的显示效果样式么?Android的Style机制真的很强大.

        首先来看一下Button的实现代码:

    @RemoteView    public class Button extends TextView {        public Button(Context context) {            this(context, null);        }        public Button(Context context, AttributeSet attrs) {            this(context, attrs, com.android.internal.R.attr.buttonStyle);        }        public Button(Context context, AttributeSet attrs, int defStyle) {            super(context, attrs, defStyle);        }        @Override        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {            super.onInitializeAccessibilityEvent(event);            event.setClassName(Button.class.getName());        }        @Override        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {            super.onInitializeAccessibilityNodeInfo(info);            info.setClassName(Button.class.getName());        }    }
        可以看到,Button继承了TextView之后,仅仅是添加了一个默认的Style:
com.android.internal.R.attr.buttonStyle。我们知道,button其实在TextView的基础之上增加了按钮的背景效果以及按钮按下去的press效果。这么一个Style文件可以搞定这件事情么?顺着这个style找下去,在Android的源码中找到style.xml,并找到相关的定义:
<style name="Widget.Button">     <item name="android:background">@android:drawable/btn_default</item>     <item name="android:focusable">true</item>     <item name="android:clickable">true</item>     <item name="android:textSize">20sp</item>     <item name="android:textStyle">normal</item>     <item name="android:textColor">@android:color/button_text</item>     <item name="android:gravity">center_vertical|center_horizontal</item></style> 

这里定义了好多style相关的属性,其他的属性都好理解,这个backgroud属性难道仅仅是一个drawable图片?如果仅仅是一个图片的化,怎么能够实现button各种状态下表现出不同背景的功能呢?还是来看看这个drawable到底是什么东西。


到drwable目录中发现这个btn_default原来也是一个xml文件,内容如下:
<selector xmlns:android="http://schemas.android.com/apk/res/android">      <item android:state_window_focused="false" android:state_enabled="true"         android:drawable="@drawable/btn_default_normal" />      <item android:state_window_focused="false" android:state_enabled="false"         android:drawable="@drawable/btn_default_normal_disable" />     <item android:state_pressed="true"          android:drawable="@drawable/btn_default_pressed" />     <item android:state_focused="true" android:state_enabled="true"         android:drawable="@drawable/btn_default_selected" />     <item android:state_enabled="true"         android:drawable="@drawable/btn_default_normal" />     <item android: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类的实例。这个类的一些核心代码如下:
public class StateListDrawable extends DrawableContainer {     public StateListDrawable(){         this(null);     }     /**      * Add a new image/string ID to the set of images.      * @param stateSet - An array of resource Ids to associate with the image.      * Switch to this image by calling setState().       * @param drawable -The image to show.      */     public void addState(int[] stateSet, Drawable drawable) {         if (drawable != null) {             mStateListState.addStateSet(stateSet, drawable);             // in case the new state matches our current state...             onStateChange(getState());         }     } ... } 

 

        xml里面每一个Item就对应一种状态,而每一个有stateSet的属性就是一个状态的描述,drawable则是真正的drawable图片(这个其实也可以是其他类型的Drawable实例)。
        当把这个实例付给View作为Background的时候,View会根据不同的state来切换不同状态的图片,从而实现了Press等诸多效果。简单看一下各种View的状态列表:
private static final int[][] VIEW_STATE_SETS = {     EMPTY_STATE_SET,                                           // 0 0 0 0 0     WINDOW_FOCUSED_STATE_SET,                                  // 0 0 0 0 1     SELECTED_STATE_SET,                                        // 0 0 0 1 0     SELECTED_WINDOW_FOCUSED_STATE_SET,                         // 0 0 0 1 1     FOCUSED_STATE_SET,                                         // 0 0 1 0 0     FOCUSED_WINDOW_FOCUSED_STATE_SET,                          // 0 0 1 0 1     FOCUSED_SELECTED_STATE_SET,                                // 0 0 1 1 0     FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 0 1 1 1     ENABLED_STATE_SET,                                         // 0 1 0 0 0     ENABLED_WINDOW_FOCUSED_STATE_SET,                          // 0 1 0 0 1     ENABLED_SELECTED_STATE_SET,                                // 0 1 0 1 0     ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 1 0 1 1     ENABLED_FOCUSED_STATE_SET,                                 // 0 1 1 0 0     ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 0 1 1 0 1     ENABLED_FOCUSED_SELECTED_STATE_SET,                        // 0 1 1 1 0     ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 0 1 1 1 1     PRESSED_STATE_SET,                                         // 1 0 0 0 0     PRESSED_WINDOW_FOCUSED_STATE_SET,                          // 1 0 0 0 1     PRESSED_SELECTED_STATE_SET,                                // 1 0 0 1 0     PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 1 0 0 1 1     PRESSED_FOCUSED_STATE_SET,                                 // 1 0 1 0 0     PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 1 0 1 0 1     PRESSED_FOCUSED_SELECTED_STATE_SET,                        // 1 0 1 1 0     PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 0 1 1 1     PRESSED_ENABLED_STATE_SET,                                 // 1 1 0 0 0     PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET,                  // 1 1 0 0 1     PRESSED_ENABLED_SELECTED_STATE_SET,                        // 1 1 0 1 0     PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 1 0 1 1     PRESSED_ENABLED_FOCUSED_STATE_SET,                         // 1 1 1 0 0     PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,          // 1 1 1 0 1     PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET,                // 1 1 1 1 0     PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 1 1 }; 
        设置background的代码:
public void setBackgroundDrawable(Drawable d) {              ...         <span style="color:#ff0000;">if (d.isStateful()) {             d.setState(getDrawableState());         } </span>        d.setVisible(getVisibility() == VISIBLE, false);         mBGDrawable = d;    ...     invalidate(); } 
        红色的部分就是首先判断这个Drawable对象是否支持state切换,当然我们这里的drawable是支持的。然后设置状态,达到切换图片的效果。
        所以,以后作一些图片需要根据状态切换不同的效果可以用这个方法啦。。。

转载地址:

ONCE的博客

东东的博客


0 0