Проблема запуска приложения Android во второй раз с пользовательским SurfaceView в XML RelativeLayout - PullRequest
1 голос
/ 04 сентября 2011

Я новичок в stackoverflow и в разработке Android.Кажется, что каждый раз, когда я использую Google для поиска ответа на вопрос Android, меня направляют сюда.

У меня есть простое приложение, которое начинается с экрана приветствия.Когда пользователь нажимает кнопку Play, вызывается следующий метод:

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

В классе GameActivity (который расширяет Activity) я использую:

setContentView(R.layout.game)

, и этот макет выглядит какследующим образом:

<?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>

Я выбрал этот подход (пользовательский SurfaceView с макетом xml для упрощения интерактивной части интерфейса.

У меня проблема в том, что, хотя эта реализация работает нормально,Когда я запускаю приложение в первый раз, когда пользователь выходит из системы, нажимая кнопку «Выйти» (где я вызываю «Завершить» ()) или нажимает кнопку «Назад» на устройстве, я не могу перезапустить приложение. Повторное нажатие на значок приложенияоткрывает пустое приложение со строкой заголовка и нажатие кнопки «Назад» на этом экране не имеет никакого эффекта (мне нужно нажать кнопку «Домой», чтобы выйти).

Если я использую:

setContentView(new GamePanel(this))

вместо:

setContentView(R.layout.game)

т.е. я просто использую пользовательский SurfaceView с событием onTouch () для вызова finish (), тогда приложение ведет себя правильно (его можно останавливать и перезапускать так часто, как я хочу).

Код для класса GamePanel выглядит следующим образом:

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

}

Что-то принципиально не так с моей реализацией?Любая помощь будет очень высоко ценится.Спасибо.

1 Ответ

0 голосов
/ 08 октября 2011

Ах, эта проблема. Я сам столкнулся с этой проблемой несколько раз, прежде чем понял, в чем дело.

Проблема в том, что метод finish () вызывается только * , когда завершается работа всего класса, тем самым закрывая ваш поток.

Однако метод finish () не будет вызываться до тех пор, пока не будет выполнен вызов thread.join (), что вызовет состояние взаимоблокировки.

Я не знаю, что такое рекомендуемое действие, но я бы предпочел прекратить поток в методе surfaceDestroyed (), непосредственно перед вызовом thread.join ().

Таким образом, он сообщает потоку, что нужно закончить, прежде чем ждать на нем.

...