android button 基本原理

原文地址:

http://li53262182.blog.163.com/blog/static/12839338720091113382422/


Android button原理  

2009-12-01 15:38:02|  分類: android相關 |字號 訂閱

在Android中Button是非常常用的一個View控件,原本以爲Button實現的代碼肯定很多,但是看了原來着實吃了一驚.Button的源碼幾乎僅僅對繼承的TextView類做了一個小小的修改,僅僅是加了一個Style. 一個Style就能夠實現Button的顯示效果樣式麼?Android的Style機制真的很強大.

首先來看一下ButtonView的實現代碼:

  1. * <p><strong>XML attributes</strong></p> 
  2. * <p>  
  3. * See {@link android.R.styleable#Button Button Attributes},  
  4. * {@link android.R.styleable#TextView TextView Attributes},   
  5. * {@link android.R.styleable#View View Attributes} 
  6. * </p> 
  7. */ 
  8. ublic class Button extends TextView { 
  9.    public Button(Context context) { 
  10.        this(context, null); 
  11.    } 
  12.  
  13.    public Button(Context context, AttributeSet attrs) { 
  14.        this(context, attrs, com.android.internal.R.attr.buttonStyle); 
  15.    } 
  16.  
  17.    public Button(Context context, AttributeSet attrs, int defStyle) { 
  18.        super(context, attrs, defStyle); 
  19.    } 

可以看到,Button繼承了TextView之後,僅僅是添加了一個默認的Style —

com.android.internal.R.attr.buttonStyle

我們知道,button其實在TextView的基礎之上增加了按鈕的背景效果以及按鈕按下去的press效果。這麼一個Style文件可以搞定這件事情麼?順着這個style找下去,在Android的源碼中找到style.xml,並找到相關的定義:

  1. <style name="Widget.Button"> 
  2.     <item name="android:background">@android:drawable/btn_default</item> 
  3.     <item name="android:focusable">true</item> 
  4.     <item name="android:clickable">true</item> 
  5.     <item name="android:textSize">20sp</item> 
  6.     <item name="android:textStyle">normal</item> 
  7.     <item name="android:textColor">@android:color/button_text</item> 
  8.     <item name="android:gravity">center_vertical|center_horizontal</item> 
  9. </style> 

這裏定義了好多style相關的屬性,其他的屬性都好理解,這個backgroud屬性難道僅僅是一個drawable圖片?如果僅僅是一個圖片的化,怎麼能夠實現button各種狀態下表現出不同背景的功能呢?還是來看看這個drawable到底是什麼東西。

到drwable目錄中發現這個btn_default原來也是一個xml文件,內容如下:

  1. <selector xmlns:android="http://schemas.android.com/apk/res/android"> 
  2. ?    <item android:state_window_focused="false" android:state_enabled="true" 
  3.         android:drawable="@drawable/btn_default_normal" /> 
  4. ?    <item android:state_window_focused="false" android:state_enabled="false" 
  5.         android:drawable="@drawable/btn_default_normal_disable" /> 
  6.     <item android:state_pressed="true"  
  7.         android:drawable="@drawable/btn_default_pressed" /> 
  8.     <item android:state_focused="true" android:state_enabled="true" 
  9.         android:drawable="@drawable/btn_default_selected" /> 
  10.     <item android:state_enabled="true" 
  11.         android:drawable="@drawable/btn_default_normal" /> 
  12.     <item android:state_focused="true" 
  13.         android:drawable="@drawable/btn_default_normal_disable_focused" /> 
  14.     <item 
  15.          android:drawable="@drawable/btn_default_normal_disable" /> 
  16. </selector> 

其實drawable在Android中有很多種,最普通的就是一個圖片。而這裏用到的是StateListDrawable。當Android的解析器解析到上面的xml時,會自動轉化成一個StateListDrawable類的實例。這個類的一些核心代碼如下:

  1. public class StateListDrawable extends DrawableContainer { 
  2.     public StateListDrawable() 
  3.     { 
  4.         this(null); 
  5.     } 
  6.  
  7.     /** 
  8.      * Add a new image/string ID to the set of images. 
  9.      * @param stateSet - An array of resource Ids to associate with the image. 
  10.      * Switch to this image by calling setState().  
  11.      * @param drawable -The image to show. 
  12.      */ 
  13.     public void addState(int[] stateSet, Drawable drawable) { 
  14.         if (drawable != null) { 
  15.             mStateListState.addStateSet(stateSet, drawable); 
  16.             // in case the new state matches our current state... 
  17.             onStateChange(getState()); 
  18.         } 
  19.     } 
  20. ... 

xml裏面每一個Item就對應一種狀態,而每一個有state_的屬性就是一個狀態的描述,drawable則是真正的drawable圖片(這個其實也可以是其他類型的Drawable實例)。

當把這個實例付給View作爲Background的時候,View會根據不同的state來切換不同狀態的圖片,從而實現了Press等諸多效果。簡單看一下View中有關狀態切換的代碼如下:

各種View的狀態列表:

  1. /** 
  2.  * The order here is very important to {@link #getDrawableState()} 
  3.  */ 
  4. private static final int[][] VIEW_STATE_SETS = { 
  5.     EMPTY_STATE_SET,                                           // 0 0 0 0 0 
  6.     WINDOW_FOCUSED_STATE_SET,                                  // 0 0 0 0 1 
  7.     SELECTED_STATE_SET,                                        // 0 0 0 1 0 
  8.     SELECTED_WINDOW_FOCUSED_STATE_SET,                         // 0 0 0 1 1 
  9.     FOCUSED_STATE_SET,                                         // 0 0 1 0 0 
  10.     FOCUSED_WINDOW_FOCUSED_STATE_SET,                          // 0 0 1 0 1 
  11.     FOCUSED_SELECTED_STATE_SET,                                // 0 0 1 1 0 
  12.     FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 0 1 1 1 
  13.     ENABLED_STATE_SET,                                         // 0 1 0 0 0 
  14.     ENABLED_WINDOW_FOCUSED_STATE_SET,                          // 0 1 0 0 1 
  15.     ENABLED_SELECTED_STATE_SET,                                // 0 1 0 1 0 
  16.     ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 1 0 1 1 
  17.     ENABLED_FOCUSED_STATE_SET,                                 // 0 1 1 0 0 
  18.     ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 0 1 1 0 1 
  19.     ENABLED_FOCUSED_SELECTED_STATE_SET,                        // 0 1 1 1 0 
  20.     ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 0 1 1 1 1 
  21.     PRESSED_STATE_SET,                                         // 1 0 0 0 0 
  22.     PRESSED_WINDOW_FOCUSED_STATE_SET,                          // 1 0 0 0 1 
  23.     PRESSED_SELECTED_STATE_SET,                                // 1 0 0 1 0 
  24.     PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 1 0 0 1 1 
  25.     PRESSED_FOCUSED_STATE_SET,                                 // 1 0 1 0 0 
  26.     PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 1 0 1 0 1 
  27.     PRESSED_FOCUSED_SELECTED_STATE_SET,                        // 1 0 1 1 0 
  28.     PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 0 1 1 1 
  29.     PRESSED_ENABLED_STATE_SET,                                 // 1 1 0 0 0 
  30.     PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET,                  // 1 1 0 0 1 
  31.     PRESSED_ENABLED_SELECTED_STATE_SET,                        // 1 1 0 1 0 
  32.     PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 1 0 1 1 
  33.     PRESSED_ENABLED_FOCUSED_STATE_SET,                         // 1 1 1 0 0 
  34.     PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,          // 1 1 1 0 1 
  35.     PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET,                // 1 1 1 1 0 
  36.     PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 1 1 
  37. }; 

設置background的代碼:

  1. /** 
  2.  * Set the background to a given Drawable, or remove the background. If the 
  3.  * background has padding, this View's padding is set to the background's 
  4.  * padding. However, when a background is removed, this View's padding isn't 
  5.  * touched. If setting the padding is desired, please use 
  6.  * {@link #setPadding(int, int, int, int)}. 
  7.  *  
  8.  * @param d The Drawable to use as the background, or null to remove the 
  9.  *        background 
  10.  */ 
  11. public void setBackgroundDrawable(Drawable d) { 
  12.          
  13.         ... 
  14.  
  15.         if (d.isStateful()) { 
  16.             d.setState(getDrawableState()); 
  17.         } 
  18.         d.setVisible(getVisibility() == VISIBLE, false); 
  19.         mBGDrawable = d; 
  20.          
  21.     ... 
  22.  
  23.     invalidate(); 

紅色的部分就是首先判斷這個Drawable對象是否支持state切換,當然我們這裏的drawable是支持的。然後設置狀態,達到切換圖片的效果。

所以,以後作一些圖片需要根據狀態切換不同的效果可以用這個方法啦。。。





發佈了48 篇原創文章 · 獲贊 6 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章