如何在表面视图上绘图?
Posted
技术标签:
【中文标题】如何在表面视图上绘图?【英文标题】:How to draw on a surfaceview? 【发布时间】:2014-12-21 07:10:26 【问题描述】:大家好,我正在尝试制作一个 QRCode 阅读器,所以我使用了 dlzaaro66 提供的 QRCodeReaderView 库,它提供了 Zxing 库的简单实现。代码正在扫描二维码,但我想制作一个参考框,以便在相机表面视图上指示代码被扫描的位置我尝试使用正常绘制技术。它没有给出任何错误,但它也没有绘制,你能帮我解决可能出现问题的地方吗?
这是我的活动课。
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;
【问题讨论】:
【参考方案1】:Surface
是生产者-消费者缓冲区队列安排的一部分。您的应用程序位于生产者端,而对于 SurfaceView
,系统合成器 (SurfaceFlinger) 位于消费者端。
一个表面一次只能有一个生产者。您已将相机预览设置为制作者,因此无法同时连接Canvas
进行绘图。您没有看到失败,因为您使用new Canvas
在真空中创建Canvas
- 它没有连接到任何东西。 (通常您会使用 Surface#lockCanvas()
来获取与 Surface 关联的 Canvas
。)
表面是一个完全独立的图层,默认情况下合成在其他所有图层之后,这意味着您可以使用自定义视图在其上进行绘制。不过,我认为您不需要额外的视图对象——我相信您可以使用SurfaceView
本身的“视图”部分来完成它,它应该有一个透明的背景。请参阅“custom drawing”文档。
如果您想变得花哨,可以将相机预览提供给 OpenGL ES,但这可能超出您的需要。 (一些例子here。)另外,如果你想了解更多关于Android图形架构的信息,请参阅this document。
【讨论】:
以上是关于如何在表面视图上绘图?的主要内容,如果未能解决你的问题,请参考以下文章