UI中最基础也是最重要的就是Button了,不论是应用还是游戏,Button以及like Button的控件都随处可见!
在AndEngine中也是支持Button的,并且在AndEngineExample中也有相应的例子,不过我下载的那个版本作者是写错了的,稍微需要一下改动,如果你的也有问题(点击没有效果),点击察看。
对于使用AndEngine的一般Button,ButtonSprite这个类基本上就可以实现了,本文会先介绍一下ButtonSprite的实现原理,然后再简单的扩展一下ButtonSprite,(使其带上相应的文字)。这个是很简单的,但是对于理解Button的切换原理是有帮助的!
一.ButtonSprite解析
ButtonSprite的实现原理很简单,首先ButtonSprite是一个Sprit,并且是一个TiledSprite的子类,(AnimatedSprite也是TiledSprite的子类),也许看到这就会明白了,其实ButtonSprite就是类似于带有动画的精灵,只是AnimatedSprite是根据时间或者其他因素来触发更换图片资源的,而ButtonSprite是根据用户点击触发!
1.ButtonSprite支持的状态:
ButtonSprite支持3种状态:未点击(正常状态),点击状态,禁止状态(禁止用户点击状态),它定义在ButtonSprite的内部枚举
private static enum State { // =========================================================== // Elements // =========================================================== NORMAL(0), PRESSED(1), DISABLED(2); // =========================================================== // Fields // =========================================================== private final int mTiledTextureRegionIndex; // =========================================================== // Constructors // =========================================================== private State(final int pTiledTextureRegionIndex) { this.mTiledTextureRegionIndex = pTiledTextureRegionIndex; } // =========================================================== // Getter & Setter // =========================================================== public int getTiledTextureRegionIndex() { return this.mTiledTextureRegionIndex; } }ButtonSprite根据为其分配的图片资源TiledTextureRegion的Tiled个数来对应其不同状态下的显示,规则为TileTextureRegion[0]是正常状态,TileTextureRegion[1]是选中状态,TileTextureRegion[2]是禁止状态,当然,你可以设置一种或者两种状态,看需求了!
2.ButtonSprite的构造方法
先是实现父类(TiledSprite)的构造,然后计算提供的状态,最后设置初始状态,很简单
public ButtonSprite(final float pX, final float pY, final ITiledTextureRegion pTiledTextureRegion, final VertexBufferObjectManager pVertexBufferObjectManager, final OnClickListener pOnClickListener) { super(pX, pY, pTiledTextureRegion, pVertexBufferObjectManager); this.mOnClickListener = pOnClickListener; this.mStateCount = pTiledTextureRegion.getTileCount(); switch (this.mStateCount) { case 1: Debug.w("No " + ITextureRegion.class.getSimpleName() + " supplied for " + State.class.getSimpleName() + "." + State.PRESSED + "."); case 2: Debug.w("No " + ITextureRegion.class.getSimpleName() + " supplied for " + State.class.getSimpleName() + "." + State.DISABLED + "."); break; case 3: break; default: throw new IllegalArgumentException("The supplied " + ITiledTextureRegion.class.getSimpleName() + " has an unexpected amount of states: '" + this.mStateCount + "'."); } this.changeState(State.NORMAL); }
@Override public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY) { if (!this.isEnabled()) { this.changeState(State.DISABLED); } else if (pSceneTouchEvent.isActionDown()) { this.changeState(State.PRESSED); } else if (pSceneTouchEvent.isActionCancel() || !this.contains(pSceneTouchEvent.getX(), pSceneTouchEvent.getY())) { this.changeState(State.NORMAL); } else if (pSceneTouchEvent.isActionUp() && this.mState == State.PRESSED) { this.changeState(State.NORMAL); if (this.mOnClickListener != null) { this.mOnClickListener.onClick(this, pTouchAreaLocalX, pTouchAreaLocalY); } } return true; }
二.弱弱的扩展一下ButtonSprite
ButtonSprite已经基本可以实现我们想要的功能了,但是如果我们想在其上面加上文字,该如何做呢?(好吧,也许这是一个无理的需求,没有几款游戏的按钮上还需要画上丑陋的文字,但是有些情况还是需要的,比如问答形式的,需要用户点击的问题选项。。。)
1.实现方案一:
先绘制ButtonSprite,然后在在Button上面绘制一个Text,完全可以实现,只是每次设置一个问题选项的时候要初始化两次,再attach两次,如果工作量少的话,还可以,如果多的话呢?那就是x2的工作量啊,会不会太傻了!
2.实现方案二:
a.继承于ButtonSprite
在其中添加一个Text的变量,这是一般最先想到的方法,确实也可以做到,但是要考虑ButtonSprite是不是为我们提供了很好的继承,我觉得作者也许并不希望我们这么做,不然为什么内部类State是私有的呢(瞎猜而已,源码在手,作者能做的只是提供思路,我们完全可以直接修改他的代码)。。。但是,这不是最简单的方法!
b.组合ButtonSprite和Text
几乎所有的面向语言的书里都会说继承和组合是最重要的两个特性,还好,没忘掉!其实想到这,我们要做的就很简单了,随便继承一个定义过onAreaTouch()方法的类,(只是为了简单),然后包含ButtonSprite和Text的对象就可以了。当点击事件发生的时候,背景的改变就交给ButtonSprite对象去做吧!如果有需要,我们只需要处理Text的点击变化!这几行的代码就足够了!
public class CustomButton extends Rectangle { private final static String TAG = "onerain"; // 背景 private ButtonSprite bgButtonSprite; // 显示文字 private Text text; /** * 构造方法 * @param pX 左上角x坐标 * @param pY 左上角y坐标 * @param width Button宽度 * @param height Button高度 * @param pTiledTextureRegion 在不同状态下背景图片资源 * @param pFont 显示字体 * @param pText 显示文字 * @param pVertexBufferObjectManager 顶点管理器 */ public CustomButton(float pX, float pY, TiledTextureRegion pTiledTextureRegion, Font pFont, String pText, VertexBufferObjectManager pVertexBufferObjectManager) { super(pX, pY, pTiledTextureRegion.getWidth(), pTiledTextureRegion.getHeight(), pVertexBufferObjectManager); // TODO Auto-generated constructor stub bgButtonSprite = new ButtonSprite(0, 0, pTiledTextureRegion, pVertexBufferObjectManager); text = new Text(0, 0, pFont, pText, pVertexBufferObjectManager); // 计算一下居中的位置 float textX = (bgButtonSprite.getWidth() - text.getWidth()) / 2; float textY = (bgButtonSprite.getHeight() - text.getHeight()) / 2; text.setPosition(textX, textY); attachChild(bgButtonSprite); attachChild(text); } // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) { // TODO Auto-generated method stub bgButtonSprite.onAreaTouched(pSceneTouchEvent, pTouchAreaLocalX, pTouchAreaLocalY); // TODO 如果文字也要发生变化,则在这里处理,比如换个颜色之类的 return super.onAreaTouched(pSceneTouchEvent, pTouchAreaLocalX, pTouchAreaLocalY); } }
粗糙的效果图:
好了,老规矩,提供源码,点击下载