HI! I have a surfaceView inside a horizontal scrollview that I want to fill with images with a onDraw() call. However, nothing is drawn.
I have a class in which the drawing is done from the thread CanvasThread.
public class PanelChart extends SurfaceView implements SurfaceHolder.Callback {
private CanvasThread canvasthread ;
public PanelChart(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
getHolder().addCallback(this);
canvasthread = new CanvasThread(getHolder(), this);
setFocusable(true);
I have tried to change the
`synchronized (_surfaceHolder) {
_panel.postInvalidate();
}`
to
synchronized (_surfaceHolder) {
_panel.postInvalidate();
}
I have also tried to add the call setWillNotDraw(false) without luck:
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
canvasthread.setRunning(true);
canvasthread.start();
setWillNotDraw(false);
This seems to be a common issue, but none of the solutions I have come across have worked for me.
Thanks!
postInvalidate will not call onDraw with surfaceView. You need to unlock canvas, draw things and then lock canvas. Here is an example of a thread for surfaceView:
class CanvasThread extends Thread {
private SurfaceHolder surfaceHolder;
private PanelChart panel;
private boolean run = false;
public CanvasThread(SurfaceHolder surfaceHolder, PanelChart panel) {
this.surfaceHolder = surfaceHolder;
this.panel = panel;
}
public void setRunning(boolean run) {
this.run = run;
}
public SurfaceHolder getSurfaceHolder() {
return surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (run) {
c = null;
//limit the frame rate to maximum 60 frames per second (16 miliseconds)
timeNow = System.currentTimeMillis();
timeDelta = timeNow - timePrevFrame;
if ( timeDelta < 16){
try{
Thread.sleep(16 - timeDelta);
}catch(InterruptedException e){
}
}
timePrevFrame = System.currentTimeMillis();
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
panel.onDraw(c); //draw canvas
computePhysics(); //calculate next frame
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c); //show canvas
}
}//try finally
} //while
}//run
}//thread
Related
I have this android application.
It use a SurfaceView, from where I get the Surface through the SurfaceHolder.
It also use ExoPlayer to stream videos. However I have instantiated an ImageReader, getting its Surface and passing to the ExoPlayer.
Now, I am in the ImageReader.OnImageAvailableListener#onImageAvailable and I access the latest Image.
I want to manipulate the Image and send the new data to the "SurfaceView" Surface.
How can I "draw" an android.media.Image to an android.view.Surface ?
The question is not clear to me.
The way to get android.media.Image is by the Camera2 API, and there you can provide a surface and the "camera" will draw over it. Please refer to Camera2Video example
Another way to get the Image object is from ImageReader (while decoding video for example). In this case you want to draw the image, but you can not provide a surface to the ImageReader(there is an internal surface that is not displayed). In this case you can draw the Image on a SurfaceView.
Assuming this is the case, you need to convert an Image to a Bitmap objects.
You have discussion about how perform this here
Possible duplicate of: how to draw image on surfaceview android
First get your canvas by using lockCanvas() (see here), second get your image and make it a drawable using:
my_bitmap = Bitmap.createBitmap(
MediaStore.Images.Media.getBitmap(getContentResolver(), uri),
0,0,90, 90);
drawable=new BitmapDrawable(my_bitmap);
After that you can draw the drawable to the locked canvas and use unlockCanvasAndPost (Canvas canvas) to post the updated canvas back to the surfaceview.
here is the answer for your question.
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mySurfaceView mySurfaceView = new mySurfaceView(getApplicationContext());
setContentView(mySurfaceView);
}
}
mySurfaceView.java
public class mySurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private TutorialThread _thread;
public mySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
_thread = new TutorialThread(getHolder(), this);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap _scratch = BitmapFactory.decodeResource(getResources(),
R.drawable.icon);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(_scratch, 10, 10, null);
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
public void surfaceCreated(SurfaceHolder arg0) {
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class TutorialThread extends Thread {
private SurfaceHolder _surfaceHolder;
private mySurfaceView _panel;
private boolean _run = false;
public TutorialThread(SurfaceHolder surfaceHolder, mySurfaceView 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);
}
}
}
}
}
}
i am trying to run a simple button animation when the game is over
i have the following classes:
GameView
GameThread
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
boolean gameover;GameThread gamethread;
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
gameover=true;
getHolder().addCallback(this); // adding the callback (this) to the surface holder to intercept events
setFocusable(true);// make the GamePanel focusable so it can handle events
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.d("TAG", "surface changed");
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gamethread=new GameThread(getHolder(),this);
gamethread.setrunning(true);
gamethread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry=true;
Log.d("TAG", "Surface destroyed entered");
while(retry){
try {
gamethread.setrunning(false);
gamethread.join();
gamethread=null;
Log.d("tag","thread is destroyed" );
if(gamethread==null){
Log.d("tag","thread is destroyed and null" );
}
retry=false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//end while
}//end method
public void update(){
if(gameover){
Button gameover_button = (Button) findViewById(R.id.gameover);
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.gameoveranimation);
gameover_button.startAnimation(animation);
}
}//end update`
public class GameThread extends Thread {
// desired fps
private boolean running;
private SurfaceHolder surfaceholder;
private GameView gameview;
public GameThread(SurfaceHolder surfaceHolder, GameView gameview){
this.gameview=gameview;
this.surfaceholder=surfaceHolder; //we need the surfaceholder since we need to lock surface before drawing
}
public void setrunning(boolean running){
this.running=running;
}
#Override
public void run(){
super.run();
Canvas canvas;
while(running){
canvas=null;
canvas = this.surfaceholder.lockCanvas();
synchronized (surfaceholder) {
if(canvas!=null){
canvas.drawColor(Color.BLACK);
this.gameview.Draw1(canvas);
this.gameview.update();
}//end if
}
}
}finally{//in case of an exception
if(canvas!=null){
surfaceholder.unlockCanvasAndPost(canvas);
}
}//end finally
}//end loop
}//end run
}
<br>
everything works fine except the animation in the update method in the gameview class
the animation runs fine in the oncreate method
i know that my problem is related to thread but i don't know much about them
thx in advance
`
If your issue is that you need to call update on the UI thread, then you can use this:
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
this.gameview.update();
}
});
Also, it looks like you are looking for your game-over button as a child of your surface view. If that is not correct, you can find the view relative to the parent activity.
Activity activity = (Activity) getContext();
Button gameover_button = (Button) activity.findViewById(R.id.gameover);
I find many SurfaceView demos use hasSurface. But I can't understand it. What is the meaning of hasSurface? Is there anybody help me?
import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MySurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder holder;
private MySurfaceViewThread mySurfaceViewThread;
private boolean hasSurface;
MySurfaceView(Context context) {
super(context);
init();
}
private void init() {
// Create a new SurfaceHolder and assign this
// class as its callback.
holder = getHolder();
holder.addCallback(this);
hasSurface = false;
}
public void resume() {
// Create and start the graphics update thread.
if (mySurfaceViewThread == null) {
mySurfaceViewThread = new MySurfaceViewThread();
if (hasSurface == true)
mySurfaceViewThread.start();
}
}
public void pause() {
// Kill the graphics update thread
if (mySurfaceViewThread != null) {
mySurfaceViewThread.requestExitAndWait();
mySurfaceViewThread = null;
}
}
public void surfaceCreated(SurfaceHolder holder) {
hasSurface = true;
if (mySurfaceViewThread != null)
mySurfaceViewThread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
pause();
}
public void surfaceChanged(SurfaceHolder holder, int format,
int w, int h) {
if (mySurfaceViewThread != null)
mySurfaceViewThread.onWindowResize(w, h);
}
class MySurfaceViewThread extends Thread {
private boolean done;
MySurfaceViewThread() {
super();
done = false;
}
#Override
public void run() {
SurfaceHolder surfaceHolder = holder;
// Repeat the drawing loop until the thread is stopped.
while (!done) {
// Lock the surface and return the canvas to draw onto.
Canvas canvas = surfaceHolder.lockCanvas();
// TODO: Draw on the canvas!
// Unlock the canvas and render the current image.
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void requestExitAndWait() {
// Mark this thread as complete and combine into
// the main application thread.
done = true;
try {
join();
} catch (InterruptedException ex) { }
}
public void onWindowResize(int w, int h) {
// Deal with a change in the available surface size.
}
}
}
The Surface is created while the SurfaceView's window is visible so you need to know if your code can or cannot access it yet. You should implement surfaceCreated() and surfaceDestroyed() to be informed when the Surface is created and destroyed as the window is shown and hidden, so basically hasSurface (or whatever name you use) keeps the last known status of your surfrace for simplicity.
We cannot acquire the Canvas from the SurfaceHolder as long as the Surface is not yet valid. However, we can check whether the Surface has been created or not via the following statement:
boolean isCreated = surfaceHolder.getSurface().isValid();
I assume hasSurface is similar to getSurface().isValid().
#Override
public void run() {
SurfaceHolder surfaceHolder = holder;
// Repeat the drawing loop until the thread is stopped.
while (!done) {
if(!holder.getSurface().isValid())
continue;
// Lock the surface and return the canvas to draw onto.
Canvas canvas = surfaceHolder.lockCanvas();
// TODO: Draw on the canvas!
// Unlock the canvas and render the current image.
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public class CannonView extends SurfaceView implements SurfaceHolder.Callback {
CannonThread cannonThread;
private Paint blockerPaint;
public CannonView(Context context, AttributeSet attrs) {
super(context, attrs);
blockerPaint = new Paint();
blockerPaint.setStrokeWidth(10.0f);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
cannonThread = new CannonThread(holder);
cannonThread.running(true);
cannonThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
cannonThread.running(false);
while (retry) {
try {
cannonThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
private class CannonThread extends Thread {
boolean setRunning;
SurfaceHolder surfaceHolder;
public CannonThread(SurfaceHolder holder) {
setRunning = true;
surfaceHolder = holder;
}
public void running(boolean isRunning) {
setRunning = isRunning;
}
#Override
public void run() {
Canvas canvas = null;
while (setRunning) {
try {
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
canvas.drawLine(0, 0, 100, 100, blockerPaint);
}
} finally {
if (canvas != null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
Although above code is very simple
but it is not drawing anything on my activity ..
Logcat says i am doing too much work on main thread ..62 frame skipped ..
Please help
Well this isn't my style of coding, so I decided that you need to simplify things a bit. You used the android api guides but they suck.
Watch the following videos and you should be fine.
http://www.youtube.com/watch?v=wUmId0rwsBQ&list=SP2F07DBCDCC01493A&index=67
http://www.youtube.com/watch?v=0wy907WZFiA&list=SP2F07DBCDCC01493A
http://www.youtube.com/watch?v=ZMcYbf9Hhe4&list=SP2F07DBCDCC01493A
http://www.youtube.com/watch?v=yowNavIDzzE&list=SP2F07DBCDCC01493A
I'm experimenting with SurfaceView and creating simple animations with it, I don't understand why my animation (changins screen color from black to white) is working here (without using SurfaceHolder.Callback)
public class MySurface extends SurfaceView {
private boolean playing = true;
private int counter = 0;
public MySurface(Context context){
super(context);
new Anim().start();
}
private class Anim extends Thread {
#Override
public void run() {
while (playing) {
try {
sleep(1000);
draw();
counter++;
} catch (Exception e){
e.printStackTrace();
}
}
}
private void draw() {
SurfaceHolder holder = getHolder();
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
if (counter % 2 == 0) {
canvas.drawColor(Color.WHITE);
} else
canvas.drawColor(Color.BLACK);
holder.unlockCanvasAndPost(canvas);
}
}
}
}
But it's not working here, what's the difference? I think it might be caused by calling .run() instead of .start()
public class MySurface extends SurfaceView implements SurfaceHolder.Callback {
Anim anim;
private boolean playing = true;
private int counter = 0;
public MySurface(Context context){
super(context);
}
// all Callbacks are overrided i didn add them to make code easier to read
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
anim = new Anim();
anim.run();
}
private class Anim extends Thread {
#Override
public void run() {
while (playing) {
try {
sleep(1000);
draw();
counter++;
} catch (Exception e){
e.printStackTrace();
}
}
}
private void draw() {
SurfaceHolder holder = getHolder();
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
if (counter % 2 == 0) {
canvas.drawColor(Color.WHITE);
} else
canvas.drawColor(Color.BLACK);
holder.unlockCanvasAndPost(canvas);
}
}
}
}
EDIT: I forgot to add getHolder().addCallback(this) that caused the issue.
Could someone still point out the differnces between those two methods and tell which one is better?
EDIT2: anim.start() was also needed