Reputation: 25
Hi guys I was trying to make a QRCode reader so I used the used the QRCodeReaderView library provided by dlzaaro66 which provides easy implementation of Zxing library. The code is scanning the qrcode but i wanted to make sort of a reference box so as to indicate the whereabouts of where the code is being scanned from on the camera surface view I tried to use the normal draw technique. Its not giving any error but its not drawing either could you help me with where the problem might be occurring.
This my activity class.
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() {
}
}
This is the library project class from where I am getting the methods and the custom 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;
}
}
}
Upvotes: 1
Views: 1107
Reputation: 52313
A Surface
is part of a producer-consumer buffer queue arrangement. Your application is on the producer end, and for a SurfaceView
the system compositor (SurfaceFlinger) is on the consumer end.
A surface can have only one producer at a time. You've established the camera preview as the producer, so it's not possible to also connect a Canvas
to perform drawing. You're not seeing failures because you're using new Canvas
to create a Canvas
in a vacuum -- it's not connected to anything. (Normally you'd use Surface#lockCanvas()
to get the Canvas
associated with the Surface.)
The surface is a completely separate layer, composited behind everything else by default, which means you can draw on top of it with a custom View. I don't think you need an additional view object though -- I believe you can do it with the 'view' part of the SurfaceView
itself, which should have a transparent background. See the "custom drawing" documentation.
If you want to get fancy you can feed the camera preview to OpenGL ES, but that's probably excessive for what you need. (Some examples here.) Also, if you want to learn more about the Android graphics architecture, see this document.
Upvotes: 1