I want to run a gameloop code inside my GameLoop class constructor in my activity, but it seems that it got no respond.
I've tried to put the code inside the OnCreate method instead of a new class and that worked.
My Activiy Class:
public class GameActivity extends Activity {
private Button btnHertz;
private TextView textView1;
private GameLoop gameloop;
private int hertz = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
gameloop = new GameLoop();
btnHertz = (Button) findViewById(R.id.btnHertz);
textView1 = (TextView) findViewById(R.id.testTextView1);
}
public void testUpdate(){
hertz++;
textView1.setText(Integer.toString(hertz));
}
GameLoop Class:
public class GameLoop {
private GameActivity gui;
public GameLoop() {
gui = new GameActivity();
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(new Runnable() {
public void run() {
gui.testUpdate();
}
}, 0, 10, TimeUnit.MILLISECONDS);
}
gui = new GameActivity();
The activity object you're passing updates to is different from the one that displays your UI.
Never instantiate activities yourself with new. Their lifecycle methods won't be invoked and they won't be good for anything. In this case you'd get an NPE at textView1.setText() since onCreate() has not been run.
Instead, pass a GameActivity reference as an argument to GameLoop, e.g.
... new GameLoop(this)
...
public GameLoop(GameActivity gui) {
Related
MyThread class is used to change the value of the myValue attribute in ActivityTwo Class.
public class MyThread implements Runnable {
private ActivityTwo activity;
public MyThread(ActivityTwo activity) {
this.activity = activity;
}
#Override
public void run() {
while(true){
activity.setValue(2);
}
}
}
ActivityTwo is an AppCompatActivity activity which runs as the main Thread.
public class ActivityTwo extends AppCompatActivity {
private MyThread myThread;
private Button startLogBtn;
private int myValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
startLogBtn = findViewById(R.id.startLogBtn);
myThread = new MyThread(this);
startLogBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new Thread(myThread).start();
}
});
}
public void setValue(int value){
this.myValue= value;
}
}
When I Click the startLogButton it goes to a white screen and then restart the app. What should I do? I have no idea what has gone wrong.
Thanks in advance.
The issue was I have violated the rule "Do not access the Android UI toolkit from outside the UI thread".
ref : https://developer.android.com/guide/components/processes-and-threads
As a solution I found that I can use thread communication using message passing.
ref : Communication between UI thread and other threads using handler
I'm doing a project in android studio. I just want to do a flashing light point using onDraw() and invalidate() but something is wrong.
This is the first class
public class flashingPoint extends View {
private ShapeDrawable mParteDibujable;
public flashingPoint(Context context){
super(context);
final Handler bridge = new Handler();
Thread time = new Thread() {
public void run() {
bridge.postDelayed(this, 1000);
invalidate();
}
};
time.start();
}
#Override
protected void onDraw(Canvas canvas) {
mParteDibujable = new ShapeDrawable(new OvalShape());
mParteDibujable.getPaint().setColor(0xff74AC23);
mParteDibujable.setBounds(10, 20, 80, 80);
mParteDibujable.draw(canvas);
//invalidate();
}
And then the main class:
public class MainActivity extends AppCompatActivity {
private ShapeDrawable mParteDibujable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout mLinearLayout = new LinearLayout(this);
flashingPoint myView = new flashingpoint(this);
mLinearLayout.addView(myView);
setContentView(mLinearLayout);
}
}
If you have the timer, you neither need nor want the invalidate in onDraw. Invalidating in onDraw is both logically weird and will lead to poor results- it would either be ignored, or it would cause an immediate redraw. Neither is desired.
Also, you can't invalidate on a Thread, you'd need to use postInvalidate. And your thread is wrong- either don't use a thread, use a Runnable and post it to the Handler, or the thread should infinitely loop, not return. Preferably the first, there's no reason to have a thread here at all.
I'm making an Android whack a mole game. I have the main activity which is basically the launcher, when you press the Play button the game activity starts. This works fine as it shows the background image and all molehills but I don't know how to call the method to start the game.
I've tried to call it from inside onCreate() but this ends up "playing the game" itself.
I've tried to call it right after the startActivity(intent) but the app crashes. And also I've tried to create an instance of the game class and call the play() method after the start activity but it doesn't work aswell. I don't know how to start the game method once the game activity is loaded.
I hope I explained well, thank you.
public class MainActivity extends AppCompatActivity {
ImageButton btnStart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide TitleBar
try { this.getSupportActionBar().hide();}
catch (NullPointerException e){}
setContentView(R.layout.activity_main);
btnStart = (ImageButton)findViewById(R.id.btnStart);
btnStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), GameView.class);
startActivity(intent);
}
});
}
And this is the code for the game_activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide TitleBar
try { this.getSupportActionBar().hide();}
catch (NullPointerException e){}
setContentView(R.layout.activity_game_view);
game();
}
The game() method is a typical game loop.
public void game() {
Random random = new Random();
int index;
/*
* Casting array to store all ImageView on the game
*/
imgViewArray[0] = (ImageView)findViewById(R.id.img1);
imgViewArray[1] = (ImageView)findViewById(R.id.img2);
imgViewArray[2] = (ImageView)findViewById(R.id.img3);
imgViewArray[3] = (ImageView)findViewById(R.id.img4);
imgViewArray[4] = (ImageView)findViewById(R.id.img5);
imgViewArray[5] = (ImageView)findViewById(R.id.img6);
imgViewArray[6] = (ImageView)findViewById(R.id.img7);
imgViewArray[7] = (ImageView)findViewById(R.id.img8);
imgViewArray[8] = (ImageView)findViewById(R.id.img9);
imgViewArray[9] = (ImageView)findViewById(R.id.img10);
int j=0;
while (j < 10) {
// Get a random image to animate
index = random.nextInt(10);
switch(index) {
case 0: imgViewArray[0].setImageResource(images[6]);
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
imgViewArray[0].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imgViewArray[0].setImageResource(images[0]);
}
});
}
},
300 // The code executes after 300ms
);
break;
I think you should put the game() call inside onResume().
There are many ways to solve the problem:
Using EventBus
Send the start game Event from Main Activity and register for the Event in the Game activity.
This is my favorite way to handle the problem. It's because the simplicity and prevent us from tightly coupled code. The major problem with using EventBus is we will lost in the sea of Event if there are too much Event in the the app.
How to do:
First, create the Event. This is just a simple class:
public class StartGameEvent {
}
Second, register for the event in the game activity:
public class GameActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
EventBus.getDefault().register(this);
}
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
Third, subscribe for the event:
public class GameActivity extends Activity {
...
#Subscribe
public void onMessageEvent(StartGameEvent event) {
game();
}
}
Last, send the event from Main activity:
EventBus.getDefault().post(new StartGameEvent());
Using LocalBroadcastManager
You need to create the message and broadcast it in from your Main activity:
Intent intent = new Intent("playEvent");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Then, in the game activity, you need to register as receiver:
#Override
public void onCreate(Bundle savedInstanceState) {
// register for the event
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
new IntentFilter("playEvent"));
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
game();
}
};
#Override
protected void onDestroy() {
// Unregister here
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(mReceiver);
super.onDestroy();
}
I slightly modifying the code from How to use LocalBroadcastManager? for your case.
Using a static method in Game activity
This is the simplest way but highly discouraged. Because we can't ensure the state of the activity. Do not use this in production code. This is for learning sake only.
You can make the game() method as a static method like this:
public class GameActivity extends Activity {
...
public static void game() {
// game logic.
}
}
Then call the method when you want with:
GameActivity.game();
I read other questions on this, but I feel like my architecture is a bit different, so I'm posting.
I have a game with one MainActivity and multiple views. Basically, there is one LinearLayout that fills MainActivity, and different views are set on the layout depending on the state. For example, menuState (menuView), gameState (gameView), gameOverState (gameOverView).
In my BattleView (which extends SurfaceView), I tried to implement a game loop. However, my BattleView cannot implement a Callback due to the fact getHolder() returns null.
How would I go on about fixing this?
public class MainActivity extends AppCompatActivity {
GameScreen gs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.init();
}
private void init(){
this.gs = new GameScreen(this);
this.hideSystemUI(this.gs);
setContentView(this.gs);
}
Then my GameScreen class contains BattleView class, which is called when appropriate.
public GameScreen(Context context){
super(context);
this.init();
}
private void init(){
this.ms = new MapScreen(this.getContext(), this);
((MainActivity)this.getContext()).hideSystemUI(this.ms);
this.addView(this.ms);
}
private void newMapScreen(){
this.ms = new MapScreen(this.getContext(), this);
}
public void addBattleScreen(BattleScreen bs){
//this.bs = new BattleScreen(this.getContext(), ms);
this.removeView(this.ms);
this.bs = bs;
((MainActivity)this.getContext()).hideSystemUI(this.bs);
this.addView(this.bs);
}
Well.. BattleScreen contains BattleView class and is initialized there. Then, BattleView class
public BattleView(Context context, int monsterNumber, Hero hero){
super(context);
this.gameLoopInit();
this.setBackgroundImage();
}//end
private void gameLoopInit(){
holder = getHolder(); <------------- ERROR (null)
System.out.println(holder);
holder.addCallback(this);
}
Can you tell me where is the problem on this line: timerText.setText(seconds);.
public class ShowTimer extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.timer_test_xml);
Timer myTimer = new Timer();
myTimer.schedule(new TimerTask() {
int seconds;
TextView timerText = (TextView) findViewById(R.id.TimerTestId);
#Override
public void run() {
seconds++;
timerText.setText(seconds);
}
}, 0, 1000);
}}
I think what you want to do is display seconds in the text view. However, the TextView.setText(int) function does not do this (Im not actually sure what it does). What you want to do is timerText.setText(""+seconds); to convert the parameter into a string and change the function call to a different overloaded function.
seconds is an int, whereas I think you want to be passing as character sequence, or a reference to one via a resource id, as per the documentation.
Though this doesn't answer the OP's original question, there are alternative (and - if you agree with the recommendations from the Android docs - better) ways to do this described in this thread.
As with Richard's suggestion, your other problem is updating the TextView on the non-UI thread, so consider using a Handler.
Example
public class ShowTimer extends Activity {
private Handler mHandler;
private TextView timerText = null;
private int seconds;
private Runnable timerRunnable = new Runnable() {
#Override
public void run() {
timerText.setText(String.valueOf(seconds++));
mHandler.postDelayed(timerRunnable, 1000);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timer_test_xml);
mHandler = new Handler();
timerText = (TextView) findViewById(R.id.TimerTestId);
timerRunnable.run();
}
}