Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)—完整实现(原理:底层SurfaceView+上层绘制ImageView)

最后更新于:2022-04-01 07:24:00

**【后注:】下载代码的注意,我的手机是4.3寸的屏,华为U9200.如果不能运行的请修改参数。看[前文](http://blog.csdn.net/yanzi1225627/article/details/8577756)的第四条。Y的,省的说我传的代码不能用![发火](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-19_569e21ac277db.gif) ** 最近一直在审视以前做过的东西,关于android摄像头预览,预览界面上呈现矩形框,在前文( ### [Android开发 摄像头SurfaceView预览 背景带矩形框 实现(原理:双surfaceview,顶层画矩形框,底层预览视频)](http://blog.csdn.net/yanzi1225627/article/details/7934710) )----http://blog.csdn.net/yanzi1225627/article/details/7934710已经实现。最近发现上层绘制矩形框,用surfaceview有点大材小用了。SurfaceView绘制动画更合适,只绘制个矩形框用ImageView足够了。但有些时候必须要用SurfaceView来实现。比如360手机安全卫士扫描二维码的实现应该就是通过上下两层SurfaceView实现的(见下图)。上层SurfaceView用于显示那个可以旋转的扫描示意框,底层SurfaceView预览摄像头视频。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-19_569e21ac36948.jpg)     废话不说了,稍候几天我会仿照上面360这个扫描二维码的界面做一个工程(结合PreviewCallback),公开出来。这次先谈用底层surfaceView+上层ImageView实现只拍摄矩形框中的图像。新建一个类继承ImageView,源码如下: ~~~ package yan.guoqi.rectphoto; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.ImageView; public class DrawImageView extends ImageView{ public DrawImageView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } Paint paint = new Paint(); { paint.setAntiAlias(true); paint.setColor(Color.RED); paint.setStyle(Style.STROKE); paint.setStrokeWidth(2.5f);//设置线宽 paint.setAlpha(100); }; @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); canvas.drawRect(new Rect(100, 200, 400, 500), paint);//绘制矩形 } } ~~~ 布局文件里与前文http://blog.csdn.net/yanzi1225627/article/details/8577756这里一样,只是在帧布局里加一个上面自定义的DrawImageView,整个布局文件示下: ~~~ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/BestWish" tools:context=".RectPhoto" /> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <SurfaceView android:id="@+id/previewSV" android:layout_width="fill_parent" android:layout_height="800px" /> <yan.guoqi.rectphoto.DrawImageView android:id="@+id/drawIV" android:layout_width="fill_parent" android:layout_height="800px" /> </FrameLayout> <ImageButton android:id="@+id/photoImgBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/photo_img_btn" android:layout_gravity="center" /> </LinearLayout> ~~~      在主程序文件里,onCreate()函数里设置底层SurfaceView为底层且透明(如果不设也可以,默认就是如此): mPreviewSV.setZOrderOnTop(false); mySurfaceHolder.setFormat(PixelFormat.TRANSPARENT);//translucent半透明 transparent透明     在主UI线程里的onCreate()函数里添加代码:        //绘制矩形的ImageView      mDrawIV = (yan.guoqi.rectphoto.DrawImageView)findViewById(R.id.drawIV);      mDrawIV.onDraw(new Canvas());       看上面的DrawImageView的函数里的onDraw,画的矩形是Rect(100, 200, 400, 500)。在onPictureTaken(byte[] data, Camera camera)函数里,先将图片旋转90度,大小成为宽×高(960×1280)。由于预览surfaceview的大小是宽×高(540×800),所以在onPictureTaken函数里将960×1280的图片缩放到540×800, 缩放相同大小后就可以用矩阵的坐标直接截取子图了。核心函数就是这两句:      //将960×1280缩放到540×800 **           Bitmap sizeBitmap = Bitmap.createScaledBitmap(rotaBitmap, 540, 800, true);          Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap, 100, 200, 300, 300);//截取**     注意这个截取的函数参数和矩阵的坐标关系,分别是x轴 y轴起始坐标及 x轴宽度 y轴宽度。截取出来的图片大小应该是300×300. onPictureTaken()函数的源码如下: ~~~ public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub Log.i(tag, "myJpegCallback:onPictureTaken..."); if(null != data){ mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);//data是字节数据,将其解析成位图 myCamera.stopPreview(); isPreview = false; } //设置FOCUS_MODE_CONTINUOUS_VIDEO)之后,myParam.set("rotation", 90)失效。图片竟然不能旋转了,故这里要旋转下 Matrix matrix = new Matrix(); matrix.postRotate((float)90.0); Bitmap rotaBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), matrix, false); //旋转后rotaBitmap是960×1280.预览surfaview的大小是540×800 //将960×1280缩放到540×800 Bitmap sizeBitmap = Bitmap.createScaledBitmap(rotaBitmap, 540, 800, true); Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap, 100, 200, 300, 300);//截取 //保存图片到sdcard if(null != rectBitmap) { saveJpeg(rectBitmap); } //再次进入预览 myCamera.startPreview(); isPreview = true; } ~~~ 涉及到的其他函数如saveJpeg()参见前文: ### [2013新春奉送:Android摄像头开发完美demo---(循环聚焦,缩放大小,旋转picture,查询支持的picturesize, ImageButton按键效果)------------](http://blog.csdn.net/yanzi1225627/article/details/8577756) http://blog.csdn.net/yanzi1225627/article/details/8577756   重复的东西我就不发了。     效果图如下所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-19_569e21ac535f8.jpg) 点击拍照,查看保存后的图片如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-19_569e21ac6d30e.jpg)     反思: 1,SurfaceView为啥 无论translucent半透明还是 transparent透明基本没啥区别?而且surfaceview的setAlpha函数不能用。  2,在这里surfaceview一定要在底层(默认如此),如果设成顶层会看不到红色矩形框。可以自己测试下。 3,最纠结的一点,第一副图片里的360扫描二维码的界面,底层的预览surfaceview是半透明的,底色是灰色的,只有中间的扫描矩形框是透明的,亮色。这一块究竟是怎么实现的??下午实验了n种方法愣是无济于事。我擦。。。如果有高人,希望能不吝指点下。 不过说实话,人家已经设计出来的产品界面看着就是好,不得不服阿。以后要多多模仿钻研这些成型产品的设计。 源码下载:[http://download.csdn.net/detail/yanzi1225627/5063105](http://download.csdn.net/detail/yanzi1225627/5063105)                                            欢迎android爱好者加群248217350,备注:yanzi                                                                     ----------------------------------------本文系原创,转载请注明作者:yanzi1225627    
';