├── .gitignore
├── README.md
├── app
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── cn
│ │ └── bingoogolapple
│ │ └── camera
│ │ ├── CameraPreview.kt
│ │ ├── MainActivity.kt
│ │ ├── PlayActivity.kt
│ │ ├── SettingsFragment.kt
│ │ ├── StudyActivity.kt
│ │ └── StudyFragment.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout-land
│ └── activity_main.xml
│ ├── layout
│ ├── activity_main.xml
│ └── activity_study.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
│ ├── arrays.xml
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ ├── preferences.xml
│ └── study_preference_fragment.xml
├── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 | /*/build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Eclipse project files
30 | .classpath
31 | .project
32 | .settings/
33 |
34 | # Intellij project files
35 | *.iml
36 | *.ipr
37 | *.iws
38 | .idea/
39 |
40 | # Mac system files
41 | .DS_Store
42 |
43 | *.keystore
44 |
45 | captures
46 |
47 | .externalNativeBuild/
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | :running:BGACamera-Android:running:
2 | ============
3 |
4 | Android 相机开发学习笔记,参考 [Android 相机开发系列](https://www.polarxiong.com/archives/Android%E7%9B%B8%E6%9C%BA%E5%BC%80%E5%8F%91%E7%B3%BB%E5%88%97.html)
5 |
6 | ## 作者联系方式
7 |
8 | | 个人主页 | 邮箱 |
9 | | ------------- | ------------ |
10 | | bingoogolapple.cn | bingoogolapple@gmail.com |
11 |
12 | | 个人微信号 | 微信群 | 公众号 |
13 | | ------------ | ------------ | ------------ |
14 | |
|
|
|
15 |
16 | | 个人 QQ 号 | QQ 群 |
17 | | ------------ | ------------ |
18 | |
|
|
19 |
20 | ## 打赏支持作者
21 |
22 | 如果您觉得 BGA 系列开源库或工具软件帮您节省了大量的开发时间,可以扫描下方的二维码打赏支持。您的支持将鼓励我继续创作,打赏后还可以加我微信免费开通一年 [上帝小助手浏览器扩展/插件开发平台](https://github.com/bingoogolapple/bga-god-assistant-config) 的会员服务
23 |
24 | | 微信 | QQ | 支付宝 |
25 | | ------------- | ------------- | ------------- |
26 | |
|
|
|
27 |
28 | ## 作者项目推荐
29 |
30 | * 欢迎您使用我开发的第一个独立开发软件产品 [上帝小助手浏览器扩展/插件开发平台](https://github.com/bingoogolapple/bga-god-assistant-config)
31 |
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.config.KotlinCompilerVersion
2 |
3 | plugins {
4 | id("com.android.application")
5 | id("kotlin-android")
6 | id("kotlin-android-extensions")
7 | }
8 |
9 | android {
10 | compileSdkVersion(28)
11 |
12 | defaultConfig {
13 | minSdkVersion(19)
14 | targetSdkVersion(15)
15 | versionCode = 100
16 | versionName = "1.0.0"
17 | }
18 |
19 | dataBinding {
20 | isEnabled = true
21 | }
22 |
23 | buildTypes {
24 | getByName("release") {
25 | isMinifyEnabled = true
26 | proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
27 | }
28 | }
29 | }
30 |
31 | dependencies {
32 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
33 | implementation("com.android.support:appcompat-v7:28.0.0")
34 | implementation("com.android.support.constraint:constraint-layout:1.1.3")
35 |
36 | implementation(kotlin("stdlib", KotlinCompilerVersion.VERSION))
37 | implementation("org.jetbrains.anko:anko-commons:0.10.5")
38 | implementation("pub.devrel:easypermissions:1.0.1")
39 | }
40 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGACamera-Android/23873c64de42fcb87d4bc751decb01c559578b29/app/proguard-rules.pro
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
37 |
38 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/bingoogolapple/camera/CameraPreview.kt:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.camera
2 |
3 | import android.content.Context
4 | import android.graphics.Rect
5 | import android.graphics.RectF
6 | import android.hardware.Camera
7 | import android.media.CamcorderProfile
8 | import android.media.MediaActionSound
9 | import android.media.MediaRecorder
10 | import android.media.ThumbnailUtils
11 | import android.net.Uri
12 | import android.os.Environment
13 | import android.preference.PreferenceManager
14 | import android.provider.MediaStore
15 | import android.support.v4.math.MathUtils
16 | import android.util.Log
17 | import android.view.*
18 | import android.widget.ImageView
19 | import java.io.File
20 | import java.io.FileOutputStream
21 | import java.io.IOException
22 | import java.text.SimpleDateFormat
23 | import java.util.*
24 |
25 |
26 | /**
27 | * 作者:王浩
28 | * 创建时间:2018/9/28
29 | * 描述:
30 | */
31 | class CameraPreview(context: Context) : SurfaceView(context), SurfaceHolder.Callback, Camera.PreviewCallback {
32 | private var mCamera: Camera? = null
33 | private var mOutputMediaFileUri: Uri? = null
34 | private var mOutputMediaFileType: String? = null
35 | private var mMediaRecorder: MediaRecorder? = null
36 | private var oldDist = 1f
37 | private var touchFocusing = false
38 |
39 | init {
40 | holder.addCallback(this)
41 | }
42 |
43 | fun getCamera(): Camera? {
44 | return mCamera
45 | }
46 |
47 | private fun log(parameters: Camera.Parameters) {
48 | val log = StringBuilder()
49 | log.append("\n相机信息如下:")
50 | log.append("\n 支持的预览尺寸有:\n ")
51 | for (previewSize in parameters.supportedPreviewSizes) {
52 | log.append("${previewSize.width}x${previewSize.height}").append("、")
53 | }
54 | log.append("\n支持的拍照尺寸有:\n ")
55 | for (pictureSize in parameters.supportedPictureSizes) {
56 | log.append("${pictureSize.width}x${pictureSize.height}").append("、")
57 | }
58 | log.append("\n支持的录像尺寸有:\n ")
59 | for (videoPictureSize in parameters.supportedVideoSizes) {
60 | log.append("${videoPictureSize.width}x${videoPictureSize.height}").append("、")
61 | }
62 | log.append("\n支持的对焦模式有:\n ")
63 | for (focusMode in parameters.supportedFocusModes) {
64 | log.append(focusMode).append("、")
65 | }
66 | log.append("\n支持的白平衡有:\n ")
67 | for (whiteBalance in parameters.supportedWhiteBalance) {
68 | log.append(whiteBalance).append("、")
69 | }
70 | parameters.supportedSceneModes?.let {
71 | log.append("\n支持的场景模式有:\n ")
72 | for (sceneMode in it) {
73 | log.append(sceneMode).append("、")
74 | }
75 | }
76 | log.append("\n支持的闪光灯模式有:\n ")
77 | for (flashMode in parameters.supportedFlashModes) {
78 | log.append(flashMode).append("、")
79 | }
80 | log.append("\n曝光补偿范围为:\n ${parameters.minExposureCompensation}~${parameters.maxExposureCompensation}")
81 | Log.d(TAG, log.toString())
82 | }
83 |
84 | private fun adjustDisplayRatio(rotation: Int) {
85 | mCamera?.let {
86 | val parent = parent as ViewGroup
87 | val rect = Rect()
88 | parent.getLocalVisibleRect(rect)
89 | val width = rect.width()
90 | val height = rect.height()
91 | val previewSize = it.parameters.previewSize
92 | val previewWidth: Int
93 | val previewHeight: Int
94 | if (rotation == 90 || rotation == 270) {
95 | previewWidth = previewSize.height
96 | previewHeight = previewSize.width
97 | } else {
98 | previewWidth = previewSize.width
99 | previewHeight = previewSize.height
100 | }
101 |
102 | if (width * previewHeight > height * previewWidth) {
103 | val scaledChildWidth = previewWidth * height / previewHeight
104 | layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height)
105 | } else {
106 | val scaledChildHeight = previewHeight * width / previewWidth
107 | layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2)
108 | }
109 | }
110 | }
111 |
112 | private fun openCamera() {
113 | if (mCamera != null) {
114 | return
115 | }
116 |
117 | try {
118 | mCamera = Camera.open()
119 | } catch (e: Exception) {
120 | Log.e(TAG, "相机资源被占用:" + e.message)
121 | }
122 | }
123 |
124 | public fun startPreview() {
125 | openCamera()
126 | try {
127 | mCamera?.apply {
128 | // 告知将预览帧数据交给谁
129 | setPreviewDisplay(holder)
130 | // 每当有预览帧生成时就会回调 onPreviewFrame 方法
131 | setPreviewCallback(this@CameraPreview)
132 | // 开始预览
133 | startPreview()
134 | log(parameters)
135 | }
136 | } catch (e: IOException) {
137 | Log.e(TAG, "开始预览失败:" + e.message)
138 | }
139 | }
140 |
141 | public fun stopPreview() {
142 | holder.removeCallback(this)
143 | // 相机是共享资源,使用完后需要释放相机资源
144 | mCamera?.apply {
145 | setPreviewCallback(null)
146 | stopPreview()
147 | release()
148 | }
149 | mCamera = null
150 | }
151 |
152 | override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
153 | try {
154 | Log.i(TAG, "模拟处理预览帧数据")
155 | Thread.sleep(500)
156 | } catch (e: InterruptedException) {
157 | e.printStackTrace()
158 | }
159 | }
160 |
161 | override fun surfaceCreated(surfaceHolder: SurfaceHolder) {
162 | startPreview()
163 | }
164 |
165 | override fun surfaceDestroyed(surfaceHolder: SurfaceHolder) {
166 | stopPreview()
167 | }
168 |
169 | override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
170 | mCamera?.apply {
171 | val rotation = getDisplayOrientation()
172 | val newParameters = parameters
173 | // 设置预览帧数据,以及拍摄照片的方向
174 | newParameters.setRotation(rotation)
175 | parameters = newParameters
176 | // 指定预览的旋转角度
177 | setDisplayOrientation(getDisplayOrientation())
178 | // 实时调整预览纵横比
179 | adjustDisplayRatio(rotation)
180 | }
181 | }
182 |
183 | private fun getDisplayOrientation(): Int {
184 | val display = (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
185 | val rotation = display.rotation
186 | var degrees = 0
187 | when (rotation) {
188 | Surface.ROTATION_0 -> degrees = 0
189 | Surface.ROTATION_90 -> degrees = 90
190 | Surface.ROTATION_180 -> degrees = 180
191 | Surface.ROTATION_270 -> degrees = 270
192 | }
193 |
194 | val camInfo = Camera.CameraInfo()
195 | Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, camInfo)
196 | return (camInfo.orientation - degrees + 360) % 360
197 | }
198 |
199 | private fun getAppName(): String {
200 | return try {
201 | context.packageManager.getPackageInfo(context.packageName, 0).applicationInfo.loadLabel(context.packageManager).toString()
202 | } catch (e: Exception) {
203 | // 利用系统api getPackageName()得到的包名,这个异常根本不可能发生
204 | ""
205 | }
206 | }
207 |
208 | private fun getOutputMediaFile(type: Int): File? {
209 | val mediaStorageDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), getAppName())
210 | if (!mediaStorageDir.exists()) {
211 | if (!mediaStorageDir.mkdirs()) {
212 | Log.d(TAG, "创建媒体文件目录失败,请检查存储权限")
213 | return null
214 | }
215 | }
216 | val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
217 | val mediaFile: File
218 | when (type) {
219 | MEDIA_TYPE_IMAGE -> {
220 | mediaFile = File(mediaStorageDir.path + File.separator + "IMG_" + timeStamp + ".jpg")
221 | mOutputMediaFileType = "image/*"
222 | }
223 | MEDIA_TYPE_VIDEO -> {
224 | mediaFile = File(mediaStorageDir.path + File.separator + "VID_" + timeStamp + ".mp4")
225 | mOutputMediaFileType = "video/*"
226 | }
227 | else -> return null
228 | }
229 | mOutputMediaFileUri = Uri.fromFile(mediaFile)
230 | return mediaFile
231 | }
232 |
233 | fun getOutputMediaFileUri(): Uri? {
234 | return mOutputMediaFileUri
235 | }
236 |
237 | fun getOutputMediaFileType(): String? {
238 | return mOutputMediaFileType
239 | }
240 |
241 | fun takePicture(previewIv: ImageView) {
242 | mCamera?.apply {
243 | takePicture(Camera.ShutterCallback {
244 | Log.d(TAG, "按下了快门,播放声音")
245 | MediaActionSound().play(MediaActionSound.SHUTTER_CLICK)
246 | }, Camera.PictureCallback { data, camera ->
247 | Log.d(TAG, "原始数据,不知道为什么返回的 data 一直为空")
248 | }, Camera.PictureCallback { data, camera ->
249 | Log.d(TAG, "jpeg 数据。主线程回调的 " + data.size)
250 | // TODO 子线程保存图片文件
251 | val pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE)
252 | if (pictureFile == null) {
253 | Log.d(TAG, "创建媒体文件失败,请检查存储权限")
254 | return@PictureCallback
255 | }
256 | try {
257 | FileOutputStream(pictureFile).use {
258 | it.write(data)
259 | }
260 |
261 | previewIv.setImageURI(mOutputMediaFileUri)
262 |
263 | camera.startPreview()
264 | } catch (e: Exception) {
265 | Log.d(TAG, "保存图片失败 " + e.message)
266 | }
267 | })
268 | }
269 | }
270 |
271 | fun startRecording(): Boolean {
272 | if (prepareVideoRecorder()) {
273 | mMediaRecorder?.start()
274 | return true
275 | } else {
276 | releaseMediaRecorder()
277 | }
278 | return false
279 | }
280 |
281 | fun stopRecording(previewIv: ImageView) {
282 | mMediaRecorder?.stop()
283 | mOutputMediaFileUri?.apply {
284 | val thumbnail = ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MINI_KIND)
285 | previewIv.setImageBitmap(thumbnail)
286 | }
287 | releaseMediaRecorder()
288 | }
289 |
290 | fun isRecording(): Boolean {
291 | return mMediaRecorder != null
292 | }
293 |
294 | private fun prepareVideoRecorder(): Boolean {
295 | openCamera()
296 | mMediaRecorder = MediaRecorder()
297 |
298 | mCamera?.unlock()
299 | mMediaRecorder?.apply {
300 |
301 | setCamera(mCamera)
302 |
303 | setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
304 | setVideoSource(MediaRecorder.VideoSource.CAMERA)
305 | setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH))
306 |
307 | val prefs = PreferenceManager.getDefaultSharedPreferences(context)
308 | val prefVideoSize = prefs.getString("video_size", "")
309 | if (prefVideoSize.isNotBlank()) {
310 | val split = prefVideoSize.split("x".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
311 | setVideoSize(Integer.parseInt(split[0]), Integer.parseInt(split[1]))
312 | }
313 | setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString())
314 | setPreviewDisplay(holder.surface)
315 |
316 | try {
317 | // 视频的旋转并不是编码层面的旋转,视频帧数据并没有发生旋转,而只是在视频中增加了参数,希望播放器按照指定的旋转角度旋转后播放,所以具体效果因播放器而异
318 | setOrientationHint(getDisplayOrientation())
319 | prepare()
320 | } catch (e: IllegalStateException) {
321 | Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.message)
322 | releaseMediaRecorder()
323 | return false
324 | } catch (e: IOException) {
325 | Log.d(TAG, "IOException preparing MediaRecorder: " + e.message)
326 | releaseMediaRecorder()
327 | return false
328 | }
329 | }
330 | return true
331 | }
332 |
333 | private fun releaseMediaRecorder() {
334 | mMediaRecorder?.apply {
335 | reset()
336 | release()
337 | mMediaRecorder = null
338 | }
339 | mCamera?.apply { lock() }
340 | }
341 |
342 | override fun onTouchEvent(event: MotionEvent): Boolean {
343 | mCamera?.let {
344 | if (event.pointerCount == 1) {
345 | if (event.action and MotionEvent.ACTION_MASK == MotionEvent.ACTION_UP) {
346 | handleFocusMetering(event, it)
347 | }
348 | } else {
349 | when (event.action and MotionEvent.ACTION_MASK) {
350 | MotionEvent.ACTION_POINTER_DOWN -> oldDist = getFingerSpacing(event)
351 | MotionEvent.ACTION_UP, MotionEvent.ACTION_MOVE -> {
352 | val newDist = getFingerSpacing(event)
353 | if (newDist > oldDist) {
354 | handleZoom(true, it)
355 | } else if (newDist < oldDist) {
356 | handleZoom(false, it)
357 | }
358 | oldDist = newDist
359 | }
360 | }
361 | }
362 | }
363 | return true
364 | }
365 |
366 | private fun handleFocusMetering(event: MotionEvent, camera: Camera) {
367 | if (touchFocusing) {
368 | return
369 | }
370 | touchFocusing = true
371 |
372 | var isNeedUpdate = false
373 | val newParams = camera.parameters
374 | val currentFocusMode = newParams.focusMode
375 | Log.i(TAG, "老的对焦模式为$currentFocusMode")
376 | if (newParams.maxNumFocusAreas > 0) {
377 | Log.i(TAG, "支持触摸对焦")
378 | isNeedUpdate = true
379 | val focusAreas = arrayListOf()
380 | val focusRect = calculateTapArea(event.x, event.y, 1f, width, height)
381 | focusAreas.add(Camera.Area(focusRect, 800))
382 | newParams.focusAreas = focusAreas
383 | newParams.focusMode = Camera.Parameters.FOCUS_MODE_MACRO
384 | } else {
385 | Log.i(TAG, "不支持触摸对焦")
386 | }
387 |
388 | if (newParams.maxNumMeteringAreas > 0) {
389 | Log.i(TAG, "支持触摸测光")
390 | isNeedUpdate = true
391 | val meteringAreas = arrayListOf()
392 | val meteringRect = calculateTapArea(event.x, event.y, 1.5f, width, height)
393 | meteringAreas.add(Camera.Area(meteringRect, 800))
394 | newParams.meteringAreas = meteringAreas
395 | } else {
396 | Log.i(TAG, "不支持触摸测光")
397 | }
398 |
399 | if (isNeedUpdate) {
400 | camera.cancelAutoFocus()
401 | camera.parameters = newParams
402 | camera.autoFocus { success, camera ->
403 | if (success) {
404 | Log.i(TAG, "对焦成功")
405 | } else {
406 | Log.i(TAG, "对焦失败")
407 | }
408 | touchFocusing = false
409 | val recoverParams = camera.parameters
410 | recoverParams.focusMode = currentFocusMode
411 | camera.parameters = recoverParams
412 | Log.i(TAG, "还原对焦模式")
413 | }
414 | } else {
415 | touchFocusing = false
416 | }
417 | }
418 |
419 | private fun handleZoom(isZoomIn: Boolean, camera: Camera) {
420 | val params = camera.parameters
421 | if (params.isZoomSupported) {
422 | var zoom = params.zoom
423 | if (isZoomIn && zoom < params.maxZoom) {
424 | Log.i(TAG, "放大")
425 | zoom++
426 | } else if (!isZoomIn && zoom > 0) {
427 | Log.i(TAG, "缩小")
428 | zoom--
429 | } else {
430 | Log.i(TAG, "既不放大也不缩小")
431 | }
432 | params.zoom = zoom
433 | camera.parameters = params
434 | } else {
435 | Log.i(TAG, "不支持缩放")
436 | }
437 | }
438 |
439 | companion object {
440 | private val TAG = CameraPreview::class.java.simpleName
441 | private const val MEDIA_TYPE_IMAGE = 1
442 | private const val MEDIA_TYPE_VIDEO = 2
443 |
444 | private fun calculateTapArea(x: Float, y: Float, coefficient: Float, width: Int, height: Int): Rect {
445 | val focusAreaSize = 300f
446 | val areaSize = (focusAreaSize * coefficient).toInt()
447 | val centerX = (x / width * 2000 - 1000).toInt()
448 | val centerY = (y / height * 2000 - 1000).toInt()
449 |
450 | val halfAreaSize = areaSize / 2
451 | val rectF = RectF(MathUtils.clamp(centerX - halfAreaSize, -1000, 1000).toFloat(),
452 | MathUtils.clamp(centerY - halfAreaSize, -1000, 1000).toFloat(),
453 | MathUtils.clamp(centerX + halfAreaSize, -1000, 1000).toFloat(),
454 | MathUtils.clamp(centerY + halfAreaSize, -1000, 1000).toFloat())
455 | return Rect(Math.round(rectF.left), Math.round(rectF.top), Math.round(rectF.right), Math.round(rectF.bottom))
456 | }
457 |
458 | private fun getFingerSpacing(event: MotionEvent): Float {
459 | val x = event.getX(0) - event.getX(1)
460 | val y = event.getY(0) - event.getY(1)
461 | return Math.sqrt((x * x + y * y).toDouble()).toFloat()
462 | }
463 | }
464 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/bingoogolapple/camera/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.camera
2 |
3 | import android.Manifest
4 | import android.app.Activity
5 | import android.content.Intent
6 | import android.os.Bundle
7 | import android.preference.PreferenceManager
8 | import android.view.View
9 | import android.widget.Button
10 | import android.widget.FrameLayout
11 | import android.widget.ImageView
12 | import pub.devrel.easypermissions.AfterPermissionGranted
13 | import pub.devrel.easypermissions.EasyPermissions
14 |
15 | class MainActivity : Activity(), EasyPermissions.PermissionCallbacks, View.OnClickListener {
16 | private lateinit var mPreViewContainerFl: FrameLayout
17 | private lateinit var mPreview: CameraPreview
18 | private lateinit var mPreviewIv: ImageView
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_main)
23 | mPreViewContainerFl = findViewById(R.id.fl_main_preview_container)
24 |
25 | mPreview = CameraPreview(this)
26 | mPreViewContainerFl.addView(mPreview)
27 |
28 | mPreviewIv = findViewById(R.id.iv_main_preview)
29 |
30 | findViewById