Reputation: 10817
The following program draws a little cloud
at the location the user touches.
If you replace /*A*/
with ///*A*/
, it runs fine.
But it is sluggish. Hence (without commenting out the /*A*/s
) the drawing is done on a separate thread. To maintain a smooth interaction, the thread is interrupted when the user moves the pointer.
How would you modify this code to properly draw on a separate thread?
public class MyView extends View {
/*A*/ protected MyDrawThread myDrawThread;
protected float x, y;
protected Paint paint = new Paint();
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
/*A*/ myDrawThread = new MyDrawThread();
}
@Override protected void onDraw(Canvas canvas)
{
/*A*/ if( myDrawThread.isAlive() )
/*A*/ myDrawThread.interrupt();
/*A*/
/*A*/ myDrawThread.setCanvas( canvas );
/*A*/ myDrawThread.start();
/*A*/ }
/*A*/
/*A*/ class MyDrawThread extends Thread {
/*A*/ private Canvas canvas;
/*A*/
/*A*/ public MyDrawThread() {
/*A*/ super();
/*A*/ }
/*A*/ public void setCanvas( Canvas cnvs ) {
/*A*/ canvas = cnvs;
/*A*/ }
/*A*/ public void run()
/*A*/ {
for(int i=0;i<10000;++i) {
double angle = (double) (Math.random() * 2.0 * Math.PI);
float distance = (float) (Math.random() * getWidth()/25.0f);
float dx = (float) (distance*Math.cos(angle));
float dy = (float) (distance*Math.sin(angle));
canvas.drawCircle(x+dx, y+dy, 0.1f, paint);
}
/*A*/ }
}
@Override public boolean onTouchEvent(MotionEvent event) {
x = event.getX();
y = event.getY();
invalidate();
return true;
}
}
The layout activity_main
consists of just
<.MyView
android:id="@+id/myView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and the activity sets the View through
setContentView(R.layout.activity_main);
Upvotes: 0
Views: 138
Reputation: 988
Drawing has to be done on the UI thread in this case. Another thing is I'm pretty sure the drawing can only be safely done inside of onDraw(). Your code is sluggish because you go through a long loop.
So, instead, you will create a separate thread that will generate that image into a Bitmap. Then sharing that bitmap with the UI thread, you can actually draw that bitmap onto the canvas. You may need a set of wait() and notify() calls to get the job in a lockstep fashion, however, this might still be too slow being done on another thread. If you really want to make this image that dynamic, I would either
1) Only update the bitmap on the UI every several frames, but you can still generate the image with the other thread
2) Use another method for generating the image that would not have to loop so many times
In best practice, your onDraw() should really be as fast as possible, not doing any intensive work.
Upvotes: 1
Reputation: 98501
You can't. The Canvas
passed to View.onDraw()
must only be used from the main thread (UI thread.) You should either use a SurfaceView
(which lets you use a Canvas
from an arbitrary thread), a TextureView
(same thing) or render into an intermediate bitmap.
Upvotes: 1