這幾天有個需求是須要在拍照的時候,截圖選定框內的照片,以下,截取綠色框內的照片java
一般咱們拍照 的話,都是直接調用系統的相機, 這樣的話只能拍出整個屏幕的照片,不能達到我如今想要的效果,因此這時候咱們須要從新來自定義個相機拍照android
首先這是主界面的佈局,也就是拍照的界面
canvas
<FrameLayout 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" > <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <SurfaceView android:id="@+id/previewSV" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <com.example.partialphoto.DrawViewFrame android:id="@+id/drawIV" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </FrameLayout> <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageButton android:id="@+id/takephoto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:background="#01000000" android:src="@drawable/btn_takepic"/> </RelativeLayout> </FrameLayout>
其次這是我程序的入口布局,爲的就是拍照結束後,在ImageView上顯示出來app
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/iv_take" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="拍照" /> <ImageView android:layout_gravity="center" android:id="@+id/iv" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/ic_launcher" /> </LinearLayout>
這是程序入口的代碼,這裏對圖片作了些處理ide
package com.example.partialphoto; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; public class IntentTakePhoto extends Activity { private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_take); Button btn = (Button) findViewById(R.id.iv_take); iv = (ImageView) findViewById(R.id.iv); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(IntentTakePhoto.this,MainActivity.class); // Uri uriPath = Uri.fromFile(new File(Environment.getExternalStorageDirectory(),"image.jpg")); // intent.putExtra("take", uriPath); startActivityForResult(intent, 1); } }); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data); Uri uri = null; Bitmap bitmap; if(resultCode == 0&&requestCode == 1){ // newUri = data.getData(); if(null != data){ String path = data.getStringExtra("path"); uri = Uri.fromFile(new File(path)); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Config.RGB_565; try { bitmap = BitmapFactory.decodeStream(getContentResolver() .openInputStream(uri), null, options); float width = bitmap.getWidth(); float height = bitmap.getHeight(); // 建立操做圖片用的matrix對象 Matrix matrix = new Matrix(); // 計算寬高縮放率 if (width > 600) { float sacleFactor = 0; if (width < height) { matrix.postRotate(90); sacleFactor = 600 / height; } else { sacleFactor = 600 / width; } matrix.postScale(sacleFactor, sacleFactor); bitmap = Bitmap.createBitmap(bitmap, 0, 0, (int) width, (int) height, matrix, true); float newwidth = bitmap.getWidth(); float newheight = bitmap.getHeight(); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法,這裏100表示不壓縮,把壓縮後的數據存放到baos中 int opt = 100; while ( baos.toByteArray().length / 1024>50) { //循環判斷若是壓縮後圖片是否大於100kb,大於繼續壓縮 baos.reset();//重置baos即清空baos bitmap.compress(Bitmap.CompressFormat.JPEG, opt, baos);//這裏壓縮options%,把壓縮後的數據存放到baos中 opt -= 10;//每次都減小10 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把壓縮後的數據baos存放到ByteArrayInputStream中 bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream數據生成圖片 ,而後再賦值給bitmap System.out.println("------回來了"); iv.setImageBitmap(bitmap); Toast.makeText(IntentTakePhoto.this, "-------"+bitmap.getWidth(), 0).show(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
而後這是拍照界面上的代碼佈局
package com.example.partialphoto; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.ShutterCallback; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.View.OnClickListener; import android.widget.ImageButton; import android.widget.Toast; public class MainActivity extends Activity implements Callback { private static final String tag="PartialPhoto"; public static int frame = 0; private boolean isPreview = false; private SurfaceView mPreviewSV = null; private DrawViewFrame mDrawIV = null; private SurfaceHolder mySurfaceHolder = null; private ImageButton mPhotoImgBtn = null; private ImageButton btnAdd = null; private ImageButton btnMinus = null; private Camera myCamera = null; private Bitmap mBitmap = null; private AutoFocusCallback myAutoFocusCallback = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; Window myWindow = this.getWindow(); myWindow.setFlags(flag, flag); setContentView(R.layout.activity_rect_photo); mPreviewSV = (SurfaceView)findViewById(R.id.previewSV); mPreviewSV.setZOrderOnTop(false); mySurfaceHolder = mPreviewSV.getHolder(); mySurfaceHolder.setFormat(PixelFormat.TRANSPARENT); mySurfaceHolder.addCallback(this); mDrawIV = (DrawViewFrame)findViewById(R.id.drawIV); mDrawIV.onDraw(new Canvas()); myAutoFocusCallback = new AutoFocusCallback() { public void onAutoFocus(boolean success, Camera camera) { // TODO Auto-generated method stub if(success) //focus successfully Log.i(tag, "myAutoFocusCallback: success..."); else Log.i(tag, "myAutoFocusCallback: failure"); } }; mPhotoImgBtn = (ImageButton)findViewById(R.id.takephoto); mPhotoImgBtn.setOnClickListener(new OnClickListener(){ public void onClick(View v) { // TODO Auto-generated method stub if(isPreview && myCamera!=null){ // myCamera.takePicture(null, null, myJpegCallback); //without shutter sound myCamera.takePicture(new ShutterCallback() { @Override public void onShutter() { Toast.makeText(MainActivity.this,"myShutterCallback:onShutter..." , MODE_PRIVATE).show(); } }, null, myJpegCallback); //with shutter sound } } }); } //three important callback functions of SurfaceHolder.Callback as follows: //surfaceChanged, surfaceCreated, surfaceDestroyed //This is called immediately after any structural changes have been made to the surface. public void surfaceChanged(SurfaceHolder holder, int format, int prevWidth,int prevHeight) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "surface改變了 了... ... ", 0).show(); if(isPreview){ myCamera.stopPreview(); } if(null != myCamera){ Camera.Parameters myParam = myCamera.getParameters(); myParam.setPictureFormat(ImageFormat.JPEG); //set the stored picture format int width = mDrawIV.getWidth(); int height = mDrawIV.getHeight(); myParam.setPictureSize(width, height); //NOTE: //set the size of preview interface if (getWindowManager().getDefaultDisplay().getRotation() == 0){ myCamera.setDisplayOrientation(90); myParam.setPreviewSize(prevHeight, prevWidth); } else myParam.setPreviewSize(prevWidth, prevHeight); try { myCamera.setParameters(myParam); } catch(Exception e) { e.printStackTrace(); } myCamera.startPreview(); myCamera.autoFocus(myAutoFocusCallback); isPreview = true; } } //This is called immediately after the surface is first created. public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub myCamera = Camera.open(); try { myCamera.setPreviewDisplay(mySurfaceHolder); } catch (IOException e) { // TODO Auto-generated catch block if(null != myCamera){ myCamera.release(); myCamera = null; //Writes a printable representation of this Throwable's stack trace e.printStackTrace(); } } } //This is called immediately before a surface is being destroyed. public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub if(null != myCamera) { myCamera.setPreviewCallback(null); myCamera.stopPreview(); isPreview = false; myCamera.release(); myCamera = null; } } //supply original image data from a photo capture PictureCallback myJpegCallback = new PictureCallback() { public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub if(null != data){ mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length); myCamera.stopPreview(); isPreview = false; } int width = mDrawIV.getWidth(); int height = mDrawIV.getHeight(); int point_dw = (int)(width/10); int point_dh = (int)(5*height/12-height*10/30); int rect_dw = width-2*point_dw; int rect_dh = height-2*point_dh; // Bitmap sizeBitmap = Bitmap.createScaledBitmap(mBitmap, width, height, true); mBitmap = Bitmap.createScaledBitmap(mBitmap, width, height, true); //NOTE: //set the format of the picture of the interception // Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap, (int)width/30, (int)width/45,width-20*width/30, height-20*width/45); mBitmap = Bitmap.createBitmap(mBitmap, point_dw,point_dh, rect_dw,rect_dh); myCamera.startPreview(); isPreview = true; if(null != mBitmap) { String CurrentPhotoFile = ""; CurrentPhotoFile = saveJpeg(mBitmap); //save bitmap as a picture in sdcard Intent intent = new Intent(); intent.putExtra("path", CurrentPhotoFile); setResult(0, intent); finish(); } } }; //save the picture public String saveJpeg(Bitmap bm){ String jpegName = ""; String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/pp/"; //save directory File folder = new File(savePath); if(!folder.exists()) folder.mkdir(); long dataTake = System.currentTimeMillis(); jpegName = savePath + dataTake +".jpg"; try { FileOutputStream fout = new FileOutputStream(jpegName); BufferedOutputStream bos = new BufferedOutputStream(fout); bm.compress(Bitmap.CompressFormat.JPEG, 100, bos); bos.flush(); bos.close(); Log.i(tag, "saveJpeg錛歴torage completed!"); } catch (IOException e) { // TODO Auto-generated catch block Log.i(tag, "saveJpeg: storage failed!"); e.printStackTrace(); } return jpegName; } }
最後這是自定義指定框的類post
package com.example.partialphoto; import android.app.Activity; 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.util.DisplayMetrics; import android.widget.ImageView; public class DrawViewFrame extends ImageView{ public DrawViewFrame(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } Paint paint1 = new Paint(); { paint1.setAntiAlias(true); paint1.setColor(Color.GREEN); paint1.setStyle(Style.STROKE); paint1.setStrokeWidth(8f); paint1.setAlpha(100); }; Paint paint2 = new Paint(); { paint2.setAntiAlias(true); paint2.setColor(Color.BLACK); paint2.setStyle(Style.FILL); paint2.setAlpha(150); }; protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); DisplayMetrics dm = new DisplayMetrics(); ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels; int height = dm.heightPixels; int point_dw1 = (int)(width/10); int point_dh1 = (int)(5*height/12-height*10/30); //int point_dh1 = (int)(height/3); int point_dw2 = width-point_dw1; int point_dh2 = height-point_dh1; //draw a rectangle around viewing frame canvas.drawRect(new Rect(point_dw1, point_dh1, point_dw2, point_dh2), paint1); // canvas.drawRect(new Rect((int)width/30,(int)width/45,(int)11*height/7,(int)19*height/20), paint1); //這裏是框外 陰影部分 canvas.drawRect(new Rect(0, 0, point_dw1, height), paint2); canvas.drawRect(new Rect(point_dw2, 0, width, height), paint2); canvas.drawRect(new Rect(point_dw1, 0, point_dw2, point_dh1), paint2); canvas.drawRect(new Rect(point_dw1, point_dh2, point_dw2, height), paint2); } }
值得說一下:起初我用的是Intent直接傳遞的Bimap對象,可是試了幾回,有時候成功,有時候不成功(onActivityResult方法執行不到),後來百度半天才知道,原來用Intent傳遞Bitmap對象很吃力,由於他只能傳遞小於40k的Bitmap,超過的就不行了,因此最後選擇傳遞Bitmap的保存路徑,而後再根據傳遞的String路徑,經過Uri.fronFile()轉化成Uri,而後經過BitmapFactory.decodeStream(getContentResolver.openInputStream(uri),null,options)來轉化成bitmapthis
補充一下,有時候拍照的話,會很模糊, 緣由是聚焦可能失敗,因此能夠添加下面的方法,調用surfaceview的touch事件.net
mPreviewSV.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { myCamera.autoFocus(myAutoFocusCallback); System.out.println("點擊聚焦"); return false; } });