Android 拍照適配方案

拍照功能實現重點內容

Android 程序上實現拍照功能的方式分強調內容爲兩種:第一種是利用相機的 API 來自定義相機,第二種是利用 Intent 調用系統指定的相機拍照。下面講的內容都是針對第二種實現方式的適配。

第一:系統有沒有相機;
第二:拍出來的照片是否有旋轉角度,如果有要根據旋轉角度進行旋轉;
第三:對圖片進行壓縮;
第四:通過 onSaveInstanceState 和 onRestoreInstanceState 這對方法。去到 onSaveInstanceState 把數據保存

拍完照後,先獲取照片到旋轉角度,在對照片進行壓縮,最後在旋轉照片,這樣做內存佔用小
具體代碼如下:

import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    protected Button btn;
    protected ImageView img;
    private String filename;
    private String name;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.activity_main);
        initView();
        filename = Environment.getExternalStorageDirectory().getAbsolutePath();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //在這保存 name 的數據
        outState.putString("name", name);
    }
    //讀取數據
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        name = savedInstanceState.getString("name");
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.btn) {
            takephoto();
        }
    }
    //拍照
    private void takephoto() {
        //判斷是否有相機
        boolean b = hasCamera();
        if (b) {
            name = System.currentTimeMillis() + ".jpg";
            File mPhotoFile = new File(filename, name);
            Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            Uri fileUri = Uri.fromFile(mPhotoFile);
            captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
            startActivityForResult(captureIntent, 1);
        } else {
            Toast.makeText(MainActivity.this, "系統無相機", Toast.LENGTH_SHORT).show();
        }

    }

    //拿到照片
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 1 & resultCode == RESULT_OK) {
            String filestr = filename + "/" + name;
            //獲取旋轉角度
            int degree = getBitmapDegree(filestr);
            //壓縮圖片
            Bitmap bitmap1 = decodeSampledBitmapFromFd(filestr, 150, 150);
            //根據旋轉角度旋轉圖片
            Bitmap bitmap2 = rotateBitmapByDegree(bitmap1, degree);

            img.setImageBitmap(bitmap2);
        }
    }

    /**
     * 判斷系統中是否存在可以啓動的相機應用
     *
     * @return 存在返回true,不存在返回false
     */
    public boolean hasCamera() {
        PackageManager packageManager = getPackageManager();
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        List list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }

    /**
     * 獲取圖片的旋轉角度
     *
     * @param path 圖片絕對路徑
     * @return 圖片的旋轉角度
     */
    public static int getBitmapDegree(String path) {
        int degree = 0;
        try {
            // 從指定路徑下讀取圖片,並獲取其EXIF信息
            ExifInterface exifInterface = new ExifInterface(path);
            // 獲取圖片的旋轉信息
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    /**
     * 將圖片按照指定的角度進行旋轉
     *
     * @param bitmap 需要旋轉的圖片
     * @param degree 指定的旋轉角度
     * @return 旋轉後的圖片
     */
    public static Bitmap rotateBitmapByDegree(Bitmap bitmap, int degree) {
        // 根據旋轉角度,生成旋轉矩陣
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        // 將原始圖片按照旋轉矩陣進行旋轉,並得到新的圖片
        Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        if (bitmap != null & !bitmap.isRecycled()) {
            bitmap.recycle();
        }
        return newBitmap;
    }

    // 從sd卡上加載圖片
    public static Bitmap decodeSampledBitmapFromFd(String pathName,
                                                   int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        //我們需要先拿到原有圖片的寬和高,所以可以通過設置inJustDecodeBounds = true來忽略能夠顯示在屏幕上的色素點
        //而只採集邊框。
        options.inJustDecodeBounds = true;
        //採集完圖片信息之後把圖片屬性存儲到options
        BitmapFactory.decodeFile(pathName, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        //已經計算出來縮放的比例,我們可以按照縮放的比例對圖片進行處理,生成新的圖片,
        //這個事時候吧inJustDecodeBounds設置爲false,意味着並不是只採集邊框,還需要像素點。
        options.inJustDecodeBounds = false;
        Bitmap src = BitmapFactory.decodeFile(pathName, options);
        return createScaleBitmap(src, reqWidth, reqHeight);
    }

    //縮放的比例
    private static int calculateInSampleSize(BitmapFactory.Options options,
                                             int reqWidth, int reqHeight) {
        //圖片的本身的大小
        final int height = options.outHeight;
        final int width = options.outWidth;
        //縮放比例
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            //圖片本身大小與期望圖片的大小做比較,若本身寬高大於期望,需要縮小本身的大小
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

    // 如果是放大圖片,filter決定是否平滑,如果是縮小圖片,filter無影響
    private static Bitmap createScaleBitmap(Bitmap src, int dstWidth,
                                            int dstHeight) {
        Bitmap dst = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false);
        if (src != dst) { // 如果沒有縮放,那麼不回收
            src.recycle(); // 釋放Bitmap的native像素數組
        }
        return dst;
    }

    private void initView() {
        btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(MainActivity.this);
        img = (ImageView) findViewById(R.id.img);
    }
}
xml代碼:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.ljp.takephoto.MainActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拍照" />

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
發佈了23 篇原創文章 · 獲贊 15 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章