Wie zeichne ich eine Überlagerung auf einer SurfaceView, die von Camera unter Android verwendet wird?

72

Ich habe ein einfaches Programm, das die Vorschau des Camerain ein zeichnet SurfaceView. Ich versuche, die onPreviewFrameMethode zu verwenden, die jedes Mal aufgerufen wird, wenn ein neuer Frame in die gezeichnet SurfaceViewwird, um die invalidateMethode auszuführen, die die Methode aufrufen soll onDraw. Tatsächlich wird die onDrawMethode aufgerufen, aber es wird nichts gedruckt (ich denke, die Kameravorschau überschreibt den Text, den ich zeichnen möchte).

Dies ist eine vereinfachte Version der SurfaceViewUnterklasse, die ich habe:

public class Superficie extends SurfaceView implements SurfaceHolder.Callback {
 SurfaceHolder mHolder;
 public Camera camera;
 Superficie(Context context) {
  super(context);
  mHolder = getHolder();
  mHolder.addCallback(this);
  mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
 }
 public void surfaceCreated(final SurfaceHolder holder) {
  camera = Camera.open();
  try {
   camera.setPreviewDisplay(holder);
   camera.setPreviewCallback(new PreviewCallback() {
    public void onPreviewFrame(byte[] data, Camera arg1) {
     invalidar();
    }
   });
  } catch (IOException e) {}
 }
 public void invalidar(){
  invalidate();
 }
 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
  Camera.Parameters parameters = camera.getParameters();
  parameters.setPreviewSize(w, h);
  camera.setParameters(parameters);
  camera.startPreview();
 }
 @Override
 public void draw(Canvas canvas) {
  super.draw(canvas);
  // nothing gets drawn :(
  Paint p = new Paint(Color.RED);
  canvas.drawText("PREVIEW", canvas.getWidth() / 2,
    canvas.getHeight() / 2, p);
 }
}
Cristian
quelle
Kannst du mir helfen, wie du es getan hast? Ich versuche, die Zeichnungsfarbe beim Klicken auf die Schaltfläche zu ändern, wie BUTTON1-> rote Schaltfläche2-> Gelb usw. Wie mache ich das mit Leinwand? Ich möchte auf Live-Kamera genauso wie Live-Kamera-Effekte
anwenden

Antworten:

87

SurfaceViewfunktioniert Viewin dieser Hinsicht wahrscheinlich nicht wie ein Stammgast .

Gehen Sie stattdessen wie folgt vor:

  1. Fügen Sie Ihr SurfaceViewInneres in eine FrameLayoutoder RelativeLayoutin Ihre Layout-XML-Datei ein, da beide das Stapeln von Widgets auf der Z-Achse ermöglichen
  2. Verschieben Sie Ihre Zeichenlogik in eine separate benutzerdefinierte ViewKlasse
  3. Fügen Sie der Layout-XML-Datei als untergeordnetes Element des FrameLayoutoder eine Instanz der benutzerdefinierten View-Klasse hinzu, lassen Sie RelativeLayoutsie jedoch nach demSurfaceView

Dadurch wird Ihre benutzerdefinierte ViewKlasse über dem angezeigt SurfaceView.

Hier finden Sie ein Beispielprojekt , bei dem Popup-Bedienfelder über ein SurfaceViewfür die Videowiedergabe verwendetes Fenster gelegt werden .

CommonsWare
quelle
Vielen Dank ... das ist ein schönes Beispiel und ich bin sicher, das ist alles, was ich tun muss, um zu tun, was ich will :)
Cristian
@CommonsWare Funktioniert diese von Ihnen angegebene Antwort also in einer Situation, in der Sie versuchen, eine Oberflächenansicht über eine andere Oberflächenansicht zu legen? Was meinst du mit benutzerdefinierter View-Klasse? Der Grund, den ich frage
GobiasKoffi
@rohanbk: Sie können nicht zwei SurfaceViewWidgets überlagern , wie ich es verstehe. "Was meinst du mit benutzerdefinierter View-Klasse?" - Ihre eigene Unterklasse android.view.Viewoder Ihre eigene Unterklasse einer vorhandenen Widget-Klasse.
CommonsWare
@CommonsWare Gibt es Ihrer ehrlichen Meinung nach eine Möglichkeit, das SurfaceViewOverlay-Beispiel in den API-Demos ( developer.android.com/resources/samples/ApiDemos/src/com/… ) so zu ändern , dass ich eine Bitmap zeichnen kann Bilder in einer Oberflächenansicht über einer Kameraoberflächenansicht mit dem FrameLayout?
GobiasKoffi
@rohanbk: Sie brauchen keine, SurfaceViewum Bitmap-Bilder zu zeichnen. ImageViewVerwendet beispielsweise kein SurfaceViewAFAIK.
CommonsWare
17

Versuchen Sie anzurufen setWillNotDraw(false)von surfaceCreated:

public void surfaceCreated(SurfaceHolder holder) {
    try {
        setWillNotDraw(false); 
        mycam.setPreviewDisplay(holder);
        mycam.startPreview();
    } catch (Exception e) {
        e.printStackTrace();
        Log.d(TAG,"Surface not created");
    }
}

@Override
protected void onDraw(Canvas canvas) {

    canvas.drawRect(area, rectanglePaint);
    Log.w(this.getClass().getName(), "On Draw Called");
}

und anrufen invalidatevon onTouchEvent:

public boolean onTouch(View v, MotionEvent event) {

    invalidate();
    return true;
}
maximus
quelle
1

Ich denke, Sie sollten die super.draw()Methode zuerst aufrufen , bevor Sie etwas in der Zeichenmethode von surfaceView tun.

Devashish Kunal
quelle
1
Genau das macht er in seinem Originalcode :)
ITisha