I am beginner to multithreading in Java and in my Android application, i have that SurfaceView on wich am drawing a circle randomlly but i want to be able to pause that drawing by pressing the screen (ACTION_DOWN) and resume it the next time i press it again :
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GameView extends SurfaceView /**/implements SurfaceHolder.Callback {
private float x = 100;
private float y = 100;
private int radius = 20;
private Paint paint;
private SurfaceHolder mSurfaceHolder;
private DrawingThread mThread;
private Context myContext;
public GameView(Context context) {
super(context);
this.myContext = context;
setWillNotDraw(false);
paint = new Paint();
/**/
paint.setAntiAlias(true);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setTextAlign(Paint.Align.LEFT);
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
//paint.setTextSize(15);
}
public void onDraw(Canvas canvas){
canvas.drawCircle(x, y, radius, paint);
}
/**/
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mThread = new DrawingThread(mSurfaceHolder, myContext);
mThread.mRun = true;
mThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
public synchronized boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
int X = (int)event.getX();
int Y = (int)event.getY();
switch (eventaction ) {
case MotionEvent.ACTION_DOWN:
this.mThread.canPause = !this.mThread.canPause;
synchronized(this.mSurfaceHolder){
if(this.mThread.canPause){
this.mSurfaceHolder.notify();
}
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
public final class DrawingThread extends Thread {
public boolean canPause = false;
boolean mRun;
Canvas mcanvas;
SurfaceHolder surfaceHolder;
Context context;
public DrawingThread(SurfaceHolder sholder, Context ctx)
{
surfaceHolder = sholder;
context = ctx;
mRun = false;
}
void setRunning(boolean bRun)
{
mRun = bRun;
}
boolean keepDrawing = true;
#Override
public void run() {
while (keepDrawing) {
Canvas canvas = null;
try {
canvas = mSurfaceHolder.lockCanvas();
synchronized (mSurfaceHolder) {
while(canPause){
try {
mSurfaceHolder.wait();
} catch(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
waitThreaed();
draw(canvas);
}
}
catch(Exception e){
}
finally {
if (canvas != null)
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
public void waitThreaed() {
try {
x = (float) (getWidth()*Math.random());
y = (float) (getHeight()*Math.random());
this.sleep(1000);
postInvalidate();
} catch (InterruptedException e) {
}
}
}
}
In fact with that code the drawing can be paused but can not be resumed
In order to call wait() you must have synchronized on the object you are waiting on.
See this question for a discussion of that: Why must wait() always be in synchronized block
I am using a SurfaceView to draw paths on screen. Everything works fine if I don't set any background to the drawPanel; if I set a background to the SurfaceView the drawing does not work. I need some colorful screen for drawing.
Thanks in advance.
Below is my code:
package com.example.draw;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
public class MainActivity extends Activity implements OnTouchListener
{
DrawPanel dp;
private ArrayList<Path> pointsToDraw = new ArrayList<Path>();
private Paint mPaint;
Path path;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
dp = new DrawPanel(this);
dp.setBackgroundColor(R.drawable.imge);
dp.setOnTouchListener(this);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(30);
FrameLayout fl = new FrameLayout(this);
fl.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
//fl.setBackgroundResource(R.drawable.imge);
fl.addView(dp);
setContentView(fl);
}
#Override
protected void onPause()
{
// TODO Auto-generated method stub
super.onPause();
dp.pause();
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
dp.resume();
}
public boolean onTouch(View v, MotionEvent me)
{
// TODO Auto-generated method stub
synchronized(pointsToDraw)
{
if(me.getAction() == MotionEvent.ACTION_DOWN)
{
path = new Path();
path.moveTo(me.getX(), me.getY());
//path.lineTo(me.getX(), me.getY());
pointsToDraw.add(path);
}
else if(me.getAction() == MotionEvent.ACTION_MOVE)
{
path.lineTo(me.getX(), me.getY());
}else if(me.getAction() == MotionEvent.ACTION_UP)
{
//path.lineTo(me.getX(), me.getY());
}
}
return true;
}
public class DrawPanel extends SurfaceView implements Runnable
{
Thread t = null;
SurfaceHolder holder;
boolean isItOk = false ;
public DrawPanel(Context context)
{
super(context);
// TODO Auto-generated constructor stub
holder = getHolder();
}
public void run()
{
// TODO Auto-generated method stub
while( isItOk == true)
{
if(!holder.getSurface().isValid())
{
continue;
}
Canvas c = holder.lockCanvas();
c.drawARGB(255, 0, 0, 0);
onDraw(c);
holder.unlockCanvasAndPost(c);
}
}
#Override
protected void onDraw(Canvas canvas)
{
// TODO Auto-generated method stub
super.onDraw(canvas);
synchronized(pointsToDraw)
{
for (Path path : pointsToDraw)
{
canvas.drawPath(path, mPaint);
}
}
}
public void pause()
{
isItOk = false;
while(true)
{
try
{
t.join();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
break;
}
t = null;
}
public void resume()
{
isItOk = true;
t = new Thread(this);
t.start();
}
}
}
I have created a class from SurfaceView.I wanted it that when i touch on the screen then the screen will be refreshed by calling invalidate() function. But in my implementation invalidate () is not called when i touched on the screen.
Here is my code.
public class GameBoard extends SurfaceView implements Callback {
private SurfaceHolder holder;
int clicked =0;
public GameBoard(Context context)
{
super(context);
holder = getHolder();
holder.addCallback(this);
setFocusable(true);
requestFocus();
}
#Override
public void surfaceCreated(SurfaceHolder holder)
{
Canvas c = holder.lockCanvas(null);
onDraw(c);
holder.unlockCanvasAndPost(c);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
// TODO Auto-generated method stub
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getAction();
if (action==MotionEvent.ACTION_UP)
{
clicked++;
// Canvas c = holder.lockCanvas(null);
// onDraw(c);
// holder.unlockCanvasAndPost(c);
invalidate();
}
return true;
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(0xFF00ff00);
Paint p = new Paint();
p.setTextSize(20);
p.setColor( Color.RED );
p.setAntiAlias(true);
canvas.drawText(""+clicked, 100, 500, p);
}
}
I'm trying to create moving text.I have crated surface view and thread for looping it.but it not show me a moving motion but draw that text each after like * ** * * in infinite.
But what I need was move that this point to that** -> ** got it?
this is my code
package com.CurvePackage.Curve;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
public class Origin extends SurfaceView implements SurfaceHolder.Callback {
Context context1;
private MainThread thread;
private int x=0;
private int y=0;
public Origin(Context context) {
super(context);
// TODO Auto-generated constructor stub
setWillNotDraw(false);
context1 = context;
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Paint paint = new Paint();
paint.setTextSize(23);
paint.setFakeBoldText(true);
paint.setColor(Color.YELLOW);
// int score=(10-sprites.size()*100);
x=x+20;
y=y+20;
canvas.drawText("ewqewqe", x, y, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return super.onTouchEvent(event);
// Toast.makeText(getba, "Replay clicked!", Toast.LENGTH_SHORT).show();
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
thread.setRunning(false);
// TODO Auto-generated method stub
// boolean retry = true;
// while (retry) {
// try {
// thread.join();
// retry = false;
// } catch (InterruptedException e) {
// // try again shutting down the thread
// }
// }
}
}
Main Thread
package com.CurvePackage.Curve;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
// flag to hold game state
private boolean running;
private SurfaceHolder surfaceHolder;
private Origin origin;
static final long FPS = 15;
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = origin.getHolder().lockCanvas();
synchronized (origin.getHolder()) {
origin.onDraw(c);
}
} finally {
if (c != null) {
origin.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
public MainThread(SurfaceHolder surfaceHolder, Origin origin) {
super();
this.surfaceHolder = surfaceHolder;
this.origin = origin;
}
}
You need to paint the entire background in your onDraw method, before you paint your text.
Paint p2 = new Paint();
p2.setStyle(Style.FILL);
canvas.drawRect(0, 0, screenWidth, screenHeight, p2);
//draw text here
This will completely paint over the text from the previous time the canvas was drawn, and will remove this dragging effect.
I am trying to rotate 2 circles on the screen. On the press of a button, one circles rotates clockwise, and the other circles rotates counterclockwise. Both will rotate by 90 degrees and then stop until the next button click.
Its working but it looks very bad. Instead of rotating at the same time, 1st one circle rotates, and then the 2nd.
I read about animation but all the examples I found showed how to rotate the entire canvas. Possibly I am not looking in the right places and there is a way to assign animation to an object somehow.
I've added my code below. I apologies for it not being a true SSCCE but I got errors when my custom SurfaceView was an internal class under the main activity.
Any advice or lead on how to do this properly is very appreciated.
Activity
package sscce.android.rotation;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
public class SscceRotationActivity extends Activity implements OnClickListener {
private MySurfaceView mySurfaceView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.btnClockwise).setOnClickListener(this);
findViewById(R.id.btnCounterClockwise).setOnClickListener(this);
mySurfaceView = (MySurfaceView) (findViewById(R.id.surfaceView1));
}
public void onClick(View arg0) {
switch (arg0.getId()) {
case R.id.btnClockwise:
mySurfaceView.rotate(true);
break;
case R.id.btnCounterClockwise:
mySurfaceView.rotate(false);
break;
}
}
}
Custom SurfaceView
package sscce.android.rotation;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MySurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private Circle circle1;
private Circle circle2;
private DrawThread drawThread;
public MySurfaceView(Context context) {
super(context);
initialize();
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
getHolder().addCallback(this);
drawThread = new DrawThread(getHolder(), this);
setFocusable(true);
}
public void surfaceCreated(SurfaceHolder holder) {
circle1 = new Circle(getWidth() / 2, getHeight() / 2, 50);
circle2 = new Circle(getWidth() / 2, getHeight() / 2, 80);
drawThread.setRunning(true);
drawThread.start();
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
drawThread.setRunning(false);
while (retry) {
try {
drawThread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
public void onDraw(Canvas canvas) {
circle2.onDraw(canvas);
circle1.onDraw(canvas);
}
public void rotate(boolean clockWise) {
Rotator rotator1 = new Rotator(circle1, clockWise);
Rotator rotator2 = new Rotator(circle2, !clockWise);
rotator1.run();
rotator2.run();
}
private class Circle {
private RectF rectF;
private int rotationAngle = 0;
MyPaint bluePaint = new MyPaint(1, Paint.Cap.SQUARE, Paint.Style.FILL,
Color.BLUE);
MyPaint redPaint = new MyPaint(1, Paint.Cap.SQUARE, Paint.Style.FILL,
Color.RED);
MyPaint yellowPaint = new MyPaint(1, Paint.Cap.SQUARE,
Paint.Style.FILL, Color.YELLOW);
MyPaint greenPaint = new MyPaint(1, Paint.Cap.SQUARE, Paint.Style.FILL,
Color.GREEN);
MyPaint borderPaint = new MyPaint(3, Paint.Cap.SQUARE,
Paint.Style.STROKE, Color.WHITE);
public Circle(int centerX, int centerY, int radius) {
rectF = new RectF(new Rect(centerX - radius, centerY - radius,
centerX + radius, centerY + radius));
}
public void rotateClockwise() {
for (int i = 0; i < 90; i++) {
rotationAngle++;
if (rotationAngle == 360) {
rotationAngle = 0;
return;
}
try {
Thread.sleep(20, 0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void rotateCounterClockwise() {
for (int i = 0; i < 90; i++) {
rotationAngle--;
if (rotationAngle == 0) {
rotationAngle = 360;
return;
}
try {
Thread.sleep(20, 0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void onDraw(Canvas canvas) {
canvas.drawArc(rectF, (0 + rotationAngle) % 360, 90, true,
bluePaint);
canvas.drawArc(rectF, (90 + rotationAngle) % 360, 90, true,
redPaint);
canvas.drawArc(rectF, (180 + rotationAngle) % 360, 90, true,
yellowPaint);
canvas.drawArc(rectF, (270 + rotationAngle) % 360, 90, true,
greenPaint);
canvas.drawArc(rectF, 0, 360, true, borderPaint);
}
private class MyPaint extends Paint {
public MyPaint(int strokeWidth, Paint.Cap cap, Paint.Style style,
int color) {
setStrokeWidth(strokeWidth);
setAntiAlias(true);
setStrokeCap(cap);
setStyle(style);
setColor(color);
}
}
}
private class Rotator extends Thread {
private Circle circle;
private boolean clockwise;
public Rotator(Circle circle, boolean clockwise) {
this.circle = circle;
this.clockwise = clockwise;
}
#Override
public void run() {
if (clockwise) {
circle.rotateClockwise();
} else {
circle.rotateCounterClockwise();
}
}
}
private class DrawThread extends Thread {
private SurfaceHolder surfaceHolder;
private MySurfaceView surfaceView;
private boolean run = false;
public DrawThread(SurfaceHolder surfaceHolder, MySurfaceView surfaceView) {
this.surfaceHolder = surfaceHolder;
this.surfaceView = surfaceView;
run = false;
}
public void setRunning(boolean run) {
Log.d("setRunning#DrawThread", "Run status is " + run);
this.run = run;
}
#Override
public void run() {
Canvas canvas = null;
while (run) {
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
surfaceView.onDraw(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
}
Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<sscce.android.rotation.MySurfaceView
android:id="#+id/surfaceView1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center" >
<Button
android:id="#+id/btnClockwise"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clockwise" />
<Button
android:id="#+id/btnCounterClockwise"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Counter Clockwise" />
</LinearLayout>
</LinearLayout>
I would like to advise a different approach to rotation using matrices.The code would look like
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(cwRotation);
//draw first circle here
canvas.restore();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(ccwRotation);
//draw second circle here
canvas.restore();
This approach has the advantage of being very straightforward and requiring no additional classes and APIs and it is similar to what you would do with OpenGL.
You'll have a lot more success if you replace your rotator1/2.run() lines with rotator1/2.start()