当前位置: 代码迷 >> Android >> 原创-解剖android Style原理从Button着手
  详细解决方案

原创-解剖android Style原理从Button着手

热度:46   发布时间:2016-05-01 14:11:46.0
原创--解剖android Style原理从Button入手
转载 声明原处 :博客http://pk272205020.blog.163.com/


    参考论坛外国android论坛http://www.androidpeople.com/
    参考资料:android Button 原理




    这几日都是看android SDK原码,想封装一个HERO 效果的UI界面。刚想以为很容易,但越做越难,为有百度,Google求救,但这方面的资料还是不多,这个我也不怪了,可能android 在中国的市场还是刚刚起步。外面的一些网站 android 技术论坛打不开,闷 ...

但我发现http://www.android.com/ 可以打开了,以前要用XX软件先打得开,但里面的developer标签还是俾中国网关封,这个更郁闷... 不讲了,直入正题 android Styel原理



刚刚开始得写时从最简单的Button 入手,下载SDK原码候Button 继续TextView 原码里就三个构造方法....
@RemoteViewpublic 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);    }}[


默认样式:com.android.internal.R.attr.buttonStyle ,android 的style 太强大, 网上有人说过是 GWT模式, 在校的时候我也用过GWT写过小网页,HTML文件里标签里嵌入GWT标签,通过服务端Java代码生成页面,GWT就讲到这里,有开展过GWT的同志就知道这个也很像android的Layout布局文件,哈哈 我也是认同网上的人说。

     知道android 的Style模式后,我们要进一步了解内部的实现,我们要打开 com.android.internal.R.attr.buttonStyle这个对应的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 > < itemname="android:gravity">center_vertical|center_horizontal< /item>< /style >

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

还是埋头苦干地找出答案

在drwable目录中发现这个btn_default这个文件,还有许多这样的xml文件,看名字可以估到是什么来的

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 >

在android 中drawable文件是看图片存放的,最普通的就是一个图片。而这里用到的是StateListDrawable。当Android的解析器解析到上面的xml时,会自动转化成一个StateListDrawable类的实例,看看SDK是这样说的



      Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string ID value.


It can be defined in an XML file with the <selector> element. Each state Drawable is defined in a nested <item> element. For more information, see the guide to Drawable Resources.

意思就是通过字符串标识符值ID  分配单个可绘制可切换 的可视图形项

看看核心代码吧:大部多代码删除了

public class StateListDrawable extends DrawableContainer {/*** To be proper, we should have a getter for dither (and alpha, etc.)* so that proxy classes like this can save/restore their delegates'* values, but we don't have getters. Since we do have setters* (e.g. setDither), which this proxy forwards on, we have to have some* default/initial setting.** The initial setting for dither is now true, since it almost always seems* to improve the quality at negligible cost.*/private static final boolean DEFAULT_DITHER = true;private final StateListState mStateListState;private boolean mMutated;public StateListDrawable() {this(null, 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());}}@Overridepublic boolean isStateful() {return true;}@Overrideprotected boolean onStateChange(int[] stateSet) {int idx = mStateListState.indexOfStateSet(stateSet);if (idx < 0) {idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);}if (selectDrawable(idx)) {return true;}return super.onStateChange(stateSet);}/*** Gets the state set at an index.** @param index The index of the state set.* @return The state set at the index.* @hide pending API council* @see #getStateCount()* @see #getStateDrawable(int)*/public int[] getStateSet(int index) {return mStateListState.mStateSets[index];}static final class StateListState extends DrawableContainerState {private int[][] mStateSets;StateListState(StateListState orig, StateListDrawable owner, Resources res) {super(orig, owner, res);if (orig != null) {mStateSets = orig.mStateSets;} else {mStateSets = new int[getChildren().length][];}}  int addStateSet(int[] stateSet, Drawable drawable) {            final int pos = addChild(drawable);            mStateSets[pos] = stateSet;            return pos;        }}private StateListDrawable(StateListState state, Resources res) {StateListState as = new StateListState(state, this, res);mStateListState = as;setConstantState(as);onStateChange(getState());}}


xml中每一个Item就对应一种状态,而每一个有state_的属性就是描述状态,drawable则是真正的drawable图片。当把这个实例付给View作为Background的时候,View会根据不同的state来切换不同状态的图片,从而实现了Press等诸多效果。简单看一下View中有关状态切换的代码吧:

/*** The order here is very important to [email protected] #getDrawableState()}*/private static final int[][] VIEW_STATE_SETS = {EMPTY_STATE_SET, // 0 0 0 0 0WINDOW_FOCUSED_STATE_SET, // 0 0 0 0 1SELECTED_STATE_SET, // 0 0 0 1 0SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 0 0 1 1FOCUSED_STATE_SET, // 0 0 1 0 0FOCUSED_WINDOW_FOCUSED_STATE_SET, // 0 0 1 0 1FOCUSED_SELECTED_STATE_SET, // 0 0 1 1 0FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 0 1 1 1ENABLED_STATE_SET, // 0 1 0 0 0ENABLED_WINDOW_FOCUSED_STATE_SET, // 0 1 0 0 1ENABLED_SELECTED_STATE_SET, // 0 1 0 1 0ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 1 0 1 1ENABLED_FOCUSED_STATE_SET, // 0 1 1 0 0ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, // 0 1 1 0 1ENABLED_FOCUSED_SELECTED_STATE_SET, // 0 1 1 1 0ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 0 1 1 1 1PRESSED_STATE_SET, // 1 0 0 0 0PRESSED_WINDOW_FOCUSED_STATE_SET, // 1 0 0 0 1PRESSED_SELECTED_STATE_SET, // 1 0 0 1 0PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 0 0 1 1PRESSED_FOCUSED_STATE_SET, // 1 0 1 0 0PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET, // 1 0 1 0 1PRESSED_FOCUSED_SELECTED_STATE_SET, // 1 0 1 1 0PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 0 1 1 1PRESSED_ENABLED_STATE_SET, // 1 1 0 0 0PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET, // 1 1 0 0 1PRESSED_ENABLED_SELECTED_STATE_SET, // 1 1 0 1 0PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 0 1 1PRESSED_ENABLED_FOCUSED_STATE_SET, // 1 1 1 0 0PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 0 1PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, // 1 1 1 1 0PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 1 1};

详细打开View.java 自己看

下面是setBackground方法,红字就是是state切换  ,View 这个类太长了, android2.2 版一共9321行

/**     * 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     * [email protected] #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) {        boolean requestLayout = false;        mBackgroundResource = 0;           ...............                         if (d.isStateful()) {                d.setState(getDrawableState());            }            d.setVisible(getVisibility() == VISIBLE, false);            mBGDrawable = d;           ...............        mBackgroundSizeChanged = true;        invalidate();}

setBackgound方法先判断Drawable对象是否支持state切换 如果支持,设置状态就可达到图片切换的效果。



就写到这里


1 楼 zhouxiaoli521 2011-03-18  
不错 看完之后大概明白style是怎么回事了
2 楼 wdw8217 2011-07-06  
....good 看完之后同楼上。

继续学英语 。英语太一般。。