原创--解剖android Style原理从Button入手
转载声明原处:博客http://pk272205020.blog.163.com/
参考论坛外国android论坛http://www.androidpeople.com/
参考资料:androidButton原理
这几日都是看androidSDK原码,想封装一个HERO效果的UI界面。刚想以为很容易,但越做越难,为有百度,Google求救,但这方面的资料还是不多,这个我也不怪了,可能android在中国的市场还是刚刚起步。外面的一些网站android技术论坛打不开,闷...
但我发现http://www.android.com/可以打开了,以前要用XX软件先打得开,但里面的developer标签还是俾中国网关封,这个更郁闷...不讲了,直入正题androidStyel原理
刚刚开始得写时从最简单的Button入手,下载SDK原码候Button继续TextView原码里就三个构造方法....
@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); } }[
默认样式: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是这样说的
LetsyouassignanumberofgraphicimagestoasingleDrawableandswapoutthevisibleitembyastringIDvalue.
ItcanbedefinedinanXMLfilewiththe<selector>element.EachstateDrawableisdefinedinanested<item>element.Formoreinformation,seetheguidetoDrawableResources.
意思就是通过字符串标识符值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()); } } @Override public boolean isStateful() { return true; } @Override protected 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 {@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 };
详细打开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 * {@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) { boolean requestLayout = false; mBackgroundResource = 0; ............... if (d.isStateful()) { d.setState(getDrawableState()); } d.setVisible(getVisibility() == VISIBLE, false); mBGDrawable = d; ............... mBackgroundSizeChanged = true; invalidate(); }
setBackgound方法先判断Drawable对象是否支持state切换如果支持,设置状态就可达到图片切换的效果。
就写到这里
相关推荐
preserve log:勾选,页面发生跳转,接口不丢失;;Disable cache:不使用缓存,勾选,拿服务器的缓存;不勾选,用本地缓存;;All那列,表示浏览器的请求类型,对应下面的列type;