├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── louisgeek
│ │ └── louiscustomcamerademo
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── louisgeek
│ │ │ └── louiscustomcamerademo
│ │ │ ├── CameraActivity.java
│ │ │ ├── CameraLine.java
│ │ │ ├── CameraPreview.java
│ │ │ ├── DoubleClickConfig.java
│ │ │ ├── ImageViewUtil.java
│ │ │ ├── PreviewActivity.java
│ │ │ ├── ScreenSwitchUtils.java
│ │ │ └── Utils.java
│ └── res
│ │ ├── drawable
│ │ └── camera_selector.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── activity_preview.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ ├── camera_normal.png
│ │ ├── camera_pressed.png
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ ├── camera_flash_light.png
│ │ ├── cancel.png
│ │ ├── ic_launcher.png
│ │ └── ok.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── camera_change.png
│ │ ├── camera_config.png
│ │ ├── camera_flash_auto.png
│ │ ├── camera_flash_light.png
│ │ ├── camera_flash_off.png
│ │ ├── camera_flash_on.png
│ │ ├── camera_normal_new.png
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── louisgeek
│ └── louiscustomcamerademo
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshots
├── pic.jpg
└── picA.jpg
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | LouisCustomCameraDemo
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
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 | 1.8
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LouisCustomCameraDemo
2 | Android 自定义相机 切换相机 参考线(辅助线) 闪光灯 缩放 自动聚焦
3 |
4 | 
5 | 
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/build.gradle
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\LouisIDE\Android\android-sdk-windows/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/louisgeek/louiscustomcamerademo/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/CameraActivity.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.pm.ActivityInfo;
7 | import android.database.Cursor;
8 | import android.graphics.Bitmap;
9 | import android.hardware.Camera;
10 | import android.net.Uri;
11 | import android.os.Bundle;
12 | import android.os.Environment;
13 | import android.os.Handler;
14 | import android.os.Message;
15 | import android.provider.MediaStore;
16 | import android.text.TextUtils;
17 | import android.util.Log;
18 | import android.view.Display;
19 | import android.view.Surface;
20 | import android.view.View;
21 | import android.view.Window;
22 | import android.view.WindowManager;
23 | import android.widget.ImageView;
24 | import android.widget.RelativeLayout;
25 | import android.widget.Toast;
26 |
27 | import java.io.BufferedOutputStream;
28 | import java.io.File;
29 | import java.io.FileNotFoundException;
30 | import java.io.FileOutputStream;
31 | import java.io.IOException;
32 | import java.text.SimpleDateFormat;
33 | import java.util.Date;
34 |
35 | /**
36 | * 遇到的问题:显示相机SurfaceView尺寸,相机预览尺寸 和 相机保存图片尺寸 三者不一致
37 | */
38 | public class CameraActivity extends Activity implements Camera.PictureCallback, Camera.ShutterCallback {
39 |
40 | public static final int FLAG_CHOOCE_PICTURE = 2001;
41 | private final int FLAG_AUTO_FOCUS = 1001;
42 | private final int TAKE_PHOTO_FINISH = 1002;
43 |
44 | private final int FOCUS_DURATION = 3000;//延迟聚焦
45 |
46 | // private View centerWindowView;
47 | private int mScreenHeight, mScreenWidth;
48 | private int viewHeight;
49 |
50 | public static final int ZOOM_FACTOR = 5;//缩放因子
51 | private int zoomValue = 0;
52 | private boolean safeToTakePicture = true;
53 |
54 | private CameraPreview mPreview;
55 | Camera mCamera;
56 | int numberOfCameras;
57 | int cameraCurrentlyLocked;
58 |
59 |
60 | private ImageView preview_iv;
61 | private Handler handler;
62 | Bitmap rightBitmap;
63 | ImageView id_iv_flash_switch;
64 | int Request_Code_Camera = 10;
65 | CameraLine mCameraLine;
66 | // 两个相机的情况下
67 | // The first rear facing camera
68 | private int defaultCameraId = 1;
69 | int cameraPosition = 1;
70 |
71 | private ScreenSwitchUtils mScreenSwitchInstance;
72 | private boolean isPortrait = true;
73 | private int orientationState = ScreenSwitchUtils.ORIENTATION_HEAD_IS_UP;
74 |
75 |
76 | @Override
77 | public void onCreate(Bundle savedInstanceState) {
78 | super.onCreate(savedInstanceState);
79 | // Hide the window title.
80 | requestWindowFeature(Window.FEATURE_NO_TITLE);
81 | // getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
82 | //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//强制横屏
83 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//【旋转问题】首先强制竖屏,手机横过来时候 控件不变
84 |
85 | setContentView(R.layout.activity_main);
86 |
87 | //【重力感应处理】 app内锁定横屏 或用户锁定横屏时候获得方向
88 | mScreenSwitchInstance = ScreenSwitchUtils.init(getApplicationContext());
89 | //android M
90 | //判断是否有权限
91 | /* if (ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA)
92 | != PackageManager.PERMISSION_GRANTED){
93 |
94 | }else{
95 |
96 | //请求权限
97 | ActivityCompat.requestPermissions(this,
98 | new String[]{Manifest.permission.CAMERA},
99 | Request_Code_Camera);
100 | }
101 | //判断是否需要 向用户解释,为什么要申请该权限
102 | ActivityCompat.shouldShowRequestPermissionRationale(this,
103 | Manifest.permission.CAMERA);*/
104 |
105 |
106 | if (!Utils.checkCameraHardware(this)) {
107 | Toast.makeText(CameraActivity.this, "设备没有摄像头", Toast.LENGTH_SHORT).show();
108 | return;
109 | }
110 | mCameraLine = (CameraLine) findViewById(R.id.id_cl);
111 | //
112 | preview_iv = (ImageView) findViewById(R.id.id_preview_iv);
113 | RelativeLayout id_rl_cp_view = (RelativeLayout) findViewById(R.id.id_rl_cp_view);
114 | DoubleClickConfig.registerDoubleClickListener(id_rl_cp_view, new DoubleClickConfig.OnDoubleClickListener() {
115 |
116 | @Override
117 | public void OnSingleClick(View v) {
118 | // TODO Auto-generated method stub
119 | zoomDown();//单机缩小
120 | }
121 |
122 | @Override
123 | public void OnDoubleClick(View v) {
124 | // TODO Auto-generated method stub
125 | zoomUp();//双击放大
126 | }
127 | });
128 |
129 | id_iv_flash_switch = (ImageView) findViewById(R.id.id_iv_flash_switch);
130 |
131 | handler = new Handler() {
132 | @Override
133 | public void handleMessage(Message msg) {
134 | if (msg.what == FLAG_AUTO_FOCUS) {
135 | if (mCamera != null && safeToTakePicture && !TextUtils.isEmpty(mCamera.getParameters().getFlashMode())) {
136 | mCamera.startPreview();
137 | mCamera.autoFocus(null);
138 | //Toast.makeText(CameraActivity.this, "auto focus", Toast.LENGTH_SHORT).show();
139 | }
140 |
141 | handler.sendEmptyMessageDelayed(FLAG_AUTO_FOCUS, FOCUS_DURATION);
142 |
143 | } else if (msg.what == TAKE_PHOTO_FINISH) {
144 | // byte[] bitmapByte= (byte[]) msg.obj;
145 | if (msg.obj == null) {
146 | return;
147 | }
148 | String filePath = msg.obj.toString();
149 | Intent intent = new Intent(CameraActivity.this, PreviewActivity.class);
150 | intent.putExtra("filePath", filePath);
151 | CameraActivity.this.startActivity(intent);
152 | }
153 | }
154 | };
155 |
156 | // centerWindowView = findViewById(R.id.center_window_view);
157 | Log.d("CameraSurfaceView", "CameraSurfaceView onCreate currentThread : " + Thread.currentThread());
158 | // 得到屏幕的大小
159 | WindowManager wManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
160 | Display display = wManager.getDefaultDisplay();
161 | mScreenHeight = display.getHeight();
162 | mScreenWidth = display.getWidth();
163 | viewHeight = mScreenWidth / 2;
164 | // centerWindowView.getLayoutParams().width = viewHeight;
165 | // centerWindowView.getLayoutParams().height = viewHeight;
166 |
167 | // Create a RelativeLayout container that will hold a SurfaceView,
168 | // and set it as the content of our activity.
169 | mPreview = (CameraPreview) findViewById(R.id.camera_preview);
170 |
171 | startCamera();
172 |
173 | }
174 |
175 | @Override
176 | protected void onStart() {
177 | super.onStart();
178 | //【重力感应处理】
179 | mScreenSwitchInstance.start(this);
180 | }
181 |
182 | @Override
183 | protected void onStop() {
184 | super.onStop();
185 | //【重力感应处理】
186 | mScreenSwitchInstance.stop();
187 | }
188 |
189 |
190 | public void onClick(View view) {
191 | switch (view.getId()) {
192 | case R.id.id_iv_shutter:
193 | /* // 拍照,设置相关参数
194 | Camera.Parameters params = mCamera.getParameters();
195 | params.setPictureFormat(ImageFormat.JPEG);
196 | params.setPreviewSize(800, 400);
197 | // 自动对焦
198 | params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
199 | mCamera.setParameters(params);
200 | mCamera.takePicture(null, null, mPictureCallback);*/
201 |
202 |
203 | //快门
204 | //stopFocus();
205 |
206 | /* mCamera.autoFocus(new Camera.AutoFocusCallback() {//自动对焦
207 | @Override
208 | public void onAutoFocus(boolean success, Camera camera) {
209 | // TODO Auto-generated method stub
210 | if (success) {
211 | *//* //设置参数,并拍照
212 | Camera.Parameters params = camera.getParameters();
213 | params.setPictureFormat(PixelFormat.JPEG);//图片格式
214 | params.setPreviewSize(800, 480);//图片大小
215 | camera.setParameters(params);//将参数设置到我的camera
216 | camera.takePicture(null, null, mPictureCallback);//将拍摄到的照片给自定义的对象*//**//**//**//*
217 | // camera.takePicture(null, null, CameraActivity.this);*//*
218 | takePicture(null, null, CameraActivity.this);
219 | }
220 | }
221 | });*/
222 | stopFocus();
223 | takePicture(null, null, this);
224 | break;
225 | case R.id.id_iv_flash_switch:
226 | toggleFlash();
227 | break;
228 | case R.id.id_iv_config_line:
229 | mCameraLine.changeLineStyle();
230 | break;
231 | /*case R.id.id_iv_config_line:
232 | //###choosePicture();
233 | break;*/
234 | case R.id.id_iv_change:
235 | // changeCamera();
236 | changeCameraTwo();
237 |
238 | break;
239 | default:
240 |
241 | break;
242 | }
243 |
244 | }
245 |
246 | @Override
247 | protected void onResume() {
248 | super.onResume();
249 |
250 | startCamera();
251 | }
252 |
253 | private void changeCamera() {
254 | // Find the total number of cameras available
255 | numberOfCameras = Camera.getNumberOfCameras();
256 |
257 | // Find the ID of the default camera
258 | Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
259 | for (int i = 0; i < numberOfCameras; i++) {
260 | Camera.getCameraInfo(i, cameraInfo);
261 | if (defaultCameraId == 1) {
262 | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
263 |
264 | releaseCamera();
265 | mCamera = Camera.open(i);//打开当前选中的摄像头
266 |
267 | defaultCameraId = 0;
268 | break;
269 | }
270 | } else {
271 | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
272 |
273 | mCamera.release();//释放资源
274 | mCamera = Camera.open(i);//打开当前选中的摄像头
275 |
276 | defaultCameraId = 1;
277 | break;
278 | }
279 | }
280 | }
281 | }
282 |
283 | void changeCameraTwo() {
284 | //切换前后摄像头
285 | int cameraCount = 0;
286 | Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
287 | cameraCount = Camera.getNumberOfCameras();//得到摄像头的个数
288 | for (int i = 0; i < cameraCount; i++) {
289 | Camera.getCameraInfo(i, cameraInfo);//得到每一个摄像头的信息
290 | if (cameraPosition == 1) {
291 | Toast.makeText(this, "1现在是后置,变更为前置", Toast.LENGTH_SHORT).show();
292 | //现在是后置,变更为前置
293 | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
294 | mCamera.stopPreview();//停掉原来摄像头的预览
295 | /*
296 | mCamera.release();//释放资源
297 | mCamera = null;//取消原来摄像头*/
298 | releaseCamera();
299 | mCamera = Camera.open(i);//打开当前选中的摄像头
300 | try {
301 | mCamera.setPreviewDisplay(mPreview.getLouisSurfaceHolder());//通过surfaceview显示取景画面
302 | // mCamera.setDisplayOrientation(90); //
303 | mPreview.setCamera(mCamera);
304 | } catch (IOException e) {
305 | // TODO Auto-generated catch block
306 | e.printStackTrace();
307 | }
308 | mCamera.startPreview();//开始预览
309 | cameraPosition = 0;
310 | break;
311 | }
312 | } else {
313 | Toast.makeText(this, "2现在是前置, 变更为后置", Toast.LENGTH_SHORT).show();
314 | //现在是前置, 变更为后置
315 | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
316 | mCamera.stopPreview();//停掉原来摄像头的预览
317 | /*
318 | mCamera.release();//释放资源
319 | mCamera = null;//取消原来摄像头*/
320 | releaseCamera();
321 | mCamera = Camera.open(i);//打开当前选中的摄像头
322 | try {
323 | mCamera.setPreviewDisplay(mPreview.getLouisSurfaceHolder());//通过surfaceview显示取景画面
324 | // mCamera.setDisplayOrientation(90); //
325 | mPreview.setCamera(mCamera);
326 | } catch (IOException e) {
327 | // TODO Auto-generated catch block
328 | e.printStackTrace();
329 | }
330 | mCamera.startPreview();//开始预览
331 | cameraPosition = 1;
332 | break;
333 | }
334 | }
335 |
336 | }
337 | }
338 |
339 | private void startCamera() {
340 | // Open the default i.e. the first rear facing camera.
341 | try {
342 | if (mCamera == null) {
343 | mCamera = Camera.open();
344 | }
345 | } catch (Exception e) {
346 | e.printStackTrace();
347 | Toast.makeText(this, "启动照相机失败,请检查设备并打开权限", Toast.LENGTH_SHORT).show();
348 | }
349 | cameraCurrentlyLocked = defaultCameraId;
350 | mPreview.setCamera(mCamera);
351 |
352 | startFocus();
353 | }
354 |
355 | @Override
356 | protected void onPause() {
357 | super.onPause();
358 | stopFocus();
359 | releaseCamera();
360 | }
361 |
362 | /**
363 | * 开启自动对焦
364 | */
365 | private void startFocus() {
366 | stopFocus();
367 | handler.sendEmptyMessageDelayed(FLAG_AUTO_FOCUS, FOCUS_DURATION);
368 | }
369 |
370 | /**
371 | * 关闭自动对焦
372 | */
373 | private void stopFocus() {
374 | handler.removeMessages(FLAG_AUTO_FOCUS);
375 | }
376 |
377 | @Override
378 | protected void onDestroy() {
379 | super.onDestroy();
380 | releaseCamera();
381 | }
382 |
383 | /**
384 | * 释放mCamera
385 | */
386 | private void releaseCamera() {
387 | // Because the Camera object is a shared resource, it's very
388 | // important to release it when the activity is paused.
389 | if (mCamera != null) {
390 | mPreview.setCamera(null);
391 | mCamera.release();
392 | mCamera = null;
393 | }
394 | }
395 |
396 | /**
397 | * 释放mCamera
398 | */
399 | private void releaseCameraTwo() {
400 | if (mCamera != null) {
401 | mCamera.setPreviewCallback(null);
402 | mCamera.stopPreview();// 停掉原来摄像头的预览
403 | mCamera.release();
404 | mCamera = null;
405 | }
406 | }
407 |
408 | /**
409 | * 拍照
410 | *
411 | * @param shutter
412 | * @param raw
413 | * @param jpeg
414 | */
415 | public void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw,
416 | Camera.PictureCallback jpeg) {
417 | if (mCamera != null) {
418 | if (safeToTakePicture) {
419 | mCamera.takePicture(shutter, raw, jpeg);
420 | safeToTakePicture = false;
421 | }
422 | }
423 | }
424 |
425 | /**
426 | * 缩小
427 | */
428 | public void zoomDown() {
429 | if (mCamera == null) {
430 | return;
431 | }
432 | Camera.Parameters p = mCamera.getParameters();
433 | if (!p.isZoomSupported()) return;
434 |
435 | if (zoomValue > 0) {
436 | zoomValue--;
437 | } else {
438 | zoomValue = 0;
439 | // Toast.makeText(getApplicationContext(), "已缩小到最小级别", Toast.LENGTH_SHORT).show();
440 | return;
441 | }
442 | int value = (int) (1F * zoomValue / ZOOM_FACTOR * p.getMaxZoom());
443 | p.setZoom(value);
444 | mCamera.setParameters(p);
445 | }
446 |
447 | /**
448 | * 放大
449 | */
450 | public void zoomUp() {
451 | if (mCamera == null) {
452 | return;
453 | }
454 | Camera.Parameters p = mCamera.getParameters();
455 | if (!p.isZoomSupported()) return;
456 |
457 | if (zoomValue < ZOOM_FACTOR) {
458 | zoomValue++;
459 | } else {
460 | zoomValue = ZOOM_FACTOR;
461 | Toast.makeText(getApplicationContext(), "已放大到最大级别", Toast.LENGTH_SHORT).show();
462 | return;
463 | }
464 | int value = (int) (1F * zoomValue / ZOOM_FACTOR * p.getMaxZoom());
465 | p.setZoom(value);
466 | mCamera.setParameters(p);
467 | }
468 |
469 | /**
470 | * 开关闪光灯
471 | *
472 | * 持续的亮灯FLASH_MODE_TORCH
473 | * 闪一下FLASH_MODE_ON
474 | * 关闭模式FLASH_MODE_OFF
475 | * 自动感应是否要用闪光灯FLASH_MODE_AUTO
476 | */
477 | public void toggleFlash() {
478 | if (mCamera == null) {
479 | return;
480 | }
481 |
482 | Camera.Parameters p = mCamera.getParameters();
483 | if (Camera.Parameters.FLASH_MODE_OFF.equals(p.getFlashMode())) {
484 | p.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
485 | mCamera.setParameters(p);
486 | id_iv_flash_switch.setImageDrawable(getResources().getDrawable(R.mipmap.camera_flash_on));
487 | } else if (Camera.Parameters.FLASH_MODE_ON.equals(p.getFlashMode())) {
488 | p.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
489 | mCamera.setParameters(p);
490 | id_iv_flash_switch.setImageDrawable(getResources().getDrawable(R.mipmap.camera_flash_auto));
491 | } else if (Camera.Parameters.FLASH_MODE_AUTO.equals(p.getFlashMode())) {
492 | p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//持续的亮灯
493 | mCamera.setParameters(p);
494 | id_iv_flash_switch.setImageDrawable(getResources().getDrawable(R.mipmap.camera_flash_light));
495 | } else if (Camera.Parameters.FLASH_MODE_TORCH.equals(p.getFlashMode())) {
496 | p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
497 | mCamera.setParameters(p);
498 | id_iv_flash_switch.setImageDrawable(getResources().getDrawable(R.mipmap.camera_flash_off));
499 | } else {
500 | Toast.makeText(this, "Flash mode setting is not supported.", Toast.LENGTH_SHORT).show();
501 | }
502 |
503 | }
504 |
505 | /**
506 | * 选择图片
507 | */
508 | private void choosePicture() {
509 | Intent intent = new Intent();
510 | intent.setType("image/*");
511 | intent.setAction(Intent.ACTION_GET_CONTENT);
512 | startActivityForResult(intent, FLAG_CHOOCE_PICTURE);
513 | }
514 |
515 | @Override
516 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
517 | super.onActivityResult(requestCode, resultCode, data);
518 | if (requestCode == FLAG_CHOOCE_PICTURE && resultCode == RESULT_OK) {
519 | Uri uri = data.getData();
520 | String imgPath = getUrl(uri);
521 | Log.d("", "CameraSurfaceView imgPath : " + imgPath);
522 | }
523 | }
524 |
525 | @Override
526 | public void onPictureTaken(byte[] data, Camera camera) {
527 |
528 | // cameraSurfaceView.restartPreview();
529 | /*2016年4月22日15:54:46 if (mCamera != null) {
530 | mCamera.startPreview();
531 | if (!TextUtils.isEmpty(mCamera.getParameters().getFlashMode())) {
532 | mCamera.autoFocus(mPreview);
533 | }
534 | }*/
535 |
536 | if (data == null || data.length <= 0) {
537 | safeToTakePicture = true;
538 | return;
539 | }
540 |
541 | Log.d("CameraSurfaceView", "CameraSurfaceView onPictureTaken data.length : " + data.length);
542 | //Toast.makeText(this, "data.length : " + data.length, Toast.LENGTH_SHORT).show();
543 | //获取方向
544 | //int previewDegree=getDisplayRotation(this);
545 | //获取配置的方向
546 | //int xx=getRequestedOrientation();
547 |
548 | /* Configuration cf= this.getResources().getConfiguration();
549 | int ori = cf.orientation ;
550 | if(ori == cf.ORIENTATION_LANDSCAPE){
551 | // 横屏
552 | Toast.makeText(this,"1横屏",Toast.LENGTH_SHORT).show();
553 | }else if(ori == cf.ORIENTATION_PORTRAIT){
554 | // 竖屏
555 | Toast.makeText(this,"1竖屏",Toast.LENGTH_SHORT).show();
556 | }
557 | DisplayMetrics dm = new DisplayMetrics();
558 | getWindowManager().getDefaultDisplay().getMetrics(dm);
559 | int mWidth = dm.widthPixels;
560 | int mHeight = dm.heightPixels;
561 |
562 | if (mHeight > mWidth){
563 | // 竖屏
564 | Toast.makeText(this,"2竖屏",Toast.LENGTH_SHORT).show();
565 | }else{
566 | // 横屏
567 | Toast.makeText(this,"2横屏",Toast.LENGTH_SHORT).show();
568 | }
569 | */
570 |
571 | isPortrait = mScreenSwitchInstance.isPortrait();
572 | orientationState = mScreenSwitchInstance.getOrientationState();
573 | Log.i("xxx", "louis==xx==isPortrait:" + isPortrait);
574 | Log.i("xxx", "louis==xx==orientationState:" + orientationState);
575 | // 保存图片
576 | final byte[] b = data;
577 | new Thread(new Runnable() {
578 | @Override
579 | public void run() {
580 | handleAndSaveBitmap(b);
581 | }
582 | }).start();
583 |
584 | safeToTakePicture = true;
585 | }
586 |
587 | @Override
588 | public void onShutter() {
589 | Log.d("CameraSurfaceView", "CameraSurfaceView onShutter");
590 | }
591 |
592 | /**
593 | * 处理拍照图片并保存
594 | *
595 | * @param data
596 | */
597 | private synchronized void handleAndSaveBitmap(byte[] data) {
598 | // 保存图片
599 | //### Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length);
600 | Bitmap b = Utils.Bytes2Bitmap(data);
601 |
602 |
603 | if (cameraPosition == 1) {
604 | //后置摄像头
605 | //rightBitmap = Utils.rotate(b, 0);
606 | rightBitmap = Utils.rotate(b, 90);//摆正位置
607 | //rightBitmap = Utils.rotate(b, 180);
608 | // rightBitmap = Utils.rotate(b, 270);
609 | //根据重力感应 更正旋转
610 | switch (orientationState) {
611 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_UP:
612 | break;
613 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_DOWN:
614 | rightBitmap = Utils.rotate(rightBitmap, 180);
615 | break;
616 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_LEFT:
617 | rightBitmap = Utils.rotate(rightBitmap, 270);
618 | break;
619 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_RIGHT:
620 | rightBitmap = Utils.rotate(rightBitmap, 90);
621 | break;
622 | }
623 | } else {
624 | //前置摄像头
625 | rightBitmap = Utils.rotate(b, 270);//摆正位置
626 | //根据重力感应 更正旋转
627 | switch (orientationState) {
628 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_UP:
629 | break;
630 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_DOWN:
631 | rightBitmap = Utils.rotate(rightBitmap, 180);
632 | break;
633 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_LEFT:
634 | rightBitmap = Utils.rotate(rightBitmap, 90);
635 | break;
636 | case ScreenSwitchUtils.ORIENTATION_HEAD_IS_RIGHT:
637 | rightBitmap = Utils.rotate(rightBitmap, 270);
638 | break;
639 | }
640 | }
641 |
642 | String filePath = saveImageFile(rightBitmap);
643 | Message message = handler.obtainMessage();
644 | message.what = TAKE_PHOTO_FINISH;
645 | message.obj = filePath;
646 | message.sendToTarget();
647 |
648 |
649 | //## Bitmap rightBitmap = Utils.rotate(b, 90);
650 |
651 | /* if (!mScreenSwitchInstance.isPortrait()){
652 | rightBitmap=Utils.rotate(rightBitmap,270);
653 | }*/
654 |
655 | //### ???Utils.compress(rightBitmap, 2 * 1024 * 1024);
656 |
657 | /* // 偏移量
658 | int moveX = mPreview.moveX * 2;
659 | int previewW = mPreview.mPreviewSize.width;
660 | int previewH = mPreview.mPreviewSize.height;
661 | int pictureW = mPreview.mPictureSize.width;
662 | int pictureH = mPreview.mPictureSize.height;
663 | int viewW = mPreview.getWidth();
664 | int viewH = mPreview.getHeight();
665 |
666 | rightBitmap = Utils.scale(rightBitmap, previewW, 1F * previewW * pictureW / pictureH);
667 | // moveX = 0;
668 |
669 | int cropWidth = (int) (1F * viewHeight / mScreenWidth * (rightBitmap.getWidth() - moveX));
670 |
671 | int statusBarHeight = Utils.getStatusBarHeight(CameraActivity.this);
672 | int cropX = (int) (rightBitmap.getWidth() / 2 - cropWidth / 2 + 1F * statusBarHeight * pictureW / pictureH / 2);
673 | int cropY = rightBitmap.getHeight() / 2 - cropWidth / 2 - Utils.dip2px(CameraActivity.this, 59)
674 | + statusBarHeight / 2;
675 | Log.d("", "mPreview.moveY : " + mPreview.moveY);
676 | if (rightBitmap.getWidth() < cropWidth + cropX) {
677 | cropX = rightBitmap.getWidth() - cropWidth;
678 | }
679 | if (rightBitmap.getHeight() < cropWidth + cropY) {
680 | cropY = rightBitmap.getHeight() - cropY;
681 | }
682 | // Log.d("CameraSurfaceView", "CameraSurfaceView viewWidth : " + centerWindowView.getWidth());
683 | Log.d("CameraSurfaceView", "CameraSurfaceView bitmapWidth : " + rightBitmap.getWidth() / 2);
684 |
685 | final Bitmap bmp = Bitmap.createBitmap(rightBitmap, cropX, cropY, cropWidth, cropWidth);*/
686 |
687 | /* handler.post(new Runnable() {
688 | @Override
689 | public void run() {
690 | //####2016年4月20日20:02:15 preview_iv.setImageBitmap(bmp);
691 | preview_iv.setImageBitmap(rightBitmap);
692 | // preview_iv.setScaleType(ScaleType.FIT_XY);
693 | }
694 | });*/
695 |
696 | // Bitmap bmp = Utils.getCroppedImage(b, centerWindowView);
697 | // saveImageFile(bmp);
698 | /* if (b != null && !b.isRecycled()) {
699 | b.recycle();
700 | b = null;
701 | }*/
702 | }
703 |
704 | private String saveImageFile(Bitmap bmp) {
705 | String filePath = null;
706 | File file = Utils.getDiskCacheDir(this, "bitmap");
707 | if (!file.exists()) {
708 | file.mkdirs();
709 | }
710 | File f = new File(file, "picture.jpg");
711 | try {
712 | BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(f));
713 | bmp.compress(Bitmap.CompressFormat.JPEG, 100, bos);
714 | bos.flush();
715 | bos.close();
716 | filePath = f.getAbsolutePath();
717 | } catch (FileNotFoundException e) {
718 | e.printStackTrace();
719 | } catch (IOException e) {
720 | e.printStackTrace();
721 | } finally {
722 |
723 | if (bmp != null && !bmp.isRecycled()) {
724 | bmp.recycle();
725 | bmp = null;
726 | }
727 | }
728 | return filePath;
729 | }
730 |
731 | /**
732 | * 获取从相册中选择的图片的据对路径
733 | *
734 | * @param uri
735 | * @return
736 | */
737 | private String getUrl(Uri uri) {
738 | if (uri == null) {
739 | return null;
740 | }
741 |
742 | String[] proj = {MediaStore.Images.Media.DATA};
743 | Cursor actualimagecursor = managedQuery(uri, proj, null, null, null);
744 | int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
745 | actualimagecursor.moveToFirst();
746 | String img_path = actualimagecursor.getString(actual_image_column_index);
747 | return TextUtils.isEmpty(img_path) ? null : img_path;
748 | }
749 |
750 |
751 | /**
752 | * 重力感应导致横竖屏切换的条件下:获取当前屏幕旋转角度,要是锁定竖屏就一直返回0了。。。
753 | *
754 | * @param activity
755 | * @return 0表示是竖屏; 90表示是左横屏; 180表示是反向竖屏; 270表示是右横屏
756 | */
757 | public static int getDisplayRotation(Activity activity) {
758 | if (activity == null) {
759 | return 0;
760 | }
761 |
762 | int rotation = activity.getWindowManager().getDefaultDisplay()
763 | .getRotation();
764 | switch (rotation) {
765 | case Surface.ROTATION_0:
766 | return 0;
767 | case Surface.ROTATION_90:
768 | return 90;
769 | case Surface.ROTATION_180:
770 | return 180;
771 | case Surface.ROTATION_270:
772 | return 270;
773 | }
774 | return 0;
775 | }
776 |
777 | public String saveImageFilePath() {
778 | String imageFilePath = Environment.getExternalStorageDirectory().getPath();
779 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd");
780 | imageFilePath = imageFilePath + File.separator + "TongueImage";
781 |
782 | IfNotExistMkdir(imageFilePath);
783 |
784 | String subPath = File.separator + sdf.format(new Date());
785 | imageFilePath += subPath;
786 |
787 | IfNotExistMkdir(imageFilePath);
788 |
789 | SimpleDateFormat hms = new SimpleDateFormat("HHMMSS");
790 | String FileName = hms.format(new Date());
791 | return imageFilePath = File.separator + imageFilePath + File.separator + FileName + ".jpg";
792 | }
793 |
794 | private void IfNotExistMkdir(String filePath) {
795 | File file = new File(filePath);
796 | if (!file.exists()) {
797 | try {
798 | file.mkdir();
799 | } catch (Exception e) {
800 | //no do
801 | }
802 |
803 |
804 | }
805 | }
806 |
807 | public void saveToSDCard(byte[] data) throws IOException {
808 | // Log.d(TAG, "saveToSDCard");
809 | Date date = new Date();
810 | SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化时间
811 | String filename = format.format(date) + ".jpg";
812 | File fileFolder = new File(Environment.getExternalStorageDirectory() + "/ansen/");// Environment.getRootDirectory()
813 | if (!fileFolder.exists()) {
814 | fileFolder.mkdir();
815 | }
816 | File jpgFile = new File(fileFolder, filename);
817 | FileOutputStream outputStream = new FileOutputStream(jpgFile); // 文件输出流
818 | outputStream.write(data);
819 | outputStream.close();
820 | mCamera.startPreview(); // 拍完照后,重新开始预览
821 | if (false) {
822 | Bitmap b = Utils.BytesToBitmap(data);
823 | // 获取手机屏幕的宽高
824 | WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
825 | int windowWidth = windowManager.getDefaultDisplay().getWidth();
826 | int windowHight = windowManager.getDefaultDisplay().getHeight();
827 | Bitmap bitmap = Bitmap.createBitmap(b, 0, 0, windowWidth, windowHight);
828 | // 图片压缩
829 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
830 | outputStream.flush();
831 |
832 | }
833 | }
834 |
835 |
836 | }
837 |
838 |
839 |
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/CameraLine.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.util.AttributeSet;
9 | import android.util.Log;
10 | import android.view.View;
11 |
12 |
13 | public class CameraLine extends View {
14 |
15 | private Paint mLinePaint;
16 | private Paint mLineCrossPaint;
17 |
18 |
19 | final int LINE_STYLE_HIDE_LINE = 0;
20 | final int LINE_STYLE_SHOW_LINES = 1;
21 | final int LINE_STYLE_SHOW_LINES_LINE_IS_WIDE = 2;
22 |
23 |
24 | int nowLineStyle = LINE_STYLE_HIDE_LINE;//默认隐藏
25 | boolean showLines;
26 |
27 |
28 | // 窄 对称 网格线
29 | // 宽 井字 网格线
30 | boolean lineIsWide = false;
31 |
32 | float crossLineLength = 0;//默认
33 |
34 | public CameraLine(Context context) {
35 | super(context);
36 | Log.i("XXX", "louis=xx:CameraLine(Context context)");
37 | init();
38 | }
39 |
40 | public CameraLine(Context context, AttributeSet attrs) {
41 | super(context, attrs);
42 | Log.i("XXX", "louis=xx:CameraLine(Context context, AttributeSet attrs)");
43 | getAttrsAndInit(context, attrs);
44 |
45 | }
46 |
47 | public CameraLine(Context context, AttributeSet attrs, int defStyleAttr) {
48 | super(context, attrs, defStyleAttr);
49 | Log.i("XXX", "louis=xx:CameraLine(Context context, AttributeSet attrs, int defStyleAttr)");
50 | getAttrsAndInit(context, attrs);
51 | }
52 |
53 | private void getAttrsAndInit(Context context, AttributeSet attrs) {
54 | init();
55 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CameraLine_Attrs);
56 | boolean lineIsWide = typedArray.getBoolean(R.styleable.CameraLine_Attrs_lineIsWide, false);
57 | int lineColor = typedArray.getInt(R.styleable.CameraLine_Attrs_lineColor, 0);
58 | //float lineWidth = typedArray.getFloat(R.styleable.CameraLine_Attrs_lineWidth, 0);
59 | float lineWidth = typedArray.getDimension(R.styleable.CameraLine_Attrs_lineWidth, 0);
60 |
61 | float lineCrossLength = typedArray.getDimension(R.styleable.CameraLine_Attrs_lineCrossLength, 0);
62 | float lineCrossWidth = typedArray.getDimension(R.styleable.CameraLine_Attrs_lineCrossWidth, 0);
63 |
64 | int lineCrossColor = typedArray.getInt(R.styleable.CameraLine_Attrs_lineCrossColor, 0);
65 | boolean lineIsShow = typedArray.getBoolean(R.styleable.CameraLine_Attrs_lineIsShow, true);
66 | typedArray.recycle();
67 | Log.i("XXX", "louis=xx:lineColor:" + lineColor + "lineWidth:" + lineWidth);
68 |
69 | this.lineIsWide = lineIsWide;
70 | showLines = lineIsShow;
71 | // 覆盖
72 | if (lineColor != 0) {
73 | mLinePaint.setColor(lineColor);
74 | }
75 | if (lineWidth != 0) {
76 | mLinePaint.setStrokeWidth(lineWidth);
77 | }
78 | if (lineCrossLength != 0) {
79 | crossLineLength = Utils.dip2px(getContext(), lineCrossLength);
80 | }
81 | if (lineCrossWidth != 0) {
82 | mLineCrossPaint.setStrokeWidth(Utils.dip2px(getContext(), 1));
83 | }
84 | if (lineCrossColor != 0) {
85 | mLineCrossPaint.setColor(lineColor);
86 | }
87 |
88 | }
89 |
90 | public void changeLineStyle() {
91 | switch (nowLineStyle) {
92 | case LINE_STYLE_HIDE_LINE:
93 | nowLineStyle = LINE_STYLE_SHOW_LINES;
94 | lineIsWide = true;
95 | showLines = true;
96 | break;
97 | case LINE_STYLE_SHOW_LINES:
98 | nowLineStyle = LINE_STYLE_SHOW_LINES_LINE_IS_WIDE;
99 | lineIsWide = false;
100 | showLines = true;
101 | break;
102 | case LINE_STYLE_SHOW_LINES_LINE_IS_WIDE:
103 | nowLineStyle = LINE_STYLE_HIDE_LINE;
104 | showLines = false;
105 | break;
106 | }
107 | this.invalidate();
108 | }
109 |
110 | // 默认
111 | private void init() {
112 | mLinePaint = new Paint();
113 | mLinePaint.setAntiAlias(true);
114 | mLinePaint.setColor(Color.parseColor("#60E0E0E0"));//默认#45e0e0e0
115 | mLinePaint.setStrokeWidth(Utils.dip2px(getContext(), 1));//默认1dp
116 |
117 |
118 | mLineCrossPaint = new Paint();
119 | mLineCrossPaint.setColor(Color.parseColor("#55000000"));//00-FF
120 | mLineCrossPaint.setStrokeWidth(Utils.dip2px(getContext(), 1));//默认1dp
121 |
122 | }
123 |
124 | @Override
125 | protected void onDraw(Canvas canvas) {
126 | if (showLines) {
127 | int screenWidth = Utils.getScreenWH(getContext()).widthPixels;
128 | int screenHeight = Utils.getScreenWH(getContext()).heightPixels;
129 |
130 | /*
131 | * int width = screenWidth/3; int height = screenHeight/3;
132 | *
133 | * for (int i = width, j = 0;i < screenWidth && j<2;i += width, j++) {
134 | * canvas.drawLine(i, 0, i, screenHeight, mLinePaint); } for (int j =
135 | * height,i = 0;j < screenHeight && i < 2;j += height,i++) {
136 | * canvas.drawLine(0, j, screenWidth, j, mLinePaint); }
137 | */
138 |
139 | if (lineIsWide) {
140 | int lineCount = 3;// 为了隐藏最中间的线,线数量基本是奇数数量
141 | int width = screenWidth / (lineCount + 1);
142 | int height = screenHeight / (lineCount + 1);
143 | int centerLineNum = lineCount / 2;
144 | for (int i = width, j = 0; i < screenWidth && j < lineCount; i += width, j++) {
145 | if (centerLineNum != j) {
146 | canvas.drawLine(i, 0, i, screenHeight, mLinePaint);
147 | }
148 |
149 | }
150 | for (int j = height, i = 0; j < screenHeight && i < lineCount; j += height, i++) {
151 | if (centerLineNum != i) {
152 | canvas.drawLine(0, j, screenWidth, j, mLinePaint);
153 | }
154 | }
155 |
156 | } else {
157 | int lineCount = 2;
158 | int width = screenWidth / (lineCount + 1);
159 | int height = screenHeight / (lineCount + 1);
160 | for (int i = width, j = 0; i < screenWidth && j < lineCount; i += width, j++) {
161 | canvas.drawLine(i, 0, i, screenHeight, mLinePaint);
162 |
163 | }
164 | for (int j = height, i = 0; j < screenHeight && i < lineCount; j += height, i++) {
165 | canvas.drawLine(0, j, screenWidth, j, mLinePaint);
166 | }
167 | }
168 |
169 |
170 | if (crossLineLength != 0) {
171 | float centerX = canvas.getWidth() / 2;
172 | float centerY = canvas.getHeight() / 2;
173 |
174 | canvas.drawLine(centerX - crossLineLength, centerY, centerX + crossLineLength, centerY, mLineCrossPaint);
175 | canvas.drawLine(centerX, centerY - crossLineLength, centerX, centerY + crossLineLength, mLineCrossPaint);
176 | }
177 | }
178 | }
179 |
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/CameraPreview.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.content.Context;
4 | import android.hardware.Camera;
5 | import android.util.AttributeSet;
6 | import android.util.Log;
7 | import android.view.SurfaceHolder;
8 | import android.view.SurfaceView;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import java.io.IOException;
13 | import java.util.List;
14 |
15 | /**
16 | * A simple wrapper around a Camera and a SurfaceView that renders a centered preview of the Camera
17 | * to the surface. We need to center the SurfaceView because not all devices have cameras that
18 | * support preview sizes at the same aspect ratio as the device's display.
19 | *
20 | */
21 | public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback, Camera.AutoFocusCallback {
22 | private final String TAG = "Preview";
23 |
24 | /**
25 | * 图片的偏移
26 | */
27 | public int moveX = 0;
28 | public int moveY = 0;
29 |
30 | public SurfaceView mSurfaceView;
31 | private SurfaceHolder mHolder;
32 | public Camera.Size mPreviewSize;
33 | private List mSupportedPreviewSizes;
34 |
35 | public Camera.Size mPictureSize;
36 | private List mSupportedPictureSizes;
37 |
38 | private Camera mCamera;
39 |
40 | CameraPreview(Context context) {
41 | super(context);
42 | init(context);
43 | }
44 |
45 | public CameraPreview(Context context, AttributeSet attrs) {
46 | super(context, attrs);
47 | init(context);
48 | }
49 |
50 | public CameraPreview(Context context, AttributeSet attrs, int defStyle) {
51 | super(context, attrs, defStyle);
52 | init(context);
53 | }
54 |
55 | public SurfaceHolder getLouisSurfaceHolder() {
56 | return mHolder;
57 | }
58 |
59 | private void init(Context context) {
60 | mSurfaceView = new SurfaceView(context);
61 | ViewGroup.LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
62 | addView(mSurfaceView, layoutParams);
63 | // mSurfaceView.setBackgroundColor(Color.RED);
64 |
65 | // Install a SurfaceHolder.Callback so we get notified when the
66 | // underlying surface is created and destroyed.
67 | mHolder = mSurfaceView.getHolder();
68 | mHolder.addCallback(this);
69 | mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
70 | }
71 |
72 | public void setCamera(Camera camera) {
73 | mCamera = camera;
74 | if (mCamera != null) {
75 | try {
76 | mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
77 | mSupportedPictureSizes = mCamera.getParameters().getSupportedPictureSizes();
78 | for (Camera.Size size : mSupportedPreviewSizes) {
79 | Log.d(TAG, "Preview for mPreviewSize w - h : " + size.width + " - " + size.height);
80 | }
81 | for (Camera.Size size : mSupportedPictureSizes) {
82 | Log.d(TAG, "Preview for mPictureSize w - h : " + size.width + " - " + size.height);
83 | }
84 | mCamera.setDisplayOrientation(90);
85 | } catch (Exception e) {
86 | e.printStackTrace();
87 | }
88 | requestLayout();
89 | }
90 | }
91 |
92 | @Override
93 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
94 | // We purposely disregard child measurements because act as a
95 | // wrapper to a SurfaceView that centers the camera preview instead
96 | // of stretching it.
97 | final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
98 | final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
99 | setMeasuredDimension(width, height);
100 | Log.d(TAG, "Preview w - h : " + width + " - " + height);
101 | if (mSupportedPreviewSizes != null) {
102 | // 需要宽高切换 因为相机有90度的角度
103 | mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, height, width);
104 | Log.d(TAG, "Preview mPreviewSize w - h : " + mPreviewSize.width + " - " + mPreviewSize.height);
105 | }
106 | if (mSupportedPictureSizes != null) {
107 | mPictureSize = getOptimalPreviewSize(mSupportedPictureSizes, height, width);
108 | Log.d(TAG, "Preview mPictureSize w - h : " + mPictureSize.width + " - " + mPictureSize.height);
109 | }
110 |
111 | }
112 |
113 | @Override
114 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
115 | if (changed && getChildCount() > 0) {
116 | final View child = getChildAt(0);
117 |
118 | final int width = r - l;
119 | final int height = b - t;
120 |
121 | int previewWidth = width;
122 | int previewHeight = height;
123 | if (mPreviewSize != null) {
124 | previewWidth = mPreviewSize.height;
125 | previewHeight = mPreviewSize.width;
126 | }
127 |
128 | // Center the child SurfaceView within the parent.
129 | if (width * previewHeight > height * previewWidth) {
130 |
131 | final int scaleWidth = width;
132 | final int scaleHeight = width * previewHeight / previewWidth;
133 | moveX = 0;
134 | moveY = (scaleHeight - height) / 2;
135 | if (moveY < 0) {
136 | moveY = 0;
137 | }
138 | child.layout(-moveX, -moveY, scaleWidth, scaleHeight);
139 | } else {
140 |
141 | final int scaleHeight = height;
142 | final int scaleWidth = height * previewWidth / previewHeight;
143 | moveX = (scaleWidth - width) / 2;
144 | moveY = 0;
145 | if (moveX < 0) {
146 | moveX = 0;
147 | }
148 | child.layout(-moveX, -moveY, scaleWidth, scaleHeight);
149 | }
150 |
151 | }
152 | }
153 |
154 | public void surfaceCreated(SurfaceHolder holder) {
155 | Log.d("", "surface surfaceCreated()");
156 | // The Surface has been created, acquire the camera and tell it where
157 | // to draw.
158 | try {
159 | if (mCamera != null) {
160 | mCamera.setPreviewDisplay(holder);
161 | }
162 | } catch (IOException exception) {
163 | Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
164 | }
165 | }
166 |
167 | public void surfaceDestroyed(SurfaceHolder holder) {
168 | Log.d("", "surface surfaceDestroyed()");
169 | // Surface will be destroyed when we return, so stop the preview.
170 | if (mCamera != null) {
171 | mCamera.stopPreview();
172 | }
173 | }
174 |
175 | public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
176 | Log.d("", "surface surfaceChanged()");
177 | if (mCamera == null) {
178 | return;
179 | }
180 | try {
181 | // Now that the size is known, set up the camera parameters and begin
182 | // the preview.
183 | Camera.Parameters parameters = mCamera.getParameters();
184 | parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
185 | parameters.setPictureSize(mPictureSize.width, mPictureSize.height);
186 | requestLayout();
187 |
188 | mCamera.setParameters(parameters);
189 | mCamera.startPreview();
190 | mCamera.autoFocus(this);
191 | } catch (Exception e) {
192 | e.printStackTrace();
193 | }
194 | }
195 |
196 | @Override
197 | public void onAutoFocus(boolean success, Camera camera) {
198 |
199 | }
200 |
201 | private Camera.Size getOptimalPreviewSize(List sizes, int w, int h) {
202 | final double ASPECT_TOLERANCE = 0.1;
203 | double targetRatio = (double) w / h;
204 | if (sizes == null) return null;
205 |
206 | Camera.Size optimalSize = null;
207 | double minDiff = Double.MAX_VALUE;
208 |
209 | int targetHeight = h;
210 |
211 | // Try to find an size match aspect ratio and size
212 | for (Camera.Size size : sizes) {
213 | double ratio = (double) size.width / size.height;
214 | if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
215 | if (Math.abs(size.height - targetHeight) < minDiff) {
216 | optimalSize = size;
217 | minDiff = Math.abs(size.height - targetHeight);
218 | }
219 | }
220 |
221 | // Cannot find the one match the aspect ratio, ignore the requirement
222 | if (optimalSize == null) {
223 | minDiff = Double.MAX_VALUE;
224 | for (Camera.Size size : sizes) {
225 | if (Math.abs(size.height - targetHeight) < minDiff) {
226 | optimalSize = size;
227 | minDiff = Math.abs(size.height - targetHeight);
228 | }
229 | }
230 | }
231 | return optimalSize;
232 | }
233 |
234 | }
235 |
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/DoubleClickConfig.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 | import android.view.View;
6 |
7 | public class DoubleClickConfig {
8 | public interface OnDoubleClickListener {
9 | public void OnSingleClick(View v);
10 |
11 | public void OnDoubleClick(View v);
12 | }
13 |
14 | /**
15 | * 注册一个双击事件
16 | * 改自网友的,增加 Handler 处理,如果不加这个,会引起线程安全之类错误。
17 | */
18 | public static void registerDoubleClickListener(View view, final OnDoubleClickListener listener) {
19 | if (listener == null) return;
20 | view.setOnClickListener(new View.OnClickListener() {
21 | private static final int DOUBLE_CLICK_TIME = 350; //双击间隔时间350毫秒
22 | private boolean waitDouble = true;
23 |
24 | private Handler handler = new Handler() {
25 | @Override
26 | public void handleMessage(Message msg) {
27 | listener.OnSingleClick((View) msg.obj);
28 | }
29 |
30 | };
31 |
32 | //等待双击
33 | public void onClick(final View v) {
34 | if (waitDouble) {
35 | waitDouble = false; //与执行双击事件
36 | new Thread() {
37 |
38 | public void run() {
39 | try {
40 | Thread.sleep(DOUBLE_CLICK_TIME);
41 | } catch (InterruptedException e) {
42 | // TODO Auto-generated catch block
43 | e.printStackTrace();
44 | } //等待双击时间,否则执行单击事件
45 | if (!waitDouble) {
46 | //如果过了等待事件还是预执行双击状态,则视为单击
47 | waitDouble = true;
48 | Message msg = handler.obtainMessage();
49 | msg.obj = v;
50 | handler.sendMessage(msg);
51 | }
52 | }
53 |
54 | }.start();
55 | } else {
56 | waitDouble = true;
57 | listener.OnDoubleClick(v); //执行双击
58 | }
59 | }
60 | });
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/ImageViewUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013, Edmodo, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License.
5 | * You may obtain a copy of the License in the LICENSE file, or at:
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
10 | * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
11 | * governing permissions and limitations under the License.
12 | */
13 |
14 | package com.louisgeek.louiscustomcamerademo;
15 |
16 | import android.graphics.Bitmap;
17 | import android.graphics.Rect;
18 | import android.view.View;
19 | import android.widget.ImageView;
20 |
21 | /**
22 | * Utility class that deals with operations with an ImageView.
23 | */
24 | public class ImageViewUtil {
25 |
26 | /**
27 | * Gets the rectangular position of a Bitmap if it were placed inside a View
28 | * with scale type set to {@link ImageView#ScaleType #CENTER_INSIDE}.
29 | *
30 | * @param bitmap the Bitmap
31 | * @param view the parent View of the Bitmap
32 | * @return the rectangular position of the Bitmap
33 | */
34 | public static Rect getBitmapRectCenterInside(Bitmap bitmap, View view) {
35 |
36 | final int bitmapWidth = bitmap.getWidth();
37 | final int bitmapHeight = bitmap.getHeight();
38 | final int viewWidth = view.getWidth();
39 | final int viewHeight = view.getHeight();
40 |
41 | return getBitmapRectCenterInsideHelper(bitmapWidth, bitmapHeight, viewWidth, viewHeight);
42 | }
43 |
44 | /**
45 | * Gets the rectangular position of a Bitmap if it were placed inside a View
46 | * with scale type set to {@link ImageView#ScaleType #CENTER_INSIDE}.
47 | *
48 | * @param bitmapWidth the Bitmap's width
49 | * @param bitmapHeight the Bitmap's height
50 | * @param viewWidth the parent View's width
51 | * @param viewHeight the parent View's height
52 | * @return the rectangular position of the Bitmap
53 | */
54 | public static Rect getBitmapRectCenterInside(int bitmapWidth,
55 | int bitmapHeight,
56 | int viewWidth,
57 | int viewHeight) {
58 | return getBitmapRectCenterInsideHelper(bitmapWidth, bitmapHeight, viewWidth, viewHeight);
59 | }
60 |
61 | /**
62 | * Helper that does the work of the above functions. Gets the rectangular
63 | * position of a Bitmap if it were placed inside a View with scale type set
64 | * to {@link ImageView#ScaleType #CENTER_INSIDE}.
65 | *
66 | * @param bitmapWidth the Bitmap's width
67 | * @param bitmapHeight the Bitmap's height
68 | * @param viewWidth the parent View's width
69 | * @param viewHeight the parent View's height
70 | * @return the rectangular position of the Bitmap
71 | */
72 | private static Rect getBitmapRectCenterInsideHelper(int bitmapWidth,
73 | int bitmapHeight,
74 | int viewWidth,
75 | int viewHeight) {
76 | double resultWidth;
77 | double resultHeight;
78 | int resultX;
79 | int resultY;
80 |
81 | double viewToBitmapWidthRatio = Double.POSITIVE_INFINITY;
82 | double viewToBitmapHeightRatio = Double.POSITIVE_INFINITY;
83 |
84 | // Checks if either width or height needs to be fixed
85 | if (viewWidth < bitmapWidth) {
86 | viewToBitmapWidthRatio = (double) viewWidth / (double) bitmapWidth;
87 | }
88 | if (viewHeight < bitmapHeight) {
89 | viewToBitmapHeightRatio = (double) viewHeight / (double) bitmapHeight;
90 | }
91 |
92 | // If either needs to be fixed, choose smallest ratio and calculate from
93 | // there
94 | if (viewToBitmapWidthRatio != Double.POSITIVE_INFINITY || viewToBitmapHeightRatio != Double.POSITIVE_INFINITY) {
95 | if (viewToBitmapWidthRatio <= viewToBitmapHeightRatio) {
96 | resultWidth = viewWidth;
97 | resultHeight = (bitmapHeight * resultWidth / bitmapWidth);
98 | } else {
99 | resultHeight = viewHeight;
100 | resultWidth = (bitmapWidth * resultHeight / bitmapHeight);
101 | }
102 | }
103 | // Otherwise, the picture is within frame layout bounds. Desired width
104 | // is simply picture size
105 | else {
106 | resultHeight = bitmapHeight;
107 | resultWidth = bitmapWidth;
108 | }
109 |
110 | // Calculate the position of the bitmap inside the ImageView.
111 | if (resultWidth == viewWidth) {
112 | resultX = 0;
113 | resultY = (int) Math.round((viewHeight - resultHeight) / 2);
114 | } else if (resultHeight == viewHeight) {
115 | resultX = (int) Math.round((viewWidth - resultWidth) / 2);
116 | resultY = 0;
117 | } else {
118 | resultX = (int) Math.round((viewWidth - resultWidth) / 2);
119 | resultY = (int) Math.round((viewHeight - resultHeight) / 2);
120 | }
121 |
122 | final Rect result = new Rect(resultX,
123 | resultY,
124 | resultX + (int) Math.ceil(resultWidth),
125 | resultY + (int) Math.ceil(resultHeight));
126 |
127 | return result;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/PreviewActivity.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.os.Bundle;
8 | import android.view.View;
9 | import android.view.Window;
10 | import android.widget.ImageView;
11 | import android.widget.Toast;
12 |
13 | import java.io.FileInputStream;
14 | import java.io.FileNotFoundException;
15 | import java.io.IOException;
16 |
17 | public class PreviewActivity extends Activity {
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 |
23 | requestWindowFeature(Window.FEATURE_NO_TITLE);
24 | setContentView(R.layout.activity_preview);
25 |
26 |
27 | ImageView id_iv_preview_photo = (ImageView) this.findViewById(R.id.id_iv_preview_photo);
28 |
29 | ImageView id_iv_cancel = (ImageView) this.findViewById(R.id.id_iv_cancel);
30 | ImageView id_iv_ok = (ImageView) this.findViewById(R.id.id_iv_ok);
31 |
32 | id_iv_cancel.setOnClickListener(new View.OnClickListener() {
33 | @Override
34 | public void onClick(View v) {
35 | PreviewActivity.this.finish();
36 | }
37 | });
38 | id_iv_ok.setOnClickListener(new View.OnClickListener() {
39 | @Override
40 | public void onClick(View v) {
41 |
42 | }
43 | });
44 |
45 | Intent intent = this.getIntent();
46 | if (intent != null) {
47 | //byte [] bis=intent.getByteArrayExtra("bitmapByte");
48 |
49 | String filePath = intent.getStringExtra("filePath");
50 | // Toast.makeText(this, "图片加载filePath:"+filePath, Toast.LENGTH_SHORT).show();
51 | id_iv_preview_photo.setImageBitmap(getBitmapByUrl(filePath));
52 | } else {
53 | Toast.makeText(this, "图片加载错误", Toast.LENGTH_SHORT).show();
54 | }
55 |
56 | }
57 |
58 |
59 | /**
60 | * 根据图片路径获取本地图片的Bitmap
61 | *
62 | * @param url
63 | * @return
64 | */
65 | public Bitmap getBitmapByUrl(String url) {
66 | FileInputStream fis = null;
67 | Bitmap bitmap = null;
68 | try {
69 | fis = new FileInputStream(url);
70 | bitmap = BitmapFactory.decodeStream(fis);
71 |
72 | } catch (FileNotFoundException e) {
73 | // TODO Auto-generated catch block
74 | e.printStackTrace();
75 | bitmap = null;
76 | } finally {
77 | if (fis != null) {
78 | try {
79 | fis.close();
80 | } catch (IOException e) {
81 | // TODO Auto-generated catch block
82 | e.printStackTrace();
83 | }
84 | fis = null;
85 | }
86 | }
87 |
88 | return bitmap;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/ScreenSwitchUtils.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.pm.ActivityInfo;
6 | import android.hardware.Sensor;
7 | import android.hardware.SensorEvent;
8 | import android.hardware.SensorEventListener;
9 | import android.hardware.SensorManager;
10 | import android.os.Handler;
11 | import android.os.Message;
12 | import android.util.Log;
13 |
14 | public class ScreenSwitchUtils {
15 |
16 | private static final String TAG = ScreenSwitchUtils.class.getSimpleName();
17 |
18 | private volatile static ScreenSwitchUtils mInstance;
19 |
20 | private Activity mActivity;
21 |
22 | // 是否是竖屏
23 | private boolean isPortrait = true;
24 |
25 | public static final int ORIENTATION_HEAD_IS_UP = 0;
26 | public static final int ORIENTATION_HEAD_IS_LEFT = 1;
27 | public static final int ORIENTATION_HEAD_IS_RIGHT = 2;
28 | public static final int ORIENTATION_HEAD_IS_DOWN = 3;
29 |
30 | private int orientationState = ORIENTATION_HEAD_IS_UP;//默认
31 | private SensorManager sm;
32 | private OrientationSensorListener listener;
33 | private Sensor sensor;
34 |
35 | private SensorManager sm1;
36 | private Sensor sensor1;
37 | private OrientationSensorListener1 listener1;
38 |
39 | private Handler mHandler = new Handler() {
40 | public void handleMessage(Message msg) {
41 | switch (msg.what) {
42 | case 88888:
43 | int orientation = msg.arg1;
44 | if (orientation > 45 && orientation < 135) {
45 | orientationState = ORIENTATION_HEAD_IS_RIGHT;
46 | } else if (orientation > 135 && orientation < 225) {
47 | orientationState = ORIENTATION_HEAD_IS_DOWN;
48 | } else if (orientation > 225 && orientation < 315) {
49 | if (isPortrait) {
50 | Log.d(TAG, "切换成横屏");
51 | //### mActivity.setRequestedOrientation(0);
52 | isPortrait = false;
53 | }
54 | orientationState = ORIENTATION_HEAD_IS_LEFT;
55 | } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {
56 | if (!isPortrait) {
57 | Log.d(TAG, "切换成竖屏");
58 | //### mActivity.setRequestedOrientation(1);
59 | isPortrait = true;
60 | }
61 | orientationState = ORIENTATION_HEAD_IS_UP;
62 | }
63 | break;
64 | default:
65 | break;
66 | }
67 |
68 | }
69 |
70 | ;
71 | };
72 |
73 | /**
74 | * 返回ScreenSwitchUtils单例
75 | **/
76 | public static ScreenSwitchUtils init(Context context) {
77 | if (mInstance == null) {
78 | synchronized (ScreenSwitchUtils.class) {
79 | if (mInstance == null) {
80 | mInstance = new ScreenSwitchUtils(context);
81 | }
82 | }
83 | }
84 | return mInstance;
85 | }
86 |
87 | private ScreenSwitchUtils(Context context) {
88 | Log.d(TAG, "init orientation listener");
89 | // 注册重力感应器,监听屏幕旋转
90 | sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
91 | sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
92 | listener = new OrientationSensorListener(mHandler);
93 |
94 | // 根据 旋转之后/点击全屏之后 两者方向一致,激活sm.
95 | sm1 = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
96 | sensor1 = sm1.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
97 | listener1 = new OrientationSensorListener1();
98 | }
99 |
100 | /**
101 | * 开始监听
102 | */
103 | public void start(Activity activity) {
104 | Log.d(TAG, "start orientation listener");
105 | mActivity = activity;
106 | sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
107 | }
108 |
109 | /**
110 | * 停止监听
111 | */
112 | public void stop() {
113 | Log.d(TAG, "stop orientation listener");
114 | sm.unregisterListener(listener);
115 | sm1.unregisterListener(listener1);
116 | }
117 |
118 | /**
119 | * 手动横竖屏切换方向 有点问题
120 | */
121 | public void toggleScreen() {
122 | sm.unregisterListener(listener);
123 | sm1.registerListener(listener1, sensor1, SensorManager.SENSOR_DELAY_UI);
124 | if (isPortrait) {
125 | isPortrait = false;
126 | // 切换成横屏
127 | mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
128 | } else {
129 | isPortrait = true;
130 | // 切换成竖屏
131 | mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
132 | }
133 | }
134 |
135 | public boolean isPortrait() {
136 | return this.isPortrait;
137 | }
138 |
139 | public int getOrientationState() {
140 | return orientationState;
141 | }
142 |
143 | /**
144 | * 重力感应监听者
145 | */
146 | public class OrientationSensorListener implements SensorEventListener {
147 | private static final int _DATA_X = 0;
148 | private static final int _DATA_Y = 1;
149 | private static final int _DATA_Z = 2;
150 |
151 | public static final int ORIENTATION_UNKNOWN = -1;
152 |
153 | private Handler rotateHandler;
154 |
155 | public OrientationSensorListener(Handler handler) {
156 | rotateHandler = handler;
157 | }
158 |
159 | public void onAccuracyChanged(Sensor arg0, int arg1) {
160 | }
161 |
162 | public void onSensorChanged(SensorEvent event) {
163 | float[] values = event.values;
164 | int orientation = ORIENTATION_UNKNOWN;
165 | float X = -values[_DATA_X];
166 | float Y = -values[_DATA_Y];
167 | float Z = -values[_DATA_Z];
168 | float magnitude = X * X + Y * Y;
169 | // Don't trust the angle if the magnitude is small compared to the y
170 | // value
171 | if (magnitude * 4 >= Z * Z) {
172 | // 屏幕旋转时
173 | float OneEightyOverPi = 57.29577957855f;
174 | float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi;
175 | orientation = 90 - (int) Math.round(angle);
176 | // normalize to 0 - 359 range
177 | while (orientation >= 360) {
178 | orientation -= 360;
179 | }
180 | while (orientation < 0) {
181 | orientation += 360;
182 | }
183 | }
184 | if (rotateHandler != null) {
185 | rotateHandler.obtainMessage(88888, orientation, 0).sendToTarget();
186 | }
187 | }
188 | }
189 |
190 | public class OrientationSensorListener1 implements SensorEventListener {
191 | private static final int _DATA_X = 0;
192 | private static final int _DATA_Y = 1;
193 | private static final int _DATA_Z = 2;
194 |
195 | public static final int ORIENTATION_UNKNOWN = -1;
196 |
197 | public OrientationSensorListener1() {
198 | }
199 |
200 | public void onAccuracyChanged(Sensor arg0, int arg1) {
201 | }
202 |
203 | public void onSensorChanged(SensorEvent event) {
204 | float[] values = event.values;
205 | int orientation = ORIENTATION_UNKNOWN;
206 | float X = -values[_DATA_X];
207 | float Y = -values[_DATA_Y];
208 | float Z = -values[_DATA_Z];
209 | float magnitude = X * X + Y * Y;
210 | // Don't trust the angle if the magnitude is small compared to the y
211 | // value
212 | if (magnitude * 4 >= Z * Z) {
213 | // 屏幕旋转时
214 | float OneEightyOverPi = 57.29577957855f;
215 | float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi;
216 | orientation = 90 - (int) Math.round(angle);
217 | // normalize to 0 - 359 range
218 | while (orientation >= 360) {
219 | orientation -= 360;
220 | }
221 | while (orientation < 0) {
222 | orientation += 360;
223 | }
224 | }
225 | if (orientation > 225 && orientation < 315) {// 检测到当前实际是横屏
226 | if (!isPortrait) {
227 | sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
228 | sm1.unregisterListener(listener1);
229 | }
230 | } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {// 检测到当前实际是竖屏
231 | if (isPortrait) {
232 | sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
233 | sm1.unregisterListener(listener1);
234 | }
235 | }
236 | }
237 | }
238 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/louisgeek/louiscustomcamerademo/Utils.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.Matrix;
9 | import android.graphics.Point;
10 | import android.graphics.Rect;
11 | import android.os.Environment;
12 | import android.util.DisplayMetrics;
13 | import android.util.Log;
14 | import android.view.View;
15 |
16 | import java.io.ByteArrayOutputStream;
17 | import java.io.File;
18 | import java.lang.reflect.Field;
19 |
20 | public class Utils {
21 |
22 | public static File getDiskCacheDir(Context context, String uniqueName) {
23 | String cachePath;
24 | if ((Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
25 | || !Environment.isExternalStorageRemovable()) && context.getExternalCacheDir() != null) {
26 | cachePath = context.getExternalCacheDir().getPath();
27 | } else {
28 | cachePath = context.getCacheDir().getPath();
29 | }
30 | return new File(cachePath + File.separator + uniqueName);
31 | }
32 |
33 |
34 | /**
35 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
36 | *
37 | * @param context 上下文
38 | * @param dpValue 尺寸dip
39 | * @return 像素值
40 | */
41 | public static int dip2px(Context context, float dpValue) {
42 | final float scale = context.getResources().getDisplayMetrics().density;
43 | return (int) (dpValue * scale + 0.5f);
44 | }
45 |
46 | /**
47 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
48 | *
49 | * @param context 上下文
50 | * @param pxValue 尺寸像素
51 | * @return DIP值
52 | */
53 | public static int px2dip(Context context, float pxValue) {
54 | final float scale = context.getResources().getDisplayMetrics().density;
55 | return (int) (pxValue / scale + 0.5f);
56 | }
57 |
58 | /**
59 | * 根据手机的分辨率从 px(像素) 的单位 转成为 sp
60 | *
61 | * @param context 上下文
62 | * @param pxValue 尺寸像素
63 | * @return SP值
64 | */
65 | public static int px2sp(Context context, float pxValue) {
66 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
67 | return (int) (pxValue / fontScale + 0.5f);
68 | }
69 |
70 | /**
71 | * 根据手机的分辨率从 sp 的单位 转成为 px
72 | *
73 | * @param context 上下文
74 | * @param spValue SP值
75 | * @return 像素值
76 | */
77 | public static int sp2px(Context context, float spValue) {
78 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
79 | return (int) (spValue * fontScale + 0.5f);
80 | }
81 |
82 | /**
83 | * Gets the cropped image based on the current crop window.
84 | *
85 | * @return a new Bitmap representing the cropped image
86 | */
87 | public static Bitmap getCroppedImage(Bitmap mBitmap, View mImageView) {
88 |
89 | final Rect displayedImageRect = ImageViewUtil.getBitmapRectCenterInside(mBitmap, mImageView);
90 |
91 | // Get the scale factor between the actual Bitmap dimensions and the
92 | // displayed dimensions for width.
93 | final float actualImageWidth = mBitmap.getWidth();
94 | final float displayedImageWidth = displayedImageRect.width();
95 | final float scaleFactorWidth = actualImageWidth / displayedImageWidth;
96 |
97 | // Get the scale factor between the actual Bitmap dimensions and the
98 | // displayed dimensions for height.
99 | final float actualImageHeight = mBitmap.getHeight();
100 | final float displayedImageHeight = displayedImageRect.height();
101 | final float scaleFactorHeight = actualImageHeight / displayedImageHeight;
102 |
103 | // Get crop window position relative to the displayed image.
104 | final float cropWindowX = displayedImageRect.left;
105 | final float cropWindowY = displayedImageRect.top;
106 | final float cropWindowWidth = displayedImageRect.width();
107 | final float cropWindowHeight = displayedImageRect.height();
108 |
109 | // Scale the crop window position to the actual size of the Bitmap.
110 | final float actualCropX = cropWindowX * scaleFactorWidth;
111 | final float actualCropY = cropWindowY * scaleFactorHeight;
112 | final float actualCropWidth = cropWindowWidth * scaleFactorWidth;
113 | final float actualCropHeight = cropWindowHeight * scaleFactorHeight;
114 |
115 | // Crop the subset from the original Bitmap.
116 | final Bitmap croppedBitmap = Bitmap.createBitmap(mBitmap,
117 | (int) actualCropX,
118 | (int) actualCropY,
119 | (int) actualCropWidth,
120 | (int) actualCropHeight);
121 |
122 | return croppedBitmap;
123 | }
124 |
125 | /**
126 | * 图片压缩方法:(使用compress的方法)
127 | *
128 | *
129 | * 说明 如果bitmap本身的大小小于maxSize,则不作处理
130 | *
131 | * @param bitmap 要压缩的图片
132 | * @param maxSize 压缩后的大小,单位kb
133 | */
134 | public static void compress(Bitmap bitmap, double maxSize) {
135 | // 将bitmap放至数组中,意在获得bitmap的大小(与实际读取的原文件要大)
136 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
137 | // 格式、质量、输出流
138 | bitmap.compress(Bitmap.CompressFormat.PNG, 70, baos);
139 | byte[] b = baos.toByteArray();
140 | // 将字节换成KB
141 | double mid = b.length / 1024;
142 | // 获取bitmap大小 是允许最大大小的多少倍
143 | double i = mid / maxSize;
144 | // 判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩
145 | if (i > 1) {
146 | // 缩放图片 此处用到平方根 将宽带和高度压缩掉对应的平方根倍
147 | // (保持宽高不变,缩放后也达到了最大占用空间的大小)
148 | bitmap = scale(bitmap, bitmap.getWidth() / Math.sqrt(i),
149 | bitmap.getHeight() / Math.sqrt(i));
150 | }
151 | }
152 |
153 | /**
154 | * 图片的缩放方法
155 | *
156 | * @param src :源图片资源
157 | * @param newWidth :缩放后宽度
158 | * @param newHeight :缩放后高度
159 | */
160 | public static Bitmap scale(Bitmap src, double newWidth, double newHeight) {
161 | // 记录src的宽高
162 | float width = src.getWidth();
163 | float height = src.getHeight();
164 | // 创建一个matrix容器
165 | Matrix matrix = new Matrix();
166 | // 计算缩放比例
167 | float scaleWidth = ((float) newWidth) / width;
168 | float scaleHeight = ((float) newHeight) / height;
169 | // 开始缩放
170 | matrix.postScale(scaleWidth, scaleHeight);
171 | // 创建缩放后的图片
172 | return Bitmap.createBitmap(src, 0, 0, (int) width, (int) height,
173 | matrix, true);
174 | }
175 |
176 | /**
177 | * 获取屏幕长宽比
178 | *
179 | * @param context
180 | * @return
181 | */
182 | public static float getScreenRate(Context context) {
183 | Point P = getScreenMetrics(context);
184 | float H = P.y;
185 | float W = P.x;
186 | return (H / W);
187 | }
188 |
189 | /**
190 | * 获取屏幕宽度和高度,单位为px
191 | *
192 | * @param context
193 | * @return
194 | */
195 | public static Point getScreenMetrics(Context context) {
196 | DisplayMetrics dm = context.getResources().getDisplayMetrics();
197 | int w_screen = dm.widthPixels;
198 | int h_screen = dm.heightPixels;
199 | // Log.i(TAG, "Screen---Width = " + w_screen + " Height = " + h_screen + " densityDpi = " + dm.densityDpi);
200 | return new Point(w_screen, h_screen);
201 |
202 | }
203 |
204 | /**
205 | * 获取状态栏高度
206 | *
207 | * @param context
208 | * @return
209 | */
210 | public static int getStatusBarHeight(Activity context) {
211 | /*Rect frame = new Rect();
212 | context.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
213 |
214 | int statusBarHeight = frame.top;
215 | Log.d("", "statusBarHeight : " + statusBarHeight);
216 | return statusBarHeight;*/
217 |
218 | Class> c = null;
219 | Object obj = null;
220 | Field field = null;
221 | int x = 0, sbar = 0;
222 | try {
223 | c = Class.forName("com.android.internal.R$dimen");
224 | obj = c.newInstance();
225 | field = c.getField("status_bar_height");
226 | x = Integer.parseInt(field.get(obj).toString());
227 | sbar = context.getResources().getDimensionPixelSize(x);
228 | } catch (Exception e1) {
229 | // loge("get status bar height fail");
230 | e1.printStackTrace();
231 | }
232 | Log.d("", "statusBarHeight : " + sbar);
233 | return sbar;
234 | }
235 |
236 | //===
237 | public static DisplayMetrics getScreenWH(Context context) {
238 | DisplayMetrics dMetrics = new DisplayMetrics();
239 | dMetrics = context.getResources().getDisplayMetrics();
240 | return dMetrics;
241 | }
242 |
243 | /**
244 | * 计算焦点及测光区域
245 | *
246 | * @param focusWidth
247 | * @param focusHeight
248 | * @param areaMultiple
249 | * @param x
250 | * @param y
251 | * @param previewleft
252 | * @param previewRight
253 | * @param previewTop
254 | * @param previewBottom
255 | * @return Rect(left, top, right, bottom) : left、top、right、bottom是以显示区域中心为原点的坐标
256 | */
257 | public static Rect calculateTapArea(int focusWidth, int focusHeight,
258 | float areaMultiple, float x, float y, int previewleft,
259 | int previewRight, int previewTop, int previewBottom) {
260 | int areaWidth = (int) (focusWidth * areaMultiple);
261 | int areaHeight = (int) (focusHeight * areaMultiple);
262 | int centerX = (previewleft + previewRight) / 2;
263 | int centerY = (previewTop + previewBottom) / 2;
264 | double unitx = ((double) previewRight - (double) previewleft) / 2000;
265 | double unity = ((double) previewBottom - (double) previewTop) / 2000;
266 | int left = clamp((int) (((x - areaWidth / 2) - centerX) / unitx),
267 | -1000, 1000);
268 | int top = clamp((int) (((y - areaHeight / 2) - centerY) / unity),
269 | -1000, 1000);
270 | int right = clamp((int) (left + areaWidth / unitx), -1000, 1000);
271 | int bottom = clamp((int) (top + areaHeight / unity), -1000, 1000);
272 |
273 | return new Rect(left, top, right, bottom);
274 | }
275 |
276 | public static int clamp(int x, int min, int max) {
277 | if (x > max)
278 | return max;
279 | if (x < min)
280 | return min;
281 | return x;
282 | }
283 |
284 | /**
285 | * 检测摄像头设备是否可用
286 | * Check if this device has a camera
287 | *
288 | * @param context
289 | * @return
290 | */
291 | public static boolean checkCameraHardware(Context context) {
292 | if (context != null && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
293 | // this device has a camera
294 | return true;
295 | } else {
296 | // no camera on this device
297 | return false;
298 | }
299 | }
300 |
301 | /**
302 | * @param context
303 | * @return app_cache_path/dirName
304 | */
305 | public static String getDBDir(Context context) {
306 | String path = null;
307 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
308 | path = Environment.getExternalStorageDirectory().getAbsolutePath() +
309 | File.separator + "bbk" + File.separator + "cloudteacher" + File.separator + "db";
310 | File externalCacheDir = context.getExternalCacheDir();
311 | if (externalCacheDir != null) {
312 | path = externalCacheDir.getPath();
313 | }
314 | }
315 | if (path == null) {
316 | File cacheDir = context.getCacheDir();
317 | if (cacheDir != null && cacheDir.exists()) {
318 | path = cacheDir.getPath();
319 | }
320 | }
321 | return path;
322 | }
323 |
324 | /**
325 | * 旋转图片
326 | *
327 | * @param angle 旋转角度
328 | * @param bitmap 要旋转的图片
329 | * @return 旋转后的图片
330 | */
331 | public static Bitmap rotate(Bitmap bitmap, int angle) {
332 | Matrix matrix = new Matrix();
333 | matrix.postRotate(angle);
334 | return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
335 | bitmap.getHeight(), matrix, true);
336 | }
337 |
338 | /**
339 | * bitmap旋转
340 | *
341 | * @param b
342 | * @param degrees
343 | * @return
344 | */
345 | public static Bitmap rotateTwo(Bitmap b, int degrees) {
346 | if (degrees != 0 && b != null) {
347 | Matrix m = new Matrix();
348 | m.setRotate(degrees, (float) b.getWidth() / 2, (float) b.getHeight() / 2);
349 | try {
350 | Bitmap b2 = Bitmap.createBitmap(
351 | b, 0, 0, b.getWidth(), b.getHeight(), m, true);
352 | if (b != b2) {
353 | b.recycle(); //Android开发网再次提示Bitmap操作完应该显示的释放
354 | b = b2;
355 | }
356 | } catch (OutOfMemoryError ex) {
357 | // Android123建议大家如何出现了内存不足异常,最好return 原始的bitmap对象。.
358 | }
359 | }
360 | return b;
361 | }
362 |
363 | public static final int getHeightInPx(Context context) {
364 | final int height = context.getResources().getDisplayMetrics().heightPixels;
365 | return height;
366 | }
367 |
368 | public static final int getWidthInPx(Context context) {
369 | final int width = context.getResources().getDisplayMetrics().widthPixels;
370 | return width;
371 | }
372 |
373 | /**
374 | * 把图片byte流转换成bitmap
375 | */
376 | public static Bitmap BytesToBitmap(byte[] data) {
377 | BitmapFactory.Options options = new BitmapFactory.Options();
378 | options.inJustDecodeBounds = true;
379 | Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length, options);
380 | int i = 0;
381 | while (true) {
382 | if ((options.outWidth >> i <= 1000)
383 | && (options.outHeight >> i <= 1000)) {
384 | options.inSampleSize = (int) Math.pow(2.0D, i);
385 | options.inJustDecodeBounds = false;
386 | b = BitmapFactory.decodeByteArray(data, 0, data.length, options);
387 | break;
388 | }
389 | i += 1;
390 | }
391 | return b;
392 | }
393 |
394 | /**
395 | * Bitmap → byte[]
396 | *
397 | * @param bm
398 | * @return
399 | */
400 | public static byte[] BitmapToBytes(Bitmap bm) {
401 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
402 | bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
403 | return baos.toByteArray();
404 | }
405 |
406 | /**
407 | * byte[] → Bitmap
408 | *
409 | * @param b
410 | * @return
411 | */
412 | public static Bitmap Bytes2Bitmap(byte[] b) {
413 | if (b.length != 0) {
414 | return BitmapFactory.decodeByteArray(b, 0, b.length);
415 | } else {
416 | return null;
417 | }
418 | }
419 | }
420 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/camera_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
6 |
7 | -
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
22 |
23 |
26 |
27 |
38 |
39 |
50 |
51 |
63 |
67 |
68 |
69 |
73 |
74 |
84 |
85 |
86 |
98 |
99 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_preview.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
18 |
28 |
29 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/camera_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xhdpi/camera_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/camera_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xhdpi/camera_pressed.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/camera_flash_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxhdpi/camera_flash_light.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxhdpi/cancel.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxhdpi/ok.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/camera_change.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/camera_change.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/camera_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/camera_config.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/camera_flash_auto.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/camera_flash_auto.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/camera_flash_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/camera_flash_light.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/camera_flash_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/camera_flash_off.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/camera_flash_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/camera_flash_on.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/camera_normal_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/camera_normal_new.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LouisCustomCameraDemo
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/louisgeek/louiscustomcamerademo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.louisgeek.louiscustomcamerademo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.5.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/screenshots/pic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/screenshots/pic.jpg
--------------------------------------------------------------------------------
/screenshots/picA.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/6ea12ee3ede7a547c03f3aaa3934ffe2cc21433f/screenshots/picA.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------