I wanted to show a dialog pop-up to the player (user), to tell them a message, such as the high score, obtaining user names, etc. for the scoreboard. Anything involving a dialog and user input, all is welcome.
Design:
I have a background rendering Canvas, displaying the basic objects on screen. And then there's the dialog. When a player meets a simple goal, the dialog should pop-up and congratulates the player for completing the goal, and then returns to the screen, where the screen itself continues to be rendering/drawing the objects onto the screen.
Problem:
When calling a dialog to show up, I do not understand why it is required to called upon Looper.prepare() and Looper.loop(). And how do you get back to the main thread, so that the dialog can be dismissed while the rendering thread continues rendering/drawing. Even more complicated is that, for a simple dialog to show up while the background rendering is drawing objects onto the screen is hard. I do not understand how to link one part of the game logic to the next one.
Source:
This entire project took months to try to get this dialog to work, yet failed. I recreated only the problems at hand and scaled it down very much, so that the same error occurs at a suitable and manageable size. Below is the rewritten work, everything else (artwork, objects, classes, etc.) is removed, so as to concentrate on the core problem.
public Render(Context context) {
super(context);
holder = this.getHolder();
r = g = b = 0;
builder = new AlertDialog.Builder(context);
builder.setTitle("Enter");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Render Dialog", "Working...");
Log.d("Render Dialog", "Exiting the Looper loop...");
new Thread(new Runnable(){
public void run(){
Looper.getMainLooper().quit(); //<---------------------- ERROR WHERE IT OCCURRED.
}
}).start();
}
});
dialog = builder.create();
}
public void setLoopFlag(boolean value) {
running = value;
}
public void run() {
boolean flag = false;
while(running) {
if (holder.getSurface().isValid()) {
Canvas c = null;
try {
c = holder.getSurface().lockCanvas(null);
}
catch(IllegalArgumentException e) {
e.printStackTrace();
}
catch(OutOfResourcesException e) {
e.printStackTrace();
}
c.drawARGB(255, r, g, b);
r++;
g++;
b++;
if (r > 250 || g > 250 || b > 250) {
r = 0;
g = 0;
b = 0;
}
if (!flag){
flag = true;
Looper.prepare();
dialog.show();
Looper.loop();
}
holder.getSurface().unlockCanvasAndPost(c);
}
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new Render(this);
view.setLoopFlag(true);
setContentView(view);
Thread thread = new Thread(view);
thread.setName("Render Thread");
thread.start();
}
}
Neatly packed into 1 Java class.
Conclusion:
I hoped this is enough information needed to solve this matter.
hmm. I'll be honest I'm not sure what you are doing there. Your onclick event doesn't make a lot of sense to me, also I'm not sure why you are creating an instance of the class in its onCreate method.
If what you want to do is create a dialog that for example asks the player if they want Save on exit. Then I'd expect something like this:
AlertDialog.Builder builder = new AlertDialog.Builder(GameRender.this);
builder.setMessage("Save?").setCancelable(false).setPositiveButton("Yes", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
AccountUpdater.updateAccount(getContentResolver(), _gameState);
GameRender.this.finish();
}
}).setNegativeButton("No", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
GameRender.this.finish();
}
}).show();
you can control the pausing and resuming of the render thread through the relevant activity life cycle methods onPause() and onResume().
I do believed that the point is missed. I now couldn't rephrase my question, so that the point is hard to miss... Because I don't know how to rephrase it.
No one ever thought of showing a dialog while the background is drawing objects at the same time?
Here's a mockup:
Screen background changes color while dialog stays popped up.
Okay I understand what you are looking for now. Ya that's perfectly doable. I knocked together a quick example based on an adaptation i did to one the sample projects.
TravelRenderThread.java
package njd.rougetrader;
import njd.FalseProphet.EntityFramework.Entity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.view.SurfaceHolder;
public class TravelRenderThread extends Thread
{
// private Bitmap _backgroundImage;
private int _canvasHeight = 1;
private int _canvasWidth = 1;
private SurfaceHolder _surfaceHolder;
private boolean _active;
Paint _lineColor; // = new Paint();
Paint _backColor; // = new Paint();
int _radius = 10;
int _red = 0;
public void setRunning(boolean b)
{
_active = b;
}
/* Callback invoked when the surface dimensions change. */
public void setSurfaceSize(int width, int height)
{
// synchronized to make sure these all change atomically
synchronized (_surfaceHolder)
{
_canvasWidth = width;
_canvasHeight = height;
// don't forget to resize the background image
// _backgroundImage =
// _backgroundImage.createScaledBitmap(_backgroundImage, width,
// height, true);
}
}
@Override
public void run()
{
while (_active)
{
Canvas canvas = null;
try
{
canvas = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder)
{
render(canvas);
}
}
finally
{
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (canvas != null)
{
_surfaceHolder.unlockCanvasAndPost(canvas);
}
}
_red++;
if (_red > 255)
_red = 0;
}
}
private void render(Canvas canvas)
{
// Draw the background image. Operations on the Canvas accumulate
// so this is like clearing the screen.
_lineColor = new Paint();
_lineColor.setAntiAlias(true);
public TravelRenderThread getThread()
{
return _thread;
}
public TravelView(Context context, AttributeSet attrs)
{
super(context, attrs);
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// create thread only; it's started in surfaceCreated()
_thread = new TravelRenderThread(holder, context, new Handler());
setFocusable(true); // make sure we get key events
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
{
_thread.setSurfaceSize(width, height);
}
public void surfaceCreated(SurfaceHolder holder)
{
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder)
{
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
_thread.setRunning(false);
while (retry)
{
try
{
_thread.join();
retry = false;
}
catch (InterruptedException e)
{
}
}
}
public void setIsRed()
{
_thread.setIsRed();
}
public void setIsGreen()
{
_thread.setIsGreen();
}
}
TravelActivity.java
package njd.rougetrader;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
public class TravelActivity extends Activity
{
TravelView _view;
TravelRenderThread _thread;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.travel_activity);
_view = (TravelView) findViewById(R.id.Render_Canvas);
_thread = _view.getThread();
}
public void openDialog(View view)
{
AlertDialog.Builder builder = new AlertDialog.Builder(TravelActivity.this);
builder.setMessage("Red or Green?").setCancelable(false).setPositiveButton("Red", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
_view.setIsRed();
}
}).setNegativeButton("Green", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
_view.setIsGreen();
}
}).show();
}