前言:
|
證件照相機 |
有一種相機,叫做證件相機!
這邊提供客製化相機的做法。
在網路上找資料很多,但一直沒有確切可以用的code。
不是不完整,不然就是blog寫的當下android版本太低已不適用現今環境。
發現網路還是有一篇能用的。
https://blog.csdn.net/LHBTM/article/details/55505668
這篇不完整,但都把釣竿給你了。
如果你整篇複製貼上,你會發現拍完照後,並不會返回MainActivity,就卡在拍照頁。
仔細看一下Code,其實說明都很完整!
合併一下官方文擋獲取儲存File位置的方式,來修改這篇。
先佈局好畫面:
|
acitvity_main.xml |
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hello.kaiser.customcamera.MainActivity">
<ImageView
android:id="@+id/show_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/ic_launcher_background"
android:text="Hello World!" />
<Button
android:id="@+id/btn_take_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/show_image"
android:layout_centerHorizontal="true"
android:text="拍照" />
</RelativeLayout>
然後
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button mBtnPic;
private ImageView mShowImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mBtnPic = (Button) findViewById(R.id.btn_take_pic);
mShowImage = (ImageView) findViewById(R.id.show_image);
}
}
沒啥功能。
接下來開始大家都很會的
crtl+c 和
crtl+v吧
https://blog.csdn.net/LHBTM/article/details/55505668
把上面這篇全部搬到你個人的專案內。
|
會動到的地方 |
會動到的地方,大概會長這樣
比較需要注意的,就是
xml的
View
記得換成自己包名的位置
|
記得更換View的名稱 |
記得在MainActivity.java製作畫面轉移事件
mBtnPic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, TakePicActivity.class));
}
});
此時,大致上完成了
開啟後你會發現,事情沒你想得順利XD
至少畫面出來了,是吧!
這像我上一篇講得不太一樣,上一篇甚至連權限都不用。
這邊因為是客製化,就必須加權限進來。
此時要加入三個權限:
寫入相機權限、寫入SDCard權限、和相機權限
<!--寫入手機權限-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--相機權限-->
<uses-permission android:name="android.permission.CAMERA" />
權限的部分就請參考:
http://nikeru8.blogspot.tw/2017/04/android-permission.html
權限加入
MainActivity.java
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Activity activity;
public static final int PermissionCode = 1000;
public static final int GetPhotoCode = 1001;
private Button mBtnPic;
private ImageView mShowImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activity = this;
initView();
initListener();
}
private void initView() {
mBtnPic = (Button) findViewById(R.id.btn_take_pic);
mShowImage = (ImageView) findViewById(R.id.show_image);
}
private void initListener() {
mBtnPic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//檢查是否取得權限
final int permissionCheck = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
//沒有權限時
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA,
Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
PermissionCode);
} else { //已獲得權限
Toast.makeText(activity, "已經拿到權限囉!", Toast.LENGTH_SHORT).show();
startActivityForResult(new Intent(MainActivity.this, TakePicActivity.class), GetPhotoCode);
}
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PermissionCode) {
//假如允許了
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//do something
Toast.makeText(this, "感謝賜予權限!", Toast.LENGTH_SHORT).show();
startActivityForResult(new Intent(MainActivity.this, TakePicActivity.class), GetPhotoCode);
}
//假如拒絕了
else {
//do something
Toast.makeText(this, "CAMERA權限FAIL", Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
此時,你按相機,已經會出現畫面了吧!
但按下去的時候,他會頓一下(已經拍照
ing)然後...就沒有然後了。
為什麼呢,其實已經有拍照的效果,只是沒有把畫面帶回
MainActivity內而已
此時就要往takePicture這方法追到底發生什麼事情
仔細看一下作者寫的
CameraSurfaceView 會發現
public void takePicture(){
//设置参数,并拍照
setCameraParams(mCamera, mScreenWidth, mScreenHeight);
// 当调用camera.takePiture方法后,camera关闭了预览,这时需要调用startPreview()来重新开启预览
mCamera.takePicture(null,null, jpeg);
}
作者在這邊只拍了照,卻沒有結束掉
finish();
這邊可以自己加入finish();
我們這邊設計,你按拍照後...
一、檢查相機權限。
二、帶畫面。(創建相片存放位置
FilePath並且把
filePath帶到拍照頁面的
activity,拍完照後存入到
filePath的位置)
上面已經增加了權限。
這邊我們來帶畫面吧
在
MainActivity先把要存照片的地方位置創建好。
//創造檔案名稱、和存擋路徑
String imageFilePath;
private File createImageFile() throws IOException {
String timeStamp =
new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(new Date());
String imageFileName = "IMG_" + timeStamp + "_";
File storageDir =
getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
imageFilePath = image.getAbsolutePath();
return image;
}
此時,我們就獲取到創建的
File。
整理一下MainActivity
把權限和帶畫面的Code整理在同一個方法內
MainActivity
public class MainActivity extends AppCompatActivity {
private Activity activity;
public static final int PermissionCode = 1000;
public static final int GetPhotoCode = 1001;
private Button mBtnPic;
private ImageView mShowImage;
String imageFilePath;
private boolean isCameraPermission = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activity = this;
initView();
initListener();
}
private void initView() {
mBtnPic = (Button) findViewById(R.id.btn_take_pic);
mShowImage = (ImageView) findViewById(R.id.show_image);
}
private void initListener() {
mBtnPic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openCamera();
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PermissionCode) {
//假如允許了
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isCameraPermission = true;
//do something
Toast.makeText(this, "感謝賜予權限!", Toast.LENGTH_SHORT).show();
startActivityForResult(new Intent(MainActivity.this, TakePicActivity.class), GetPhotoCode);
}
//假如拒絕了
else {
isCameraPermission = false;
//do something
Toast.makeText(this, "CAMERA權限FAIL,請給權限", Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
//創造檔案名稱、和存擋路徑
private File createImageFile() throws IOException {
String timeStamp =
new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(new Date());
String imageFileName = "IMG_" + timeStamp + "_";
File storageDir =
getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
imageFilePath = image.getAbsolutePath();
return image;
}
private void openCamera() {
//已獲得權限
if (isCameraPermission) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException e) {
Log.d("checkpoint", "error for createImageFile 創建路徑失敗");
}
//成功創建路徑的話
if (photoFile != null) {
Intent intent = new Intent(MainActivity.this, TakePicActivity.class);
Bundle bundle = new Bundle();
bundle.putString("url", photoFile.getAbsolutePath());
intent.putExtras(bundle);
startActivityForResult(intent, GetPhotoCode);
}
}
//沒有獲得權限
else {
getPermission();
}
}
private void getPermission() {
//檢查是否取得權限
final int permissionCheck = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
//沒有權限時
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
isCameraPermission = false;
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA,
Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
PermissionCode);
} else { //已獲得權限
isCameraPermission = true;
openCamera();
}
}
}
再來就會動到原作者的Code了
CameraSurfaceView
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.FrameLayout;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
/**
* Created by Administrator on 2017/2/15 0015.//自定义相机
*/
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {
private static final String TAG = "CameraSurfaceView";
private Context mContext;
private SurfaceHolder holder;
private Camera mCamera;
private int mScreenWidth;
private int mScreenHeight;
private CameraTopRectView topView;
//更動
private String filePath;
private Activity activity;
public CameraSurfaceView(Context context) {
this(context, null);
}
public CameraSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
getScreenMetrix(context);
topView = new CameraTopRectView(context, attrs);
initView();
}
//拿到手机屏幕大小
private void getScreenMetrix(Context context) {
WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
WM.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
mScreenHeight = outMetrics.heightPixels;
}
private void initView() {
holder = getHolder();//获得surfaceHolder引用
holder.addCallback(this);
// holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置类型
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated");
if (mCamera == null) {
mCamera = Camera.open();//开启相机
try {
mCamera.setPreviewDisplay(holder);//摄像头画面显示在Surface上
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged");
setCameraParams(mCamera, mScreenWidth, mScreenHeight);
mCamera.startPreview();
// mCamera.takePicture(null, null, jpeg);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(TAG, "surfaceDestroyed");
mCamera.stopPreview();//停止预览
mCamera.release();//释放相机资源
mCamera = null;
holder = null;
}
@Override
public void onAutoFocus(boolean success, Camera Camera) {
if (success) {
Log.i(TAG, "onAutoFocus success=" + success);
System.out.println(success);
}
}
private void setCameraParams(Camera camera, int width, int height) {
Log.i(TAG, "setCameraParams width=" + width + " height=" + height);
Camera.Parameters parameters = mCamera.getParameters();
// 获取摄像头支持的PictureSize列表
List pictureSizeList = parameters.getSupportedPictureSizes();
for (Camera.Size size : pictureSizeList) {
Log.i(TAG, "pictureSizeList size.width=" + size.width + " size.height=" + size.height);
}
/**从列表中选取合适的分辨率*/
Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
if (null == picSize) {
Log.i(TAG, "null == picSize");
picSize = parameters.getPictureSize();
}
Log.i(TAG, "picSize.width=" + picSize.width + " picSize.height=" + picSize.height);
// 根据选出的PictureSize重新设置SurfaceView大小
float w = picSize.width;
float h = picSize.height;
parameters.setPictureSize(picSize.width, picSize.height);
this.setLayoutParams(new FrameLayout.LayoutParams((int) (height * (h / w)), height));
// 获取摄像头支持的PreviewSize列表
List previewSizeList = parameters.getSupportedPreviewSizes();
for (Camera.Size size : previewSizeList) {
Log.i(TAG, "previewSizeList size.width=" + size.width + " size.height=" + size.height);
}
Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
if (null != preSize) {
Log.i(TAG, "preSize.width=" + preSize.width + " preSize.height=" + preSize.height);
parameters.setPreviewSize(preSize.width, preSize.height);
}
parameters.setJpegQuality(100); // 设置照片质量
if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式
}
mCamera.cancelAutoFocus();//自动对焦。
mCamera.setDisplayOrientation(90);// 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示
mCamera.setParameters(parameters);
}
/**
* 从列表中选取合适的分辨率
* 默认w:h = 4:3
* 注意:这里的w对应屏幕的height
* h对应屏幕的width
*/
private Camera.Size getProperSize(List pictureSizeList, float screenRatio) {
Log.i(TAG, "screenRatio=" + screenRatio);
Camera.Size result = null;
for (Camera.Size size : pictureSizeList) {
float currentRatio = ((float) size.width) / size.height;
if (currentRatio - screenRatio == 0) {
result = size;
break;
}
}
if (null == result) {
for (Camera.Size size : pictureSizeList) {
float curRatio = ((float) size.width) / size.height;
if (curRatio == 4f / 3) {// 默认w:h = 4:3
result = size;
break;
}
}
}
return result;
}
// 拍照瞬间调用
private Camera.ShutterCallback shutter = new Camera.ShutterCallback() {
@Override
public void onShutter() {
Log.i(TAG, "shutter");
System.out.println("执行了吗+1");
}
};
// 获得没有压缩过的图片数据
private Camera.PictureCallback raw = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera Camera) {
Log.i(TAG, "raw");
System.out.println("执行了吗+2");
}
};
//创建jpeg图片回调数据对象
private Camera.PictureCallback jpeg = new Camera.PictureCallback() {
private Bitmap bitmap;
@Override
public void onPictureTaken(byte[] data, Camera Camera) {
topView.draw(new Canvas());
BufferedOutputStream bos = null;
Bitmap bm = null;
if (data != null) {
}
try {
// 获得图片
bm = BitmapFactory.decodeByteArray(data, 0, data.length);
Log.d("checkpoint", "checkpoint - " + bm);
// if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// String filePath = "/sdcard/dyk" + System.currentTimeMillis() + ".JPEG";//照片保存路径
// //图片存储前旋转
Matrix m = new Matrix();
int height = bm.getHeight();
int width = bm.getWidth();
m.setRotate(90);
//旋转后的图片
bitmap = Bitmap.createBitmap(bm, 0, 0, width, height, m, true);
System.out.println("执行了吗+3");
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
bos = new BufferedOutputStream(new FileOutputStream(file));
Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap,
topView.getViewWidth(), topView.getViewHeight(), true);
bm = Bitmap.createBitmap(sizeBitmap, topView.getRectLeft(),
topView.getRectTop(),
topView.getRectRight() - topView.getRectLeft(),
topView.getRectBottom() - topView.getRectTop());// 截取
bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);//将图片压缩到流中
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.flush();//输出
bos.close();//关闭
bm.recycle();// 回收bitmap空间
mCamera.stopPreview();// 关闭预览
activity.setResult(Activity.RESULT_OK);
activity.finish();
// mCamera.startPreview();// 开启预览
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
public void takePicture(Activity activity, String filePath) {
this.filePath = filePath;
this.activity = activity;
//设置参数,并拍照
setCameraParams(mCamera, mScreenWidth, mScreenHeight);
// 当调用camera.takePiture方法后,camera关闭了预览,这时需要调用startPreview()来重新开启预览
mCamera.takePicture(null, null, jpeg);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
我主要把
activity和
filePath帶入這邊的
takePicture方法。
註解掉做者獲取
filePath的方法,帶入自己的
filePath。
並且增加
activity關掉的
finish()
再回到
TakePicActivity,接收在
MainActivity創建的
filePath。
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
public class TakePicActivity extends AppCompatActivity {
private Button button;
private CameraSurfaceView mCameraSurfaceView;
private Activity activity;
String filePath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity = this;
getBundleData();
initSet();
initView();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mCameraSurfaceView.takePicture(activity, filePath);
}
});
}
private void initSet() {
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_take_pic);
}
private void initView() {
mCameraSurfaceView = (CameraSurfaceView) findViewById(R.id.cameraSurfaceView);
button = (Button) findViewById(R.id.takePic);
}
private void getBundleData() {
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
filePath = bundle.getString("url");
}
Log.d("checkpoint", "check filePath - " + filePath);
}
}
再回到
MainActivity做一次整理
多增加
onActivityResult 和 自寫的方法
setPic
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == GetPhotoCode) {
setPic(imageFilePath);
}
}
private void setPic(String mCurrentPhotoPath) {
// Get the dimensions of the View
int targetW = mShowImage.getWidth();
int targetH = mShowImage.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
mShowImage.setImageBitmap(bitmap);
}
就完成囉!
有仔細看的朋友一定會想知道...
原作者filePath寫得好好的為啥要動?
拍照會延遲,可能照片還沒拍好、圖片都還沒存到路徑
TackPicActivity就被關掉了。
所以我才改動filePath的取得方式。
屁話講了那麼多,重點大家就是要程式碼吧 哈哈哈
Demo:
https://github.com/nikeru8/CustomCamera
如果有錯誤或者不懂的地方歡迎提問!
文獻:
https://developer.android.com/training/camera/photobasics.html#TaskScalePhoto
https://blog.csdn.net/LHBTM/article/details/55505668