├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── debug │ ├── app-debug.apk │ └── output.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hyfun │ │ └── camera │ │ └── demo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hyfun │ │ │ └── camera │ │ │ └── demo │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── hyfun │ └── camera │ └── demo │ └── ExampleUnitTest.java ├── build.gradle ├── camera ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── hyfun │ │ └── camera │ │ ├── FunCamera.java │ │ ├── audio │ │ ├── FunAudioDialogFragment.java │ │ ├── FunAudioRecordListener.java │ │ └── Util.java │ │ ├── p2v │ │ ├── BaseFragment.java │ │ ├── CameraCaptureActivity.java │ │ ├── CameraCaptureInterface.java │ │ ├── CameraCapturePreviewFragment.java │ │ ├── CameraCaptureRecordFragment.java │ │ ├── CameraOrientationListener.java │ │ ├── Capture.java │ │ ├── OnCameraCaptureListener.java │ │ ├── PreviewInfo.java │ │ └── Util.java │ │ └── widget │ │ ├── AutoFitTextureView.java │ │ ├── CaptureButton.java │ │ └── FunWaveView.java │ └── res │ ├── anim │ ├── fun_camera_activity_down_in.xml │ ├── fun_camera_activity_down_out.xml │ ├── fun_camera_activity_up_in.xml │ └── fun_camera_activity_up_out.xml │ ├── drawable │ ├── camera_ic_camera_flash_auto_24dp.xml │ ├── camera_ic_capture_flash_off_24dp.xml │ ├── camera_ic_capture_flash_on_24dp.xml │ ├── camera_ic_capture_switch_24dp.xml │ ├── camera_ic_keyboard_arrow_down_black_24dp.xml │ ├── camera_ic_preview_back_24dp.xml │ ├── camera_shape_background_preview_confirm.xml │ ├── camera_shape_background_preview_outline.xml │ ├── fun_camera_layer_audio_record_seekbar_style.xml │ ├── fun_camera_shape_audio_record_seekbar_bar.xml │ ├── fun_camera_shape_background_circle.xml │ ├── fun_camera_shape_background_circle_outline.xml │ ├── func_camera_ic_audio_record_play_black_24dp.xml │ └── func_camera_ic_audio_record_stop_black_24dp.xml │ ├── layout │ ├── activity_camera_capture.xml │ ├── camera_fragment_capture_preview.xml │ ├── camera_fragment_capture_record.xml │ └── fun_camera_audio_record_dialog_fragment.xml │ ├── mipmap-hdpi │ └── fun_camera_img_capture_focus.png │ └── values │ ├── color.xml │ ├── fun_camera_attr.xml │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot └── demo.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | /.idea 16 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-Library-Camera 2 | 3 | 使用自定义相机进行拍照、录像的library。 4 | 5 | ![](/screenshot/demo.gif) 6 | 7 | **说明** 8 | 9 | - 拍摄的照片/视频默认存储在`内部存储/DCIM/Camera/`下面,文件命名格式为`yyyyMMdd_HHmmss`。 10 | 拍摄视频最大的时间限制为`10分钟`,超过该时间可能会出现异常。 11 | 12 | - 已解决预览画面拉伸问题,已解决不同的方向拍摄照片/视频的问题。 13 | 14 | - 拍照默认使用`1920 * 1080`的分辨率,没有则会选择其他`16:9`的分辨率。 15 | 16 | - 拍摄视频默认使用`16:9`的分辨率,帧数为`30`帧,如果没有,则选择一个小于30帧的帧数。码率使用`3M+`的码率,以保证视频不会太模糊,也保证视频大小不会太大。 17 | 18 | - 图片压缩:library中引用鲁班压缩,拍摄的原图存储在`内部存储/DCIM/Camera/`下,并会刷新图库。压缩图片会存储在`内部存储/Android/data/应用包名/DCIM`下面,不会刷新图库。 19 | 20 | 21 | [demo.apk下载地址](app/debug/app-debug.apk) 22 | 23 | ## 一、依赖 24 | 25 | **Project build.gradle中** 26 | 27 | ``` 28 | allprojects { 29 | repositories { 30 | jcenter() 31 | maven { 32 | url "https://jitpack.io" 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | Module build.gradle中 39 | 40 | ``` 41 | dependencies { 42 | implementation 'com.github.HyFun:Android-Library-Camera:{last-version}' 43 | } 44 | ``` 45 | 46 | 47 | ## 二、注意事项 48 | 49 | **Android 6.0 运行时权限处理** 50 | 51 | ```xml 52 | 53 | 54 | 55 | 56 | ``` 57 | 58 | ## 三、使用方法 59 | 60 | ### 启动 61 | 62 | - 拍照 63 | ```java 64 | new RxPermissions(this) 65 | .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) 66 | .subscribe(new Consumer() { 67 | @Override 68 | public void accept(Boolean aBoolean) throws Exception { 69 | if (aBoolean) { 70 | FunCamera.capturePhoto(MainActivity.this, 10); 71 | } else { 72 | Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show(); 73 | } 74 | } 75 | }); 76 | ``` 77 | 78 | 79 | - 录像 80 | ```java 81 | new RxPermissions(this) 82 | .request( 83 | Manifest.permission.CAMERA, 84 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 85 | Manifest.permission.RECORD_AUDIO 86 | ) 87 | .subscribe(new Consumer() { 88 | @Override 89 | public void accept(Boolean aBoolean) throws Exception { 90 | if (aBoolean) { 91 | FunCamera.captureRecord(MainActivity.this, 20, 10000); 92 | } else { 93 | Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show(); 94 | } 95 | } 96 | }); 97 | ``` 98 | 99 | - 拍照+录像 100 | 101 | ```java 102 | new RxPermissions(this) 103 | .request( 104 | Manifest.permission.CAMERA, 105 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 106 | Manifest.permission.RECORD_AUDIO 107 | ) 108 | .subscribe(new Consumer() { 109 | @Override 110 | public void accept(Boolean aBoolean) throws Exception { 111 | if (aBoolean) { 112 | FunCamera.capturePhoto2Record(MainActivity.this, 30, 10000); 113 | } else { 114 | Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show(); 115 | } 116 | } 117 | }); 118 | ``` 119 | 120 | ### 回调 121 | 122 | ```java 123 | @Override 124 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 125 | super.onActivityResult(requestCode, resultCode, data); 126 | if (resultCode == RESULT_OK) { 127 | String path = data.getStringExtra(FunCamera.DATA); 128 | String pathOrigin = data.getStringExtra(FunCamera.DATA_ORIGIN); 129 | StringBuilder sb = new StringBuilder(); 130 | sb.append("压缩后地址:" + path + "\n"); 131 | sb.append("原图的地址:" + pathOrigin); 132 | textView.setText(sb.toString()); 133 | } 134 | } 135 | ``` 136 | 137 | 138 | ## 四、TODO 139 | 140 | - 拍完照后跳转到预览的时候会闪一下黑屏(看着难受,微信都不带动的~) 141 | 142 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "29.0.2" 6 | defaultConfig { 7 | applicationId "com.hyfun.camera.demo" 8 | minSdkVersion 16 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0.1" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'com.android.support:appcompat-v7:28.0.0' 25 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 26 | testImplementation 'junit:junit:4.12' 27 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 28 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 29 | implementation project(path: ':camera') 30 | 31 | 32 | // 必须依赖的库 33 | // rx java rx android 34 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' 35 | implementation 'io.reactivex.rxjava2:rxjava:2.2.2' 36 | implementation 'com.github.tbruyelle:rxpermissions:0.10.2' 37 | } 38 | -------------------------------------------------------------------------------- /app/debug/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yofun/android-lib-camera/c2432a22f6b5d70cffb86fa9332091e5e179ec92/app/debug/app-debug.apk -------------------------------------------------------------------------------- /app/debug/output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0.1","enabled":true,"outputFile":"app-debug.apk","fullName":"debug","baseName":"debug"},"path":"app-debug.apk","properties":{}}] -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hyfun/camera/demo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.hyfun.camera.demo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 23 | 24 | assertEquals("com.hyfun.camera.demo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/hyfun/camera/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hyfun.camera.demo; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | 12 | import com.hyfun.camera.FunCamera; 13 | import com.hyfun.camera.audio.FunAudioRecordListener; 14 | import com.tbruyelle.rxpermissions2.RxPermissions; 15 | 16 | import io.reactivex.functions.Consumer; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private TextView textView; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_main); 26 | textView = findViewById(R.id.textView); 27 | } 28 | 29 | public void capture(View view) { 30 | new RxPermissions(this) 31 | .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) 32 | .subscribe(new Consumer() { 33 | @Override 34 | public void accept(Boolean aBoolean) throws Exception { 35 | if (aBoolean) { 36 | FunCamera.capturePhoto(MainActivity.this, 10); 37 | } else { 38 | Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show(); 39 | } 40 | } 41 | }); 42 | } 43 | 44 | public void record(View view) { 45 | new RxPermissions(this) 46 | .request( 47 | Manifest.permission.CAMERA, 48 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 49 | Manifest.permission.RECORD_AUDIO 50 | ) 51 | .subscribe(new Consumer() { 52 | @Override 53 | public void accept(Boolean aBoolean) throws Exception { 54 | if (aBoolean) { 55 | FunCamera.captureRecord(MainActivity.this, 20, 10000); 56 | } else { 57 | Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show(); 58 | } 59 | } 60 | }); 61 | } 62 | 63 | public void captureRecord(View view) { 64 | new RxPermissions(this) 65 | .request( 66 | Manifest.permission.CAMERA, 67 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 68 | Manifest.permission.RECORD_AUDIO 69 | ) 70 | .subscribe(new Consumer() { 71 | @Override 72 | public void accept(Boolean aBoolean) throws Exception { 73 | if (aBoolean) { 74 | FunCamera.capturePhoto2Record(MainActivity.this, 30, 10000); 75 | } else { 76 | Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show(); 77 | } 78 | } 79 | }); 80 | } 81 | 82 | public void captureAudio(View view) { 83 | new RxPermissions(this) 84 | .request(Manifest.permission.RECORD_AUDIO) 85 | .subscribe(new Consumer() { 86 | @Override 87 | public void accept(Boolean aBoolean) throws Exception { 88 | if (aBoolean) { 89 | FunCamera.captureAudioRecord(MainActivity.this, new FunAudioRecordListener() { 90 | @Override 91 | public void onAudioRecordResult(String filePath) { 92 | textView.setText("录音地址:" + filePath); 93 | } 94 | }); 95 | } else { 96 | Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show(); 97 | } 98 | } 99 | }); 100 | } 101 | 102 | @Override 103 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 104 | super.onActivityResult(requestCode, resultCode, data); 105 | if (resultCode == RESULT_OK) { 106 | String path = data.getStringExtra(FunCamera.DATA); 107 | String pathOrigin = data.getStringExtra(FunCamera.DATA_ORIGIN); 108 | StringBuilder sb = new StringBuilder(); 109 | sb.append("压缩后地址:" + path + "\n"); 110 | sb.append("原图的地址:" + pathOrigin); 111 | textView.setText(sb.toString()); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 |