自定義拍照會用到SurfaceView控件顯示照片的預覽區域,以下是布局文件:
兩個TextView是用來顯示提示信息和倒計時的秒數的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<RelativeLayout 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:background= "#266194" android:orientation= "vertical" tools:context= ".TestActivity" > <SurfaceView android:id= "@+id/surfaceView" android:layout_width= "match_parent" android:layout_height= "match_parent" android:layout_centerInParent= "true" /> <LinearLayout android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_centerInParent= "true" android:orientation= "vertical" > <TextView android:layout_width= "match_parent" android:layout_height= "wrap_content" android:text= "請調整位置到此區域" android:textColor= "#ff0000" android:textSize= "32sp" /> <TextView android:id= "@+id/tv_time" android:layout_width= "match_parent" android:layout_height= "wrap_content" android:paddingTop= "10dp" android:gravity= "center_horizontal" android:textColor= "#266194" android:textSize= "32sp" /> </LinearLayout> </RelativeLayout> |
接下來是mainActivity中的具體實現以及詳細注釋:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
package com.dhsr.pujiejia.ui; import java.io.File; import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.Date; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.CameraInfo; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.Window; import android.view.WindowManager; import android.widget.TextView; import com.example.pujiejiaapp.R; @SuppressLint ({ "NewApi" , "SdCardPath" }) public class CameraActivity extends Activity implements Runnable { // 預覽圖片范圍 private SurfaceView surfaceView; private TextView tv_time; // 倒計時拍攝 private int cameratime = 4 ; private Camera camera; private boolean preview = false ; // 文件名字 private String filename; // 文件名字的帶的時間戳 private String timeString; // 格式化時間 private SimpleDateFormat dateFormat; // 日期對象 private Date date; // 控制線程 boolean stopThread = false ; private File file; String photo; private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { int what = msg.what; switch (what) { case 222 : tv_time.setText( "" + cameratime); if ( "0" .equals(tv_time.getText().toString())) { tv_time.setText( "拍攝成功!" ); takePhoto(); } break ; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super .onCreate(savedInstanceState); setContentView(R.layout.activity_test); CameraActivity. this .setFinishOnTouchOutside( false ); // 初始化數據 findView(); surfaceView.getHolder().addCallback( new SufaceListener()); /* 下面設置Surface不維護自己的緩沖區,而是等待屏幕的渲染引擎將內容推送到用戶面前 */ surfaceView.getHolder() .setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceView.getHolder().setFixedSize(200, 200); // 設置分辨率 } @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); // 開啟線程 new Thread(this).start(); } private final class SufaceListener implements SurfaceHolder.Callback { /** * surface改變 */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * surface創建 */ @Override public void surfaceCreated(SurfaceHolder holder) { try { for (int i = 0; i < Camera.getNumberOfCameras(); i++) { CameraInfo info = new CameraInfo(); Camera.getCameraInfo(i, info); // 調用系統的前置攝像頭 if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { camera = Camera.open(i); } } Camera.Parameters parameters = camera.getParameters(); /* 每秒從攝像頭捕獲5幀畫面, */ parameters.setPreviewFrameRate(5); /* 設置照片的輸出格式:jpg */ parameters.setPictureFormat(PixelFormat.JPEG); /* 照片質量 */ parameters.set("jpeg-quality", 85); WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); camera.setParameters(parameters); camera.setPreviewDisplay(surfaceView.getHolder());// 通過SurfaceView顯示取景畫面 camera.startPreview(); preview = true; } catch (Exception e) { } } /** * surface銷毀 */ @Override public void surfaceDestroyed(SurfaceHolder holder) { if (camera != null) { if (preview) camera.stopPreview(); camera.release(); camera = null; } } } /** * 拍攝照片 */ private void takePhoto() { // 執行拍照效果 camera.takePicture( null , null , new Camera.PictureCallback() { @Override public void onPictureTaken( byte [] data, Camera camera) { try { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0 , data.length); timeString = formatDate(); //保存到data/data目錄自定義文件夾下 filename = "/data/data/com.example.pujiejiaapp/images/" + timeString + ".jpg" ; File file = new File(filename); boolean createNewFile = file.createNewFile() System.out.println( "創建文件夾成功沒有" + createNewFile); System.out.println(file); FileOutputStream outStream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.JPEG, 60 , outStream); outStream.flush(); outStream.close(); // 重新瀏覽 camera.stopPreview(); camera.startPreview(); preview = true ; } catch (Exception e) { e.printStackTrace(); } finally { } } }); } @Override public void run() { while (!stopThread) { try { //按秒數倒計時 Thread.sleep( 1000 ); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } cameratime--; mHandler.sendEmptyMessage( 222 ); if (cameratime <= 0 ) { break ; } } } // 初始化數據 private void findView() { surfaceView = (SurfaceView) this .findViewById(R.id.surfaceView); tv_time = (TextView) findViewById(R.id.tv_time); } // 格式化系統的時間 public String formatDate() { date = new Date(System.currentTimeMillis()); // 日期格式 dateFormat = new SimpleDateFormat( "'IMG'_yyyyMMddHHmmss" ); return dateFormat.format(date); } @Override protected void onDestroy() { // TODO Auto-generated method stub // 線程已關閉 super .onDestroy(); stopThread = true ; } } |
核心代碼詳解:
1.創建SurfaceView時,surfaceCreated()方法中
1
2
3
4
5
6
7
8
|
for ( int i = 0 ; i < Camera.getNumberOfCameras(); i++) { CameraInfo info = new CameraInfo(); Camera.getCameraInfo(i, info); // 調用系統的前置攝像頭 if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { camera = Camera.open(i); } } |
此部分代碼為打開相機時默認打開前置攝像頭CameraInfo.CAMERA_FACING_BACK為默認打開后置攝像頭,CameraInfo.CAMERA_FACING_FRONT前置攝像頭
2.照片拍攝takePhoto()方法中:
1
2
3
4
5
6
7
8
9
10
|
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0 , data.length); timeString = formatDate(); filename = "/data/data/com.example.pujiejiaapp/images/" + timeString + ".jpg" ; photo = timeString + ".jpg" ; File file = new File(filename); boolean createNewFile = file.createNewFile(); FileOutputStream outStream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.JPEG, 60 , outStream); |
此部分代碼為將拍攝到的圖片保存為以bitmap格式保存在指定的目錄下
3.開子線程用于倒計時拍攝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public void run() { while (!stopThread) { try { Thread.sleep( 1000 ); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } cameratime--; mHandler.sendEmptyMessage( 222 ); if (cameratime <= 0 ) { break ; } } } |
希望大家理解核心代碼的詳細注釋,歡迎提供意見,希望能給大家帶來幫助,謝謝!