My Android app has a tab bar that uses an Intent to launch the content in each tab, as in Android’s API Demos example. I’ve put an AndEngine activity under one of the tabs. My first question is: is this a reasonable idea? Or if I’m using AndEngine, is it better to implement my app’s whole UI in AndEngine?
Assuming I’m not already onto a non-starter, the specific problem I’ve run up against is as follows. This AndEngine activity – the one I’ve incorporated in my tab-based app – is a simplified version of the AndEngine PinchZoom example. It works well until you switch away from the tab and then back again. At that point, the view reappears, but you can’t scroll or zoom any more.
Here’s my activity class. Let me know if you want to see the tab bar class too. As you can see, I’ve tried to switch off the various listeners and detectors when the user leaves the tab, and switch them back on when they return. But I’ve established, using a breakpoint, that onSceneTouchEvent isn’t called when I touch the screen after returning to the tab. Is something intervening and capturing my touch events? Or is there something that goes inactive and needs to be brought back to life?
I’ve also posted this question on the AndEngine forums.
Edit:
Thanks for your suggestions, Guillaume. You’re right that onResumeGame isn’t called when the user returns to the tab. I thought I’d checked that, but I must have confused myself by putting the scrollable graphic under two tabs at once. The onResume method is called at that point, while onCreateGame isn’t. I’ve therefore changed the code so that the touch detectors are switched on in onResume. This method is definitely being called when I want it to be, so I guess that’s progress, but onSceneTouchEvent still isn’t being triggered when I touch the screen after returning to the tab. The updated code is below, with the changes marked as // NEW.
public class HomeActivity extends SimpleBaseGameActivity implements IOnSceneTouchListener,
IScrollDetectorListener, IPinchZoomDetectorListener {
private static final int CAMERA_WIDTH = 480;
private static final int CAMERA_HEIGHT = 628;
private boolean mInitialised = false; // NEW
private Scene mScene;
private ZoomCamera mZoomCamera;
private ITexture mTexture;
private ITextureRegion mGrassTextureRegion;
private SurfaceScrollDetector mScrollDetector;
private PinchZoomDetector mPinchZoomDetector;
private float mPinchZoomStartedCameraZoomFactor;
@Override
public EngineOptions onCreateEngineOptions() {
this.mZoomCamera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
final EngineOptions engineOptions = new EngineOptions(true,
ScreenOrientation.PORTRAIT_FIXED,
new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), this.mZoomCamera);
if (MultiTouch.isSupported(this)) {
if (MultiTouch.isSupportedDistinct(this)) {
Toast.makeText(this, "MultiTouch detected.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(
this,
"MultiTouch detected, but your device has problems distinguishing between "
+ "fingers.", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(this, "Sorry your device does NOT support MultiTouch!",
Toast.LENGTH_LONG).show();
}
return engineOptions;
}
@Override
public void onCreateResources() {
try {
this.mTexture = new BitmapTexture() {
@Override
protected InputStream onGetInputStream() throws IOException {
return getAssets().open("gfx/background_grass.png");
}
}.load(this.getTextureManager());
this.mGrassTextureRegion = TextureRegionFactory.extractFromTexture(mTexture);
} catch (IOException e) {
Debug.e(e);
}
}
@Override
public Scene onCreateScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
this.mScene = new Scene();
this.mScene.setOnAreaTouchTraversalFrontToBack();
this.mScene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
// Calculate the coordinates for the sprite, so it's centered on the
// camera.
final float centerX = (CAMERA_WIDTH - this.mGrassTextureRegion.getWidth()) / 2;
final float centerY = (CAMERA_HEIGHT - this.mGrassTextureRegion.getHeight()) / 2;
// Create the sprite and add it to the scene.
final Sprite grass = new Sprite(centerX, centerY, this.mGrassTextureRegion,
this.getVertexBufferObjectManager());
this.mScene.attachChild(grass);
enableTouchDetectors(); // NEW
mInitialised = true; // NEW
return this.mScene;
}
@Override
public void onPause() {
this.mScene.setTouchAreaBindingOnActionDownEnabled(false);
this.mScene.setOnSceneTouchListener(null);
this.mPinchZoomDetector = null;
this.mScrollDetector = null;
super.onPause();
}
// NEW method
@Override
public void onResume() {
super.onResume();
// If returning to the tab, switch the touch detectors back on.
if (mInitialised) {
enableTouchDetectors();
}
}
// This method has been removed since Guillaume's answer.
// @Override
// public void onResumeGame() {
// super.onResumeGame();
// this.mScrollDetector = new SurfaceScrollDetector(this);
// this.mPinchZoomDetector = new PinchZoomDetector(this);
// this.mScene.setOnSceneTouchListener(this);
// this.mScene.setTouchAreaBindingOnActionDownEnabled(true);
// }
@Override
public void onScrollStarted(final ScrollDetector pScollDetector, final int pPointerID,
final float pDistanceX, final float pDistanceY) {
}
@Override
public void onScroll(final ScrollDetector pScollDetector, final int pPointerID,
final float pDistanceX, final float pDistanceY) {
final float zoomFactor = this.mZoomCamera.getZoomFactor();
this.mZoomCamera.offsetCenter(-pDistanceX / zoomFactor, -pDistanceY / zoomFactor);
}
@Override
public void onScrollFinished(final ScrollDetector pScollDetector, final int pPointerID,
final float pDistanceX, final float pDistanceY) {
}
@Override
public void onPinchZoomStarted(final PinchZoomDetector pPinchZoomDetector,
final TouchEvent pTouchEvent) {
this.mPinchZoomStartedCameraZoomFactor = this.mZoomCamera.getZoomFactor();
}
@Override
public void onPinchZoom(final PinchZoomDetector pPinchZoomDetector,
final TouchEvent pTouchEvent, final float pZoomFactor) {
this.mZoomCamera.setZoomFactor(this.mPinchZoomStartedCameraZoomFactor * pZoomFactor);
}
@Override
public void onPinchZoomFinished(final PinchZoomDetector pPinchZoomDetector,
final TouchEvent pTouchEvent, final float pZoomFactor) {
}
@Override
public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
this.mPinchZoomDetector.onTouchEvent(pSceneTouchEvent);
if (this.mPinchZoomDetector.isZooming()) {
this.mScrollDetector.setEnabled(false);
} else {
if (pSceneTouchEvent.isActionDown()) {
this.mScrollDetector.setEnabled(true);
}
this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
}
return true;
}
// NEW method
private void enableTouchDetectors() {
this.mScrollDetector = new SurfaceScrollDetector(this);
this.mPinchZoomDetector = new PinchZoomDetector(this);
this.mScene.setOnSceneTouchListener(this);
this.mScene.setTouchAreaBindingOnActionDownEnabled(true);
}
}
Tommy:
I had this same scenario in my own app, in which I needed three tabs with one being andEngine. But i found that TabActivity did not re-initialize the tabs in the same way that firing a new Intent does. If you log the lifecycle of one of your other tabs, you will see this is not just an andengine thing, but a TabActivity thing.
There is a really useful thread here about the issue:
http://groups.google.com/group/android-developers/browse_thread/thread/d89f1d5f913bb6f7
Including an example that sets each tab as views within a single activity. here is the link. I’ve used code from commonsware myeslf and found their libraries and examples to be well tested and high-quality.
https://github.com/commonsguy/cw-android/tree/master/Fancy/Tab/
In anycase, I could not find a way to make a tabactivity work without re-writing the core classes of Andengine, which was beyond the scope of what I could do in the project.
So what I did as a workaround was this:
I build the tabs myself as three activities that shared a footer that lets you swap between them. I had to monkey around a bit with the history, and override the behavior of the back button to keep within the tabs when appropriate.
Also, I used an intent flag in the Android manifest:
Which helped the app run smoothly by assuring that there was only ever one instance of the Andengine tab ever created.
Hope this helps. It would be great if andengine could be made compatible with TabActivity, but I do not think it is fully at this moment.