I am trying to draw on a surface view, but I get a black screen. My xml layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<com.example.surfaceview.MySurface android:id="#+id/padview"
android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>
Important parts of my code: MySurface.java
public class MySurface extends SurfaceView implements SurfaceHolder.Callback {
public DrawingThread thread;
public MySurface(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder surfaceholder = getHolder();
surfaceholder.addCallback(this);
thread = new DrawingThread(surfaceholder, context, new Handler() {
#Override
public void handleMessage(Message m) {
// Do some handle thing
}
});
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder tholder, int format, int width, int height) {
thread.setSurfaceSize(width, height);
thread.holder = tholder;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.running = true;
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.running = false;
while(retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
public Thread getThread() {
return thread;
}
public class DrawingThread extends Thread {
private SurfaceHolder holder;
private Context context;
private Handler handle;
private Paint background;
private Rect blank;
public boolean running;
public boolean created;
public int canvasheight;
public int canvaswidth;
public PadThread(SurfaceHolder tholder, Context tcontext, Handler thandler) {
holder = tholder;
context = tcontext;
handle = thandler;
// Temporary canvas dimentions
canvaswidth = 1;
canvasheight = 1;
running = false;
created = false;
background = new Paint();
background.setColor(R.color.white);
blank = new Rect(0, 0, canvaswidth, canvasheight);
}
#Override
public void run() {
Log.d("SurfaceView Test", "Drawing thread run");
while(running) {
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
synchronized(holder) {
// update object states
// get user input gestures
drawing(canvas);
}
} finally {
if(canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
}
private void drawing(Canvas canvas) {
// Clear screen
canvas.drawRect(blank, background);
// Draw Things
}
public void setSurfaceSize(int width, int height) {
synchronized(holder) {
canvaswidth = width;
canvasheight = height;
// New background rect
blank.set(0, 0, canvaswidth, canvasheight);
}
}
}
}
The code is based off of Google's Lunar Landar SurfaceView example at http://developer.android.com/resources/samples/LunarLander/index.html
I know that all of the code is being reached through logging.
change drawing function
background.setColor(Color.RED);
canvas.drawRect(new Rect(10, 10, 100, 100), background);
(There are test values for colors and rect)
Related
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.
I'm making a simple rotating animation of 2 images, one on top of another (each with a different speed). I know there is already an Animation API, but I need to use this one instead.
It works well, but from time to time, the animations stop for a very short time and then continue, making it "jumpy" (stays on some frames much more than on the others).
Why does it occur? because of GC? What can I do to avoid it? I intend to do some background operations, so it might get even slower.
I know that using sleep() doesn't really mean the thread will wake up right after the given time, but this is too much and it's very noticeable .
Are there any tricks to make it smoother? maybe use a better alternative?
Here's the code (of the view that does it) :
public class AnimView extends SurfaceView implements SurfaceHolder.Callback
{
int _angle;
private final Bitmap _bitmap;
private final Paint _paint =new Paint();
private AnimationThread _animationThread;
private Matrix _matrix;
private int _width;
private int _height;
public AnimView(final Context context)
{
super(context);
_bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
_paint.setAntiAlias(true);
getHolder().addCallback(this);
}
public AnimView(final Context context,final AttributeSet attrs)
{
super(context,attrs);
_bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
_paint.setAntiAlias(true);
getHolder().addCallback(this);
}
public AnimView(final Context context,final AttributeSet attrs,final int defStyle)
{
super(context,attrs,defStyle);
_bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
_paint.setAntiAlias(true);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(final SurfaceHolder holder,final int format,final int width,final int height)
{
_width=width;
_height=height;
}
#Override
public void surfaceCreated(final SurfaceHolder holder)
{
if(isInEditMode())
return;
_matrix=new Matrix();
setWillNotDraw(false);
_animationThread=new AnimationThread();
_animationThread.start();
}
#Override
public void surfaceDestroyed(final SurfaceHolder holder)
{
_animationThread.cancelAnimation();
}
#Override
protected void onDraw(final Canvas canvas)
{
if(isInEditMode())
{
canvas.drawColor(0xffff0000);
return;
}
_paint.setAntiAlias(true);
_matrix.reset();
_matrix.postScale(_width/_bitmap.getWidth(),_height/_bitmap.getHeight());
_matrix.postRotate(_angle,_width/2,_height/2);
canvas.drawBitmap(_bitmap,_matrix,_paint);
_matrix.reset();
_matrix.postScale(_width/_bitmap.getWidth(),_height/_bitmap.getHeight());
_matrix.postRotate(_angle*2,_width/2,_height/2);
canvas.drawBitmap(_bitmap,_matrix,_paint);
}
private class AnimationThread extends Thread
{
private boolean _isRunning =true;
public AnimationThread()
{}
public void cancelAnimation()
{
_isRunning=false;
}
#Override
public void run()
{
while(_isRunning)
{
postInvalidate();
refreshDrawableState();
try
{
sleep(5);
}
catch(final InterruptedException e)
{
e.printStackTrace();
}
_angle=(_angle+1)%360;
}
}
}
}
Try something like this
#Override
public void run()
{
long last = System.currentTimeMillis();
long currentTime = last;
long frameTime = 40;
while(_isRunning)
{
currentTime = System.currentTimeMillis();
if(currentTime - last > frameTime){
postInvalidate();
refreshDrawableState();
//I FORGOT THIS
last = currentTime;
}
try
{
sleep(5);
}
catch(final InterruptedException e)
{
e.printStackTrace();
}
_angle=(_angle+1)%360;
}
}
}
This way you refresh your screen every 40ms and update your position every 5ms. Also this is just one part, you can set your matrix update and calculations also out of the drawing part, and this will increase your speed too. The less calculations you do on actuall draw time the faster it will be.
EDIT: Take a look at this
public class AnimView extends SurfaceView implements SurfaceHolder.Callback {
int _angle;
private final Bitmap _bitmap;
private final Paint _paint = new Paint();
private AnimationThread _animationThread;
private Matrix _matrix;
private int _width = 100;
private int _height = 100;
private Bitmap backBitmap;
private Canvas backCanvas;
public AnimView(final Context context) {
super(context);
_bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
_paint.setAntiAlias(true);
getHolder().addCallback(this);
}
public AnimView(final Context context, final AttributeSet attrs) {
super(context, attrs);
_bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
_paint.setAntiAlias(true);
getHolder().addCallback(this);
}
public AnimView(final Context context, final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
_bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
_paint.setAntiAlias(true);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(final SurfaceHolder holder, final int format,
final int width, final int height) {
_width = width;
_height = height;
createImage();
}
#Override
public void surfaceCreated(final SurfaceHolder holder) {
if (isInEditMode())
return;
_matrix = new Matrix();
setWillNotDraw(false);
_animationThread = new AnimationThread();
_animationThread.start();
}
#Override
public void surfaceDestroyed(final SurfaceHolder holder) {
_animationThread.cancelAnimation();
}
#Override
protected void onDraw(final Canvas canvas) {
if (isInEditMode() || backBitmap == null) {
canvas.drawColor(0xffff0000);
return;
}
canvas.drawBitmap(backBitmap, 0, 0, _paint);
}
private class AnimationThread extends Thread {
private boolean _isRunning = true;
public AnimationThread() {
}
public void cancelAnimation() {
_isRunning = false;
}
#Override
public void run() {
long last = System.currentTimeMillis();
long currentTime = last;
long frameTime = 40;
long updateTime = 20;
boolean updated = false;
while (_isRunning) {
currentTime = System.currentTimeMillis();
if (currentTime - last > frameTime) {
postInvalidate();
refreshDrawableState();
last = currentTime;
updated = false;
}
else if(currentTime - last > updateTime){
if(!updated){
drawImage();
updated = true;
}
}
try {
sleep(5);
} catch (final InterruptedException e) {
e.printStackTrace();
}
_angle = (_angle + 1) % 360;
}
}
}
private void createImage(){
backBitmap = Bitmap.createBitmap(_width, _height, Config.ARGB_8888);
backCanvas = new Canvas();
backCanvas.setBitmap(backBitmap);
}
private void drawImage(){
backCanvas.drawRGB(0, 0, 0);
_paint.setAntiAlias(true);
_matrix.reset();
_matrix.postScale(_width / _bitmap.getWidth(),
_height / _bitmap.getHeight());
_matrix.postRotate(_angle, _width / 2, _height / 2);
backCanvas.drawBitmap(_bitmap, _matrix, _paint);
_matrix.reset();
_matrix.postScale(_width / _bitmap.getWidth(),
_height / _bitmap.getHeight());
_matrix.postRotate(_angle * 2, _width / 2, _height / 2);
backCanvas.drawBitmap(_bitmap, _matrix, _paint);
}
}
Hope this helps and enjoy your work
I want to draw an animated picture in my MainActivity
My MainActivity code is as follows :
public class LaozhangActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
Ch0_1 view=new Ch0_1(this);
super.onCreate(savedInstanceState);
setContentView(view);
}
}
and I have defined a class that extends SurfaceView in order to show an animated picture;
The code is like this:
public class Ch0_1 extends SurfaceView implements Callback, Runnable
{
SurfaceHolder surfaceHolder;
private boolean isThreadRunning = true;
Canvas canvas;
float r = 10;
public Ch0_1(Context context)
{
super(context);
surfaceHolder = this.getHolder();
surfaceHolder.addCallback(this);
canvas=new Canvas();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
{
}
public void surfaceCreated(SurfaceHolder holder)
{
new Thread(this).start();
}
public void surfaceDestroyed(SurfaceHolder holder)
{
}
private void Method(int x,int y)
{
Paint pen=new Paint();
pen.setColor(Color.GREEN);
canvas=surfaceHolder.lockCanvas();
canvas.drawLine(0, 0, x, y, pen);
surfaceHolder.unlockCanvasAndPost(canvas);
}
public void run()
{
while (isThreadRunning)
{
int x=0,y=0;
Method(x,y);
try
{
Thread.sleep(100);
} catch (Exception e)
{
e.printStackTrace();
}
x+=10;
y+=10;
if(x>400)break;
}
}
}
When running, the MainActivity is black, it didn't show the picture.
Where is the problem?
With your code, x and y are reset to 0 at each iteration...
You must move the initialization of x and y outside of the while loop:
public void run() {
int x = 0, y = 0;
while (isThreadRunning) {
Method(x, y);
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
x += 10;
y += 10;
if (x > 400)
break;
}
}
I am creating a paint app but i am not able to draw /paint anything over an image please help me out :
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback {
private Boolean _run;
protected DrawThread thread;
private Bitmap mBitmap;
public boolean isDrawing = true;
public DrawingPath previewPath;
private CommandManager commandManager;
public DrawingSurface(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
commandManager = new CommandManager();
thread = new DrawThread(getHolder());
}
private Handler previewDoneHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
isDrawing = false;
}
};
class DrawThread extends Thread{
private SurfaceHolder mSurfaceHolder;
public DrawThread(SurfaceHolder surfaceHolder){
mSurfaceHolder = surfaceHolder;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
Canvas canvas = null;
while (_run){
if(isDrawing == true){
try{
canvas = mSurfaceHolder.lockCanvas(null);
if(mBitmap == null){
// mBitmap = Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);
mBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.splashscreen);
}
// final Canvas c = new Canvas (mBitmap);
mBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.splashscreen);
// c.drawColor(0, PorterDuff.Mode.CLEAR);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
// commandManager.executeAll(c,previewDoneHandler);
// previewPath.draw(c);
canvas.drawBitmap (mBitmap, 0, 0,null);
} finally {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
public void addDrawingPath (DrawingPath drawingPath){
commandManager.addCommand(drawingPath);
}
// Bitmap mBitmap = Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter);
public boolean hasMoreRedo(){
return commandManager.hasMoreRedo();
}
public void redo(){
isDrawing = true;
commandManager.redo();
}
public void undo(){
isDrawing = true;
commandManager.undo();
}
public boolean hasMoreUndo(){
return commandManager.hasMoreUndo();
}
public Bitmap getBitmap(){
return mBitmap;
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
mBitmap = Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);;
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
Why not make it easier on yourself and split your application across multiple surfaceviews. Use framelayout so that you can control the order of the views. I assume you want to be able to handle touch events (its a paint program right?) so handle those in one view and your drawing in another that way the code will be more manageable. Create two custom surfaceviews (In my case one was a GLSurfaceView, and the other was a SurfaceView). One view implements the image and the second can handle a GUI with graphics and or buttons. The key is creating a custom GLSurfaceView (or in your case two Surfaceviews) in XML that uses the class names of the extended SurfaceViews. Also you must make the GUI view transparent. So your Surface view that calls your drawing class and handles touch events would have code like this:
public class MainSurfaceView extends GLSurfaceView
//Constructor
public MainSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "Constructor called...");
this.context = context;
mRenderer = new MainRenderer();
setEGLConfigChooser(8, 8, 8, 8, 16, 0);
setRenderer(mRenderer);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
setZOrderOnTop(true);
//Render only when there is a change
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
//TouchEvent handler
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent called...");
x = event.getX();
y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
//Reverse direction of rotation if above midline
if (y > getHeight() / 2) {
dx = dx * -1;
}
//Reverse direction of rotation if of the midline
if (y < getWidth() / 2) {
dy = dy * -1;
}
Main.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR;
TextView txtView = (TextView) ((Activity)context).findViewById(R.id.mangle);
txtView.setText("Angle: " + String.valueOf(Main.mAngle));
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
Put you image to be drawn on a separate surface view (it will also make it easier to change the image). So you Activity class would have code like this (note my second view is a GLsurfaceview):
public class Main extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate called...");
Log.d(TAG, "state is: " + state.toString());
super.onCreate(savedInstanceState);
// When working with the camera, it's useful to stick to one orientation.
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//Set full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
//Substantiate surfaces
cameraView = (CameraView) findViewById(R.id.camera);
mGLView = (MainSurfaceView) findViewById(R.id.glSurfaceView);
//Initialize TextViews
txtView = (TextView) findViewById(R.id.mangle);
txtView.setText("Angle: " + String.valueOf(mAngle));
}
}
Hope that helps a little...
i want to rotate an image(png) in android depending upon the intensity of touch.
Any Idea?
public class GetmouseActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
}
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
private Bitmap mBitmap;
private ViewThread mThread;
private int mX;
private int mY;
public Panel(Context context) {
super(context);
mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.icon);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}
public void doDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
Paint paint = new Paint();
paint.setStrokeWidth(25);
for (int y = 30, alpha = 255; alpha > 2; alpha >>= 1, y += 10) {
paint.setAlpha(alpha);
canvas.drawLine(0, y, 100, y, paint);
}
canvas.drawBitmap(mBitmap, mX, mY, null);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
if (!mThread.isAlive()) {
mThread = new ViewThread(this);
mThread.setRunning(true);
mThread.start();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()) {
mThread.setRunning(false);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mX = (int) event.getX() - mBitmap.getWidth() / 2;
mY = (int) event.getY() - mBitmap.getHeight() / 2;
return super.onTouchEvent(event);
}
}
public class ViewThread extends Thread {
private Panel mPanel;
private SurfaceHolder mHolder;
private boolean mRun = false;
public ViewThread(Panel panel) {
mPanel = panel;
mHolder = mPanel.getHolder();
}
public void setRunning(boolean run) {
mRun = run;
}
#Override
public void run() {
Canvas canvas = null;
while (mRun) {
canvas = mHolder.lockCanvas();
if (canvas != null) {
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
try this