I have custom SurfaceView that containt a circle. I want to rotate this circle via touch events. Everything good except I can't pause run() method of SurfaveView when touch finished.
Here my view and thread
public class CircleSurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private Circle mCircle;
private DrawThread thread;
private float mRotation = 0;
long mLastMillis = 0L;
Paint clearPaint = new Paint();
public CircleSurfaceView(Context context) {
super(context);
mCircle = new Circle(0, 200, 200);
getHolder().addCallback(this);
this.setZOrderOnTop(true);
this.getHolder().setFormat(PixelFormat.TRANSPARENT);
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
public void startDrawing() {
if (thread == null) {
thread = new DrawThread(this);
thread.startThread();
}
}
public void stopDrawing() {
if (thread != null) {
thread.stopThread();
// Waiting for the thread to die by calling thread.join,
// repeatedly if necessary
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
thread = null;
}
}
#Override
protected void onDraw(final Canvas canvas) {
canvas.drawPaint(clearPaint);
mCircle.init();
mCircle.draw(canvas);
super.onDraw(canvas);
}
float degrees;
public void rotateCircle() {
mCircle.rotate(mRotation);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float currentX = event.getX();
float currentY = event.getY();
float deltaX, deltaY;
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
// Modify rotational angles according to movement
deltaX = currentX - previousX;
deltaY = currentY - previousY;
mRotation += deltaX + deltaY;
}
// Save current x, y
previousX = currentX;
previousY = currentY;
rotateCircle();
return true; // Event handled
}
private float previousX;
private float previousY;
private static int SEG_COUNT = 62;
private static float SEG_IN_GRAD = (float) 360 / SEG_COUNT;
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
startDrawing();
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
stopDrawing();
}
public class DrawThread extends Thread {
private boolean mRun = false;
private SurfaceHolder surfaceHolder = null;
private CircleSurfaceView circleSurfaceView = null;
public DrawThread(CircleSurfaceView circleSurfaceView) {
this.circleSurfaceView = circleSurfaceView;
this.surfaceHolder = circleSurfaceView.getHolder();
}
public void startThread() {
if (mRun == false) {
mRun = true;
super.start();
}
}
public void stopThread() {
if (mRun == true) {
mRun = false;
}
}
#Override
public void run() {
Canvas c = null;
while (mRun) {
c = null;
try {
c = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
if (c != null) {
Log.i("run", "run");
circleSurfaceView.rotateCircle();
circleSurfaceView.onDraw(c);
}
}
} finally {
// do this in a finally so that if an exception is thrown
// we don't leave the Surface in an inconsistent state
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
Related
This is my first time working with android, and I'm having some trouble drawing on a canvas repeatedly inside a thread. It seems that it only gets drawn once, although the thread is running which I made sure using a toast that popped up each time the thread executed (I've removed it because it was bothering me) but the thread is running and the code inside gets executed, it just sort of doesn't redraw...
public class GameActivity extends Activity implements Runnable{
ImageView gameView;
public int screenWidth, screenHeight, objectSize;
private static int FPS = 30;
private boolean rightPressed = false, leftPressed = false;
private Player player;
private Thread thread;
private Canvas canvas;
private Bitmap blankBitmap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenWidth = size.x;
screenHeight = size.y;
objectSize = screenWidth/8;
player = new Player(objectSize, screenWidth/2 - objectSize/2, screenHeight - (objectSize+10));
blankBitmap = Bitmap.createBitmap(screenWidth,screenHeight,Bitmap.Config.ARGB_8888);
canvas = new Canvas(blankBitmap);
gameView = new ImageView(this);
gameView.setImageBitmap(blankBitmap);
draw();
setContentView(gameView);
thread = new Thread(this);
thread.start();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
leftPressed = true;
if((int)event.getX()<=screenWidth/2){
leftPressed = true;
rightPressed = false;
Toast.makeText(this, "left", Toast.LENGTH_SHORT).show();
}
if((int)event.getX()>screenWidth/2){
rightPressed = true;
leftPressed = false;
Toast.makeText(this, "right", Toast.LENGTH_SHORT).show();
}
}
if(event.getAction() == MotionEvent.ACTION_UP) {
rightPressed = false;
leftPressed = false;
Toast.makeText(this, "released", Toast.LENGTH_SHORT).show();
}
return super.onTouchEvent(event);
}
public void draw(){
canvas.drawColor(Color.argb(255, 255, 255, 255));
player.draw(canvas);
}
#Override
public void run() {
while (!player.shouldDie()) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (!rightPressed && !leftPressed) player.setDx(0);
if(rightPressed) player.setDx(player.getSpeed());
if(leftPressed) player.setDx(-player.getSpeed());
player.update();
draw();
}
});
Thread.sleep(1000/FPS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I also checked that the x of the player does actually change, and if I manually set the dx to like 100 it does actually move the first time it is executed, but not later even though the thread is still running
anyway, here's the Player.class
public class Player {
private int health, size, x, y;
private double dx, speed;
private Paint paint;
public Player(int size, int x, int y){
health = 3;
speed = 1;
dx = 0;
this.size = size;
this.x = x;
this.y = y;
paint = new Paint();
paint.setColor(Color.argb(255, 26, 128, 182));
}
public void draw(Canvas canvas){
canvas.drawRect(x, y, x+size, y+size, paint);
}
public void update(){
x += dx;
}
public void hurt(){
health--;
}
public boolean shouldDie(){
if(health<=0)return true;
return false;
}
public void posToast(Context context){
Toast.makeText(context, "X: " + x, Toast.LENGTH_SHORT).show();
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public Paint getPaint() {
return paint;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public double getDx() {
return dx;
}
public void setDx(double dx) {
this.dx = dx;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
If anyone sees anything wrong in here I'd appreciate some help as I'm really confused as to why this doesn't work, especially since this is my first time working with android.
Seems you forgot to call rePaint() method. In Android, you have something called inValidate() which does redraw your canvas. You can either add in player draw method or activity draw method. That should do the trick.
I made a rotating knob ,but I want to stop the knob at specific angles for 2 seconds. I want to stop it on 260f and -20f.
Can anyone suggest how to do it ?
This is the code from a blog. I made many changes according to my requirements.
public class RotatoryKnobView extends ImageView {
private float angle = -20f;
private float theta_old=0f;
private RotaryKnobListener listener;
public interface RotaryKnobListener {
public void onKnobChanged(float arg);
}
public void setKnobListener(RotaryKnobListener l )
{
listener = l;
}
public RotatoryKnobView(Context context) {
super(context);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs)
{
super(context, attrs);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initialize();
}
private float getTheta(float x, float y)
{
float sx = x - (getWidth() / 2.0f);
float sy = y - (getHeight() / 2.0f);
float length = (float)Math.sqrt( sx*sx + sy*sy);
float nx = sx / length;
float ny = sy / length;
float theta = (float)Math.atan2( ny, nx );
final float rad2deg = (float)(180.0/Math.PI);
float thetaDeg = theta*rad2deg;
return (thetaDeg < 0) ? thetaDeg + 360.0f : thetaDeg;
}
public void initialize()
{
this.setImageResource(R.drawable.rotoron);
setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX(0);
float y = event.getY(0);
float theta = getTheta(x,y);
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_POINTER_DOWN:
theta_old = theta;
break;
case MotionEvent.ACTION_MOVE:
invalidate();
float delta_theta = theta - theta_old;
theta_old = theta;
int direction = (delta_theta > 0) ? 1 : -1;
angle += 5*direction;
notifyListener(angle+20);
break;
}
return true;
}
});
}
private void notifyListener(float arg)
{
if (null!=listener)
listener.onKnobChanged(arg);
}
protected void onDraw(Canvas c)
{if(angle==257f){
try {
synchronized (c) {
c.wait(5000);
angle=260f;
}
} catch (InterruptedException e) {
}
}
else if(angle==-16f)
{
try {
synchronized (c) {
c.wait(5000);
angle=-20f;
}
} catch (InterruptedException e) {
}
}
else
if(angle>260f)
{
angle=-20f;
}
else if(angle<-20f)
{
angle=260f;
}
else{
c.rotate(angle,getWidth()/2,getHeight()/2);
}
super.onDraw(c);
}
}
You may set a fixed angle and use postDelayed to clear it after 2 seconds.
public class RotatoryKnobView extends ImageView {
private float angle = -20f;
private float theta_old=0f;
private RotaryKnobListener listener;
private Float fixedAngle;
private float settleAngle;
private Runnable unsetFixedAngle = new Runnable() {
#Override
public void run() {
angle = settleAngle;
fixedAngle = null;
invalidate();
}
};
public interface RotaryKnobListener {
public void onKnobChanged(float arg);
}
public void setKnobListener(RotaryKnobListener l )
{
listener = l;
}
public RotatoryKnobView(Context context) {
super(context);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs)
{
super(context, attrs);
initialize();
}
public RotatoryKnobView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initialize();
}
private float getTheta(float x, float y)
{
float sx = x - (getWidth() / 2.0f);
float sy = y - (getHeight() / 2.0f);
float length = (float)Math.sqrt( sx*sx + sy*sy);
float nx = sx / length;
float ny = sy / length;
float theta = (float)Math.atan2( ny, nx );
final float rad2deg = (float)(180.0/Math.PI);
float thetaDeg = theta*rad2deg;
return (thetaDeg < 0) ? thetaDeg + 360.0f : thetaDeg;
}
public void initialize()
{
this.setImageResource(R.drawable.rotoron);
setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX(0);
float y = event.getY(0);
float theta = getTheta(x,y);
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_POINTER_DOWN:
theta_old = theta;
break;
case MotionEvent.ACTION_MOVE:
invalidate();
float delta_theta = theta - theta_old;
theta_old = theta;
int direction = (delta_theta > 0) ? 1 : -1;
angle += 5*direction;
notifyListener(angle+20);
break;
}
return true;
}
});
}
private void notifyListener(float arg)
{
if (null!=listener)
listener.onKnobChanged(arg);
}
void setFixedAngle(float angle, float settleAngle) {
fixedAngle = angle;
this.settleAngle = settleAngle;
postDelayed(unsetFixedAngle, 2000);
}
protected void onDraw(Canvas c)
{
if(fixedAngle==null) {
if (angle > 270) {
setFixedAngle(270, -15);
} else if (angle < -20f) {
setFixedAngle(-20, 260);
}
}
Log.d("angle", "angle: " + angle + " fixed angle: " + fixedAngle);
c.rotate(fixedAngle == null ? angle : fixedAngle,getWidth()/2,getHeight()/2);
super.onDraw(c);
}
}
`
I think the ultimate answer here is to implement your own class by extending SurfaceView and then overriding onDraw( Canvas canvas )
You can then use the Canvas routines to render your control.
There are a lot of good examples out there if you google.
To get started initialize the surface view:
// So things actually render
setDrawingCacheEnabled(true);
setWillNotDraw(false);
setZOrderOnTop(true);
// Controls the drawing thread.
getHolder().addCallback(new CallbackSurfaceView());
Override onDraw and add your rendering routines. You can layer them
as you go.
public void onDraw(Canvas canvas) {
// Always Draw
super.onDraw(canvas);
drawBackground(canvas);
drawKnobIndentWell(canvas);
drawKnob(canvas);
drawKnobLED( canvas ); //etc....
}
An example of a Callback and an update thread:
/**
* This is the drawing callback.
* It handles the creation and destruction of the drawing thread when the
* surface for drawing is created and destroyed.
*/
class CallbackSurfaceView implements SurfaceHolder.Callback {
Thread threadIndeterminant;
RunnableProgressUpdater runnableUpdater;
boolean done = false;
/**
* Kills the running thread.
*/
public void done() {
done = true;
if (null != runnableUpdater) {
runnableUpdater.done();
}
}
/**
* Causes the UI to render once.
*/
public void needRedraw() {
if (runnableUpdater != null) {
runnableUpdater.needRedraw();
}
}
/**
* When the surface is created start the drawing thread.
* #param holder
*/
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!done) {
threadIndeterminant = new Thread(runnableUpdater = new RunnableProgressUpdater());
threadIndeterminant.start();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* When the surface is destroyed stop the drawing thread.
* #param holder
*/
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (null != runnableUpdater) {
runnableUpdater.done();
threadIndeterminant = null;
runnableUpdater = null;
}
}
}
/**
* This is the runnable for the drawing operations. It is started and stopped by the callback class.
*/
class RunnableProgressUpdater implements Runnable {
boolean surfaceExists = true;
boolean needRedraw = false;
public void done() {
surfaceExists = false;
}
public void needRedraw() {
needRedraw = true;
}
#Override
public void run() {
canvasDrawAndPost();
while (surfaceExists) {
// Renders continuously during a download operation.
// Otherwise only renders when requested.
// Necessary so that progress bar and cirlce activity update.
if (syncContext.isRunning()) {
canvasDrawAndPost();
needRedraw = true;
} else if (needRedraw) {
canvasDrawAndPost();
needRedraw = false;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Don't care
}
}
// One final update
canvasDrawAndPost();
}
/**
* Routine the redraws the controls on each loop.
*/
private synchronized void canvasDrawAndPost() {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
try {
draw(canvas);
} finally {
getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
If you decide to go this route you can customize your control from XML using
custom values.
<com.killerknob.graphics.MultimeterVolumeControl
android:id="#+id/volume_control"
android:layout_below="#id/divider_one"
android:background="#android:color/white"
android:layout_width="match_parent"
android:layout_height="60dp"
android:minHeight="60dp"
custom:ledShadow="#357BBB"
custom:ledColor="#357BBB"
custom:knobBackground="#color/gray_level_13"
custom:knobColor="#android:color/black"
/>
When you create a custom control you reference it by its package name.
You create custom variable in a resource file under /values and then reference
them in your class.
More details here:
http://developer.android.com/training/custom-views/create-view.html
This may be more work then you want to do, but I think you will end up with a more professional looking control and the animations will be smoother.
At any rate, looks like a fun project. Good Luck.
I have a SurfaceView on wich am drawing a small circle periodically with a special thread
but i want to when i press the surfaceView the thread stops drawing the circle and when i repress it the thread resume drawing the circle again.
I tried with thread methods and i can stop temporarily it with its sleep() method but i did not understand how to use wait and notify and i even found some exemples but did not get help from them
My code is :
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 mTh ead;
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);
}
public void onDraw(Canvas canvas){
canvas.drawCircle(x, y, radius, paint);
}
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
int X = (int)event.getX();
int Y = (int)event.getY();
switch (eventaction ) {
case MotionEvent.ACTION_DOWN:
// I want to do my job here
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mThread = new DrawingThread(mSurfaceHolder, myContext);
mThread.mRun = true;
mThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
public final class DrawingThread extends Thread {
public boolean att = true;
public long delaiAttente = 1000;
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) {
draw(canvas);
}
}
catch(Exception e){
}
finally {
if (canvas != null)
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
waitThreaed();
}
}
public void waitThreaed() {
try {
x = (float) (getWidth()*Math.random());
y = (float) (getHeight()*Math.random());
this.sleep(1000);
postInvalidate();
} catch (InterruptedException e) {
}
}
}
}
Can't you just use something like this:
Timer drawTimer = new Timer("draw");
updateTimer.schedule(new TimerTask() {
public void run() {
draw();
}
}, 0, 1000);
private void draw() {
runOnUiThread(new Runnable() {
public void run() { ...}}}
I don't see why you need to subclass Thread.
In the main activity I have a handler to exchange messages with the bluetooth service. This works fine.
Now, I want launch second activity (SurfaceViewAnimation). I do this with:
startActivity (new Intent (this, SurfaceViewAnimation.class));
but I want change some attributes of SufaceViewAnimation class from main activity when it receives a command for bluetooth.
How I can do this?
The code of SufaceViewAnimation class is:
class BouncingBallView extends SurfaceView implements SurfaceHolder.Callback {
private BouncingBallAnimationThread bbThread = null;
private int xPosition = getWidth()/2;
private int yPosition = getHeight()/2;
private int xDirection = 20;
private int yDirection = 40;
private static int radius = 20;
private static int ballColor = Color.RED;
public BouncingBallView(Context ctx, AttributeSet attrs, int defStyle) {
super(ctx, attrs, defStyle);
getHolder().addCallback(this);
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
canvas.drawRect(0,0,getWidth(),getHeight(), paint);
paint.setColor(ballColor);
canvas.drawCircle(xPosition, yPosition, radius, paint);
}
public void surfaceCreated(SurfaceHolder holder) {
if (bbThread!=null) return;
bbThread = new BouncingBallAnimationThread(getHolder());
bbThread.start();
}
public void surfaceChanged(SurfaceHolder holder,int format, int width, int height) { }
public void surfaceDestroyed(SurfaceHolder holder) {
bbThread.stop = true;
}
private class BouncingBallAnimationThread extends Thread {
public boolean stop = false;
private SurfaceHolder surfaceHolder;
public BouncingBallAnimationThread(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
}
public void run() {
while (!stop) {
xPosition += xDirection;
yPosition += yDirection;
if (xPosition<0) {
xDirection = -xDirection;
xPosition = radius; }
if (xPosition>getWidth()-radius) {
xDirection = -xDirection;
xPosition = getWidth()-radius; }
if (yPosition<0) {
yDirection = -yDirection;
yPosition = radius; }
if (yPosition>getHeight()-radius) {
yDirection = -yDirection;
yPosition = getHeight()-radius-1; }
Canvas c = null;
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
onDraw(c);
}
} finally {
if (c != null) surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_DOWN) return false;
if (xDirection!=0 || yDirection!=0)
xDirection = yDirection = 0;
else {
xDirection = (int) event.getX() - xPosition;
yDirection = (int) event.getY() - yPosition;
}
if(ballColor==Color.RED)
ballColor=Color.GREEN;
else
ballColor=Color.RED;
return true;
}
public void setxDirection(int xDirection) {
this.xDirection = xDirection;
}
public void setyDirection(int yDirection) {
this.yDirection = yDirection;
}
public void setballColor(int ballColor) {
this.ballColor = ballColor;
}
public void setradius(int radius) {
this.radius = radius;
}
}
public class SurfaceViewAnimation extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new BouncingBallView(this,null,0));
}
}
If you want to change the data in SurfaceViewAnimation while SurfaceViewAnimation is running, you're best off with creating an own handler.
Besides that you can fall back to basic Inter(Process/Activity)Communication using Handlers, static variables or the Observer pattern.
I have a problem with the flickering.
Here is my code.
public class Tutorial2D3 extends Activity {
Panel panel;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
panel = new Panel(this);
setContentView(panel);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(1, 1, 1, "Clean Canvas");
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
panel.cleanCanvas();
return true;
}
class Panel extends SurfaceView implements SurfaceHolder.Callback {
TutorialThread thread;
Bitmap icon;
int iconWidth;
int iconHeight;
int touchX;
int touchY;
int mCount = 0;
public Panel(Context context) {
super(context);
icon = BitmapFactory
.decodeResource(getResources(), R.drawable.icon);
iconWidth = icon.getWidth();
iconHeight = icon.getHeight();
getHolder().addCallback(this);
thread = new TutorialThread(getHolder(), this);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
int x = touchX - (iconWidth / 2);
int y = touchY - (iconHeight / 2);
if(mCount>0) {
canvas.drawColor(Color.BLACK);
mCount--;
}
canvas.drawBitmap(icon, (x > 0 ? x : 0), (y > 0 ? y : 0), null);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
do {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (retry);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchX = (int) event.getX();
touchY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
touchX = (int) event.getX();
touchY = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
private void cleanCanvas() {
mCount = 2;
}
}
class TutorialThread extends Thread {
private SurfaceHolder _surfaceHolder;
private Panel _panel;
private boolean _run = false;
public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
The drawn image flickers.
It looks like the bitmap that is drawn at one point is drawn on one surface and not the other so it looks like flickering, the bitmap that is drawn when we touch action_up is done, that is a solid image and does not flickers. Could someone please help me with this one.
Thanks
When you are drawing in the Canvas of a SurfaceView, you must always draw every pixel of the surface.
Here you are not always clearing the Canvas in onDraw(), hence the flickering.
One thing you could do to mitigate that (and to kinda contradict Guillaume :)) is to use surfaceholder.lockCanvas(rectangle), where it is only the specified rectangle part of the canvas which is then drawn (but you must draw every pixel of that rect). Here it is, ripped from the LunarLandar sample:
#Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(Rectangle);
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING) updatePhysics();
doDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
I didn't read through all of your code, but I think this article will help.
The essence of the article is that flickering is due to double buffering and can be eliminated by drawing not to the argument Canvas but to a bitmap used as the canvas and then drawing that bitmap to the arg Canvas:
int myCanvas_w, myCanvas_h;
Bitmap myCanvasBitmap = null;
Canvas myCanvas = null;
Matrix identityMatrix;
#Override
public void surfaceCreated(SurfaceHolder holder) {
myCanvas_w = getWidth();
myCanvas_h = getHeight();
myCanvasBitmap = Bitmap.createBitmap(myCanvas_w, myCanvas_h, Bitmap.Config.ARGB_8888);
myCanvas = new Canvas();
myCanvas.setBitmap(myCanvasBitmap);
identityMatrix = new Matrix();
}
#Override
protected void onDraw(Canvas canvas) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
//int w = myCanvas.getWidth();
//int h = myCanvas.getHeight();
int x = random.nextInt(myCanvas_w-1);
int y = random.nextInt(myCanvas_h-1);
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);
paint.setColor(0xff000000 + (r << 16) + (g << 8) + b);
myCanvas.drawPoint(x, y, paint); // <--------- Here's where you draw on your bitmap
canvas.drawBitmap(myCanvasBitmap, identityMatrix, null);
// ^---------- And here's where you draw that bitmap to the canvas
}