CameraとSurface Viewを使用したカスタム写真機能

24865 ワード

Fragmentレイアウトの作成
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" >

    <!--      android:layout_width="wrap_content"   ,Button            ,    android:layout_width="0dp"    ,SurfaceView         。            ,      layout_height  ,  SurfaceView     Button         。-->
    <LinearLayout  android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" >

        <SurfaceView  android:id="@+id/crime_camera_surfaceview" android:layout_width="0dp" android:layout_height="fill_parent" android:layout_weight="1" />

        <Button  android:id="@+id/crime_camera_takePictureButton" android:layout_width="wrap_content" android:layout_height="fill_parent" android:text="@string/take" />
    </LinearLayout>

</FrameLayout>

CrimeCameraActivity
package com.huangfei.criminalintent;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Window;
import android.view.WindowManager;

public class CrimeCameraActivity extends SingleFragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        /** *       activity      ?   Activity.setContentView(...)  (    CrimeCameraActivity   * onCreate(Bundle)          。)  activity    ,     requestWindowFeature(...)   addFlags(...) *   。 fragment      activity        ,  。   activity                 。 */
        requestWindowFeature(Window.FEATURE_NO_TITLE);//     
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//     
        super.onCreate(savedInstanceState);
    }

    @Override
    protected Fragment createFragment() {
        return new CrimeCameraFragment();
    }

}

CrimeCameraActivityを登録し、撮影権限を追加
uses-permission android:name="android.permission.CAMERA"/>
    <!-- uses-feature                   。  android.hardware.camera       ,                          Google Play    。 -->
    <uses-feature android:name="android.hardware.camera"/>

......

<activity  android:name="com.huangfei.criminalintent.CrimeCameraActivity" android:screenOrientation="landscape" android:label="@string/app_name" >
        </activity>

CrimeCameraFragmentの作成
package com.huangfei.criminalintent;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

public class CrimeCameraFragment extends Fragment {
    private static final String TAG = "CrimeCameraFragment";
    public static final String EXTRA_PHOTO_FILENAME = "com.huangfei.criminalintent.photo_filename";

    /** * Camera                 。          :       activity      。 */
    private Camera mCamera;
    /** * SurfaceView         。SurfaceView        ,                     。 */
    private SurfaceView mSurfaceView;
    private View mProgressContainer;

    private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {

        @Override
        public void onShutter() {
            Log.i(TAG, "ShutterCallback");
            mProgressContainer.setVisibility(View.VISIBLE);
        }
    };

    private Camera.PictureCallback mJepgCallback = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            Log.i(TAG, "PictureCallback");
            String fileName = UUID.randomUUID().toString() + ".jpg";
            FileOutputStream fos = null;
            boolean sucess = true;

            try {
                fos = getActivity().openFileOutput(fileName, Context.MODE_PRIVATE);
                fos.write(data);
            } catch (Exception e) {
                e.printStackTrace();
                sucess =false;
            }finally{
                if(fos != null)
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        sucess =false;
                    }
            }

            if(sucess){
                Intent intent = new Intent();
                intent.putExtra(EXTRA_PHOTO_FILENAME, fileName);
                getActivity().setResult(Activity.RESULT_OK, intent);
            }else {
                getActivity().setResult(Activity.RESULT_CANCELED);
            }

            getActivity().finish();
        }
    };

    @SuppressWarnings("deprecation")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_crime_camera, container,
                false);

        mProgressContainer = view
                .findViewById(R.id.crime_camera_progressContainer);
        mProgressContainer.setVisibility(View.INVISIBLE);
        Button takePictureButton = (Button) view
                .findViewById(R.id.crime_camera_takePictureButton);
        takePictureButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if(mCamera != null)
                    /** * ShutterCallback               ,   ,          。 *    PictureCallback                 ,    ,                   。 *    PictureCallback      JPEG          。 */
                    mCamera.takePicture(mShutterCallback, null, mJepgCallback);
            }
        });

        mSurfaceView = (SurfaceView) view
                .findViewById(R.id.crime_camera_surfaceview);
        /** * SurfaceView   SurfaceHolder  。SurfaceHolder    Surface       。 * Surface               。 * Surface        :SurfaceView       ,   Surface; * SurfaceView       ,Surface     。Surface     ,                 。 * *         ,SurfaceView                 。           Surface      , *      Surface    。  CrimeCameraFragment  ,Camera   Surface    。 * * Surface     ,   Camera   SurfaceHolder ;Surface   , *   Camera SurfaceHolder   。 * SurfaceHolder       :SurfaceHolder.Callback * 。     Surface        ,       Surface          。 * */
        SurfaceHolder holder = mSurfaceView.getHolder();
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//     Honeycomb       。

        holder.addCallback(new Callback() {

            /** * SurfaceView       ,Surface      。     。    Surface        Surface * 。 */
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if (mCamera != null) {
                    mCamera.stopPreview();
                }
            }

            /** *   SurfaceView                   。    Surface            。 */
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (mCamera != null) {
                    try {
                        mCamera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            /** * Surface              。       ,    Surface              。         * Surface    ,            。 */
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format,
                    int width, int height) {
                if (mCamera == null)
                    return;

                Parameters parameters = mCamera.getParameters();
                //        
                Size size = getBestSupportedSize(
                        parameters.getSupportedPreviewSizes(), width, height);
                parameters.setPreviewSize(size.width, size.height);
                //      
                size = getBestSupportedSize(parameters.getSupportedPictureSizes(), width, height);
                parameters.setPictureSize(size.width, size.height);
                mCamera.setParameters(parameters);
                try {
                    mCamera.startPreview();
                } catch (Exception e) {
                    /** *      ,                 。    ,          ,         , *          。 */
                    e.printStackTrace();
                    mCamera.release();
                    mCamera = null;
                }
            }
        });

        return view;
    }

    /** *             。 */
    private Size getBestSupportedSize(List<Size> sizes, int width, int height) {
        Size bestSize = null;
        for (Size size : sizes) {
            if(size.width == 640 && size.height == 480){
                bestSize = size;
                break;
            }
            if (bestSize == null) {
                bestSize = sizes.get(sizes.size() / 2);
            }
        }

        return bestSize;
    }

    /** *        fragment     ,       。        。 */
    @Override
    public void onResume() {
        super.onResume();
        mCamera = Camera.open(0);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
    }
}

CrimeCameraActivityを起動し、写真のアドレスを受け入れます.
mPhotoButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), CrimeCameraActivity.class);
                startActivity(intent);
            }
        });

        /** *          ,        。 * FEATURE_CAMERA         * FEATURE_CAMERA_FRONT         */
        PackageManager pm = getActivity().getPackageManager();
        boolean hasACamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) ||
                pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) ||
                Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD ||
                Camera.getNumberOfCameras() > 0;
        if(!hasACamera)
            mPhotoButton.setEnabled(false);
@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK)
            return;

        if (requestCode == REQUEST_DATE) {
            Date date = (Date) data
                    .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
            mCrime.setDate(date);
            mDateButton.setText(date.toLocaleString());
        } else if (requestCode == REQUEST_PHOTO) {
            String fileName = data
                    .getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
            if (fileName != null) {
                Photo photo = new Photo(fileName);
                mCrime.setPhoto(photo);
                showPhoto();
            }
        }
    }
private void showPhoto() {
        Photo photo = mCrime.getPhoto();
        BitmapDrawable b = null;

        if (photo != null) {
            String path = getActivity().getFileStreamPath(photo.getFilename())
                    .getAbsolutePath();
            b = PictureUtils.getScaleDrawable(getActivity(), path);
        }
        mPhotoView.setImageDrawable(b);
    }
package com.huangfei.criminalintent;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.BitmapDrawable;
import android.view.Display;
import android.widget.ImageView;

public class PictureUtils {

    /** *                 */
    @SuppressWarnings("deprecation")
    public static BitmapDrawable getScaleDrawable(Activity a, String path){
        Display display = a.getWindowManager().getDefaultDisplay();
        float destWidth = display.getWidth();
        float destHeight = display.getHeight();

        Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;

        int inSampleSize = 1;
        if(srcHeight > destHeight || srcWidth > destWidth){
            if(srcHeight > destHeight){
                inSampleSize = Math.round(srcHeight / destHeight);
            }else {
                inSampleSize = Math.round(srcWidth / destWidth);
            }
        }

        options = new BitmapFactory.Options();
        options.inSampleSize = inSampleSize;

        Bitmap bitmap = BitmapFactory.decodeFile(path, options);
        return new BitmapDrawable(a.getResources(), bitmap);
    }

    /** *   ImageView BitmapDrawable */
    public static void cleanImageView(ImageView imageView){
        if(!(imageView.getDrawable() instanceof BitmapDrawable))
            return;

        BitmapDrawable b = (BitmapDrawable) imageView.getDrawable();
        b.getBitmap().recycle();
        imageView.setImageBitmap(null);
    }
}

コードアドレス