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);
}
3.最重要的也许就是点击的部分了,当见听到用户点击的事件,改变ButtonSprite的显示状态
@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;
}
4.最后就是一些零散的东东,比如设置禁止状态之类的,不再赘述!
二.弱弱的扩展一下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);
}
}
粗糙的效果图:
好了,老规矩,提供源码,点击下载