天天看點

Android Camera——拍照

 <b>Android Camera——拍照</b>

         Camera定義在package android.hardware内,具體用法SDK裡叙述的可清楚了。架構解析什麼的網上也有很多,沒什麼必要講了(你認為我不知道我會說嗎)。

         這篇呢,就整理了下Camera的拍照,其他還木有==

<b>一、系統相機</b>

<b>         </b><b>1)調用方式</b>

         系統相機的入口Action:MediaStore.ACTION_IMAGE_CAPTURE。隻需以startActivityForResult(…)啟動該Activity即可。

// 調用系統相機 

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 

startActivityForResult(intent, 1); 

<b>         2</b><b>)處理方式</b>

         在onActivityResult(…)中,處理傳回資訊。

protected void onActivityResult(int requestCode, int resultCode, Intent data) { 

    if (1 == requestCode) { // 系統相機傳回處理 

        if (resultCode == Activity.RESULT_OK) { 

            Bitmap cameraBitmap = (Bitmap) data.getExtras().get("data"); 

            …… // 處理圖像 

        } 

        takeBtn1.setClickable(true); 

    }  

    super.onActivityResult(requestCode, resultCode, data); 

<b>二、自定義相機</b>

<b>         1</b><b>)照相預覽</b>

         繼承SufaceView寫自己的預覽界面,繼而放到你的照相Activity的布局裡。這裡面有個相機拍照監聽接口,用于在Activity裡再處理這些操作。<b></b>

public class CameraPreview extends SurfaceView implements 

        SurfaceHolder.Callback { 

    /** LOG辨別 */ 

    // private static final String TAG = "CameraPreview"; 

    /** 分辨率 */ 

    public static final int WIDTH = 1024; 

    public static final int HEIGHT = 768; 

    /** 監聽接口 */ 

    private OnCameraStatusListener listener; 

    private SurfaceHolder holder; 

    private Camera camera; 

    // 建立一個PictureCallback對象,并實作其中的onPictureTaken方法 

    private PictureCallback pictureCallback = new PictureCallback() { 

        // 該方法用于處理拍攝後的照片資料 

        @Override 

        public void onPictureTaken(byte[] data, Camera camera) { 

            // 停止照片拍攝 

            camera.stopPreview(); 

            camera = null; 

            // 調用結束事件 

            if (null != listener) { 

                listener.onCameraStopped(data); 

            } 

    }; 

    // Preview類的構造方法 

    public CameraPreview(Context context, AttributeSet attrs) { 

        super(context, attrs); 

        // 獲得SurfaceHolder對象 

        holder = getHolder(); 

        // 指定用于捕捉拍照事件的SurfaceHolder.Callback對象 

        holder.addCallback(this); 

        // 設定SurfaceHolder對象的類型 

        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

    } 

    // 在surface建立時激發 

    public void surfaceCreated(SurfaceHolder holder) { 

        // Log.e(TAG, "==surfaceCreated=="); 

        // 獲得Camera對象 

        camera = Camera.open(); 

        try { 

            // 設定用于顯示拍照攝像的SurfaceHolder對象 

            camera.setPreviewDisplay(holder); 

        } catch (IOException e) { 

            e.printStackTrace(); 

            // 釋放手機攝像頭 

            camera.release(); 

    // 在surface銷毀時激發 

    public void surfaceDestroyed(SurfaceHolder holder) { 

        // Log.e(TAG, "==surfaceDestroyed=="); 

        // 釋放手機攝像頭 

        camera.release(); 

    // 在surface的大小發生改變時激發 

    public void surfaceChanged(final SurfaceHolder holder, int format, int w, 

            int h) { 

        // Log.e(TAG, "==surfaceChanged=="); 

            // 擷取照相機參數 

            Camera.Parameters parameters = camera.getParameters(); 

            // 設定照片格式 

            parameters.setPictureFormat(PixelFormat.JPEG); 

            // 設定預浏尺寸 

            parameters.setPreviewSize(WIDTH, HEIGHT); 

            // 設定照片分辨率 

            parameters.setPictureSize(WIDTH, HEIGHT); 

            // 設定照相機參數 

            camera.setParameters(parameters); 

            // 開始拍照 

            camera.startPreview(); 

        } catch (Exception e) { 

    // 停止拍照,并将拍攝的照片傳入PictureCallback接口的onPictureTaken方法 

    public void takePicture() { 

        // Log.e(TAG, "==takePicture=="); 

        if (camera != null) { 

            // 自動對焦 

            camera.autoFocus(new AutoFocusCallback() { 

                @Override 

                public void onAutoFocus(boolean success, Camera camera) { 

                    if (null != listener) { 

                        listener.onAutoFocus(success); 

                    } 

                    // 自動對焦成功後才拍攝 

                    if (success) { 

                        camera.takePicture(null, null, pictureCallback); 

                } 

            }); 

    // 設定監聽事件 

    public void setOnCameraStatusListener(OnCameraStatusListener listener) { 

        this.listener = listener; 

    /** 

     * 相機拍照監聽接口 

     */ 

    public interface OnCameraStatusListener { 

        // 相機拍照結束事件 

        void onCameraStopped(byte[] data); 

        // 拍攝時自動對焦事件 

        void onAutoFocus(boolean success); 

<b>         2</b><b>)照相活動</b>

         就是我們自己做的照相Activity了。完成後調用自己的相機,也就是跳轉入這個Activity。這裡面,照片以自定義路徑的形式存入的媒體庫。<b></b>

public class CameraActivity extends Activity implements 

        CameraPreview.OnCameraStatusListener { 

    public static final Uri IMAGE_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 

    public static final String PATH = Environment.getExternalStorageDirectory() 

            .toString() + "/AndroidMedia/"; 

    private CameraPreview mCameraPreview; 

    private ImageView focusView; 

    private boolean isTaking = false; // 拍照中 

    @Override 

    public void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        // 設定橫屏 

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 

        // 設定全屏 

        requestWindowFeature(Window.FEATURE_NO_TITLE); 

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 

                WindowManager.LayoutParams.FLAG_FULLSCREEN); 

        // 設定布局視圖 

        setContentView(R.layout.camera); 

        // 照相預覽界面 

        mCameraPreview = (CameraPreview) findViewById(R.id.preview); 

        mCameraPreview.setOnCameraStatusListener(this); 

        // 焦點圖檔 

        focusView = (ImageView) findViewById(R.id.focusView); 

     * 觸屏事件 

    public boolean onTouchEvent(MotionEvent event) { 

        if (event.getAction() == MotionEvent.ACTION_DOWN &amp;&amp; !isTaking) { 

            isTaking = true; 

            mCameraPreview.takePicture(); 

        return super.onTouchEvent(event); 

     * 存儲圖像并将資訊添加入媒體資料庫 

    private Uri insertImage(ContentResolver cr, String name, long dateTaken, 

            String directory, String filename, Bitmap source, byte[] jpegData) { 

        OutputStream outputStream = null; 

        String filePath = directory + filename; 

            File dir = new File(directory); 

            if (!dir.exists()) { 

                dir.mkdirs(); 

            File file = new File(directory, filename); 

            if (file.createNewFile()) { 

                outputStream = new FileOutputStream(file); 

                if (source != null) { 

                    source.compress(CompressFormat.JPEG, 75, outputStream); 

                } else { 

                    outputStream.write(jpegData); 

        } catch (FileNotFoundException e) { 

            return null; 

        } finally { 

            if (outputStream != null) { 

                try { 

                    outputStream.close(); 

                } catch (Throwable t) { 

        ContentValues values = new ContentValues(7); 

        values.put(MediaStore.Images.Media.TITLE, name); 

        values.put(MediaStore.Images.Media.DISPLAY_NAME, filename); 

        values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken); 

        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); 

        values.put(MediaStore.Images.Media.DATA, filePath); 

        return cr.insert(IMAGE_URI, values); 

     * 相機拍照結束事件 

    public void onCameraStopped(byte[] data) { 

        Log.e("onCameraStopped", "==onCameraStopped=="); 

        // 建立圖像 

        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 

        // 系統時間 

        long dateTaken = System.currentTimeMillis(); 

        // 圖像名稱 

        String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken) 

                .toString() + ".jpg"; 

        // 存儲圖像(PATH目錄) 

        Uri uri = insertImage(getContentResolver(), filename, dateTaken, PATH, 

                filename, bitmap, data); 

        // 傳回結果 

        Intent intent = getIntent(); 

        intent.putExtra("uriStr", uri.toString()); 

        intent.putExtra("dateTaken", dateTaken); 

        // intent.putExtra("filePath", PATH + filename); 

        // intent.putExtra("orientation", orientation); // 拍攝方向 

        setResult(20, intent); 

        // 關閉目前Activity 

        finish(); 

     * 拍攝時自動對焦事件 

    public void onAutoFocus(boolean success) { 

        // 改變對焦狀态圖像 

        if (success) { 

            focusView.setImageResource(R.drawable.focus2); 

        } else { 

            focusView.setImageResource(R.drawable.focus1); 

            Toast.makeText(this, "焦距不準,請重拍!", Toast.LENGTH_SHORT).show(); 

            isTaking = false; 

<b>3</b><b>)相機調用&amp;</b><b>處理</b>

// 調用自定義相機 

Intent intent = new Intent(this, CameraActivity.class); 

startActivityForResult(intent, 2); 

    if (2 == requestCode) { // 自定義相機傳回處理 

        // 拍照成功後,響應碼是20 

        if (resultCode == 20) { 

            Bundle bundle = data.getExtras(); 

            // 獲得照片uri 

            Uri uri = Uri.parse(bundle.getString("uriStr")); 

            // 獲得拍照時間 

            long dateTaken = bundle.getLong("dateTaken"); 

            try { 

                // 從媒體資料庫擷取該照片 

                Bitmap cameraBitmap = MediaStore.Images.Media.getBitmap( 

                        getContentResolver(), uri); 

                previewBitmap(cameraBitmap); // 預覽圖像 

                // 從媒體資料庫删除該照片(按拍照時間) 

                getContentResolver().delete( 

                        CameraActivity.IMAGE_URI, 

                        MediaStore.Images.Media.DATE_TAKEN + "=" 

                                + String.valueOf(dateTaken), null); 

            } catch (Exception e) { 

                e.printStackTrace(); 

        takeBtn2.setClickable(true); 

<b>三、我加的相機蒙版</b><b>^^</b>

         這個最重要了,就是為了這個才把這還未完工的工程放上來的。

         布局檔案裡面我們在加一個自定義的MaskSurfaceView,注意放在相機預覽的前面,并要設定成透明(包括重刷圖層的時候也要注意透明度)。

 &lt;?xml version="1.0" encoding="utf-8"?&gt; 

&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 

    android:layout_width="fill_parent" 

    android:layout_height="fill_parent" 

    android:gravity="center" 

    android:orientation="horizontal" &gt; 

    &lt;RelativeLayout 

        android:layout_width="fill_parent" 

        android:layout_height="fill_parent" 

        android:layout_weight="1" &gt; 

        &lt;org.join.meida.camera.takephoto.MaskSurfaceView 

            android:layout_width="fill_parent" 

            android:layout_height="fill_parent" /&gt; 

        &lt;org.join.meida.camera.takephoto.CameraPreview 

            android:id="@+id/preview" 

        &lt;ImageView 

            android:id="@+id/focusView" 

            android:layout_width="wrap_content" 

            android:layout_height="wrap_content" 

            android:layout_centerInParent="true" 

            android:src="@drawable/focus1" /&gt; 

    &lt;/RelativeLayout&gt; 

&lt;/LinearLayout&gt; 

         蒙版來啦,注意到心了沒有?(3d愛心是網上别人實作的)<b></b>

public class MaskSurfaceView extends SurfaceView implements 

        SurfaceHolder.Callback, Runnable { 

    // 定義SurfaceHolder對象 

    private SurfaceHolder mSurfaceHolder; 

    // 循環标記 

    private boolean loop = true; 

    // 循環間隔 

    private static final long TIME = 300; 

    // 計數器 

    private int mCount; 

    // 繪制方式 

    private int mode; 

    public MaskSurfaceView(Context context, AttributeSet attrs) { 

        mSurfaceHolder = getHolder(); // 擷取SurfaceHolder 

        mSurfaceHolder.addCallback(this); // 添加回調 

        mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT); // 設定透明 

        mode = new Random().nextInt(2); // 随機一個[0-2)數 

        new Thread(this).start(); // 開始繪制 

    public void surfaceChanged(SurfaceHolder holder, int format, int width, 

            int height) { 

        loop = false; 

    public void run() { 

        while (loop) { 

                Thread.sleep(TIME); 

            } catch (InterruptedException e) { 

            synchronized (mSurfaceHolder) { 

                drawMask(); 

     * 繪制蒙版 

    private void drawMask() { 

        // 鎖定畫布,得到canvas 

        Canvas mCanvas = mSurfaceHolder.lockCanvas(); 

        // 避免surface銷毀後,線程喚醒仍進入繪制 

        if (mSurfaceHolder == null || mCanvas == null) 

            return; 

        int w = mCanvas.getWidth(); 

        int h = mCanvas.getHeight(); 

        /* 建立一個畫筆 */ 

        Paint mPaint = new Paint(); 

        mPaint.setAntiAlias(true); // 設定抗鋸齒 

        mPaint.setColor(0x00000000); // 設定透明黑色 

        mCanvas.drawRect(0, 0, w, h, mPaint); // 重繪背景 

        setPaintColor(mPaint); // 循環設定畫筆顔色 

        mPaint.setStyle(Paint.Style.STROKE); // 描邊 

        if (0 == mode) { 

            drawHeart2D(mCanvas, mPaint, w / 2, h / 2, h / 2); // 畫一個2d愛心 

            drawHeart3D(mCanvas, mPaint); // 畫一個3d愛心 

        // 繪制後解鎖,繪制後必須解鎖才能顯示 

        mSurfaceHolder.unlockCanvasAndPost(mCanvas); 

    /** 畫一個2d愛心(半圓+sin曲線) */ 

    private void drawHeart2D(Canvas mCanvas, Paint mPaint, int centerX, 

            int centerY, float height) { 

        float r = height / 4; 

        /* 心兩半圓結點處 */ 

        float topX = centerX; 

        float topY = centerY - r; 

        /* 左上半圓 */ 

        RectF leftOval = new RectF(topX - 2 * r, topY - r, topX, topY + r); 

        mCanvas.drawArc(leftOval, 180f, 180f, false, mPaint); 

        /* 右上半圓 */ 

        RectF rightOval = new RectF(topX, topY - r, topX + 2 * r, topY + r); 

        mCanvas.drawArc(rightOval, 180f, 180f, false, mPaint); 

        /* 下半兩sin曲線 */ 

        float base = 3 * r; 

        double argu = Math.PI / 2 / base; 

        float y = base, value; 

        while (y &gt;= 0) { 

            value = (float) (2 * r * Math.sin(argu * (base - y))); 

            mCanvas.drawPoint(topX - value, topY + y, mPaint); 

            mCanvas.drawPoint(topX + value, topY + y, mPaint); 

            y -= 1; 

        // 1)心形函數:x²+(y-³√x²)²=1 

        // &gt;&gt; x^2+(y-(x^2)^(1/3))^2=1 

        // 

        // 2)心形的各種畫法: 

        // &gt;&gt; http://woshao.com/article/1a855728bda511e0b40e000c29fa3b3a/ 

        // 3)笛卡爾情書的秘密——心形函數的繪制 

        // &gt;&gt; http://www.cssass.com/blog/index.php/2010/808.html 

    /** 畫一個3d愛心 */ 

    private void drawHeart3D(Canvas mCanvas, Paint mPaint) { 

        /* 畫一個3d愛心 */ 

        int i, j; 

        double x, y, r; 

        for (i = 0; i &lt;= 90; i++) { 

            for (j = 0; j &lt;= 90; j++) { 

                r = Math.PI / 45 * i * (1 - Math.sin(Math.PI / 45 * j)) * 20; 

                x = r * Math.cos(Math.PI / 45 * j) * Math.sin(Math.PI / 45 * i) 

                        + w / 2; 

                y = -r * Math.sin(Math.PI / 45 * j) + h / 4; 

                mCanvas.drawPoint((float) x, (float) y, mPaint); 

    /** 循環設定畫筆顔色 */ 

    private void setPaintColor(Paint mPaint) { 

        mCount = mCount &lt; 100 ? mCount + 1 : 0; 

        switch (mCount % 6) { 

        case 0: 

            mPaint.setColor(Color.BLUE); 

            break; 

        case 1: 

            mPaint.setColor(Color.GREEN); 

        case 2: 

            mPaint.setColor(Color.RED); 

        case 3: 

            mPaint.setColor(Color.YELLOW); 

        case 4: 

            mPaint.setColor(Color.argb(255, 255, 181, 216)); 

        case 5: 

            mPaint.setColor(Color.argb(255, 0, 255, 255)); 

        default: 

            mPaint.setColor(Color.WHITE); 

<b>四、後記</b>

         今天是幾号來着?T^T。

<a href="http://down.51cto.com/data/2359820" target="_blank">附件:http://down.51cto.com/data/2359820</a>

     本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/779942,如需轉載請自行聯系原作者