扩展SurfaceView的onDraw()方法从未被调用

39
我想修改用于相机预览的SurfaceView以显示覆盖在其上方的正方形。但是,扩展的SurfaceView的onDraw方法从未被调用。
这是源代码:
public class CameraPreviewView extends SurfaceView {

    protected final Paint rectanglePaint = new Paint();

    public CameraPreviewView(Context context, AttributeSet attrs) {
        super(context, attrs);
        rectanglePaint.setARGB(255, 200, 0, 0);
        rectanglePaint.setStyle(Paint.Style.FILL);
        rectanglePaint.setStrokeWidth(2);
    }

    @Override
    protected void onDraw(Canvas canvas){
        canvas.drawRect(new Rect(10,10,200,200), rectanglePaint);
        Log.w(this.getClass().getName(), "On Draw Called");
    }
}

public class CameraPreview extends Activity implements SurfaceHolder.Callback{

    private SurfaceHolder holder;
    private Camera camera;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        // We remove the status bar, title bar and make the application fullscreen
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // We set the content view to be the layout we made
        setContentView(R.layout.camera_preview);

        // We register the activity to handle the callbacks of the SurfaceView
        CameraPreviewView surfaceView = (CameraPreviewView) findViewById(R.id.camera_surface);
        holder = surfaceView.getHolder();

        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

        Camera.Parameters params = camera.getParameters();

        params.setPreviewSize(width, height);
        camera.setParameters(params);

        try {
            camera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }

        camera.startPreview();

    }

    public void surfaceCreated(SurfaceHolder holder) {
        camera = Camera.open();
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview();
        camera.release();   
    }


}

就记录而言,我是根据调试器在方法中断点上没有停止,而在构造函数中断点上停止的事实来说onDraw从未被调用。另一方面,相机预览正在正确显示。 - Gab Royer
4个回答

83

我在Android开发者Google小组上找到了它。你只需要添加:

setWillNotDraw(false)

传递给构造函数。如果有人能解释一下为什么,那将不胜感激。


哇,谢谢您,我一直想知道为什么我的View的onDraw()方法从未被调用。我看到javadoc上说:“通常,如果您重写onDraw(Canvas),则应清除此标志。”,所以如果您有一个没有任何小部件的View,似乎需要设置这个标志。只是猜测 :) - Shawn Lauzon
哦,天啊,感谢您的回答。我遇到了一个不同的问题,但也是由于没有设置这个标志引起的。我已经寻找解决方案很久了。 - user272043
10
SurfaceView默认情况下将setWillNotDraw设置为false,因为它的更新通常来自另一个线程(使用getHolder().lockCanvas())。在onDraw中添加额外的绘图会降低效率。如果只需要onDraw,则可以直接从android.view.View派生。 - Juozas Kontvainis

27

小修正:

在构造函数中添加setWillNotDraw(false)会导致崩溃,因为底层Surface对象尚未创建。

相反,将setWillNotDraw(false)语句放在surfaceCreated()方法中。这会延迟调用,直到有一个真正的对象可用,并且能够正常工作。

(非常感谢发布此解决方案的用户,它为我解决了一个重大问题)


虽然人们经常对 SurfaceView 进行子类化并实现 SurfaceHolder.Callback,但这不是必需的;只有在他们的类实现它时,在 surfaceCreated 中这样做才是一个选项。另外,什么情况下在构造函数中调用 setWillNotDraw(false) 会导致崩溃? - Brian Vandenberg

7
罗曼·盖伊解释了它:
为了提高效率,布局不会调用其onDraw()方法。要启用它,请调用setWillNotDrawEnabled(false)(或将等效的XML属性设置为false)。

XML属性的等效是什么? - Archimedes Trajano

5

根据以上所有评论,我使用了以下方法:

在 onAttachedToWindow() 事件中添加 setWillNotDraw(false)。

@Override protected void onAttachedToWindow() { super.onAttachedToWindow(); setWillNotDraw(false); }

这样就可以解决问题了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接