Jump to content

  • Log In with Google      Sign In   
  • Create Account

We need your feedback on a survey! Each completed response supports our community and gives you a chance to win a $25 Amazon gift card!


Trying to show Alert Dialog for messages, while rendering objects in background. Can't get the gist of this.


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
4 replies to this topic

#1 tom_mai78101   Members   -  Reputation: 577

Like
0Likes
Like

Posted 08 July 2012 - 03:53 AM

Goal:

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.

package ff.ff;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Surface.OutOfResourcesException;
public class Basic extends Activity {
private Render view;

public class Render extends SurfaceView implements Runnable {


  //TODO: Test if AlertDialog can be able to work while another
  //thread is running continuously.
  //
  // Failed miserably.

  //ERROR Received:
  /*
   * 07-08 17:34:51.035: E/AndroidRuntime(7356): FATAL EXCEPTION: Thread-12
   * 07-08 17:34:51.035: E/AndroidRuntime(7356): java.lang.RuntimeException: Main thread not allowed to quit
   * 07-08 17:34:51.035: E/AndroidRuntime(7356):  at android.os.MessageQueue.enqueueMessage(MessageQueue.java:191)
   * 07-08 17:34:51.035: E/AndroidRuntime(7356):  at android.os.Looper.quit(Looper.java:231)
   * 07-08 17:34:51.035: E/AndroidRuntime(7356):  at ff.ff.Basic$Render$1$1.run(Basic.java:45)
   * 07-08 17:34:51.035: E/AndroidRuntime(7356):  at java.lang.Thread.run(Thread.java:1027)
   *
   */


  private int r, g, b;

  private boolean running;
  private SurfaceHolder holder;
  private AlertDialog.Builder builder;
  private AlertDialog dialog;

  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.

Edited by tom_mai78101, 08 July 2012 - 03:54 AM.


Sponsor:

#2 TechnoGoth   Crossbones+   -  Reputation: 2799

Like
0Likes
Like

Posted 09 July 2012 - 06:25 AM

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().

Sorry if I missed the point.

#3 tom_mai78101   Members   -  Reputation: 577

Like
0Likes
Like

Posted 09 July 2012 - 08:24 AM

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.
Posted ImagePosted Image

#4 TechnoGoth   Crossbones+   -  Reputation: 2799

Like
0Likes
Like

Posted 09 July 2012 - 05:55 PM

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;

boolean			   _isRed	 = true;

public TravelRenderThread(SurfaceHolder surfaceHolder, Context context,
		 Handler handler)
{
  _surfaceHolder = surfaceHolder;
}

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);
 
  if (_isRed)
   _lineColor.setColor(Color.rgb(_red, 0, 255 - _red));
  else
   _lineColor.setColor(Color.rgb(0, _red, 255 - _red));
 
  _backColor = new Paint();
  _backColor.setAntiAlias(true);
  _backColor.setColor(Color.BLACK);
 
  canvas.drawRect(0, 0, _canvasWidth, _canvasHeight, _lineColor);
  canvas.drawText(String.valueOf(_red), 100, 100, _backColor);
 
  canvas.save();
  canvas.restore();
}

public void setIsRed()
{
  _isRed = true;
 
}

public void setIsGreen()
{
  _isRed = false;
}
}

TravelView.java
package njd.rougetrader;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class TravelView extends SurfaceView implements SurfaceHolder.Callback
{

TravelRenderThread _thread;

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();
}

}


#5 tom_mai78101   Members   -  Reputation: 577

Like
0Likes
Like

Posted 10 July 2012 - 01:55 AM

Ah! There's a small bit I just figured out, thanks to your hint.

Here's my actual program, please test.

package ff.ff;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class Basic extends Activity {
private AlertDialog dialog;
private AlertDialog.Builder builder;
private BackgroundColors view;

public class BackgroundColors extends SurfaceView implements Runnable {
  private Thread thread;
  private boolean running;
  private SurfaceHolder holder;
 
  public BackgroundColors(Context context) {
   super(context);
  }
 
  public void run() {
   int r = 0;
   while (running){
    if (holder.getSurface().isValid()){
	 Canvas canvas = holder.lockCanvas();
	 if (r > 250)
	  r = 0;
	 r += 10;
	 try {
	  Thread.sleep(300);
	 }
	 catch(InterruptedException e) {
	  e.printStackTrace();
	 }
	 canvas.drawARGB(255, r, 255, 255);
	 holder.unlockCanvasAndPost(canvas);
    }
   }
  }
 
  public void start() {
   running = true;
   thread = new Thread(this);
   holder = this.getHolder();
   thread.start();
  }
 
  public void stop() {
   running = false;
   boolean retry = true;
   while (retry){
    try {
	 thread.join();
	 retry = false;
    }
    catch(InterruptedException e) {
	 retry = true;
    }
   }
  }
 
  public boolean onTouchEvent(MotionEvent e){
   dialog.show();
   return false;
  }
}

public void onCreate(Bundle b) {
  super.onCreate(b);
  view = new BackgroundColors(this);
  this.setContentView(view);
  builder = new AlertDialog.Builder(this);
  builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface dialog, int which) {
    Log.d("Basic", "It worked");
   }
  });
  dialog = builder.create();
}

public void onPause(){
  super.onPause();
  view.stop();
}

public void onResume(){
  super.onResume();
  view.start();
}
}





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS