I was building a surfaceView class to display an image from the camera. It took me a while to get it working but I added some toasts, dialogs and some boolean variables to debug it in the AnnotateSurface class and the Activity class. I was using one of these in an if statement to run the thread to draw the image to the surface if it was true (this was what was stopping it from working).
if(bitmapState){
drawSomething(canvas);
}
I have got it working but only after I figured out that the boolean value, bitmapState , was being reset to false. it is initalised to false but I change it to true after in the getBitmap method. This is the only method that edits that boolean variable.
The toast in the setBitmap method displays "true", but when I call the getStuff method from the activity class it displays it as false and the if statement in the run method will also not work when I use the following:
if (surfaceState == bitmapState == true) {
drawSomething(canvas);
}
This is really confusing as all other boolean values are behaving correctly. If anyone has any insight I would love to hear it.
public class AnnotateSurface extends SurfaceView implements Callback, Runnable {
// create a holder to manage the surface of the view
SurfaceHolder ourHolder;
// Create a thread
Thread ourThread = null;
// Create a boolean to determine when to stop the animation
boolean isRunning = false;
boolean surfaceState = false;
boolean bitmapState = false;
public static Bitmap bmpIm = null;
Context c;
Canvas canvas;
int ws, hs;
public AnnotateSurface(Context context) {...
}
public AnnotateSurface(Context context, AttributeSet attrs) {...
}
public AnnotateSurface(Context context, AttributeSet attrs, int defStyle) {...
}
// create a method to pause the thread
public void pause() {...
}
public void resume() {...
}
// create a method to resume the thread
public void setBitmap(Bitmap b) {
bmpIm = b;
// bmpIm = AnnotateImage.bmpAnnotate;
// isRunning = true;
bitmapState = true;
Toast toast = new Toast(c);
Toast.makeText(c, Boolean.toString(bitmapState), Toast.LENGTH_LONG)
.show();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {...
}
#Override
public void surfaceCreated(SurfaceHolder holder) {...
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
surfaceState = false;
}
// #Override
public void drawSomething(Canvas canvas) {...
}
public String getstuff() {
String Data = "not drawing" + Boolean.toString(this.bitmapState)
+ Boolean.toString(surfaceState) + Boolean.toString(isRunning)
+ Integer.toString(ws) + Integer.toString(hs);
return Data;
}
public Bitmap getstuffImage() {...
}
#Override
public void run() {...
}
}
I have had problems with booleans and "if" statements in the past, the best way to deal with it is to be more explicit with them, for example:
if (surfaceState == bitmapState && bitmapState == true) {
drawSomething(canvas);
}
Cheers!
Mike
Related
When I update my Code to androidx I get this error
"error: method setCurrentItemInternal in class ViewPager cannot be applied to given types;
required: int,boolean,boolean,int
found: int,boolean,boolean
reason: actual and formal argument lists differ in length"
Code:
public class NonRestoringViewPager extends ViewPager implements setCurrentItemInternal {
private boolean isRestoring = false;
private final boolean useDefaultImplementation;
public NonRestoringViewPager(Context context) {
super(context);
useDefaultImplementation =
!QuranUtils.isDualPagesInLandscape(context, QuranScreenInfo.getOrMakeInstance(context));
}
public NonRestoringViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
useDefaultImplementation =
!QuranUtils.isDualPagesInLandscape(context, QuranScreenInfo.getOrMakeInstance(context));
}
#Override
public void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
if (useDefaultImplementation || !isRestoring) {
super.setCurrentItemInternal(item, smoothScroll, always);
}
}
#Override
public void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
if (useDefaultImplementation || !isRestoring) {
super.setCurrentItemInternal(item, smoothScroll, always, velocity);
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
isRestoring = true;
super.onRestoreInstanceState(state);
isRestoring = false;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
try {
return super.onTouchEvent(ev);
} catch (IllegalArgumentException e) {
return false;
}
}
}
Function signature might have changed in androidx dependencies. So check the function signature and pass required params values.
I am creating a game and would like to read the player input for the character in the characters class. I don't know if this is good practice for game development however it seems like the logical thing to do.
I have the following code within my Player class (relevant code included) however my suspicion is that it isn't working as it isn't the primary view class or something? All help is appreciated.
Player
public class Player implements View.OnTouchListener{
private static Player instance = null;
int x, y;
boolean moveRight = false;
Bitmap sprite;
public Player (Context context){
sprite = BitmapFactory.decodeResource(context.getResources(), R.drawable.test_sprite);
x = 300;
y = 0;
}
public void Update(){
if (moveRight){
x += 3;
} else {
x -= 3;
}
}
public static Player getInstance(Context context) {
if (instance == null){
instance = new Player(context);
}
return instance;
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
float x = motionEvent.getX(), y = motionEvent.getY();
switch (motionEvent.getAction() & motionEvent.getActionMasked()){
case MotionEvent.ACTION_DOWN:
moveRight = true;
break;
case MotionEvent.ACTION_UP:
moveRight = false;
break;
}
return true;
}
}
GameView
public class GameView extends SurfaceView implements Runnable {
private boolean running = true;
SurfaceHolder surfaceHolder = getHolder();
SimpleTurret simpleTurret;
Player player;
private Thread thread = new Thread(this);
public GameView (Context context){
super(context);
simpleTurret = SimpleTurret.getInstance(context);
player = Player.getInstance(context);
thread.start();
}
#Override
public void run() {
while (running){
DrawCanvas();
Control();
}
}
public void DrawCanvas(){
Canvas canvas = surfaceHolder.lockCanvas();
if (surfaceHolder.getSurface().isValid()){
canvas.drawColor(Color.RED);
canvas.drawBitmap(simpleTurret.getSprite(), simpleTurret.getX(), simpleTurret.getY(), null);
canvas.drawBitmap(player.getSprite(), player.getX(), player.getY(), null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void Control(){
try{
thread.sleep(17);
} catch(InterruptedException e){
e.printStackTrace();
}
}
}
public class GameView extends SurfaceView implements Runnable, OnTouchListener {
public GameView(Context context) {
super(context);
this.setOnTouchListener(this);
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return false;
} ...
this.setOnTouchListener(this); line puts the OnTouchListener on the GameView class which extends the SurfaceView. So now anytime there is a touch on your SurfaceView the onTouch method would be called
You can use ImageView instead a Bitmap and set your implementation of View.OnTouchListener with the method setOnTouchListener:
ImageView sprite;
public Player (Context context){
sprite = new ImageView(context);
sprite.setBackgroundResource(R.drawable.monkey);
sprite.setOnTouchListener(this);
x = 300;
y = 0;
}
I have been trying for a while to implement a Game Thread to utilise a loop to implement logic. I posted a question here not long ago, I hope no one minds the follow up.
I have managed to scrape together this code from my research:
public class GameView extends SurfaceView implements SurfaceHolder.Callback
{
class GameThread extends Thread
{
//states
public static final int STATE_LOSE = 1;
public static final int STATE_PAUSE = 2;
public static final int STATE_READY = 3;
public static final int STATE_RUNNING = 4;
private Paint m_paint;
//canvas dimensions
private int m_canvasWidth;
private int m_canvasHeight;
private long m_lastTime;
private boolean m_run = false;
private int m_mode;
public ImageView ship;
RelativeLayout.LayoutParams shipParams;
// Handle to the surface manager
private SurfaceHolder m_surfaceHolder;
public GameThread(SurfaceHolder surfaceHolder, Context context, Handler handler)
{
m_surfaceHolder = surfaceHolder;
}
//Initialise the game
public void doStart()
{
synchronized (m_surfaceHolder)
{
resetGame();
m_lastTime = System.currentTimeMillis() + 100;
setState(STATE_RUNNING);
ship = (ImageView) findViewById(R.id.imageView1);
shipParams = (RelativeLayout.LayoutParams)ship.getLayoutParams();
}
}
public void pause()
{
synchronized (m_surfaceHolder)
{
if (m_mode == STATE_RUNNING)
setState(STATE_PAUSE);
}
}
#Override
public void run()
{
while (m_run)
{
Canvas c = null;
try
{
c = m_surfaceHolder.lockCanvas(null);
synchronized (m_surfaceHolder)
{
if (m_mode == STATE_RUNNING)
{
updateGame();
}
doDraw(c);
}
}
catch(Exception e){}
finally
{
if (c != null)
{
m_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
public void setRunning(boolean b)
{
m_run = b;
}
public void setState(int mode)
{
synchronized (m_surfaceHolder)
{
setState(mode, null);
}
}
public void setState(int mode, CharSequence message)
{
synchronized (m_surfaceHolder)
{
m_mode = mode;
}
}
public void setPlayers(boolean onePlayer)
{
}
public void setSurfaceSize(int width, int height)
{
synchronized (m_surfaceHolder)
{
m_canvasWidth = width;
m_canvasHeight = height;
}
}
public void unpause()
{
synchronized (m_surfaceHolder)
{
m_lastTime = System.currentTimeMillis() + 100;
}
setState(STATE_RUNNING);
}
private void doDraw(Canvas canvas)
{
canvas.drawARGB(255, 0, 0, 0);
}
private void updateGame()
{
long now = System.currentTimeMillis();
if (m_lastTime > now)
return;
double elapsed = (now - m_lastTime) / 1000.0;
m_lastTime = now;
System.out.print("HELLO WORLD");
shipParams.topMargin++;
ship.setLayoutParams(shipParams);
}
private boolean collided(Rect rectangle)
{
return false;
}
public boolean foundWinner()
{
return false;
}
public void resetGame()
{
}
public void handleInput(MotionEvent event)
{
}
}
private Context m_context;
private GameThread m_thread;
private Handler m_handler;
public GameView(Context context, AttributeSet attrs)
{
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
m_handler = new Handler() {
#Override
public void handleMessage(Message m) {
Bundle b = m.getData();
MotionEvent e = b.getParcelable("event");
m_thread.handleInput(e);
}
};
m_thread = new GameThread(holder, context, m_handler);
setFocusable(true);
};
public GameThread getThread()
{
return m_thread;
}
#Override
public void onWindowFocusChanged(boolean hasWindowFocus)
{
if (!hasWindowFocus)
m_thread.pause();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
m_thread.setSurfaceSize(width, height);
}
public void surfaceCreated(SurfaceHolder holder)
{
if(m_thread.getState() == State.TERMINATED)
{
m_thread = new GameThread(getHolder(), m_context, m_handler);
m_thread.setRunning(true);
m_thread.start();
m_thread.doStart();
}
else
{
m_thread.setRunning(true);
m_thread.start();
}
}
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
m_thread.setRunning(false);
while (retry)
{
try
{
m_thread.join();
retry = false;
}
catch (InterruptedException e)
{
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
return true;
}
}
I am fairly certain that my issue lies here and it is merely a logical one. Everything does seem fine to me, however and I am in need of assistance.
I have attempted to draw an image at line 47 and defined a movement to take place in the update method at line 153. I also have placed a print line for extra debug, but the line doesn't show.
I am stumped.
Any help would be great, thanks.
Here are my other codes, if neccessary:
MainActivity.java
GameSetup.java
game_setup.xml
edit: I should note that I'm not getting any kind of errors within the code, it merely doesn't respond
You are initializing m_run as false,then in the while cycle in the run() method you must have set to true. Change it to true and the thread will work normally.
set m_run to true in your doStart() procedure
i have a class Handler that is used to draw an arraylist of towers in the gameview class
public class Handler {
public static int Scale=50;
public static ArrayList<Tower> towers=new ArrayList<Tower>();
public static void draw(Canvas c){
for(Tower t:towers){
t.draw(c);
}
}
public static void update(){
for(Tower t:towers){
t.update();
}
}
}
GameView class that extends surfaceview and implments surfaceholder.callback
public class GameView extends SurfaceView implements
SurfaceHolder.Callback {
public int ScreenWidth,ScreenHeight;
TouchInput touchinput;
TableLayout table;
///////////////////////////////////////
//these variables used for adding new tower
boolean addingnewtower=false;
Bitmap Tempbitmap;
public float x,y;
//////////////////////////////////////
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
table=(TableLayout) findViewById(R.id.tablelayout1);
Sprite sprite=new Sprite(context.getResources());
touchinput=new TouchInput(this);
Tempbitmap=sprite.bitmap;
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
setFocusable(true);//
}
private static final String TAG = GameView.class.getSimpleName();
private MainThread thread;
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and
// we can safely start the game loop
//if(!thread.isAlive()){
setOnTouchListener(touchinput);
thread = new MainThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
public void Draw(Canvas c) {
Paint paint=new Paint();//paint used to set color,text font,text size...
paint.setColor(Color.RED);
paint.setTextSize(paint.getTextSize()*3);
c.drawRect(100,100,200,200,paint);
c.drawText("touch is" + x + " "+ y,200,200, paint);
Handler.draw(c);
if(addingnewtower){
c.drawBitmap(Tempbitmap,x,y, paint);
}
}
public void update(){
Handler.update();
}
class mainthread which contains the game loop in the method run()
public class MainThread extends Thread {
private static final String TAG = MainThread.class.getSimpleName();
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private GameView gameview;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder,GameView gameview) {
super();
this.surfaceHolder = surfaceHolder;
this.gameview = gameview;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing on the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
if(canvas!=null){
canvas.drawColor(Color.BLACK);
gameview.Draw(canvas);}
gameview.update();
}
} finally {
// in case of an exception the surface is not left in
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
}
and finally MainActivity that implements ontouchlistesnser
public class MainActivity extends Activity implements OnTouchListener {
private ImageView tower1,tower2,tower3,tower4,tower5,tower6;
private TableLayout layout;
private GameView gameview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//hide the action bar
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//make it full screen
setContentView(R.layout.activity_main);
gameview=(GameView) findViewById(R.id.gameview1);
tower1=(ImageView) findViewById(R.id.tower1);
layout=(TableLayout) findViewById(R.id.tablelayout1);
tower1.setOnTouchListener(this);
gameview.setOnTouchListener(this);
}
#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;
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
gameview.onpause();
}
#Override
protected void onResume() {
super.onResume();
gameview.onresume();
}
//this is called whenever an imageview is touched for adding a new tower on the map
#Override
public boolean onTouch(View v, MotionEvent m) {
int location[]=new int[2];
tower1.getLocationOnScreen(location);//get the location of imageview with respect to screen since the touch area will be with respect to the view v width and height
switch(m.getAction()){
case MotionEvent.ACTION_DOWN:
if(v.getId()==tower1.getId()){//if tower1 is touched
layout.setVisibility(View.INVISIBLE);//make them invisible
}
break;
case MotionEvent.ACTION_MOVE:
gameview.handle_imageView_TouchEvent(location,m.getX(),m.getY(),false);//false meaning in touch move
break;
case MotionEvent.ACTION_UP:
gameview.handle_imageView_TouchEvent(location,m.getX(),m.getY(),true);//this will be called only once for adding the new tower
layout.setVisibility(View.VISIBLE);
gameview.addingnewtower=false;
break;
}
return true;
}
}
when this code do is that whenever i touch an imageview and drag it across the sreen,a new object machinegunner which extend tower is added to the arraylist tower in the handler class
but after few times of adding new machinegunner to arraylist i am getting a error in the draw method of handler
01-20 18:16:13.261: E/AndroidRuntime(1339): FATAL EXCEPTION: Thread-114
01-20 18:16:13.261: E/AndroidRuntime(1339): java.util.ConcurrentModificationException
01-20 18:16:13.261: E/AndroidRuntime(1339): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)
01-20 18:16:13.261: E/AndroidRuntime(1339): at com.example.test1.Handler.draw(Handler.java:15)
01-20 18:16:13.261: E/AndroidRuntime(1339): at com.example.test1.GameView.Draw(GameView.java:97)
01-20 18:16:13.261: E/AndroidRuntime(1339): at com.example.test1.MainThread.run(MainThread.java:44)
plz any help?
The problem appears to be that you are adding to the ArrayList whilst in the middle of iterating over it in the draw() method. You can't modify a collection whilst iterating over it (other than removing items using the Iterator).
You don't show what the method 'gameview.handle_imageView_TouchEvent()' does, but I think you need to make it add to the ArrayList using the same Handler that does the draw() and update(). The two processes should then queue up on the same thread and not collide.
Why mImage is not displayed?
public class Manager extends Thread{
private SurfaceHolder mSurfaceHolder;
private boolean mRunning;
public Drawable mImage;
public Manager(SurfaceHolder surfaceHolder, Context context){
mSurfaceHolder = surfaceHolder;
mRunning = false;
Resources res = context.getResources();
mImage = res.getDrawable(R.drawable.nhero2);
public void setRunning(boolean running)
{
mRunning = running;
}
public void run()
{
while (mRunning)
{
Canvas canvas = null;
try
{
// подготовка Canvas-а
canvas = mSurfaceHolder.lockCanvas();
synchronized (mSurfaceHolder)
{
// собственно рисование
//doDraw(canvas);
mImage.draw(canvas);
}
}
catch (Exception e) { }
finally
{
if (canvas != null)
{
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
P.S. sorry 4 my english
P.P.S.
i trying to create my own scrool shooter hah
full code here(sry for bad comments)
class Manager http://pastebin.com/Sjd57uqT
class View and class scrolBckgr http://pastebin.com/A5u5UJea
You need to call mImage.setBounds(...); otherwise the canvas doesn't know where to draw the Drawable.
I think the main problem you have is that as soon as you start the thread with .start(); the boolean mRunning is false and the thread will finish immediately. When you try to call .setRunning(true); after the thread has started it won't have any effect because the thread already finished.