如何在surfaceview上绘图?

时间:2014-10-25 17:08:09

标签: android android-camera android-canvas surfaceview qr-code

大家好我正在尝试制作一个QRCode阅读器,所以我使用了dlzaaro66提供的QRCodeReaderView库,它提供了Zxing库的简单实现。代码正在扫描qrcode,但我想制作一个参考盒,以便在相机表面视图上指示扫描代码的位置,我试图使用常规绘制技术。它没有给出任何错误,但它没有绘图也可以帮助我解决可能发生的问题。

这是我的活动课。

import android.app.Activity;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.Toast;

import com.dlazaro66.qrcodereaderview.QRCodeReaderView;
import com.dlazaro66.qrcodereaderview.QRCodeReaderView.OnQRCodeReadListener;


public class MyActivity extends Activity implements OnQRCodeReadListener{

    QRCodeReaderView decoder;
    Switch start_stop;
    Paint paint;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        decoder = (QRCodeReaderView) findViewById(R.id.view2);
        decoder.setOnQRCodeReadListener(this);
        start_stop=(Switch) findViewById(R.id.switch1);
        start_stop.setChecked(true);

        start_stop.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if(b){
                    decoder.getCameraManager().startPreview();
                }
                else{
                    decoder.getCameraManager().stopPreview();
                }
            }
        });
        paint= new Paint();
        paint.setColor(Color.RED);
        paint.setStrokeWidth(100);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.my, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onQRCodeRead(String text, PointF[] points) {
        start_stop.setChecked(false);
        if(text.startsWith("http")){
            Toast.makeText(getApplicationContext(),text,Toast.LENGTH_SHORT).show();
            final Intent intent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse(text));
            startActivity(intent);
        }
        else{
            Toast.makeText(getApplicationContext(),text,Toast.LENGTH_SHORT).show();
        }
        Canvas canvas=new Canvas();
        for(int i=0;i<points.length-1;i++){
            canvas.drawLine(points[i].x,points[i].y,points[i+1].x,points[i+1].y,paint);
        }

    }

    @Override
    public void cameraNotFound() {

    }

    @Override
    public void QRCodeNotFoundOnCamImage() {

    }

}

这是我从中获取方法和自定义surfaceview

的库项目类
public class QRCodeReaderView extends SurfaceView implements SurfaceHolder.Callback,Camera.PreviewCallback {

    public interface OnQRCodeReadListener {

        public void onQRCodeRead(String text, PointF[] points);
        public void cameraNotFound();
        public void QRCodeNotFoundOnCamImage();
    }

    private OnQRCodeReadListener mOnQRCodeReadListener;

    private static final String TAG = QRCodeReaderView.class.getName();

    private QRCodeReader mQRCodeReader;
    private int mPreviewWidth; 
    private int mPreviewHeight; 
    private SurfaceHolder mHolder;
    private CameraManager mCameraManager;

    public QRCodeReaderView(Context context) {
        super(context);
        init();
    }

    public QRCodeReaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public void setOnQRCodeReadListener(OnQRCodeReadListener onQRCodeReadListener) {
        mOnQRCodeReadListener = onQRCodeReadListener;
    }

    public CameraManager getCameraManager() {
        return mCameraManager;
    }

    @SuppressWarnings("deprecation")
    private void init() {
        if (checkCameraHardware(getContext())){
            mCameraManager = new CameraManager(getContext());

            mHolder = this.getHolder();
            mHolder.addCallback(this);
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  // Need to set this flag despite it's deprecated
        } else {
            Log.e(TAG, "Error: Camera not found");
            mOnQRCodeReadListener.cameraNotFound();
        }
    }



    /****************************************************
     * SurfaceHolder.Callback,Camera.PreviewCallback
     ****************************************************/

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // Indicate camera, our View dimensions
            mCameraManager.openDriver(holder,this.getWidth(),this.getHeight());
        } catch (IOException e) {
            Log.w(TAG, "Can not openDriver: "+e.getMessage());
            mCameraManager.closeDriver();
        }

        try {
            mQRCodeReader = new QRCodeReader();
            mCameraManager.startPreview();
        } catch (Exception e) {
            Log.e(TAG, "Exception: " + e.getMessage());
            mCameraManager.closeDriver();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(TAG, "surfaceDestroyed");
        mCameraManager.getCamera().setPreviewCallback(null);
        mCameraManager.getCamera().stopPreview();
        mCameraManager.getCamera().release();
        mCameraManager.closeDriver();
    }

    // Called when camera take a frame 
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {

        PlanarYUVLuminanceSource source = mCameraManager.buildLuminanceSource(data, mPreviewWidth, mPreviewHeight);

        HybridBinarizer hybBin = new HybridBinarizer(source);
        BinaryBitmap bitmap = new BinaryBitmap(hybBin);

        try {
            Result result = mQRCodeReader.decode(bitmap);  

            // Notify We're found a QRCode
            if (mOnQRCodeReadListener != null) {
                    // Transform resultPoints to View coordinates
                    PointF[] transformedPoints = transformToViewCoordinates(result.getResultPoints());
                    mOnQRCodeReadListener.onQRCodeRead(result.getText(), transformedPoints);
            }

        } catch (ChecksumException e) {
            Log.d(TAG, "ChecksumException");
            e.printStackTrace();
        } catch (NotFoundException e) {
            // Notify QR not found
            if (mOnQRCodeReadListener != null) {
                mOnQRCodeReadListener.QRCodeNotFoundOnCamImage();
            }
        } catch (FormatException e) {
            Log.d(TAG, "FormatException");
            e.printStackTrace();
        } finally {
            mQRCodeReader.reset();
        }
    }



    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.d(TAG, "surfaceChanged");

        if (mHolder.getSurface() == null){
            Log.e(TAG, "Error: preview surface does not exist");
            return;
        }

        //preview_width = width;
        //preview_height = height;

        mPreviewWidth = mCameraManager.getPreviewSize().x;
        mPreviewHeight = mCameraManager.getPreviewSize().y;


        mCameraManager.stopPreview();
        mCameraManager.getCamera().setPreviewCallback(this);
        mCameraManager.getCamera().setDisplayOrientation(90); // Portrait mode

        mCameraManager.startPreview();
    }

    /**
     * Transform result to surfaceView coordinates
     * 
     * This method is needed because coordinates are given in landscape camera coordinates.
     * Now is working but transform operations aren't very explained
     * 
     * TODO re-write this method explaining each single value    
     * 
     * @return a new PointF array with transformed points
     */
    private PointF[] transformToViewCoordinates(ResultPoint[] resultPoints) {

        PointF[] transformedPoints = new PointF[resultPoints.length];
        int index = 0;
        if (resultPoints != null){
            float previewX = mCameraManager.getPreviewSize().x;
            float previewY = mCameraManager.getPreviewSize().y;
            float scaleX = this.getWidth()/previewY;
            float scaleY = this.getHeight()/previewX;

            for (ResultPoint point :resultPoints){
                PointF tmppoint = new PointF((previewY- point.getY())*scaleX, point.getX()*scaleY);
                transformedPoints[index] = tmppoint;
                index++;
            }
        }
        return transformedPoints;

    }


    /** Check if this device has a camera */
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
            // this device has a camera
            return true;
        } 
        else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)){
            // this device has a front camera
            return true;
        }
        else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)){
            // this device has any camera
            return true;
        }
        else {
            // no camera on this device
            return false;
        }
    }

}

1 个答案:

答案 0 :(得分:1)

Surface是生产者 - 消费者缓冲队列排列的一部分。您的应用程序位于生产者端,而SurfaceView系统合成器(SurfaceFlinger)位于消费者端。

表面一次只能有一个生产者。您已将相机预览设置为制作人,因此无法连接Canvas以执行绘图。您没有看到失败,因为您正在使用new Canvas在真空中创建Canvas - 它与任何内容都没有关联。 (通常您会使用Surface#lockCanvas()来获取与Surface关联的Canvas。)

表面是一个完全独立的图层,默认情况下在其他所有图层后面合成,这意味着您可以使用自定义视图在其上绘制。我不认为你需要一个额外的视图对象 - 我相信你可以用SurfaceView本身的'view'部分来做,它应该有一个透明的背景。请参阅“custom drawing”文档。

如果你想获得幻想,你可以将相机预览提供给OpenGL ES,但这可能超出了你的需求。 (一些示例here。)另外,如果您想了解有关Android图形架构的更多信息,请参阅this document