Update programmatic Layout from a SurfaceView inside the Layout - android

So, I have searched for an answer. Most results show how to update from outside the Layout. Here is my simplified code.
MainActivity:
public class MainActivity extends Activity{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{ this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout surface = (LinearLayout)findViewById(R.id.surface);
surface.addView(new UnitActivity(this));}}
UnitActivity:
public class UnitActivity extends SurfaceView
{
private MainThread thread;
private SurfaceHolder holder;
public UnitActivity(Context context){
super(context);touch = new MyTouchListener();
this.setOnTouchListener(touch);
thread = new MainThread(this);
holder=getHolder();
holder.addCallback(new SurfaceHolder.Callback(){
#Override
public void surfaceCreated(SurfaceHolder p1)
{
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceChanged(SurfaceHolder p1, int p2, int p3, int p4)
{
// TODO: Implement this method
}
#Override
public void surfaceDestroyed(SurfaceHolder p1)
{
boolean retry = true;
while (retry)
{
try
{
thread.join();
retry = false;
}
catch(Exception e){
}
}
}
});
setFocusable(true)
}
#Override
public void onDraw(Canvas c) {
//draw to canvas here
*******Here is where I want to, in some way
make a call to add a ScrollView or
TextView to the original Layout******
}
}
Basically, my SurfaceView is updating running a Thread. I want a certain condition to trigger a TextView or ScrollView to become visible on screen. I thought maybe I could just get the Layout to run a thread and update itself, but killed my app doing so... I appreciate any help or comments.

Related

Cant find public methods from a custom SurfaceView that extends Runnable and is nested within a layout

So this app has a custom SurfaceView that extends Runnable.
At first myView was directly used in MainActivity and not through any layouts and it worked like it should. The content displayed through run() depends on the screen height that I was passing from MainActivity. Then I decided to nest the view and make its content dependent on the layout's height instead.
After nesting it under a LinearLayout I used getLayoutParams() in the code in init() and just then MainActivity stopped recognizing the pause() and resume() functions. I figure I might be declaring the custom view in MainActivity the wrong way but i cant find any relevant sources
MainActivity
public class MainActivity extends Activity {
private SurfaceView myView1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myView1 = findViewById(R.id.myView);
setContentView(R.layout.myParentLayout);
}
#Override
protected void onPause(){
super.onPause();
myView1.pause(); //cannot recognise this function
}
#Override
protected void onResume(){
super.onResume();
myView1.resume(); //and this function
}
}
myView
class myView extends SurfaceView implements Runnable {
public Context context;
private SurfaceHolder surfaceHolder;
private boolean running;
private Thread thread;
private myClass l; //imported classes
public myView(Context context) {
super(context);
init();
}
public myView(Context context, AttributeSet attrs){
super(context,attrs);
init();
}
public myView(Context context, AttributeSet attrs, int defStyleAttr){
super(context,attrs, defStyleAttr);
init();
}
private void init(){
surfaceHolder = getHolder();
int x = getLayoutParams().width; //happened after i added this
l = new myClass(x);
}
#Override
public void run(){
while (running){
/*doing stuff*/
}
}
public void pause(){
running = false;
try{
thread.join();
}catch (InterruptedException e){
e.fillInStackTrace();
}
}
public void resume(){
running = true;
thread = new Thread(this);
thread.start();
}
}

surfaceCreated() called after reopening app

I'm currently working on getting the onStop() and onResume() methods right. The problem that I have is that my surfaceCreated() methode gets called everytime when I reopen the app through the onResume() methode. Because the game gets initialized in the surfaceCreated() method, which obviously causes the game to restart. But my goal is to make that it's just continues where it stopped.
I tried to stop the initializing process with the boolean started, but this causes the app to crash even it got initialized at the start.
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback{
public MainThread thread;
private static MainActivity context;
private boolean started = false;
public GamePanel(MainActivity context){
super(context);
this.context = context;
getHolder().addCallback(this);
thread = new MainThread(this.getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if(!started){
DisplayMetrics displayMetrics = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
thread.start();
}
thread.setRunning(true);
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
thread.setRunning(false);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
I removed all the unimportant stuff
public class MainActivity extends AppCompatActivity {
private GamePanel gamePanel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
gamePanel = new GamePanel(this);
setContentView(gamePanel);
}
protected void onResume(){
super.onResume();
gamePanel.thread.setRunning(true);
}
protected void onStop(){
super.onStop();
}
protected void onPause(){
super.onPause();
gamePanel.thread.setRunning(false);
}
}
Im stopping the thread by setting thread.running to false. Which should pause the app logic and only continues as soon the onResume() method is called by setting thread.running to true again.
Do i have to recreate the SurfaceView everytime i reopen the app?
Im new to android and i couldn't find the solution on the web.
Thanks in advance!

pause game thread when home button is pressed, re-entering resumes game thread

I have got a problem
When hitting the home button, my game thread class gets paused. But when re-entering the app and the onResume() method gets called, it should resume the game thread... everythin i get is a force close.
Here is my MainActivity:
public class MainActivity extends Activity {
private RelativeLayout relativeLayout;
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameView = new GameView(this, this);
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout)findViewById(R.id.mainView);
relativeLayout.addView(gameView);
}
#Override
protected void onPause() {
super.onPause();
gameView.pause();
}
#Override
protected void onResume() {
super.onResume();
if(gameView.getGameThread() != null) {
if(gameView.getGameThread().isRunning == false) {
gameView.resume();
}
}
}
}
gameView is following class, a surfaceView getting rendered by GameThread:
public class GameView extends SurfaceView{
private GameThread gameThread;
public GameView(Context context, Activity activity) {
super(context);
gameThread = new GameThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameThread.setRunning(true);
gameThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
});
}
public GameThread getGameThread() {
return gameThread;
}
public void update() {
}
public void pause() {
gameThread.pause();
}
public void resume() {
gameThread.resumeThread();
}
}
This is the GameThread class:
public class GameThread extends Thread{
private GameView view;
public boolean isRunning = false;
public GameThread(GameView view) {
this.view = view;
}
public void setRunning(boolean setRunning) {
isRunning = setRunning;
}
public void stopThread() {
isRunning = false;
}
public void run() {
while(isRunning) {
Canvas c = null;
view.update();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.draw(c);
}
}finally {
if(c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
public void startThread() {
isRunning = true;
}
public void pause() {
isRunning = false;
}
public void resumeThread() {
isRunning = true;
}
}
I removed the irrelevant parts of the code.
When re-entering the app the android.view.Choreographer class opens
Any ideas?
Actually you're not pausing your thread (to be strict, there's no such thing as pausing a thread). You're ending your thread.
When you call pause() you set the isRunning flag to false, that makes the thread exit the loop and the thread ends. When you set your 'isRunning' to true again, the thread won't restart.
Actually you can't reuse threads in java, you have to create a new one, because once the thread loop ends, it's dead.

End SurfaceView and GameThread on exiting app

I've got a surfaceview and a gameThread class.
The gameThread keeps updating and painting on the SurfaceView class.
Now when I exit the app (By pressing the home or back button) I get a message that the app force closed. That's because the GameThread still tries to draw on the erased surfaceview...
So how do i properly end the app without getting this force close notification? I would like the GameThread class to stop when the back button is pressed. It should pause when pressing on home and running in the background. When re-entering the still running game it should resume....
Any ideas?
This is my GameThread class:
public class GameThread extends Thread{
private GameView view;
public boolean isRunning = false;
public GameThread(GameView view) {
this.view = view;
}
public void setRunning(boolean setRunning) {
isRunning = setRunning;
}
public void run() {
while(isRunning) {
Canvas c = null;
view.update();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.draw(c);
}
}finally {
if(c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
It keeps updating my GameView class:
public class GameView extends SurfaceView{
private GameThread gameThread;
public GameView(Context context, Activity activity) {
super(context);
gameThread = new GameThread(this);
init();
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameThread.setRunning(true);
gameThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
});
}
public void init() {
}
public void update() {
}
public void draw(Canvas canvas) {
super.draw(canvas);
}
}
When pressing home my logcat shows this up:
02-24 18:24:59.336: E/SurfaceHolder(839): Exception locking surface
02-24 18:24:59.336: E/SurfaceHolder(839): java.lang.IllegalStateException: Surface has already been released.
02-24 18:24:59.336: E/SurfaceHolder(839): at android.view.Surface.checkNotReleasedLocked(Surface.java:437)
02-24 18:24:59.336: E/SurfaceHolder(839): at android.view.Surface.lockCanvas(Surface.java:245)
02-24 18:24:59.336: E/SurfaceHolder(839): at android.view.SurfaceView$4.internalLockCanvas(SurfaceView.java:872)
Main activity:
public class MainActivity extends Activity {
private RelativeLayout relativeLayout;
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameView = new GameView(this, this);
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout)findViewById(R.id.mainView);
relativeLayout.addView(gameView);
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
gameView.onBackPressed();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Well you can make changes to your code like this.
In GameView
#Override
public void surfaceCreated(SurfaceHolder holder) {
MainActivity obj=new MainActivity();
stop=obj.getBoolean(); //receive status of boolean from main activity
//stop is boolean set if backPressed in main activity
if(!stop){
gameThread.setRunning(true);
gameThread.start();
}
else
gameThread.setRunning(false);
}
In MainActivity
public Boolean getBoolean(){
return stop;
}
public void setBoolean(Boolean bools){
stop=bools;
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
stop=true;
setBoolean(stop);
try{
Thread.sleep(200); // Be safeside and wait for Game thread to finish
}
catch(Exception e){
e.printStackTrace();
}
MainActivity.finish();
}
Call setBoolean once before backPress else it will give error. Make amendments to code as per your needs.
Hope it helps. Cheers. :)
The Surface underlying the SurfaceView gets destroyed between onPause() and onDestroy() in the app lifecycle. Send a message to your renderer thread in onPause(), and wait for the thread to shut down.
For an example, see the "Hardware scaler exerciser" activity in Grafika. It starts the render thread in the SurfaceHolder's surfaceCreated() callback.
Update: it felt weird to start the thread in surfaceCreated() and stop it in onPause(), so after a bit of reshuffling HardwareScalerActivity now stops the thread in surfaceDestroyed(). (The SurfaceView documentation is a bit ambiguous -- it says the Surface is valid "between surfaceCreated() and surfaceDestroyed()" -- but looking at the SurfaceView implementation this appears to be the expected usage.)
Update 2: I updated the code some more after finding cases it didn't handle well. There's now a 60-line megacomment in the sources, which can also been seen in my answer to a similar question.
Update 3: The mega-comment turned into an architecture doc appendix. Grafika's "hardware scaler exerciser" activity demonstrates approach #2, while "texture from camera" demonstrates approach #1.

Automatically Exit SurfaceView

I have a game I'm developing for Android 2.x. When the player looses, I want to automatically exit that and go back to the previous menu of the game. This may be a simple question, but how do I do that?
I have a MenuActivity, which calls GameActivity, which calls the GameView (implementation of SurfaceView) where the game logic is.
UPDATE: No, wait, it's more complex than that. To make this game I followed the MoonLander example, which used threads.
GameView:
public class GameActivity extends Activity
{
GameView gameView;
GameThread gThread;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
gameView = new GameView(this);
setContentView(gameView);
gThread = gameView.getThread();
gThread.doStart();
}
}
GameView:
package com.cp.balldrop;
public class GameView extends SurfaceView implements SurfaceHolder.Callback
{
class GameThread extends Thread
{
public GameThread(SurfaceHolder sHolder, Context context, Handler handler)
{
//Code
}
public void doStart()
{
synchronized (mSurfaceHolder)
{
}
}
#Override
public void run()
{
while (running)
{
Canvas c = null;
try
{
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder)
{
doDraw(c);
}
}
finally
{
if (c != null)
{
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
protected void doDraw(Canvas canvas)
{
//code
}
}
/** Handle to the application context, used to e.g. fetch Drawables. */
private Context mContext;
/** Pointer to the text view to display "Paused.." etc. */
private TextView mStatusText;
/** The thread that actually draws the animation */
private GameThread gThread;
public GameView(Context context)
{
super(context);
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// create thread only; it's started in surfaceCreated()
gThread = new GameThread(holder, context, new Handler()
{
#Override
public void handleMessage(Message m)
{
// mStatusText.setVisibility(m.getData().getInt("viz"));
// mStatusText.setText(m.getData().getString("text"));
}
});
setFocusable(true);
}
public GameThread getThread()
{
return gThread;
}
public void surfaceCreated(SurfaceHolder holder)
{
gThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
gThread.setRunning(false);
while (retry)
{
try
{
gThread.join();
retry = false;
}
catch (InterruptedException e)
{
}
}
}
}
In my thread, I call the following method from my surfaceview's doTouch:
public void doLose() {
synchronized (mSurfaceHolder) {
//quit to mainmenu
((Activity) mContext).finish();
}
}
This seems to shut down the surfaceview and the activity hosting it, dumping me back in my main-menu activity. Note the casting and the predefined context.
What about calling finish() on the GameActivity?

Categories

Resources