Android button原理大揭秘[亲测有效]

Android button原理大揭秘[亲测有效]在Android中Button是非常常用的一个View控件, 原本以为Button实现的代码肯定很多,但是看了原来着实吃了一惊.Button的源

欢迎大家来到IT世界,在知识的湖畔探索吧!

Android中Button是非常常用的一个View控件, 原本以为Button实现的代码肯定很多,但是看了原来着实吃了一惊.Button的源码几乎仅仅对继承的TextView类做了一个小小的修改, 仅仅是加了一个Style. 一个Style就能够实现Button的显示效果样式么?Android的Style机制真的很强大.
首先来看一下ButtonView的实现代码:

* <p><strong>XML attributes</strong></p>
* <p> 
* See {@link android.R.styleable#Button Button Attributes}, 
* {@link android.R.styleable#TextView TextView Attributes}, 
* {@link android.R.styleable#View View Attributes}
* </p>
*/
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);
 }

欢迎大家来到IT世界,在知识的湖畔探索吧!

可以看到,Button继承了TextView之后,仅仅是添加了一个默认的Style —

欢迎大家来到IT世界,在知识的湖畔探索吧!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文件,内容如下:

欢迎大家来到IT世界,在知识的湖畔探索吧!<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就对应一种状态,而每一个有state_的属性就是一个状态的描述,drawable则是真正的drawable图片(这个其实也可以是其他类型的Drawable实例)。
当把这个实例付给View作为Background的时候,View会根据不同的state来切换不同状态的图片,从而实现了Press等诸多效果。简单看一下View中有关状态切换的代码如下:
各种View的状态列表:

/**
* The order here is very important to {@link #getDrawableState()}
*/
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的代码:

/**
* Set the background to a given Drawable, or remove the background. If the
* background has padding, this View's padding is set to the background's
* padding. However, when a background is removed, this View's padding isn't
* touched. If setting the padding is desired, please use
* {@link #setPadding(int, int, int, int)}.
* 
* @param d The Drawable to use as the background, or null to remove the
* background
*/
public void setBackgroundDrawable(Drawable d) {
 
 ...

 if (d.isStateful()) {
 d.setState(getDrawableState());
 }
 d.setVisible(getVisibility() == VISIBLE, false);
 mBGDrawable = d;
 
 ...

 invalidate();
}

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17869.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信