以下程序绘制了一个小云
在用户触摸的位置。
如果您将/*A*/
替换为///*A*/
,则运行正常。
但它很迟钝。因此(没有注释/*A*/s
)绘图是在一个单独的线程上完成的。为了保持平滑的交互,当用户移动指针时线程被中断。
如何修改此代码以在单独的线程上正确绘制?
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;
}
}
布局activity_main
仅包含
<.MyView
android:id="@+id/myView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
并且活动将视图设置为
setContentView(R.layout.activity_main);
答案 0 :(得分:1)
你做不到。传递给Canvas
的{{1}}只能在主线程(UI线程)中使用。您应该使用View.onDraw()
(允许您使用任意SurfaceView
线程),Canvas
(相同的东西)或渲染到中间位图。
答案 1 :(得分:1)
在这种情况下,必须在UI线程上完成绘图。另一件事是我很确定绘图只能在onDraw()内部安全地完成。你的代码很慢,因为你经历了一个很长的循环。
因此,您将创建一个单独的线程,将该图像生成到Bitmap中。然后与UI线程共享该位图,您实际上可以将该位图绘制到画布上。您可能需要一组wait()和notify()调用来以锁步方式获取作业,但是,在另一个线程上执行此操作可能仍然太慢。如果你真的想让这个图像变得动态,我会
1)每隔几帧只更新UI上的位图,但你仍然可以用另一个线程生成图像
2)使用另一种方法生成不必循环多次的图像
在最佳实践中,你的onDraw()应该尽可能快,而不是做任何密集的工作。