I try to run a test for my android app but I get this trace. What does it mean?
java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
at android.widget.Toast$TN.<init>(Toast.java:390)
at android.widget.Toast.<init>(Toast.java:114)
at android.widget.Toast.makeText(Toast.java:277)
at android.widget.Toast.makeText(Toast.java:267)
at dev.android.gamex.CatchGame.onDraw(MainActivity.java:317)
at dev.android.gamex.JamieTest.useAppContext(JamieTest.java:45)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:375)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074)
Tests ran to completion.
My test class
package dev.android.gamex;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.test.InstrumentationRegistry;
import android.widget.TextView;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class JamieTest {
private static final String FAKE_STRING = "HELLO WORLD";
private OnScoreListener onScoreListener = new OnScoreListener() {
#Override
public void onScore(int score) {
}
};
#Mock
Canvas can;
#Test
public void useAppContext() throws Exception {
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("dev.android.gamex", appContext.getPackageName());
CatchGame cg = new CatchGame(appContext, 5, "Jamie", onScoreListener);
cg.initialize();
assertTrue(! cg.gameOver);
cg.onDraw(new Canvas());
assertTrue(! cg.paused);
}
}
The code I want to test is below.
package dev.android.gamex;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
CatchGame cg;
public TextView textView;
public LinearLayout mainLayout;
String[] spinnerValue = {"Rookie", "Advanced", "Expert", "Master"};
// start app
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainLayout = new LinearLayout(this);
mainLayout.setOrientation(LinearLayout.VERTICAL);
LinearLayout menuLayout = new LinearLayout(this);
menuLayout.setBackgroundColor(Color.parseColor("#FFFFFF"));
textView = new TextView(this);
textView.setVisibility(View.VISIBLE);
String str = "Score: 0";
textView.setText(str);
menuLayout.addView(textView);
Button button = new Button(this);
button.setText("Pause");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
togglePausePlay();
}
});
menuLayout.addView(button);
Spinner spinner2 =new Spinner(this);
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, spinnerValue);
spinner2.setAdapter(adapter);
menuLayout.addView(spinner2);
mainLayout.addView(menuLayout);
cg = new CatchGame(this, 5, "Jamie", onScoreListener);
cg.setBackground(getResources().getDrawable(R.drawable.bg_land_mdpi));
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
}
private void togglePausePlay() {
if (cg.paused) {
// play
// getSupportActionBar().hide();
Toast.makeText(MainActivity.this, "Play", Toast.LENGTH_SHORT).show();
} else {
// pause
// getSupportActionBar().show();
Toast.makeText(MainActivity.this, "Pause", Toast.LENGTH_SHORT).show();
}
cg.paused = !cg.paused;
}
private OnScoreListener onScoreListener = new OnScoreListener() {
#Override
public void onScore(int score) {
textView.setText("Score: " + score);
}
};
#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, menu);
return true;
}
// method called when top right menu is tapped
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
int difficulty = cg.NBRSTEPS;
String name = cg.heroName;
switch (item.getItemId()) {
case R.id.item11:
cg = new CatchGame(this, 3, name, onScoreListener);
setContentView(cg);
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
return true;
case R.id.item12:
cg = new CatchGame(this, 5, name, onScoreListener);
setContentView(cg);
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
return true;
case R.id.item13:
cg = new CatchGame(this, 7, name, onScoreListener);
setContentView(cg);
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
return true;
case R.id.item14:
cg = new CatchGame(this, 9, name, onScoreListener);
setContentView(cg);
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
return true;
case R.id.item15:
cg = new CatchGame(this, 11, name, onScoreListener);
setContentView(cg);
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
return true;
case R.id.item21:
cg = new CatchGame(this, difficulty, "Jamie", onScoreListener);
setContentView(cg);
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
return true;
case R.id.item22:
cg = new CatchGame(this, difficulty, "Spaceship", onScoreListener);
setContentView(cg);
//mainLayout.addView(cg);
//getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
//setContentView(mainLayout);
return true;
default:
cg.paused = true;
return super.onOptionsItemSelected(item);
}
}
}
interface OnScoreListener {
void onScore(int score);
}
class CatchGame extends View {
int NBRSTEPS; // number of discrete positions in the x-dimension; must be uneven
String heroName;
int screenW;
int screenH;
int[] x; // x-coordinates for falling objects
int[] y; // y-coordinates for falling objects
int[] hero_positions; // x-coordinates for hero
Random random = new Random();
int ballW; // width of each falling object
int ballH; // height of ditto
float dY; //vertical speed
Bitmap falling, hero, jamie2, jamieleft, jamieright;
int heroXCoord;
int heroYCoord;
int xsteps;
int score;
int offset;
boolean gameOver; // default value is false
boolean toastDisplayed;
boolean paused = false;
OnScoreListener onScoreListener;
// constructor, load images and get sizes
public CatchGame(Context context, int difficulty, String name, OnScoreListener onScoreListener) {
super(context);
NBRSTEPS = difficulty;
heroName = name;
this.onScoreListener = onScoreListener;
x = new int[NBRSTEPS];
y = new int[NBRSTEPS];
hero_positions = new int[NBRSTEPS];
int resourceIdFalling = 0;
int resourceIdHero = 0;
if (heroName.equals("Jamie")) {
resourceIdFalling = R.mipmap.falling_object2;
resourceIdHero = R.drawable.left_side_hdpi;
setBackground(getResources().getDrawable(R.mipmap.background));
}
if (heroName.equals("Spaceship")) {
resourceIdFalling = R.mipmap.falling_object;
resourceIdHero = R.mipmap.ufo;
setBackground(getResources().getDrawable(R.mipmap.space));
}
falling = BitmapFactory.decodeResource(getResources(), resourceIdFalling); //load a falling image
hero = BitmapFactory.decodeResource(getResources(), resourceIdHero); //load a hero image
jamieleft = BitmapFactory.decodeResource(getResources(), R.drawable.left_side_hdpi); //load a hero image
jamieright = BitmapFactory.decodeResource(getResources(), R.drawable.right_side_hdpi); //load a hero image
ballW = falling.getWidth();
ballH = falling.getHeight();
}
public CatchGame(Context context, int difficulty, String name, OnScoreListener onScoreListener, Drawable background) {
this(context, difficulty, name, onScoreListener);
this.setBackground(background);
}
// set coordinates, etc.
void initialize() {
if (!gameOver) { // run only once, when the game is first started
int maxOffset = (NBRSTEPS - 1) / 2;
for (int i = 0; i < x.length; i++) {
int origin = (screenW / 2) + xsteps * (i - maxOffset);
x[i] = origin - (ballW / 2);
hero_positions[i] = origin - hero.getWidth();
}
int heroWidth = hero.getWidth();
int heroHeight = hero.getHeight();
hero = Bitmap.createScaledBitmap(hero, heroWidth * 2, heroHeight * 2, true);
hero = Bitmap.createScaledBitmap(hero, heroWidth * 2, heroHeight * 2, true);
jamieleft = Bitmap.createScaledBitmap(jamieleft, jamieleft.getWidth()* 2, jamieright.getWidth() * 2, true);
jamieright = Bitmap.createScaledBitmap(jamieright, jamieright.getWidth()* 2, jamieright.getWidth() * 2, true);
heroYCoord = screenH - 2 * heroHeight; // bottom of screen
}
for (int i = 0; i < y.length; i++) {
y[i] = -random.nextInt(1000); // place items randomly in vertical direction
}
offset = (NBRSTEPS - 1) / 2; // place hero at centre of the screen
heroXCoord = hero_positions[offset];
// initialize or reset global attributes
dY = 2.0f;
score = 0;
gameOver = false;
toastDisplayed = false;
}
// method called when the screen opens
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
screenW = w;
screenH = h;
xsteps = w / NBRSTEPS;
initialize();
}
// method called when the "game over" toast has finished displaying
void restart(Canvas canvas) {
toastDisplayed = true;
initialize();
draw(canvas);
}
// update the canvas in order to display the game action
#Override
public void onDraw(Canvas canvas) {
if (toastDisplayed) {
restart(canvas);
return;
}
super.onDraw(canvas);
int heroHeight = hero.getHeight();
int heroWidth = hero.getWidth();
int heroCentre = heroXCoord + heroWidth / 2;
Context context = this.getContext();
// compute locations of falling objects
for (int i = 0; i < y.length; i++) {
if (!paused) {
y[i] += (int) dY;
}
// if falling object hits bottom of screen
if (y[i] > (screenH - ballH) && !gameOver) {
dY = 0;
gameOver = true;
paused = true;
int duration = Toast.LENGTH_SHORT;
final Toast toast = Toast.makeText(context, "GAME OVER!\nScore: " + score, duration);
toast.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
toast.cancel();
toastDisplayed = true;
}
}, 3000);
//Vibrator v = (Vibrator) context.getSystemService(context.VIBRATOR_SERVICE);
// Vibrate for 3000 milliseconds
//v.vibrate(3000);
}
// if the hero catches a falling object
if (x[i] < heroCentre && x[i] + ballW > heroCentre &&
y[i] > screenH - ballH - heroHeight) {
y[i] = -random.nextInt(1000); // reset to new vertical position
score += 1;
onScoreListener.onScore(score);
}
}
canvas.save(); //Save the position of the canvas.
for (int i = 0; i < y.length; i++) {
canvas.drawBitmap(falling, x[i], y[i], null); //Draw the falling on the canvas.
}
canvas.drawBitmap(hero, heroXCoord, heroYCoord, null); //Draw the hero on the canvas.
canvas.restore();
//Call the next frame.
invalidate();
}
// event listener for when the user touches the screen
#Override
public boolean onTouchEvent(MotionEvent event) {
if (paused) {
paused = false;
}
int action = MotionEventCompat.getActionMasked(event);
if (action != MotionEvent.ACTION_DOWN || gameOver) { // non-touchdown event or gameover
return true; // do nothing
}
int coordX = (int) event.getX();
int xCentre = (screenW / 2) - (hero.getWidth() / 2);
int maxOffset = hero_positions.length - 1; // can't move outside right edge of screen
int minOffset = 0; // ditto left edge of screen
if (coordX < xCentre && offset > minOffset) { // touch event left of the centre of screen
offset--; // move hero to the left
if(coordX < heroXCoord)// + heroWidth / 2)
hero = Bitmap.createScaledBitmap(jamieleft, jamieleft.getWidth() , jamieleft.getHeight() , true);
}
if (coordX > xCentre && offset < maxOffset) { // touch event right of the centre of screen
offset++; // move hero to the right
if(coordX > heroXCoord)
hero = Bitmap.createScaledBitmap(jamieright, jamieright.getWidth() , jamieright.getHeight() , true);
}
heroXCoord = hero_positions[offset];
return true;
}
}
The repository is available online.
You CANNOT show a Toast on non-UI thread. You need to call Toast.makeText() (and most other functions dealing with the UI) from within the main thread.
You could use Activity#runOnUiThread():
runOnUiThread(new Runnable() {
public void run() {
final Toast toast = Toast.makeText(context, "GAME OVER!\nScore: " + score, duration);
toast.show();
}
});
If you want execute a instrumentation test on main thread, add #UiThreadTest annotation:
#Test
#UiThreadTest
public void useAppContext() {
// ...
}
P.s: There are also many other ways with explain (using Handler, Looper, Observable..) in these posts: Android: Toast in a thread and Can't create handler inside thread that has not called Looper.prepare()
One cannot show a Toast from a non UI Thread. So you can do the following from the worker thread and it doesn't require Activity or Context
JAVA
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast toast = Toast.makeText(mContext, "Toast", Toast.LENGTH_SHORT);
toast.show();
}
});
KOTLIN
Handler(Looper.getMainLooper()).post {
Toast.makeText(mContext, "Toast", Toast.LENGTH_SHORT).show()
}
In kotlin put your code inside this:
runOnUiThread {
Log.i(TAG, "runOnUiThread")
Toast.makeText(MainActivity.this, "Play", Toast.LENGTH_SHORT).show()
}
Run Your UI component with runOnUiThread, this is what i did - KOTLIN
inner class yourThread : Thread() {
override fun run() {
// this is for fragment, you can use 'this' for Activity
requireActivity().runOnUiThread {
// Your Toast
Toast.makeText(requireContext(), "Toasted",Toast.LENGTH_LONG).show()
}
}
Related
I code a mini game for Android where an animal is controlled by the player when the player clicks on the sides of the Drawable.
I wonder if it is better, and if yes, how to make the Drawable touchable so that the player can drag the character to either side instead of clicking by its sides? I'm interested in both UX/UI opinion and actual solution to the problem.
package dev.android.jamie;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
// An item was selected. You can retrieve the selected item using
Log.d("myTag", "This is my message" + parent.getItemAtPosition(pos));
String name = "Jamie";
String str = parent.getItemAtPosition(pos).toString();
if (str.equals("Rookie")) {
cg = new CatchGame(this, 3, name, onScoreListener);
// setContentView(cg);
mainLayout.addView(cg);
getSupportActionBar().hide();
setContentView(mainLayout);
Log.d("game", "Started Rookie game");
} else if (str.equals("Advanced")) {
mainLayout = new LinearLayout(this);
mainLayout.setOrientation(LinearLayout.VERTICAL);
LinearLayout menuLayout = new LinearLayout(this);
menuLayout.setBackgroundColor(Color.parseColor("#FFFFFF"));
textView = new TextView(this);
textView.setVisibility(View.VISIBLE);
str = "Score: 0";
textView.setText(str);
menuLayout.addView(textView);
Button button = new Button(this);
button.setText("Pause");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
togglePausePlay();
}
});
menuLayout.addView(button);
Spinner spinner2 = new Spinner(this);
spinner2.setOnItemSelectedListener(this);
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, spinnerValue);
spinner2.setAdapter(adapter);
menuLayout.addView(spinner2);
mainLayout.addView(menuLayout);
cg = new CatchGame(this, 5, "Jamie", onScoreListener);
cg.setBackground(getResources().getDrawable(R.drawable.bg_land_mdpi));
mainLayout.addView(cg);
// getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
Log.d("game", "Started Advanced game");
} else if (str.equals("Expert")) {
cg = new CatchGame(this, 7, name, onScoreListener);
//setContentView(cg);
mainLayout.addView(cg);
getSupportActionBar().hide();
setContentView(mainLayout);
}
}
public void onNothingSelected(AdapterView<?> parent) {
// Another interface callback
}
CatchGame cg;
public TextView textView;
public LinearLayout mainLayout;
String[] spinnerValue = {"Difficulty", "Rookie", "Advanced", "Expert", "Master"};
// start app
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainLayout = new LinearLayout(this);
mainLayout.setOrientation(LinearLayout.VERTICAL);
LinearLayout menuLayout = new LinearLayout(this);
menuLayout.setBackgroundColor(Color.parseColor("#FFFFFF"));
textView = new TextView(this);
textView.setVisibility(View.VISIBLE);
String str = "Score: 0";
textView.setText(str);
menuLayout.addView(textView);
Button button = new Button(this);
button.setText("Pause");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
togglePausePlay();
}
});
menuLayout.addView(button);
Spinner spinner2 = new Spinner(this);
spinner2.setOnItemSelectedListener(this);
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, spinnerValue);
spinner2.setAdapter(adapter);
menuLayout.addView(spinner2);
mainLayout.addView(menuLayout);
cg = new CatchGame(this, 5, "Jamie", onScoreListener);
cg.setBackground(getResources().getDrawable(R.drawable.bg_land_mdpi));
mainLayout.addView(cg);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(mainLayout);
}
private void togglePausePlay() {
if (cg.paused) {
// play
// getSupportActionBar().hide();
Toast.makeText(MainActivity.this, "Play", Toast.LENGTH_SHORT).show();
} else {
// pause
// getSupportActionBar().show();
Toast.makeText(MainActivity.this, "Pause", Toast.LENGTH_SHORT).show();
}
cg.paused = !cg.paused;
}
private OnScoreListener onScoreListener = new OnScoreListener() {
#Override
public void onScore(int score) {
textView.setText("Score: " + score);
}
};
interface OnScoreListener {
void onScore(int score);
}
class CatchGame extends View {
int NBRSTEPS; // number of discrete positions in the x-dimension; must be uneven
String heroName;
int screenW;
int screenH;
int[] x; // x-coordinates for falling objects
int[] y; // y-coordinates for falling objects
int[] hero_positions; // x-coordinates for hero
Random random = new Random();
int ballW; // width of each falling object
int ballH; // height of ditto
float dY; //vertical speed
Bitmap falling, hero, jamie2, jamieleft, jamieright, falling2;
int heroXCoord;
int heroYCoord;
int xsteps;
int score;
int offset;
boolean gameOver; // default value is false
boolean toastDisplayed;
boolean paused = false;
OnScoreListener onScoreListener;
// constructor, load images and get sizes
public CatchGame(Context context, int difficulty, String name, OnScoreListener onScoreListener) {
super(context);
NBRSTEPS = difficulty;
heroName = name;
this.onScoreListener = onScoreListener;
x = new int[NBRSTEPS];
y = new int[NBRSTEPS];
hero_positions = new int[NBRSTEPS];
int resourceIdFalling = 0;
int resourceIdFalling2 = 0;
int resourceIdHero = 0;
if (heroName.equals("Jamie")) {
resourceIdFalling = R.mipmap.falling_object2;
resourceIdFalling2 = R.drawable.coconut_hdpi;
resourceIdHero = R.drawable.left_side_hdpi;
setBackground(getResources().getDrawable(R.mipmap.background));
}
falling = BitmapFactory.decodeResource(getResources(), resourceIdFalling); //load a falling image
falling2 = BitmapFactory.decodeResource(getResources(), resourceIdFalling2); //load a falling image
hero = BitmapFactory.decodeResource(getResources(), resourceIdHero); //load a hero image
jamieleft = BitmapFactory.decodeResource(getResources(), R.drawable.left_side_hdpi); //load a hero image
jamieright = BitmapFactory.decodeResource(getResources(), R.drawable.right_side_hdpi); //load a hero image
ballW = falling.getWidth();
ballH = falling.getHeight();
}
// set coordinates, etc.
void initialize() {
if (!gameOver) { // run only once, when the game is first started
int maxOffset = (NBRSTEPS - 1) / 2;
for (int i = 0; i < x.length; i++) {
int origin = (screenW / 2) + xsteps * (i - maxOffset);
x[i] = origin - (ballW / 2);
hero_positions[i] = origin - hero.getWidth();
}
int heroWidth = hero.getWidth();
int heroHeight = hero.getHeight();
hero = Bitmap.createScaledBitmap(hero, heroWidth * 2, heroHeight * 2, true);
hero = Bitmap.createScaledBitmap(hero, heroWidth * 2, heroHeight * 2, true);
jamieleft = Bitmap.createScaledBitmap(jamieleft, jamieleft.getWidth() * 2, jamieright.getWidth() * 2, true);
jamieright = Bitmap.createScaledBitmap(jamieright, jamieright.getWidth() * 2, jamieright.getWidth() * 2, true);
heroYCoord = screenH - 2 * heroHeight; // bottom of screen
}
for (int i = 0; i < y.length; i++) {
y[i] = -random.nextInt(1000); // place items randomly in vertical direction
}
offset = (NBRSTEPS - 1) / 2; // place hero at centre of the screen
heroXCoord = hero_positions[offset];
// initialize or reset global attributes
dY = 2.0f;
score = 0;
gameOver = false;
toastDisplayed = false;
}
// method called when the screen opens
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
screenW = w;
screenH = h;
xsteps = w / NBRSTEPS;
initialize();
}
// method called when the "game over" toast has finished displaying
void restart(Canvas canvas) {
toastDisplayed = true;
initialize();
draw(canvas);
}
// update the canvas in order to display the game action
#Override
public void onDraw(Canvas canvas) {
if (toastDisplayed) {
restart(canvas);
return;
}
super.onDraw(canvas);
int heroHeight = hero.getHeight();
int heroWidth = hero.getWidth();
int heroCentre = heroXCoord + heroWidth / 2;
Context context = this.getContext();
// compute locations of falling objects
for (int i = 0; i < y.length; i++) {
if (!paused) {
y[i] += (int) dY;
}
// if falling object hits bottom of screen
if (y[i] > (screenH - ballH) && !gameOver) {
dY = 0;
gameOver = true;
paused = true;
int duration = Toast.LENGTH_SHORT;
final Toast toast = Toast.makeText(context, "GAME OVER!\nScore: " + score, duration);
toast.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
toast.cancel();
toastDisplayed = true;
}
}, 3000);
//Vibrator v = (Vibrator) context.getSystemService(context.VIBRATOR_SERVICE);
// Vibrate for 3000 milliseconds
//v.vibrate(3000);
}
// if the hero catches a falling object
if (x[i] < heroCentre && x[i] + ballW > heroCentre &&
y[i] > screenH - ballH - heroHeight) {
y[i] = -random.nextInt(1000); // reset to new vertical position
score += 1;
onScoreListener.onScore(score);
}
}
canvas.save(); //Save the position of the canvas.
for (int i = 0; i < y.length; i++) {
if (i % 2 == 0)
canvas.drawBitmap(falling2, x[i], y[i], null); //Draw the falling on the canvas.
else
canvas.drawBitmap(falling, x[i], y[i], null); //Draw the falling on the canvas.
}
canvas.drawBitmap(hero, heroXCoord, heroYCoord, null); //Draw the hero on the canvas.
canvas.restore();
//Call the next frame.
invalidate();
}
// event listener for when the user touches the screen
#Override
public boolean onTouchEvent(MotionEvent event) {
if (paused) {
paused = false;
}
int action = MotionEventCompat.getActionMasked(event);
if (action != MotionEvent.ACTION_DOWN || gameOver) { // non-touchdown event or gameover
return true; // do nothing
}
int coordX = (int) event.getX();
int xCentre = (screenW / 2) - (hero.getWidth() / 2);
int maxOffset = hero_positions.length - 1; // can't move outside right edge of screen
int minOffset = 0; // ditto left edge of screen
if (coordX < xCentre && offset > minOffset) { // touch event left of the centre of screen
offset--; // move hero to the left
if (coordX < heroXCoord)// + heroWidth / 2)
hero = Bitmap.createScaledBitmap(jamieleft, jamieleft.getWidth(), jamieleft.getHeight(), true);
}
if (coordX > xCentre && offset < maxOffset) { // touch event right of the centre of screen
offset++; // move hero to the right
if (coordX > heroXCoord)
hero = Bitmap.createScaledBitmap(jamieright, jamieright.getWidth(), jamieright.getHeight(), true);
}
heroXCoord = hero_positions[offset];
return true;
}
}
}
I'm trying to make small App. This App have Activity, Custom View Class, and service.
1) Activity ask service for new Data and redraw Custom view
2) Service is listning to Bluetooth device and parse data.
Everything was fine, but I noticed that App is slowing down after 40 mins working.
I made another project remove service and find that it slowing too! So problem is my Customview class, maybe i have memory leaks in service to... but i have problem with drawings 100%.
I found that i have some objects that i'm creating on onDraw() method.. i try to move all thise staff to onSizeChanged() - but get more lags.
And now i need help. I need some example with simple drawings that depends on device width and height (I think my method is wrong - i use proportions of my 'Design' to calculate demetions in px)
By the way i'm using animator which make animations more smooth))
public class Dynamics {
/**
* Used to compare floats, if the difference is smaller than this, they are
* considered equal
*/
private static final float TOLERANCE = 0.01f;
/** The position the dynamics should to be at */
private float targetPosition;
/** The current position of the dynamics */
private float position;
/** The current velocity of the dynamics */
private float velocity;
/** The time the last update happened */
private long lastTime;
/** The amount of springiness that the dynamics has */
private float springiness;
/** The damping that the dynamics has */
private double damping;
public Dynamics(float springiness, float dampingRatio) {
this.springiness = springiness;
this.damping = dampingRatio * 2 * Math.sqrt(springiness);
}
public void setPosition(float position, long now) {
this.position = position;
lastTime = now;
}
public void setVelocity(float velocity, long now) {
this.velocity = velocity;
lastTime = now;
}
public void setTargetPosition(float targetPosition, long now) {
this.targetPosition = targetPosition;
lastTime = now;
}
public void update(long now) {
float dt = Math.min(now - lastTime, 50) / 1000f;
float x = position - targetPosition;
double acceleration = -springiness * x - damping * velocity;
velocity += acceleration * dt;
position += velocity * dt;
lastTime = now;
}
public boolean isAtRest() {
final boolean standingStill = Math.abs(velocity) < TOLERANCE;
final boolean isAtTarget = (targetPosition - position) < TOLERANCE;
return standingStill && isAtTarget;
}
public float getPosition() {
return position;
}
public float getTargetPos() {
return targetPosition;
}
public float getVelocity() {
return velocity;
}
}
In my Custom view i have this to set new data:
public void SetData(int[] NewData2,float[]newDatapoints)
{
this.NewData=NewData2;
long now = AnimationUtils.currentAnimationTimeMillis();
if (datapoints == null || datapoints.length != newDatapoints.length) {
datapoints = new Dynamics[newDatapoints.length];
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i] = new Dynamics(70f, 0.50f);
datapoints[i].setPosition(newDatapoints[i], now);
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
invalidate();
} else {
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
removeCallbacks(animator);
post(animator);
}
LastData=NewData;
//redraw();
}
Thise is "code" of my custom view, after all changes it's look terible, so i cut 90% of it. And i make some test code insted:
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.AnimationUtils;
import java.io.IOException;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Random;
public class CustomDisplayView extends View {
//paint for drawing custom view
private Paint RectPaint = new Paint();
//Динамические данные float
private Dynamics[] datapoints;
//Динамические статические Int
private int[] NewData = new int[500];
//созадем новый объект квадрат
private RectF rectf= new RectF();
//Задаем массив динамических цветов
int[] CurColors= new int[100];
int[] TargetColors= new int[100];
public CustomDisplayView(Context context, AttributeSet attrs){
super(context, attrs);
//Установка парметров красок
RectPaint.setAntiAlias(true);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != 0 && h != 0) {
//create Bitmap here
}
}
/**
* Override the onDraw method to specify custom view appearance using canvas
*/
#Override
protected void onDraw(Canvas canvas) {
//Get Screen size
//int viewWidth=this.getMeasuredWidth();
// int viewHeight = this.getMeasuredHeight();
//Выводим код цвета
RectPaint.setColor(0xff000000);
RectPaint.setTextSize(40);
canvas.restore();
canvas.drawText("V: " + datapoints[1].getPosition(), 20, 60, RectPaint);
//int saveCount = canvas.save();
for(int a=0;a<1000;a++)
{
rectf.set(datapoints[a].getPosition(), datapoints[a + 1].getPosition(), datapoints[a].getPosition() + datapoints[a + 1].getPosition() / 10, datapoints[a + 1].getPosition() + datapoints[a + 1].getPosition() / 10);
RectPaint.setColor(0x88005020);
//RectPaint.setColor(CurColor[a]);
//canvas.rotate(datapoints[1].getPosition(), viewWidth/2, viewHeight/2);
canvas.drawRoundRect(rectf, 0, 0, RectPaint);
//canvas.restore();
}
//canvas.restoreToCount(saveCount);
/*
for(int a=0;a<999;a++)
{
CurColors[a]=progressiveColor(CurColors[a], TargetColors[a], 2);
if(CurColors[a]==TargetColors[a])
{
TargetColors[a]=randomColor();
}
}
*/
canvas.restore();
}
//Рандом колор
public static int randomColor(){
Random random = new Random();
int[] ColorParams= new int[4];
ColorParams[0]=random.nextInt(235)+20;
ColorParams[1]=random.nextInt(255);
ColorParams[2]=random.nextInt(255);
ColorParams[3]=random.nextInt(255);
return Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3]);
}
//Интерполяция цвета
public static int progressiveColor(int CurColor,int TargetColor,int Step){
//Current color
int[] ColorParams= new int[4];
ColorParams[0]=(CurColor >> 24) & 0xFF;
ColorParams[1]=(CurColor >> 16) & 0xFF;
ColorParams[2]=(CurColor >> 8) & 0xFF;
ColorParams[3]=CurColor & 0xFF;
//TargetColor
int[] TargetColorParams= new int[4];
TargetColorParams[0]=(TargetColor >> 24) & 0xFF;
TargetColorParams[1]=(TargetColor >> 16) & 0xFF;
TargetColorParams[2]=(TargetColor >> 8) & 0xFF;
TargetColorParams[3]=TargetColor & 0xFF;
for(int i=0;i<4;i++)
{
if(ColorParams[i]<TargetColorParams[i])
{
ColorParams[i]+=Step;
if(ColorParams[i]>TargetColorParams[i])
{
ColorParams[i]=TargetColorParams[i];
}
}
else if(ColorParams[i]>TargetColorParams[i])
{
ColorParams[i]-=Step;
if(ColorParams[i]<TargetColorParams[i])
{
ColorParams[i]=TargetColorParams[i];
}
}
}
//int red = r - (int)((float)(r*255)/(float)all);
//int green = (int)((float)(g*255)/(float)all);
return Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3]);
//return String.format("#%06X", (0xFFFFFF & Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3])));
//return " "+opacity+" "+red+" "+green+" "+blue;
}
//each custom attribute should have a get and set method
//this allows updating these properties in Java
//we call these in the main Activity class
/**
* Get the current text label color
* #return color as an int
*/
public int getLabelColor(){
return 1;
}
/**
* Set the label color
* #param newColor new color as an int
*/
public void setLabelColor(int newColor){
//update the instance variable
//labelCol=newColor;
//redraw the view
invalidate();
requestLayout();
}
public void redraw(){
//redraw the view
invalidate();
requestLayout();
}
public void SetData(int[] NewData2,float[]newDatapoints)
{
this.NewData=NewData2;
long now = AnimationUtils.currentAnimationTimeMillis();
if (datapoints == null || datapoints.length != newDatapoints.length) {
datapoints = new Dynamics[newDatapoints.length];
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i] = new Dynamics(70f, 0.50f);
datapoints[i].setPosition(newDatapoints[i], now);
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
invalidate();
} else {
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
removeCallbacks(animator);
post(animator);
}
//redraw();
}
public int GetAction(float x,float y)
{
/*
if(x>(DicsCenterX-LineHalfSpeedZone) && x<(DicsCenterX+LineHalfSpeedZone) && y>(DicsCenterY-PowerOutRadius) && y<(DicsCenterY-SpeedZoneRadius2))
{
// private int SpeedZoneRadius2=0;
// private int PowerOutRadius=0;
//Смена режима
//начинаем смену размеру index / ms
ChangeVal(0,700);
return 1;
}
else if(x>(CofCantBGDrop*2) && x<(CofCantBGDrop*4) && y>(DicsCenterY-PowerOutRadius) && y<(DicsCenterY-SpeedZoneRadius2))
{
return 2;
}
else
{
return 0;
}
//return 0;
*/
return 1;
}
public static String fmt(double d)
{
double val = d/100;
String result;
if(val == (long) val)
result= String.format("%d",(long)d);
else
result= String.format("%s",d);
if(result.length()<2)
{
String result2=result;
result="0"+result2;
}
return result;
}
private Runnable animator = new Runnable() {
#Override
public void run() {
boolean needNewFrame = false;
long now = AnimationUtils.currentAnimationTimeMillis();
for (Dynamics dynamics : datapoints) {
dynamics.update(now);
if (!dynamics.isAtRest()) {
needNewFrame = true;
}
}
if (needNewFrame) {
postDelayed(this, 15);
}
invalidate();
}
};
}
I just want to understand where i need to declare scale values, where i need to calc real dimensions in px.. and et.c. to have no memory leaks..
If i remove color change and incrice number of Rects up to 1000 - i get lags.
All methods o any information how to debug memory leaks - you are wellcome!
You have to remove runnable for animation when view is detached.
if (needNewFrame) {
postDelayed(this, 15); <--- memory leak
}
Try like this.
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
removeCallback(your runnable);
}
Also refreshing every 15 milliseconds is very heavy.
I am trying to implement an ACTION_MOVE input that moves a paddle as long as the screen is touched, in place of the ACTION_DOWN that is currently present. However Once I switch the ACTION_MOVE in place of ACTION_DOWN, the paddle moves but with stutters(some times too slow, then stopping then going too fast all of a sudden). How would I resolve this? Would Moving the Input to another thread be any good?
Here's the code
MainActivity.java
package com.nblsoft.ballpractise;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View myball = new BallDraw(this);
myball.setBackgroundColor(Color.BLACK);
setContentView(myball);
//setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
BallDraw.java
package com.nblsoft.ballpractise;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
public class BallDraw extends View {
//Default System Variables
int screenHeight = getResources().getDisplayMetrics().heightPixels;
int screenWidth = getResources().getDisplayMetrics().widthPixels;
private int xMin = 0; // This view's bounds
private int xMax;
private int yMin = 0;
private int yMax;
//Game Variables
public int ball_pos_x = 0;
public int ball_pos_y = 800;
public int ball_vel_x = 5;
public int ball_vel_y = 5;
public int pad_1_pos = 10;
public int pad_2_pos = 10;
public int randi=1;
//Pads Implemented as Array of points
//public int[] pad_1 = {100,200,220,240};
//public int[] pad_2 = {100,200,1250,1270};
Rect pad_1 = new Rect(400 + pad_1_pos,220,500 + pad_1_pos,240);
Rect pad_2 = new Rect(400 + pad_2_pos,1250,500 + pad_1_pos,1270);
Rect pad_1_touch = new Rect(0, 0, 1080, 200);
Rect pad_2_touch = new Rect(0, 1300, 1080, 1500);
//calculations for screen sizes
private Paint paint;
public BallDraw(Context context) {
super(context);
paint = new Paint();
// To enable touch mode
this.setFocusableInTouchMode(true);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//case MotionEvent.ACTION_MOVE:
if(pad_1_touch.contains(x,y)){
//pad_1[0]+=10;
//pad_1[1]+=10;
pad_1.offset(pad_1_pos,0);
}
else if(pad_2_touch.contains(x,y)){
//pad_2[0]+=10;
//pad_2[1]+=10;
pad_2.offset(pad_2_pos,0);
}
}
return true; //Event Handled
}
#Override
protected void onDraw(Canvas canvas) {
paint.setColor(Color.WHITE);
//Touch Regions (Un-comment to see touch Regions)
//canvas.drawRect(pad_1_touch,paint);
//canvas.drawRect(pad_2_touch,paint);
//Draw Border
canvas.drawRect(0,800,1080,803,paint);
//Paddles Draw code
canvas.drawRect(pad_1,paint);
canvas.drawRect(pad_2,paint);
//canvas.drawRect(pad_1[0],pad_1[2], pad_1[1], pad_1[3], paint);
//canvas.drawRect(pad_2[0],pad_2[2],pad_2[1],pad_2[3],paint);
//Ball code
canvas.drawRect(ball_pos_x,ball_pos_y,ball_pos_x + 13, ball_pos_y+13,paint);
// Update the position of the ball, including collision detection and reaction.
update();
// Delay
try {
Thread.sleep(30);
} catch (InterruptedException e) { }
invalidate();
}
#Override
public void onSizeChanged(int w, int h, int oldW, int oldH) {
// Set the movement bounds for the ball
xMax = w-1;
yMax = h-1;
}
private void update(){
ball_pos_y += ball_vel_y;
ball_pos_x += ball_vel_x;
if(pad_1.contains(ball_pos_x,ball_pos_y)){
ball_vel_y = -ball_vel_y;
}
else if(pad_2.contains(ball_pos_x,ball_pos_y)){
ball_vel_y = -ball_vel_y;
}
else if(ball_pos_x >= xMax){
ball_vel_x = -ball_vel_x;
}
else if(ball_pos_y >= yMax){
ball_init();
}
else if(ball_pos_x <= 0){
ball_vel_x = -ball_vel_x;
}
else if(ball_pos_y <= 0){
ball_init();
}
}
private void ball_init(){
if (randi == 1){
randi = -1;
}
else{
randi= 1;
}
ball_pos_x=0;
ball_pos_y=800;
ball_vel_x=5;
ball_vel_y=5 * randi;
}
}
I'm developing an android game, trying to store the character's coordinates. I'm using the onSaveInstanceState and onRestoreInstanceState methods. But when I tested the coordinates get's changed.
So it loads the correct value but after that it saves a bad number then it loads this bad value.
Class1
package hu.cig.vob;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import cug.hu.vob.R;
public class Level extends Activity {
private LevelView lv;
private LinearLayout linearL;
private Button left, right, jump, fire;
private mListener l = new mListener();
//Konstansok az activity allapotanak mentesehez
private String playerX = "PlayerX",playerY="PlayerY",playerH="PlayerH";
#SuppressWarnings("deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.level_layout);
final ImageView healthBar = (ImageView) findViewById(R.id.life_view);
Drawable d = getResources().getDrawable(R.drawable.main_screen_bg);
linearL = (LinearLayout) findViewById(R.id.linear_layout);
linearL.setBackgroundDrawable(d);
lv = new LevelView(getApplicationContext(), getIntent().getIntExtra(
MainActivity.LEVEL_EXTRA, 0), getWindowManager());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT);
lv.setLayoutParams(params);
linearL.addView(lv, 0);
left = (Button) findViewById(R.id.left);
right = (Button) findViewById(R.id.right);
jump = (Button) findViewById(R.id.jump);
fire = (Button) findViewById(R.id.fire);
left.setOnTouchListener(l);
right.setOnTouchListener(l);
jump.setOnTouchListener(l);
fire.setOnTouchListener(l);
lv.setOnClickListener(new OnClickListener() {
// Game over screen esetén kilép
#Override
public void onClick(View v) {
if (lv.getGameState()) {
finish();
}
}
});
// Életcsik frissitése az életnek megfelelően
new Thread() {
#Override
public void run() {
while (true) {
if (lv.getRobot().getHealth() <= 0) {
runOnUiThread(new Runnable() {
#Override
public void run() {
healthBar.setImageResource(R.drawable.battery5);
}
});
} else if (lv.getRobot().getHealth() <= 25) {
runOnUiThread(new Runnable() {
#Override
public void run() {
healthBar.setImageResource(R.drawable.battery4);
}
});
} else if (lv.getRobot().getHealth() <= 50) {
runOnUiThread(new Runnable() {
#Override
public void run() {
healthBar.setImageResource(R.drawable.battery3);
}
});
} else if (lv.getRobot().getHealth() <= 75) {
runOnUiThread(new Runnable() {
#Override
public void run() {
healthBar.setImageResource(R.drawable.battery2);
}
});
}else if(lv.getRobot().getHealth() >= 75){
runOnUiThread(new Runnable() {
#Override
public void run() {
healthBar.setImageResource(R.drawable.battery);
}
});
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
#Override
protected void onPause() {
overridePendingTransition(0, 0);
super.onPause();
}
// TODO:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
lv.getRobot().setX(savedInstanceState.getInt(playerX));
lv.getRobot().setY(savedInstanceState.getInt(playerY));
Log.d("LoL","Loaded y:"+savedInstanceState.getInt(playerY));
lv.getRobot().setHealth(savedInstanceState.getInt(playerH));
super.onRestoreInstanceState(savedInstanceState);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(playerX , lv.getRobot().getX());
outState.putInt(playerY, lv.getRobot().getY());
Log.d("LoL","Saved y:"+lv.getRobot().getY());
outState.putInt(playerH, lv.getRobot().getHealth() );
super.onSaveInstanceState(outState);
}
// iránygombok kezelése
private class mListener implements OnTouchListener {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
switch (v.getId()) {
case R.id.left:
lv.getRobot().moveLeft();
break;
case R.id.right:
lv.getRobot().moveRight();
break;
case R.id.jump:
lv.getRobot().jump();
break;
case R.id.fire:
lv.getRobot().shot();
break;
}
break;
case MotionEvent.ACTION_UP:
switch (v.getId()) {
case R.id.left:
lv.getRobot().stopMovingLeft();
break;
case R.id.right:
lv.getRobot().stopMovingRight();
break;
case R.id.fire:
lv.getRobot()._shot();
break;
}
break;
}
return true;
}
}
}
class2
package hu.cig.vob;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import cug.hu.vob.R;
public class Robot {
private Bitmap icon, currIcon, iconShot;
private int x, y, speedX = 0, speedY = 0, health = 100;
private final int MOVESPEED = 3, JumpSpeed = 15;
private boolean isMovingLeft = false, isMovingRight = false,
isFalling = true, isJumping = false, canJump = true;
private Rect bottom, horizontal;
private Context context;
private List<Bullet> bullets = Collections
.synchronizedList(new ArrayList<Bullet>());
public Robot(int in_x, int in_y, Bitmap i, float sc, Context c) {
context = c;
icon = i;
currIcon = icon;
iconShot = BitmapFactory.decodeResource(c.getResources(),
R.drawable.shot_right);
x = in_x * (icon.getWidth()/2);
y = in_y * (icon.getHeight()/2);
y = (int) (sc - y);
// for colliding
horizontal = new Rect((int) (x + icon.getWidth() * (31.5 / 100)),
y + 5, (int) (x + icon.getWidth() - icon.getWidth()
* (31.5 / 100)), y + icon.getHeight() - 5);
bottom = new Rect((int) (x + icon.getWidth() * (31.5 / 100) + 5), y
+ icon.getHeight() - 10, (int) (x + icon.getWidth()
- icon.getWidth() * (31.5 / 100) - 5), y + icon.getHeight());
}
public void update(List<Block> blocks) {
int oldX = x, oldY = y;
if (!(x + speedX < 0 || x + speedX > LevelView.intScreenWidth)) {
x += speedX;
}
updateRect();
for (int i = 0; i < blocks.size(); i++) {
if (horizontal.intersect(blocks.get(i).getRect())) {
stopMovingRight();
stopMovingLeft();
x = oldX;
updateRect();
}
}
if (bullets.size() > 0) {
Iterator<Bullet> it = bullets.iterator();
while (it.hasNext()) {
Bullet b = it.next();
if (b.getCx() > LevelView.screenWidth) {
it.remove();
} else {
b.update();
}
Iterator<Block> itt = blocks.iterator();
while (itt.hasNext()) {
Block bb = itt.next();
if (bb.getRect().intersect(b.getRect())) {
it.remove();
}
}
}
}
// #graviti"
if (isJumping) {
y -= speedY;
speedY--;
if (speedY == 0) {
isFalling = true;
isJumping = false;
}
updateRect();
}
if (isFalling) {
y += JumpSpeed - 5;
canJump = false;
updateRect();
}
for (Block b : blocks) {
if (bottom.intersect(b.getRect())) {
y = oldY;
updateRect();
canJump = true;
}
}
}
private void updateRect() {
horizontal = new Rect((int) (x + icon.getWidth() * (31.5 / 100)),
y + 10, (int) (x + icon.getWidth() - icon.getWidth()
* (31.5 / 100)), y + icon.getHeight() - 10);
bottom = new Rect((int) (x + icon.getWidth() * (31.5 / 100) + 5), y
+ icon.getHeight() - 10, (int) (x + icon.getWidth()
- icon.getWidth() * (31.5 / 100) - 5), y + icon.getHeight() - 2);
}
public void moveLeft() {
speedX = -MOVESPEED;
isMovingLeft = true;
}
public void moveRight() {
speedX = MOVESPEED;
isMovingRight = true;
}
public void stopMovingLeft() {
isMovingLeft = false;
stop();
}
public void stopMovingRight() {
isMovingRight = false;
stop();
}
public void stop() {
if (isMovingRight == false && isMovingLeft == false) {
speedX = 0;
}
if (isMovingRight == true && isMovingLeft == false) {
moveRight();
}
if (isMovingRight == false && isMovingLeft == true) {
moveLeft();
}
}
public Bitmap getIcon() {
return currIcon;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getSpeed() {
return speedX;
}
public boolean isMovingLeft() {
return isMovingLeft;
}
public boolean isMovingRight() {
return isMovingRight;
}
public Rect getBottomRect() {
return bottom;
}
public Rect getHRect() {
return horizontal;
}
public int getHealth() {
return health;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setHealth(int health) {
this.health = health;
}
public void shot() {
currIcon = iconShot;
Bullet b = new Bullet((float) (x + iconShot.getWidth()),
(float) (y + ((26.31 * iconShot.getHeight()) / 100)),
Helper.convertPx_Dpi(10, context), 2);
bullets.add(b);
}
public void _shot() {
currIcon = icon;
}
public List<Bullet> getBullets() {
return bullets;
}
public void jump() {
if (!isJumping && canJump) {
isJumping = true;
isFalling = false;
speedY = JumpSpeed;
}
}
public void increaseHealth(int x){
health += x;
if(health > 100){
health = 100;
}
}
public void degreeseHealth(int a) {
health -= a;
}
}
Your app can resume while obscured by the lock screen. If you want to tell the difference between "resume is hidden by a lock screen" and "resume is actually displayed" you can check your window focus.
See Making Android Games that Play Nice for more details.
A different way to get a similar "wait until actually displayed" is to wait for the first Activity.onUserInteraction after a resume before restarting your game.
I am trying to implement the spinning activity similar to the the one I have placed below in Android. I believe I should use the ProgressDialog. My issue arises from how to actually manipulate the ProgressDialog to appear like the activity indicator.
Any thoughts are welcome. A link to an example would even be better.
Thanks.
REEDIT:
myProgress.java
public class myProgress extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ProgressDialog d = (ProgressDialog)findViewById(R.id.progres);
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/progres"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
>
<ProgressBar
android:id="#+id/progressBar"
android:indeterminate="true"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
</RelativeLayout>
I wrote my own custom LoadingIndicatorView.
It consists of two files:
LoadingIndicatorBarView
LoadingIndicatorView
Pros:
Programmatically created, no PNG antics meaning scalable and crisp :D
Customizable bar colors and corner radius (if you understand my code)
Cons:
Not as performant as the iOS version (I'm just a beginner Android developer coming from iOS background, what do you expect?) :P
Disclaimer:
Don't blame me if your project blows up, I'm putting this as free public domain code.
You'll notice my coding style and structure resemble my iOS programming codes a lot. I do everything programmatically, no XML if I can get away with it.
How to use this Loading Indicator
After you've copied and pasted all three class source codes into their Java file, you want to use the LoadingIndicatorView class, you shouldn't need to touch the other class, unless you want to customise the colour or rounded corner of each bar.
Create an instance of LoadingIndicatorView like this in your Activity:
import com.companyName.myApplication.views.LoadingIndicatorView;
public class MyActivity extends AppCompatActivity
{
public mainLayout RelativeLayout;
...
public LoadingIndicatorView loadingIndicator;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initViews();
initLayouts();
addViews();
}
public void initViews()
{
mainLayout = new RelativeLayout(this);
mainLayout.setBackgroundColor(Color.BLACK);
...
// ---------------------------------------------------
// 40 here is the radius of the circle
// try and use multiples of 2, e.g. 40, 60, 80 etc
// ---------------------------------------------------
loadingIndicator = new LoadingIndicatorView(this, 40);
// hide until ready to start animating
loadingIndicator.setAlpha(0.0f);
}
public void initLayouts()
{
...
// Need API level 17 for this, set in your AndroidManifeset.xml
mainLayout.setId(View.generateViewId());
loadingIndicator.setId(View.generateViewId());
RelativeLayout.LayoutParams loadingIndicatorLayoutParams = new RelativeLayout.LayoutParams(
(int)(loadingIndicator.radius * 2.0f),
(int)(loadingIndicator.radius * 2.0f)
);
loadingIndicatorLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
loadingIndicator.setLayoutParams(loadingIndicatorLayoutParams);
}
public void addViews()
{
...
mainLayout.addView(loadingIndicator);
setContentView(mainLayout);
}
}
Once you're ready to show it, e.g. in a button click listener, then you call:
loadingIndicator.startAnimating();
When you want to stop and hide the indicator, call:
loadingIndicator.stopAnimating();
You end up with something like this:
LoadingIndicatorView.java
package com.companyName.myApplication.views;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.animation.RotateAnimation;
import android.widget.RelativeLayout;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Zhang on 11/02/16.
*/
public class LoadingIndicatorView extends RelativeLayout
{
private Context context;
private int numberOfBars;
public ArrayList<LoadingIndicatorBarView> arrBars;
public float radius;
private boolean isAnimating;
private int currentFrame;
private final Handler handler = new Handler();
private Runnable playFrameRunnable;
public LoadingIndicatorView(Context context, float radius)
{
super(context);
this.context = context;
this.radius = radius;
this.numberOfBars = 12;
initViews();
initLayouts();
addViews();
spreadBars();
}
public void initViews()
{
arrBars = new ArrayList<LoadingIndicatorBarView>();
for(int i = 0; i < numberOfBars; i++)
{
LoadingIndicatorBarView bar = new LoadingIndicatorBarView(context, radius / 10.0f);
arrBars.add(bar);
}
}
public void initLayouts()
{
for(int i = 0; i < numberOfBars; i++)
{
LoadingIndicatorBarView bar = arrBars.get(i);
bar.setId(View.generateViewId());
RelativeLayout.LayoutParams barLayoutParams = new RelativeLayout.LayoutParams(
(int)(radius / 5.0f),
(int)(radius / 2.0f)
);
barLayoutParams.addRule(ALIGN_PARENT_TOP);
barLayoutParams.addRule(CENTER_HORIZONTAL);
bar.setLayoutParams(barLayoutParams);
}
}
public void addViews()
{
for(int i = 0; i < numberOfBars; i++)
{
LoadingIndicatorBarView bar = arrBars.get(i);
addView(bar);
}
}
public void spreadBars()
{
int degrees = 0;
for(int i = 0; i < arrBars.size(); i++)
{
LoadingIndicatorBarView bar = arrBars.get(i);
rotateBar(bar, degrees);
degrees += 30;
}
}
private void rotateBar(LoadingIndicatorBarView bar, float degrees)
{
RotateAnimation animation = new RotateAnimation(0, degrees, radius / 10.0f, radius);
animation.setDuration(0);
animation.setFillAfter(true);
bar.setAnimation(animation);
animation.start();
}
public void startAnimating()
{
setAlpha(1.0f);
isAnimating = true;
playFrameRunnable = new Runnable()
{
#Override
public void run()
{
playFrame();
}
};
// recursive function until isAnimating is false
playFrame();
}
public void stopAnimating()
{
isAnimating = false;
setAlpha(0.0f);
invalidate();
playFrameRunnable = null;
}
private void playFrame()
{
if(isAnimating)
{
resetAllBarAlpha();
updateFrame();
handler.postDelayed(playFrameRunnable, 0);
}
}
private void updateFrame()
{
if (isAnimating)
{
showFrame(currentFrame);
currentFrame += 1;
if (currentFrame > 11)
{
currentFrame = 0;
}
}
}
private void resetAllBarAlpha()
{
LoadingIndicatorBarView bar = null;
for (int i = 0; i < arrBars.size(); i++)
{
bar = arrBars.get(i);
bar.setAlpha(0.5f);
}
}
private void showFrame(int frameNumber)
{
int[] indexes = getFrameIndexesForFrameNumber(frameNumber);
gradientColorBarSets(indexes);
}
private int[] getFrameIndexesForFrameNumber(int frameNumber)
{
if(frameNumber == 0)
{
return indexesFromNumbers(0, 11, 10, 9);
}
else if(frameNumber == 1)
{
return indexesFromNumbers(1, 0, 11, 10);
}
else if(frameNumber == 2)
{
return indexesFromNumbers(2, 1, 0, 11);
}
else if(frameNumber == 3)
{
return indexesFromNumbers(3, 2, 1, 0);
}
else if(frameNumber == 4)
{
return indexesFromNumbers(4, 3, 2, 1);
}
else if(frameNumber == 5)
{
return indexesFromNumbers(5, 4, 3, 2);
}
else if(frameNumber == 6)
{
return indexesFromNumbers(6, 5, 4, 3);
}
else if(frameNumber == 7)
{
return indexesFromNumbers(7, 6, 5, 4);
}
else if(frameNumber == 8)
{
return indexesFromNumbers(8, 7, 6, 5);
}
else if(frameNumber == 9)
{
return indexesFromNumbers(9, 8, 7, 6);
}
else if(frameNumber == 10)
{
return indexesFromNumbers(10, 9, 8, 7);
}
else
{
return indexesFromNumbers(11, 10, 9, 8);
}
}
private int[] indexesFromNumbers(int i1, int i2, int i3, int i4)
{
int[] indexes = {i1, i2, i3, i4};
return indexes;
}
private void gradientColorBarSets(int[] indexes)
{
float alpha = 1.0f;
LoadingIndicatorBarView barView = null;
for(int i = 0; i < indexes.length; i++)
{
int barIndex = indexes[i];
barView = arrBars.get(barIndex);
barView.setAlpha(alpha);
alpha -= 0.125f;
}
invalidate();
}
}
LoadingIndicatorBarView.java
package com.companyName.myApplication.views;
import android.content.Context;
import android.graphics.Color;
import android.widget.RelativeLayout;
import com.companyName.myApplication.helper_classes.ToolBox;
/**
* Created by Zhang on 11/02/16.
*/
public class LoadingIndicatorBarView extends RelativeLayout
{
private Context context;
private float cornerRadius;
public LoadingIndicatorBarView(Context context, float cornerRadius)
{
super(context);
this.context = context;
this.cornerRadius = cornerRadius;
initViews();
}
public void initViews()
{
setBackground(ToolBox.roundedCornerRectWithColor(
Color.argb(255, 255, 255, 255), cornerRadius));
setAlpha(0.5f);
}
public void resetColor()
{
setBackground(ToolBox.roundedCornerRectWithColor(
Color.argb(255, 255, 255, 255), cornerRadius));
setAlpha(0.5f);
}
}
Toolbox.java
package com.companyName.myApplication.helper_classes;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
/**
* Created by Zhang on 3/02/16.
*/
public class ToolBox
{
private static ToolBox instance;
public Context context;
private ToolBox()
{
}
public synchronized static ToolBox getInstance()
{
if(instance == null)
{
instance = new ToolBox();
}
return instance;
}
public static ShapeDrawable roundedCornerRectOutlineWithColor(int color, float cornerRadius,
float strokeWidth)
{
float[] radii = new float[] {
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius
};
RoundRectShape roundedCornerShape = new RoundRectShape(radii, null, null);
ShapeDrawable shape = new ShapeDrawable();
shape.getPaint().setColor(color);
shape.setShape(roundedCornerShape);
shape.getPaint().setStrokeWidth(strokeWidth);
shape.getPaint().setStyle(Paint.Style.STROKE);
return shape;
}
public static ShapeDrawable roundedCornerRectWithColor(int color, float cornerRadius)
{
float[] radii = new float[] {
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius,
cornerRadius, cornerRadius
};
RoundRectShape roundedCornerShape = new RoundRectShape(radii, null, null);
ShapeDrawable shape = new ShapeDrawable();
shape.getPaint().setColor(color);
shape.setShape(roundedCornerShape);
return shape;
}
public static ShapeDrawable roundedCornerRectWithColor(int color, float topLeftRadius, float
topRightRadius, float bottomRightRadius, float bottomLeftRadius)
{
float[] radii = new float[] {
topLeftRadius, topLeftRadius,
topRightRadius, topRightRadius,
bottomRightRadius, bottomRightRadius,
bottomLeftRadius, bottomLeftRadius
};
RoundRectShape roundedCornerShape = new RoundRectShape(radii, null, null);
ShapeDrawable shape = new ShapeDrawable();
shape.getPaint().setColor(color);
shape.setShape(roundedCornerShape);
return shape;
}
public static int getScreenWidth()
{
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
public static int getScreenHeight()
{
return Resources.getSystem().getDisplayMetrics().heightPixels;
}
public static int getScreenOrientation(Context context)
{
return context.getResources().getConfiguration().orientation;
}
public static boolean isLandscapeOrientation(Context context)
{
return getScreenOrientation(context) == Configuration.ORIENTATION_LANDSCAPE;
}
}
This Toolbox class is my convenience helper class to create rounded corner shapes etc in all my projects.
Hope that helps :D
this is how i achieve it
here is the code
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_LOADING:
final Dialog dialog = new Dialog(this, android.R.style.Theme_Translucent);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.loading);
dialog.setCancelable(true);
dialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
//onBackPressed();
}
});
return dialog;
default:
return null;
}
};
here is the loading.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/progres"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
>
<ProgressBar
android:indeterminate="true"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
</RelativeLayout>
call the dialog with
showDialog(DIALOG_LOADING);
hide it using
dismissDialog(DIALOG_LOADING);
UPDATE
if you want and custom indicator you can do the following in the layout.xml.
replace the ProgressBar with an ImageView
set the background of the ImageView to a AnimationDrawable
you can start the animation in onPrepareDialog
You are looking for progressDialog i believe. This link can you set you start with it.
http://www.helloandroid.com/tutorials/using-threads-and-progressdialog
pd = ProgressDialog.show(this, "Working..", "Calculating Pi", true,
false);
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
pd.dismiss();
tv.setText(pi_string);
}
};
Just look at this library. IOSDialog/Spinner library
It is very easy to use and solves your problem. With it, you can easily create and use spinner like in IOS.
The example of code:
final IOSDialog dialog1 = new IOSDialog.Builder(IOSDialogActivity.this)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
dialog0.show();
}
})
.setDimAmount(3)
.setSpinnerColorRes(R.color.colorGreen)
.setMessageColorRes(R.color.colorAccent)
.setTitle(R.string.standard_title)
.setTitleColorRes(R.color.colorPrimary)
.setMessageContent("My message")
.setCancelable(true)
.setMessageContentGravity(Gravity.END)
.build();
Result
final IOSDialog dialog0 = new IOSDialog.Builder(IOSDialogActivity.this)
.setTitle("Default IOS bar")
.setTitleColorRes(R.color.gray)
.build();
Result: stadard IOS Dialog