如何在surfaceview上绘图?

问题描述 投票:1回答:2

嗨,伙计们,我想做一个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() {

    }

}

这是我获得方法和自定义表面视图的库项目类。

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;
        }
    }

}
android android-camera android-canvas surfaceview qr-code
2个回答
1
投票

A Surface 是生产者-消费者缓冲队列安排的一部分。 你的应用程序是在生产者端,对于一个叫做 SurfaceView 系统合成器(SurfaceFlinger)在消费端。

一个曲面一次只能有一个生产者。 您已经将相机预览建立为生产者,所以不可能同时连接一个新的生产者。Canvas 来执行绘图。 你没有看到失败,因为你使用的是 new Canvas 营造 Canvas 在真空中 -- 它没有连接到任何东西。 (通常你会用 Surface#lockCanvas() 以获得 Canvas 与曲面相关联。)

曲面是一个完全独立的图层,默认情况下是合成在其他所有的图层后面,这意味着你可以用自定义的视图在上面画画。 我不认为你需要一个额外的视图对象 -- 我相信你可以通过在视图中的 "视图 "部分来实现。SurfaceView 本身,它应该有一个透明的背景。 参见"风俗画"文档。

如果你想搞得花里胡哨,你可以把摄像机预览送入OpenGL ES,但这对你的需求来说可能是过分的。 (一些例子 此处.) 另外,如果你想了解更多关于Android图形架构的信息,请看 本文件.

© www.soinside.com 2019 - 2024. All rights reserved.