当前位置: 首页 > 知识库问答 >
问题:

在surfaceView中使用onTouchEvent暂停后重新启动应用程序

郎星汉
2023-03-14

嗨,我是 Android 编程的新手,我一直在看一个关于 Android 游戏编程的 youtube 教程,问题是这个特定的课程被留在了期中,所以游戏还没有完成,所以我尝试自己添加一些东西。在我的新旅程中,我一直在解决一个自己无法解决的问题,所以如果有人可以帮助我,我会在这里尝试。

问题是游戏的视觉部分,我有一个surfaceView和一个TouchEvent事件。当我暂停游戏(方法暂停活动)时出现问题,碰巧表面持有者被破坏(从方法表面被破坏),但为了不遇到麻烦,我必须停止更新游戏的线程(GameLoopThread)。当游戏取消暂停时,它会调用 onResume 方法,并创建一个新的 surfaceFolder 并初始化 GameLoopeThread 线程。

我遇到的问题是,在调用方法onResume()并重新初始化所有内容后,surfaceView中的事件onTouchEvent停止工作,因此单击它不起作用,过了一会儿,我收到一条消息,说应用程序没有响应,我可以在等待(什么也不发生)或强制关闭应用程序之间进行选择。

我把surfaceView的代码留给你(有些东西现在还没有必要,有些东西已经注释好了,可以试用)。

package net.balanze.tutorialjuego;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.media.SoundPool;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
/*
* See audio with ToneGenerator, MediaPlayer y SoundPool.
*/
public class GameView extends SurfaceView {

private SurfaceHolder holder; 
private GameLoopThread gameLoopThread; 
private List<Sprite> sprites=new ArrayList<Sprite>(); 
private long lastClick; 
private Bitmap bmpBlood; 
private List<TempSprite> temps = new ArrayList<TempSprite>(); 
private int puntos=0;
private Paint paint;
/*
 * iniciamos sndPool con un maximo de 16 flujos simultanios
 * el audio sera del flujo musical, (el ultimo parametro no
 * funciona asi que pongo 0 que es el conversor por defecto).
 */
private SoundPool sndPool = new SoundPool(16, AudioManager.STREAM_MUSIC, 0);
private int sonidoMalo;
/*private int x=0; 
private int xSpeed=1;*/

public GameView(Context context) {
    super(context);
    gameLoopThread = new GameLoopThread(this);
    holder = getHolder();
    holder.addCallback(new Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            gameLoopThread.setRunning(false);
            boolean intento=true;
            while(intento){
                try {
                    gameLoopThread.join();
                } catch (InterruptedException e) {
                    // e.printStackTrace();
                    System.out.println("ERROR --> Problema al destruir la view.");
                }
                intento=false;
            }
            //gameLoopThread=null;
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
             createSprites();
            /*
             * La asignacion de los Sprites se hace aqui ya que 
             * para calcular las posiciones en las que empieza cada
             * sprite necesitamos conocer el alto y el ancho de la 
             * pantalla y si no se acabo de crear la view no conocemos
             * sus dimensiones. 
             */
             gameLoopThread=(!gameLoopThread.isAlive())?new GameLoopThread(devolverVista()):gameLoopThread;
            if(!gameLoopThread.isRunning()){
                gameLoopThread.setRunning(true);
                gameLoopThread.start();
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
            // TODO Auto-generated method stub

        }
    });
    bmpBlood = BitmapFactory.decodeResource(getResources(), R.drawable.blood1);
    sonidoMalo=sndPool.load(context, R.raw.baddeath, 1);
    paint=new Paint();
    paint.setARGB(255, 255, 0, 0);
    paint.setTextSize(30);
    paint.setTypeface(Typeface.SANS_SERIF);
}
/**
 * <p><b>private void createSprites()</b></p>
 * <p>Añade a al array list sprites todos los sprites que veremos
 * en pantalla.</p>
 */
private void createSprites(){
    // establecemos el grafico que queremos pintar.
    sprites.add(createSprite(R.drawable.bad1, false));
    sprites.add(createSprite(R.drawable.bad2, false));
    sprites.add(createSprite(R.drawable.bad3, false));
    sprites.add(createSprite(R.drawable.bad4, false));
    sprites.add(createSprite(R.drawable.bad5, false));
    sprites.add(createSprite(R.drawable.bad6, false));
    sprites.add(createSprite(R.drawable.good1, true));
    sprites.add(createSprite(R.drawable.good2, true));
    sprites.add(createSprite(R.drawable.good3, true));
    sprites.add(createSprite(R.drawable.good4, true));
    sprites.add(createSprite(R.drawable.good5, true));
    sprites.add(createSprite(R.drawable.good6, true));
}
/**
 * <p><b>private Sprite createSprite(int Resource)</b></p>
 * <p>Devuelve un sprite con la imagen que se le pasa como recurso.</p>
 * @param Resource Recurso de imagen que queremos pintar en el Sprite.
 * @return Sprite creador con imagen pasada.
 */
private Sprite createSprite(int Resource, boolean esBueno){
    Bitmap bmp = BitmapFactory.decodeResource(getResources(), Resource);
    return new Sprite(this, bmp, esBueno);
}

@Override
protected void onDraw(Canvas canvas){
    canvas.drawColor(Color.BLACK);
    canvas.drawText("Puntuacion: "+puntos, 0, 30, paint);
    for(int i=temps.size()-1; i>=0; i--) temps.get(i).onDraw(canvas);
    for(Sprite sprite:sprites){
        sprite.onDraw(canvas);
    }
}

@Override
public boolean onTouchEvent(MotionEvent event){
     if(System.currentTimeMillis()-lastClick>300){
        lastClick=System.currentTimeMillis();
        synchronized (getHolder()) {
            float x = event.getX();
            float y = event.getY();
            for(int i=sprites.size()-1; i>=0; i--){
                Sprite sprite=sprites.get(i);
                if(sprite.isColision(x, y)){
                    if(!sprite.esBueno()) sndPool.play(sonidoMalo, 1.0f, 1.0f, 5, 0, 1.0f);
                    puntos+=sprite.puntuar();
                    sprites.remove(sprite);
                    temps.add(new TempSprite(temps, this, x, y, bmpBlood));
                    break;
                }
            }
        }
    }
    //return true;
    return super.onTouchEvent(event);
}
/**
 * <p><b>public boolean quedanMalos()</b><P>
 * <p>Devuelve verdadero si quedan sprites de enemigos y falso en caso contrario.</p>
 * @return true si quedan enemigos, false si solo quedan sprites buenos.
 */
public boolean quedanMalos(){
    boolean soloBuenos=true;
    for(int i=0; i<sprites.size() && soloBuenos; i++)     soloBuenos=sprites.get(i).esBueno();
    return !soloBuenos;        
}
/**
 * <p><b>public void pause()</b></p>
 * <p>Pausa el juego, porque pausa el gameLoop.</p>
 */
public void pause(){
    gameLoopThread.setRunning(false);
    boolean intento=true;
    //synchronized (gameLoopThread) {
        while(intento){
            try{
                gameLoopThread.join();
            }catch (InterruptedException e) {
                //e.printStackTrace();
                System.out.println("ERROR --> No se pudo para el gameLoop.");
            }
            intento=false;
        }
    //}
    gameLoopThread=null;
}
public void resume(){
    if(holder.isCreating()){
        gameLoopThread=(!gameLoopThread.isAlive())?new GameLoopThread(this):gameLoopThread;
        if(!gameLoopThread.isRunning()){
            gameLoopThread.setRunning(true);
            gameLoopThread.start();
        }
    }
}
private GameView devolverVista(){
    return this;
}

}

非常感谢你的帮助。如果你还需要代码或其他什么。

共有1个答案

潘文乐
2023-03-14

好吧,我不知道你是否还需要帮助,但这里去;

首先,做一些调试来检查当您返回时是否正在获取值,或者您的onTouch是否甚至通过在那里使用断点而被触发(尽管表单阅读您的帖子,我猜您已经这样做了)。

还要尝试查看表面视图周围的视图/视图组是否有任何接触(给布局/视图一个id和一个on的监听器和断点)。

我在线程运行的surfaceview中所做的是覆盖surfaceview视图的本体:

@Override
public boolean onTouchEvent(Mothtml" target="_blank">ionEvent event) {
    return thread.doTouchEvent(event);
}

在我的线程中,我处理触摸事件:

boolean doTouchEvent(MotionEvent event) {
        synchronized (mSurfaceHolder) 
        {
               (detect ondown etc, do something and return true/false)
            }
       }

这似乎在恢复后可以正确处理事情。不过,请务必正确处理您的线程(oncreate,ondestroy等)。

顺便说一句,为了处理输入泛洪,我有这个:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {

    /*touchTime = System.currentTimeMillis();       
    if (oldTouchAction == -1)
    {
        lastTouchTime = touchTime;
        oldTouchAction = (event.getAction() & MotionEvent.ACTION_MASK);

        super.dispatchTouchEvent(event);

        try {
            Thread.sleep(32);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       return true;
    }
    else
    {           
        if (oldTouchAction == (event.getAction() & MotionEvent.ACTION_MASK))
        {
            if ((touchTime - lastTouchTime) > 35)
            {
                lastTouchTime = touchTime;

                super.dispatchTouchEvent(event);

                return true;
            }
            lastTouchTime = touchTime;
        }
        else
        {
            lastTouchTime = touchTime;
            super.dispatchTouchEvent(event);

           return true;
        }
    }       
    lastTouchTime = touchTime;*/
    if (touchBool)
    {
        super.dispatchTouchEvent(event);
        try {
            Thread.sleep(32);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return true;
}

这可防止活动因触摸事件过多而减慢(touchBool只是我用来防止在游戏获胜/失败后两次触发事件的工具;当游戏获胜/输了,并且通过触摸屏幕触发事件时,touchBoo设置为false。我在surfaceview中还有一个方法,将其设置为true/false,这在周围活动的onresume/opause中触发)。

无论如何,我希望这有所帮助,祝你好运!

 类似资料:
  • 在WPF应用程序中,我有一个线程池(每个线程都是对返回base 64映像的REST webservice的调用),我想精确地“控制”这些线程。 这些线程是异步的,我希望能够暂停/恢复它们中的每一个。 > < li> 我想使用CancellationToken之类的东西不是一个选项,因为每个线程都只是进行一次调用并等待响应。 我只是在课堂线程中看到的= 如果有人有什么建议呢? 提前谢谢。

  • 我正在开发一个Spring Boot应用程序,它使用以Kafka主题为源的Spring集成流。我们的集成流程开始使用一个包含带有spring framework . cloud . stream . annotation . input和Output注释的SubscribableChannels的接口。这些被配置为通过spring . Cloud . stream . Kafka . bindin

  • 问题内容: 我愿意在应用程序中添加一个按钮,单击该按钮将重新启动该应用程序。我搜索谷歌,但没有发现任何有用的,除了这一个。但是,此处遵循的过程违反了Java的WORA概念。 是否有其他以Java为中心的方法来实现此功能?是否可以只派生另一个副本然后退出? 提前致谢。我感谢您的帮助。 @deporter我已经尝试过您的解决方案,但是它不起作用:( @mKorbel我写的,采取的概念下面的代码,你曾在

  • 我试图从意图服务内的循环显示文本视图中的倒计时。我使用结果接收器类进行意图服务和活动之间的通信。当我第一次启动服务时,它工作正常。文本视图显示服务中循环每次运行的倒计时。 但是当我关闭并再次启动应用程序时,文本视图不显示倒计时,只显示硬编码文本,而另一方面,服务静态在后台运行。 这是我的主活动代码片段 这是我的意向服务类代码 在实际项目中,我的意图不是使用循环来显示倒计时。它只是为了测试和调试的目

  • 可能会有暂停应用的需求,那这里给做了一个。 实现非常简单,就是对容器的启动使用做了一个Sleep 当然还有另一种方案是将 replace 设置为 0

  • 我在我的工作区中使用STS IDE运行了几个Spring Boot应用程序,在我对其中一个项目进行maven更新后,每个项目都在应用程序启动过程后立即停止。我甚至创建了一个最小的例子,只是为了开始一些事情,同样的事情发生了。 这是我的pom.xml 即使是那些入门示例也会在启动后立即停止。我会非常感谢这里的一些帮助。 编辑:正如Alexandru Marina在评论中所说,我使用的是快照而不是稳定