├── .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 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 1.8 58 | 59 | 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 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LouisCustomCameraDemo 2 | Android 自定义相机 切换相机 参考线(辅助线) 闪光灯 缩放 自动聚焦 3 | 4 | ![image](https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/master/screenshots/pic.jpg) 5 | ![image](https://raw.githubusercontent.com/louisgeek/LouisCustomCameraDemo/master/screenshots/picA.jpg) 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 | --------------------------------------------------------------------------------