开发者

Problem running Android app a 2nd time with custom SurfaceView in xml RelativeLayout

I'm new to stackoverflow and to Android development. It seems that every time I use Google to search of an answer to an Android question, I'm directed here.

I have as simple app that starts with a welcome screen. When the user presses the Play button, the following method is called:

public void onClickPlay(View v)
{
    finish();
    Intent intent = new Intent();
    intent.setClassName("ca.mytest.player", "ca.mytest.player.GameActivity");
    startActivity(intent);
}

In the class GameActivity (which extends Activity), I use:

setContentView(R.layout.game)

and this layout looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">

<ca.mytest.player.GamePanel
 android:id="@+id/gamePanel"
 android:layout_width="fill_parent"
 android:layout_height="768px"/>

<Button
 android:id="@+id/quitButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:onClick="onClickQuit"
 android:layout_alignParentBottom="true"
 android:text="Quit" />

<Button
 android:id="@+id/decoyButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:layout_above="@+id/quitButton"
 android:onClick="onClickDecoy"
 android:text="Decoy" />

<Button
 android:id="@+id/hideButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:layout_above="@+id/decoyButton"
 android:onClick="onClickHide"
 android:text="Hide" />

<Button
 android:id="@+id/shootButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:layout_above="@id/hideButton"
 android:onClick="onClickShoot"
 android:text="Assassinate" />

 <TextView
  android:id="@+id/status"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:text="Test of text view"
  android:layout_above="@id/shootButton"
 />

</RelativeLayout>

I chose this approach (custom SurfaceView with an xml layout to simplify interactive portion of the interface.

The problem that I have is that, while this implementation works fine the first time that I run the app, when the user quits by pressing the Quit button (where I call finish()) or presses the Back button on the device, I cannot re-run the app. Pressing on the app icon a second time opens a blank app with a title bar and pressing the Back button on this screen has no effect (I need to press the Home button to exit).

If I use:

setContentView(new GamePanel(this))

instead of:

setContentView(R.layout.game)

i.e. I just use the custom SurfaceView with an onTouch() event to call finish(), then the app behaves properly (it can be stopped and restarted as often as I wish).

Code for the GamePanel class is as follows:

package ca.mytest.player;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;

public class GamePanel extends SurfaceView implements SurfaceHolder.Callback
{
    private static final String TAG = GamePanel.class.getSimpleName();
    //private Context context;
    private GameThread thread;
    private final float dotSize = 20.0f;
    private final int numberOfButtons = 4;
    private ActionButton[] actionButtons;
    private WindowManager windowManager;

    public GamePanel(Context context)
    {
        super(context);
        init(context);
    }

    public GamePanel(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        init(context);
    }

    public GamePanel(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context)
    {
        Log.d(TAG, "*********************************");
        Log.d(TAG, "Creating Game Panel");
        Log.d(TAG, "*********************************");

        // add callback (this) to surface holder to intercept events
        getHolder().addCallback(this);

        // make the GamePanel focusable so it can handle events
        setFocusable(true);

        windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

        actionButtons = new ActionButton[numberOfButtons];

        thread = new GameThread(getHolder(), this);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        thread.setRunning(true);
        thread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        boolean retry = true;

        while(retry)
        {
            try
            {
                thread.joi开发者_运维百科n();
                retry = false;
            }
            catch (InterruptedException e)
            {
                Log.d(TAG, "surface destroyed");
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        if (event.getAction() == MotionEvent.ACTION_DOWN)
        {
            if (event.getY() > getHeight() - 50)
            {
                thread.setRunning(false);
                ((Activity)getContext()).finish();
            }
            else
            {
                Log.d(TAG, "Coords: x = " + event.getX() + " ,y = " + event.getY());
            }
        }

        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        canvas.drawColor(Color.BLACK);

        drawGameCircle(canvas);
    }

    private void drawGameCircle(Canvas canvas)
    {
        // Find centre of game circle
        // Game circle fits in a square at far right, full height of display
        float x = (float)(getWidth()/2);
        float y = (float)(getWidth()/2);

        float radius = (float)(getWidth()/2);

        Paint paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(x, y, radius, paint);

        // Draw dot for player
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(x, y, dotSize, paint);

        // draw a horizontal line to show game area
        canvas.drawLine(0, 767, 600, 767, paint);
    }

}

Is there something fundamentally wrong with my implementation? Any help would be very much appreciated. Thanks.


Ah, this problem. I ran into this problem myself a few times, before I figured out what was wrong.

The problem is that finish() is only called when the whole class is finished running, thereby shutting down your Thread.

However, finish() will not be called until the call to thread.join() is done, causing a deadlock condition.

I don't know what the recommended action is, but I'd prefer to terminate the thread in the surfaceDestroyed() method, right before calling thread.join().

That way, it tells the thread to finish things up before waiting on it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜