天天看點

android 自定義相機 相機開發簡介 本程式子產品效果圖及示例

方式:

調用camera api 自定義相機

調用系統相機

由于需求不同,是以選擇的方案固然也不同,至于第二種調用系統相機,這裡就不過多講解了,使用intent對象設定一個action動作即可,跳轉時使用startactivityforresult,然後在onactivityresult處理相關資料便可,關鍵代碼:

intent.setaction("android.media.action.still_image_camera");  

至于使用,較常見的一般是應用中使用者上傳頭像的時候調用,然後傳回處理圖像資料。

而第一種自定義相機的方式使用也十分普遍,但是要做好這個子產品,相對來說還是有一定難度的,之前分享過一個github上的開源相機的項目,項目由美國的一個團隊開發,集 拍照、攝影、各種特效動畫 等功能與一身,本人之前研究了下,發現功能比較全面也很強大,摳出來單獨拍照那一個子產品,我滴媽呀,真tm費勁!相機不管是預覽還是拍攝圖像都還是很清晰的,自己當時也寫了一個,比較操蛋,隻能怪自己對這一塊的優化了解淺顯吧!特别是預覽的時候,聚焦完成後,焦點周邊會出現很多白色的噪點,密密麻麻,特别嚴重,頭疼的很。不過也總算解決了,灰常感謝usa的那個什麼什麼團隊的開源相機程式。經過自己改造後的預覽效果圖:

android 自定義相機 相機開發簡介 本程式子產品效果圖及示例
android 自定義相機 相機開發簡介 本程式子產品效果圖及示例

下面說說在android中調用camera來定義相機的最基本步驟:

打開相機 —— 調用camera的open()方法。

擷取拍照參數 —— 調用camera的getparameters()方法,傳回camera.parameters對象。

拍照參數設定 —— 調用camera.parameters對象。

拍照參數控制 —— 調用camera的setparameters(),并将camera.parameters對象作為參數傳入。注:android2.3.3之後不用設定。

預覽取景 —— 調用camera的startpreview()方法,在之前注意調用camera的setpreviewdisplay(surfaceholder holder)設定使用哪個surfaceview來顯示取得的圖檔。

拍照 —— 調用camera的takepicture()

停止預覽 —— 調用camera的stoppreview()方法

資源釋放 —— camera.release()

開啟和關閉預覽的聯系如下:camera

---- surfaceholder ------ surfaceview

關于surfaceholder.callback必須實作的3個方法:

surfacecreated() 該方法在surfaceview被create時調用

surfacechanged() 該方法是當surfaceview發生改變後調用

surfacedestroyed() 這個不用說了,銷毀時調用

surfaceholder通過addcallback()方法将響應的接口綁定

注:必要camera權限,例如:

<uses-permission android:name="android.permission.mount_unmount_filesystems"/>  

<uses-permission android:name="android.permission.camera"/>  

<uses-feature android:name="android.hardware.camera" />  

<uses-permission android:name="android.hardware.camera.autofocus" />  

<uses-permission android:name="android.permission.write_external_storage" />  

關于camera下的parameters類,其中封裝了我們需要的大部分功能,下面做個簡單介紹:

setpictureformat() 方法用于設定相機照片的格式,其參數是一個字元型參數,位于pixelformat類中,如:pixelformat.jpeg。

setscenemode() 方法用于設定相機場景類型,其參是是一個字元型參數,位于parameters類中,以scene_mode_開頭。

setzoom() 方法用于設定相機焦距,其參數是一個整型的參數,該參數的範圍是0到camera.getparameters().getmaxzoom()。

setpicturesize() 方法用于設定相機照片的大小,參數為整型。

setwhitebalance() 方法用于設定相機照片白平衡,其參數是一個字元型,位于parameters類中,以white_balance開頭。

setjpegquality() 方法用于設定相機照片的品質,其參數是一個整型參數,取值範圍為1到100。

setflashmode() 方法用于設定閃光燈的類型,其參數是一個字元型參數,位于parameters類中,以flash_mode_開頭。

setcoloreffect() 方法用于設定照片顔色特效的類型,其參數是一個字元型參數,位于parameters類中,以effect_開頭。

下面分享本篇blog的示例相機子產品,此功能子產品并非上面開源項目中的剝離出來的,看下效果圖咯:

android 自定義相機 相機開發簡介 本程式子產品效果圖及示例
android 自定義相機 相機開發簡介 本程式子產品效果圖及示例
android 自定義相機 相機開發簡介 本程式子產品效果圖及示例
android 自定義相機 相機開發簡介 本程式子產品效果圖及示例

效果看着還可以吧(不點贊也太不給面子了吧  - . - ),下面個出主界面的布局代碼:

<?xml version="1.0" encoding="utf-8"?>  

<framelayout xmlns:android="http://schemas.android.com/apk/res/android"  

    android:id="@+id/layout"  

    android:layout_width="match_parent"  

    android:layout_height="match_parent" >  

    <!-- 預覽畫布 -->  

    <surfaceview  

        android:id="@+id/surfaceview"  

        android:layout_width="match_parent"  

        android:layout_height="match_parent" />  

    <!-- 閃光燈、前置攝像頭、後置攝像頭、聚焦 -->  

    <relativelayout  

        android:layout_height="match_parent" >  

        <org.gaochun.camera.cameragrid  

            android:id="@+id/camera_grid"  

            android:layout_width="match_parent"  

            android:layout_height="match_parent"  

            android:layout_alignparenttop="true" />  

        <view  

            android:id="@+id/focus_index"  

            android:layout_width="40dp"  

            android:layout_height="40dp"  

            android:background="@drawable/camera_focus"  

            android:visibility="invisible" />  

        <imageview  

            android:id="@+id/flash_view"  

            android:layout_width="wrap_content"  

            android:layout_height="wrap_content"  

            android:layout_alignparentleft="true"  

            android:onclick="onclick"  

            android:padding="15dp"  

            android:scaletype="centercrop"  

            android:src="@drawable/camera_flash_off" />  

            android:id="@+id/camera_flip_view"  

            android:layout_alignparentright="true"  

            android:src="@drawable/camera_flip" />  

        <!-- 底部按鈕 -->  

        <relativelayout  

            android:layout_width="fill_parent"  

            android:layout_height="70dp"  

            android:layout_alignparentbottom="true"  

            android:background="#a0000000"  

            android:padding="5dp" >  

            <button  

                android:id="@+id/search"  

                android:layout_width="wrap_content"  

                android:layout_height="wrap_content"  

                android:layout_marginleft="30dp"  

                android:background="@null"  

                android:drawablepadding="3dp"  

                android:drawabletop="@drawable/ic_search_selector"  

                android:onclick="onclick"  

                android:text="搜圖"  

                android:textcolor="@drawable/row_selector_text" />  

            <imageview  

                android:id="@+id/action_button"  

                android:layout_centerinparent="true"  

                android:clickable="true"  

                android:src="@drawable/btn_shutter_photo" />  

                android:id="@+id/takephoto"  

                android:layout_alignparentright="true"  

                android:layout_marginright="30dp"  

                android:drawabletop="@drawable/ic_takephoto_selector"  

                android:text="拍照"  

        </relativelayout>  

    </relativelayout>  

</framelayout>  

下面是核心子產品 camerapreview 類:

public class camerapreview extends viewgroup implements surfaceholder.callback, camera.autofocuscallback {  

    private surfaceview msurfaceview;  

    private surfaceholder mholder;  

    private size mpreviewsize;  

    private size adaptersize;  

    //private list<size> msupportedpreviewsizes;  

    private camera mcamera;  

    private boolean issupportautofocus = false;  

    private camera.parameters parameters = null;  

    private context mcontext;  

    //private int mcurrentcameraid = 0;  

    private int screenwidth;  

    private int screenheight;  

    camerapreview(context context, surfaceview sv) {  

        super(context);  

        mcontext = context;  

        msurfaceview = sv;  

        mholder = msurfaceview.getholder();  

        mholder.addcallback(this);  

        mholder.settype(surfaceholder.surface_type_push_buffers);  

        mholder.setkeepscreenon(true);  

        issupportautofocus = context.getpackagemanager().hassystemfeature(  

                packagemanager.feature_camera_autofocus);  

        displaymetrics dm = new displaymetrics();  

        ((activity) mcontext).getwindowmanager().getdefaultdisplay().getmetrics(dm);  

        screenwidth = dm.widthpixels;  

        screenheight = dm.heightpixels;  

    }  

    public void setcamera(camera camera) {  

        mcamera = camera;  

        initcamera();  

    public void initcamera() {  

        if (mcamera != null) {  

            camera.parameters params = mcamera.getparameters();  

            //msupportedpreviewsizes = mcamera.getparameters().getsupportedpreviewsizes();  

            requestlayout();  

            if (mpreviewsize == null) {  

                mpreviewsize = findbestpreviewresolution();  

            }  

            if (adaptersize == null) {  

                adaptersize = findbestpictureresolution();  

            if (adaptersize != null) {  

                params.setpicturesize(adaptersize.width, adaptersize.height);  

            if (mpreviewsize != null) {  

                params.setpreviewsize(mpreviewsize.width, mpreviewsize.height);  

            params.setpictureformat(pixelformat.jpeg);  

            list<string> focusmodes = params.getsupportedfocusmodes();  

            if (focusmodes.contains(camera.parameters.focus_mode_auto)) {  

                // set the focus mode  

                params.setfocusmode(camera.parameters.focus_mode_auto);  

                // set camera parameters  

                mcamera.setparameters(params);  

            setdispaly(params, mcamera);  

            //setcameradisplayorientation((activity) mcontext, mcurrentcameraid, mcamera);  

            mcamera.setparameters(params);  

        }  

    //控制圖像的正确顯示方向  

    private void setdispaly(camera.parameters parameters, camera camera) {  

        if (build.version.sdk_int >= 8) {  

            setdisplayorientation(camera, 90);  

        } else {  

            parameters.setrotation(90);  

    //實作的圖像的正确顯示  

    private void setdisplayorientation(camera camera, int i) {  

        method downpolymorphic;  

        try {  

            downpolymorphic = camera.getclass().getmethod("setdisplayorientation",  

                    new class[]{int.class});  

            if (downpolymorphic != null) {  

                downpolymorphic.invoke(camera, new object[]{i});  

        } catch (exception e) {  

            e.printstacktrace();  

    public static void setcameradisplayorientation(activity activity,  

            int cameraid, android.hardware.camera camera) {  

        android.hardware.camera.camerainfo info =  

                new android.hardware.camera.camerainfo();  

        android.hardware.camera.getcamerainfo(cameraid, info);  

        int rotation = activity.getwindowmanager().getdefaultdisplay()  

                .getrotation();  

        int degrees = 0;  

        switch (rotation) {  

        case surface.rotation_0:  

            degrees = 0;  

            break;  

        case surface.rotation_90:  

            degrees = 90;  

        case surface.rotation_180:  

            degrees = 180;  

        case surface.rotation_270:  

            degrees = 270;  

        int result;  

        if (info.facing == camera.camerainfo.camera_facing_front) {  

            result = (info.orientation + degrees) % 360;  

            result = (360 - result) % 360;  // compensate the mirror  

        } else {  // back-facing  

            result = (info.orientation - degrees + 360) % 360;  

        camera.setdisplayorientation(result);  

    @override  

    protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {  

        final int width = resolvesize(getsuggestedminimumwidth(), widthmeasurespec);  

        final int height = resolvesize(getsuggestedminimumheight(), heightmeasurespec);  

        setmeasureddimension(width, height);  

        //        if (msupportedpreviewsizes != null) {  

        //             mpreviewsize = getoptimalpreviewsize(msupportedpreviewsizes, width, height);  

        //        }  

    protected void onlayout(boolean changed, int l, int t, int r, int b) {  

        if (changed && getchildcount() > 0) {  

            final view child = getchildat(0);  

            final int width = r - l;  

            final int height = b - t;  

            int previewwidth = width;  

            int previewheight = height;  

                previewwidth = mpreviewsize.width;  

                previewheight = mpreviewsize.height;  

            // center the child surfaceview within the parent.  

            if (width * previewheight > height * previewwidth) {  

                final int scaledchildwidth = previewwidth * height / previewheight;  

                child.layout((width - scaledchildwidth) / 2, 0,  

                        (width + scaledchildwidth) / 2, height);  

            } else {  

                final int scaledchildheight = previewheight * width / previewwidth;  

                child.layout(0, (height - scaledchildheight) / 2,  

                        width, (height + scaledchildheight) / 2);  

    public void surfacecreated(surfaceholder holder) {  

        // the surface has been created, acquire the camera and tell it where  

        // to draw.  

            if (mcamera != null) {  

                mcamera.setpreviewdisplay(holder);  

        } catch (ioexception e) {  

            if (null != mcamera) {  

                mcamera.release();  

                mcamera = null;  

    public void surfacechanged(surfaceholder holder, int format, int w, int h) {  

        if (holder.getsurface() == null) {  

            return;  

            camera.parameters parameters = mcamera.getparameters();  

            parameters.setpreviewsize(mpreviewsize.width, mpreviewsize.height);  

            mcamera.setparameters(parameters);  

            try {  

            } catch (ioexception e) {  

                e.printstacktrace();  

            mcamera.startpreview();  

            reautofocus();  

    public void surfacedestroyed(surfaceholder holder) {  

        // surface will be destroyed when we return, so stop the preview.  

            mcamera.stoppreview();  

    /** 

     * 最小預覽界面的分辨率 

     */  

    private static final int min_preview_pixels = 480 * 320;  

     * 最大寬高比差 

    private static final double max_aspect_distortion = 0.15;  

     * 找出最适合的預覽界面分辨率 

     * 

     * @return 

    private camera.size findbestpreviewresolution() {  

        camera.parameters cameraparameters = mcamera.getparameters();  

        camera.size defaultpreviewresolution = cameraparameters.getpreviewsize();  

        list<camera.size> rawsupportedsizes = cameraparameters.getsupportedpreviewsizes();  

        if (rawsupportedsizes == null) {  

            return defaultpreviewresolution;  

        // 按照分辨率從大到小排序  

        list<camera.size> supportedpreviewresolutions = new arraylist<camera.size>(rawsupportedsizes);  

        collections.sort(supportedpreviewresolutions, new comparator<size>() {  

            @override  

            public int compare(camera.size a, camera.size b) {  

                int apixels = a.height * a.width;  

                int bpixels = b.height * b.width;  

                if (bpixels < apixels) {  

                    return -1;  

                }  

                if (bpixels > apixels) {  

                    return 1;  

                return 0;  

        });  

        stringbuilder previewresolutionsb = new stringbuilder();  

        for (camera.size supportedpreviewresolution : supportedpreviewresolutions) {  

            previewresolutionsb.append(supportedpreviewresolution.width).append('x').append(supportedpreviewresolution.height)  

            .append(' ');  

        // 移除不符合條件的分辨率  

        double screenaspectratio = (double) screenwidth  

        / screenheight;  

        iterator<size> it = supportedpreviewresolutions.iterator();  

        while (it.hasnext()) {  

            camera.size supportedpreviewresolution = it.next();  

            int width = supportedpreviewresolution.width;  

            int height = supportedpreviewresolution.height;  

            // 移除低于下限的分辨率,盡可能取高分辨率  

            if (width * height < min_preview_pixels) {  

                it.remove();  

                continue;  

            // 在camera分辨率與螢幕分辨率寬高比不相等的情況下,找出差距最小的一組分辨率  

            // 由于camera的分辨率是width>height,我們設定的portrait模式中,width<height  

            // 是以這裡要先交換然preview寬高比後在比較  

            boolean iscandidateportrait = width > height;  

            int maybeflippedwidth = iscandidateportrait ? height : width;  

            int maybeflippedheight = iscandidateportrait ? width : height;  

            double aspectratio = (double) maybeflippedwidth / (double) maybeflippedheight;  

            double distortion = math.abs(aspectratio - screenaspectratio);  

            if (distortion > max_aspect_distortion) {  

            // 找到與螢幕分辨率完全比對的預覽界面分辨率直接傳回  

            if (maybeflippedwidth == screenwidth  

                    && maybeflippedheight == screenheight) {  

                return supportedpreviewresolution;  

        // 如果沒有找到合适的,并且還有候選的像素,則設定其中最大比例的,對于配置比較低的機器不太合适  

        if (!supportedpreviewresolutions.isempty()) {  

            camera.size largestpreview = supportedpreviewresolutions.get(0);  

            return largestpreview;  

        // 沒有找到合适的,就傳回預設的  

        return defaultpreviewresolution;  

    private camera.size findbestpictureresolution() {  

        list<camera.size> supportedpicresolutions = cameraparameters.getsupportedpicturesizes(); // 至少會傳回一個值  

        stringbuilder picresolutionsb = new stringbuilder();  

        for (camera.size supportedpicresolution : supportedpicresolutions) {  

            picresolutionsb.append(supportedpicresolution.width).append('x')  

            .append(supportedpicresolution.height).append(" ");  

        camera.size defaultpictureresolution = cameraparameters.getpicturesize();  

        // 排序  

        list<camera.size> sortedsupportedpicresolutions = new arraylist<camera.size>(  

                supportedpicresolutions);  

        collections.sort(sortedsupportedpicresolutions, new comparator<camera.size>() {  

        double screenaspectratio = screenwidth  

        / (double) screenheight;  

        iterator<camera.size> it = sortedsupportedpicresolutions.iterator();  

            // 是以這裡要先交換然後在比較寬高比  

        // 如果沒有找到合适的,并且還有候選的像素,對于照片,則取其中最大比例的,而不是選擇與螢幕分辨率相同的  

        if (!sortedsupportedpicresolutions.isempty()) {  

            return sortedsupportedpicresolutions.get(0);  

        return defaultpictureresolution;  

    private size getoptimalpreviewsize(list<size> sizes, int w, int h) {  

        final double aspect_tolerance = 0.1;  

        double targetratio = (double) w / h;  

        if (sizes == null)  

            return null;  

        size optimalsize = null;  

        double mindiff = double.max_value;  

        int targetheight = h;  

        // try to find an size match aspect ratio and size  

        for (size size : sizes) {  

            double ratio = (double) size.width / size.height;  

            if (math.abs(ratio - targetratio) > aspect_tolerance)  

            if (math.abs(size.height - targetheight) < mindiff) {  

                optimalsize = size;  

                mindiff = math.abs(size.height - targetheight);  

        // cannot find the one match the aspect ratio, ignore the requirement  

        if (optimalsize == null) {  

            mindiff = double.max_value;  

            for (size size : sizes) {  

                if (math.abs(size.height - targetheight) < mindiff) {  

                    optimalsize = size;  

                    mindiff = math.abs(size.height - targetheight);  

        return optimalsize;  

    public void reautofocus() {  

        if (issupportautofocus) {  

            mcamera.autofocus(new camera.autofocuscallback() {  

                @override  

                public void onautofocus(boolean success, camera camera) {  

            });  

    public list<size> getresolutionlist() {  

        return mcamera.getparameters().getsupportedpreviewsizes();  

    public camera.size getresolution() {  

        camera.parameters params = mcamera.getparameters();  

        camera.size s = params.getpreviewsize();  

        return s;  

    /*public void setcurrentcameraid(int current) { 

        mcurrentcameraid = current; 

    }*/  

    //定點對焦的代碼  

    public void pointfocus(motionevent event) {  

        mcamera.cancelautofocus();  

        parameters = mcamera.getparameters();  

        if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) {  

            //showpoint(x, y);  

            focusontouch(event);  

        mcamera.setparameters(parameters);  

        autofocus();  

    //實作自動對焦  

    public void autofocus() {  

        new thread() {  

            public void run() {  

                try {  

                    sleep(100);  

                } catch (interruptedexception e) {  

                    e.printstacktrace();  

                if (mcamera == null) {  

                    return;  

                mcamera.autofocus(new camera.autofocuscallback() {  

                    @override  

                    public void onautofocus(boolean success, camera camera) {  

                        if (success) {  

                            initcamera();//實作相機的參數初始化  

                        }  

                    }  

                });  

        };  

    @targetapi(build.version_codes.ice_cream_sandwich)  

    private void showpoint(int x, int y) {  

        if (parameters.getmaxnummeteringareas() > 0) {  

            list<camera.area> areas = new arraylist<camera.area>();  

            windowmanager wm = (windowmanager) getcontext()  

                    .getsystemservice(context.window_service);  

            //xy變換了  

            int recty = -x * 2000 / wm.getdefaultdisplay().getwidth() + 1000;  

            int rectx = y * 2000 / wm.getdefaultdisplay().getheight() - 1000;  

            int left = rectx < -900 ? -1000 : rectx - 100;  

            int top = recty < -900 ? -1000 : recty - 100;  

            int right = rectx > 900 ? 1000 : rectx + 100;  

            int bottom = recty > 900 ? 1000 : recty + 100;  

            rect area1 = new rect(left, top, right, bottom);  

            areas.add(new camera.area(area1, 800));  

            parameters.setmeteringareas(areas);  

        parameters.setfocusmode(camera.parameters.focus_mode_continuous_picture);  

    public void focusontouch(motionevent event) {  

        rect focusrect = calculatetaparea(event.getrawx(), event.getrawy(), 1f);  

        rect meteringrect = calculatetaparea(event.getrawx(), event.getrawy(), 1.5f);  

        camera.parameters parameters = mcamera.getparameters();  

        parameters.setfocusmode(camera.parameters.focus_mode_auto);  

        if (parameters.getmaxnumfocusareas() > 0) {  

            list<camera.area> focusareas = new arraylist<camera.area>();  

            focusareas.add(new camera.area(focusrect, 1000));  

            parameters.setfocusareas(focusareas);  

            list<camera.area> meteringareas = new arraylist<camera.area>();  

            meteringareas.add(new camera.area(meteringrect, 1000));  

            parameters.setmeteringareas(meteringareas);  

        mcamera.autofocus(this);  

     * convert touch position x:y to {@link camera.area} position -1000:-1000 to 1000:1000. 

    private rect calculatetaparea(float x, float y, float coefficient) {  

        float focusareasize = 300;  

        int areasize = float.valueof(focusareasize * coefficient).intvalue();  

        int centerx = (int) (x / getresolution().width * 2000 - 1000);  

        int centery = (int) (y / getresolution().height * 2000 - 1000);  

        int left = clamp(centerx - areasize / 2, -1000, 1000);  

        int right = clamp(left + areasize, -1000, 1000);  

        int top = clamp(centery - areasize / 2, -1000, 1000);  

        int bottom = clamp(top + areasize, -1000, 1000);  

        return new rect(left, top, right, bottom);  

    private int clamp(int x, int min, int max) {  

        if (x > max) {  

            return max;  

        if (x < min) {  

            return min;  

        return x;  

    public void onautofocus(boolean success, camera camera) {  

    public void setnull() {  

        adaptersize = null;  

        mpreviewsize = null;  

}  

以下是cameraactivity類:

public class cameraactivity extends activity implements view.ontouchlistener,onclicklistener {  

    public static final string camera_path_value1 = "photo_path";  

    public static final string camera_path_value2 = "path";  

    public static final string camera_type = "camera_type";  

    public static final string camera_return_path = "return_path";  

    private int photo_size_w = 2000;  

    private int photo_size_h = 2000;  

    public static final int camera_type_1 = 1;  

    public static final int camera_type_2 = 2;  

    private final int process = 1;  

    private camerapreview preview;  

    private camera camera;  

    private view focusindex;  

    private imageview flashbtn;  

    private int mcurrentcameraid = 0; // 1是前置 0是後置  

    private cameragrid mcameragrid;  

    private int type = 1;   //引用的矩形框  

    private button mbtnsearch;  

    private button mbtntakephoto;  

    public void oncreate(bundle savedinstancestate) {  

        super.oncreate(savedinstancestate);  

        mcontext = this;  

        //requestwindowfeature(window.feature_no_title);  

        //getwindow().addflags(windowmanager.layoutparams.flag_fullscreen);//全屏  

        //getwindow().addflags(windowmanager.layoutparams.flag_keep_screen_on);//拍照過程螢幕一直處于高亮  

        setcontentview(r.layout.camera_home);  

        type = getintent().getintextra(camera_type, camera_type_2);  

        initview();  

        initdata();  

    private void initview() {  

        focusindex = (view) findviewbyid(r.id.focus_index);  

        flashbtn = (imageview) findviewbyid(r.id.flash_view);  

        msurfaceview = (surfaceview) findviewbyid(r.id.surfaceview);  

        mcameragrid = (cameragrid) findviewbyid(r.id.camera_grid);  

        mbtnsearch = (button) findviewbyid(r.id.search);  

        mbtntakephoto = (button) findviewbyid(r.id.takephoto);  

    private void initdata() {  

        preview = new camerapreview(this, msurfaceview);  

        preview.setlayoutparams(new layoutparams(layoutparams.match_parent,  

                layoutparams.match_parent));  

        ((framelayout) findviewbyid(r.id.layout)).addview(preview);  

        preview.setkeepscreenon(true);  

        msurfaceview.setontouchlistener(this);  

        mcameragrid.settype(type);  

    private handler handler = new handler();  

    private void takephoto() {  

            camera.takepicture(shuttercallback, rawcallback, jpegcallback);  

        } catch (throwable t) {  

            t.printstacktrace();  

            toast.maketext(getapplication(), "拍照失敗,請重試!", toast.length_long)  

            .show();  

                camera.startpreview();  

            } catch (throwable e) {  

    protected void onresume() {  

        super.onresume();  

        int numcams = camera.getnumberofcameras();  

        if (numcams > 0) {  

                mcurrentcameraid = 0;  

                camera = camera.open(mcurrentcameraid);  

                preview.setcamera(camera);  

                preview.reautofocus();  

            } catch (runtimeexception ex) {  

                toast.maketext(mcontext, "未發現相機", toast.length_long).show();  

    protected void onpause() {  

        if (camera != null) {  

            camera.stoppreview();  

            preview.setcamera(null);  

            camera.release();  

            camera = null;  

            preview.setnull();  

        super.onpause();  

    private void resetcam() {  

        camera.startpreview();  

        preview.setcamera(camera);  

    shuttercallback shuttercallback = new shuttercallback() {  

        public void onshutter() {  

    };  

    picturecallback rawcallback = new picturecallback() {  

        public void onpicturetaken(byte[] data, camera camera) {  

    picturecallback jpegcallback = new picturecallback() {  

            new saveimagetask(data).execute();  

            resetcam();  

    public boolean ontouch(view v, motionevent event) {  

            if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) {  

                preview.pointfocus(event);  

        relativelayout.layoutparams layout = new relativelayout.layoutparams(  

                focusindex.getlayoutparams());  

        layout.setmargins((int) event.getx() - 60, (int) event.gety() - 60, 0,0);  

        focusindex.setlayoutparams(layout);  

        focusindex.setvisibility(view.visible);  

        scaleanimation sa = new scaleanimation(3f, 1f, 3f, 1f,  

                scaleanimation.relative_to_self, 0.5f,  

                scaleanimation.relative_to_self, 0.5f);  

        sa.setduration(800);  

        focusindex.startanimation(sa);  

        handler.postattime(new runnable() {  

                focusindex.setvisibility(view.invisible);  

        }, 800);  

        return false;  

    public void onclick(view v) {  

        switch (v.getid()) {  

        /*case r.id.camera_back: 

            setresult(0); 

            finish(); 

            break;*/  

        case r.id.camera_flip_view:  

            switchcamera();  

        case r.id.flash_view:  

            turnlight(camera);  

        case r.id.action_button:  

            takephoto();  

        case r.id.search:   //處理選中狀态  

            mbtnsearch.setselected(true);  

            mbtntakephoto.setselected(false);  

        case r.id.takephoto:    //處理選中狀态  

            mbtntakephoto.setselected(true);  

            mbtnsearch.setselected(false);  

    private static string getcamerapath() {  

        calendar calendar = calendar.getinstance();  

        stringbuilder sb = new stringbuilder();  

        sb.append("img");  

        sb.append(calendar.get(calendar.year));  

        int month = calendar.get(calendar.month) + 1; // 0~11  

        sb.append(month < 10 ? "0" + month : month);  

        int day = calendar.get(calendar.date);  

        sb.append(day < 10 ? "0" + day : day);  

        int hour = calendar.get(calendar.hour_of_day);  

        sb.append(hour < 10 ? "0" + hour : hour);  

        int minute = calendar.get(calendar.minute);  

        sb.append(minute < 10 ? "0" + minute : minute);  

        int second = calendar.get(calendar.second);  

        sb.append(second < 10 ? "0" + second : second);  

        if (!new file(sb.tostring() + ".jpg").exists()) {  

            return sb.tostring() + ".jpg";  

        stringbuilder tmpsb = new stringbuilder(sb);  

        int indexstart = sb.length();  

        for (int i = 1; i < integer.max_value; i++) {  

            tmpsb.append('(');  

            tmpsb.append(i);  

            tmpsb.append(')');  

            tmpsb.append(".jpg");  

            if (!new file(tmpsb.tostring()).exists()) {  

                break;  

            tmpsb.delete(indexstart, tmpsb.length());  

        return tmpsb.tostring();  

    //處理拍攝的照片  

    private class saveimagetask extends asynctask<void, void, string> {  

        private byte[] data;  

        saveimagetask(byte[] data) {  

            this.data = data;  

        @override  

        protected string doinbackground(void... params) {  

            // write to sd card  

            string path = "";  

                showprogressdialog("進行中");  

                path = savetosdcard(data);  

            } catch (filenotfoundexception e) {  

            } finally {  

            return path;  

        protected void onpostexecute(string path) {  

            super.onpostexecute(path);  

            if (!textutils.isempty(path)) {  

                log.d("demolog", "path=" + path);  

                dismissprogressdialog();  

                intent intent = new intent();  

                intent.setclass(cameraactivity.this, photoprocessactivity.class);  

                intent.putextra(camera_path_value1, path);  

                startactivityforresult(intent, process);  

                toast.maketext(getapplication(), "拍照失敗,請稍後重試!",  

                        toast.length_long).show();  

    private alertdialog malertdialog;  

    private void dismissprogressdialog() {  

        this.runonuithread(new runnable() {  

                if (malertdialog != null && malertdialog.isshowing()  

                        && !cameraactivity.this.isfinishing()) {  

                    malertdialog.dismiss();  

                    malertdialog = null;  

    private void showprogressdialog(final string msg) {  

                if (malertdialog == null) {  

                    malertdialog = new genericprogressdialog(  

                            cameraactivity.this);  

                malertdialog.setmessage(msg);  

                ((genericprogressdialog) malertdialog)  

                .setprogressvisiable(true);  

                malertdialog.setcancelable(false);  

                malertdialog.setoncancellistener(null);  

                malertdialog.show();  

                malertdialog.setcanceledontouchoutside(false);  

     * 将拍下來的照片存放在sd卡中 

    public string savetosdcard(byte[] data) throws ioexception {  

        bitmap croppedimage;  

        // 獲得圖檔大小  

        bitmapfactory.options options = new bitmapfactory.options();  

        options.injustdecodebounds = true;  

        bitmapfactory.decodebytearray(data, 0, data.length, options);  

        // photo_size = options.outheight > options.outwidth ? options.outwidth  

        // : options.outheight;  

        photo_size_w = options.outwidth;  

        photo_size_h = options.outheight;  

        options.injustdecodebounds = false;  

        rect r = new rect(0, 0, photo_size_w, photo_size_h);  

            croppedimage = decoderegioncrop(data, r);  

        string imagepath = "";  

            imagepath = savetofile(croppedimage);  

        croppedimage.recycle();  

        return imagepath;  

    private bitmap decoderegioncrop(byte[] data, rect rect) {  

        inputstream is = null;  

        system.gc();  

        bitmap croppedimage = null;  

            is = new bytearrayinputstream(data);  

            bitmapregiondecoder decoder = bitmapregiondecoder.newinstance(is,false);  

                croppedimage = decoder.decoderegion(rect,  

                        new bitmapfactory.options());  

            } catch (illegalargumentexception e) {  

        } catch (throwable e) {  

        } finally {  

        matrix m = new matrix();  

        m.setrotate(90, photo_size_w / 2, photo_size_h / 2);  

        if (mcurrentcameraid == 1) {  

            m.postscale(1, -1);  

        bitmap rotatedimage = bitmap.createbitmap(croppedimage, 0, 0,  

                photo_size_w, photo_size_h, m, true);  

        if (rotatedimage != croppedimage)  

            croppedimage.recycle();  

        return rotatedimage;  

    // 儲存圖檔檔案  

    public static string savetofile(bitmap croppedimage)  

            throws filenotfoundexception, ioexception {  

        file sdcard = environment.getexternalstoragedirectory();  

        file dir = new file(sdcard.getabsolutepath() + "/dcim/camera/");  

        if (!dir.exists()) {  

            dir.mkdirs();  

        string filename = getcamerapath();  

        file outfile = new file(dir, filename);  

        fileoutputstream outputstream = new fileoutputstream(outfile); // 檔案輸出流  

        croppedimage.compress(bitmap.compressformat.jpeg, 70, outputstream);  

        outputstream.flush();  

        outputstream.close();  

        return outfile.getabsolutepath();  

     * 閃光燈開關 開->關->自動 

     * @param mcamera 

    private void turnlight(camera mcamera) {  

        if (mcamera == null || mcamera.getparameters() == null  

                || mcamera.getparameters().getsupportedflashmodes() == null) {  

        string flashmode = mcamera.getparameters().getflashmode();  

        list<string> supportedmodes = mcamera.getparameters()  

                .getsupportedflashmodes();  

        if (camera.parameters.flash_mode_off.equals(flashmode)  

                && supportedmodes.contains(camera.parameters.flash_mode_on)) {// 關閉狀态  

            parameters.setflashmode(camera.parameters.flash_mode_on);  

            flashbtn.setimageresource(r.drawable.camera_flash_on);  

        } else if (camera.parameters.flash_mode_on.equals(flashmode)) {// 開啟狀态  

            if (supportedmodes.contains(camera.parameters.flash_mode_auto)) {  

                parameters.setflashmode(camera.parameters.flash_mode_auto);  

                flashbtn.setimageresource(r.drawable.camera_flash_auto);  

                mcamera.setparameters(parameters);  

            } else if (supportedmodes  

                    .contains(camera.parameters.flash_mode_off)) {  

                parameters.setflashmode(camera.parameters.flash_mode_off);  

                flashbtn.setimageresource(r.drawable.camera_flash_off);  

        } else if (camera.parameters.flash_mode_auto.equals(flashmode)  

                && supportedmodes.contains(camera.parameters.flash_mode_off)) {  

            parameters.setflashmode(camera.parameters.flash_mode_off);  

            flashbtn.setimageresource(r.drawable.camera_flash_off);  

    // 切換前後置攝像頭  

    private void switchcamera() {  

        mcurrentcameraid = (mcurrentcameraid + 1) % camera.getnumberofcameras();  

            camera.setpreviewcallback(null);  

            camera = camera.open(mcurrentcameraid);  

            camera.setpreviewdisplay(msurfaceview.getholder());  

            preview.setcamera(camera);  

            camera.startpreview();  

            toast.maketext(mcontext, "未發現相機", toast.length_long).show();  

    public boolean onkeydown(int keycode, keyevent event) {  

        if (keycode == keyevent.keycode_back && event.getrepeatcount() == 0) {  

            setresult(0);  

            finish();  

            return true;  

        return super.onkeydown(keycode, event);  

    public void onactivityresult(int requestcode, int resultcode, intent data) {  

        if (requestcode == process) {  

            if (resultcode == result_ok) {  

                if (data != null) {  

                    intent.putextra(camera_return_path,  

                            data.getstringextra(camera_path_value2));  

                setresult(result_ok, intent);  

                finish();  

                    file dir = new file(data.getstringextra(camera_path_value2));  

                    if (dir != null) {  

                        dir.delete();  

總結

1、網上有些示例代碼,擔心相機初始化及開啟時間較長,将初始化及啟動工作單獨放在子線程中,偶爾出現黑屏的情況,但也不是經常出現。

導緻原因:由于單獨開辟了線程去初始化啟動相機,導緻相機的初始化和開啟工作已完成,而找不到畫布控件。若出現此情況,可調試或者将線程睡眠500毫秒。

2、按下home鍵後,再次進入時,為毛黑屏了,如何破?

導緻原因:在oncreate中find了surfaceview,按下home後程式再次進入時,找不到預覽的畫布了,可将find的工作放入onresume中,再就是别忘了在onpause中做如下操作:

@override  

注:測試機-------> 小米2a、紅米、華為p8、華為榮耀3c,魅藍note2

附:有些小夥伴經常問手機gif動畫如何制作的,在此也分享下:

注:這代碼有些問題,我會在後面貼上最新的優化代碼,歡迎繼續支援

下一篇: KMP算法

繼續閱讀