1.fragment的xml文件中所需的控件
<Button
android:id="@+id/btn_one"
app:backgroundTint="@color/white"
android:text="点击拍照"
android:textSize="35sp"
app:strokeColor="@color/home_center_bg"
android:layout_marginTop="60dp"
app:strokeWidth="2dp"
app:cornerRadius="25dp"
android:textColor="@color/home_center_bg"
android:layout_width="200dp"
android:layout_height="80dp"/>
<TextView
android:id="@+id/tv_pic_dir"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="160dp"
android:text="图片路径:"
android:textColor="#000000"
android:textSize="14sp" />
<SurfaceView
android:id="@+id/congig_access_SurfaceView"
android:layout_width="408dp"
android:layout_height="408dp"
android:layout_marginTop="205dp"
app:round="204dp"
android:src="@mipmap/door_icon"
/>
<ImageView
android:id="@+id/iv_photo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="650dp"
android:src="@mipmap/door_icon"
app:round="204dp" />
2.创建工具类和接口
2.1 CameraTakeListener 接口
package com.doshare.boardroom.cameratake.listener
import android.graphics.Bitmap
import java.io.File
interface CameraTakeListener {
fun onSuccess(bitmapFile: File?, mBitmap: Bitmap?)
fun onFail(error: String?)
}
2.2 FileUtil工具类
package com.doshare.boardroom.cameratake.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Environment
import android.os.StatFs
import android.text.TextUtils
import top.zibin.luban.CompressionPredicate
import top.zibin.luban.Luban
import top.zibin.luban.OnCompressListener
import java.io.*
import java.util.*
object FileUtil {//sd卡大小相关变量
//获得Sdcard上每个block的size
//获取可供程序使用的Block数量
//计算标准大小使用:1024,当然使用1000也可以
/**
* 计算Sdcard的剩余大小
*
* @return MB
*/
fun getAvailableSize():Long
{
//sd卡大小相关变量
val statFs: StatFs
val file = Environment.getExternalStorageDirectory()
statFs = StatFs(file.path)
//获得Sdcard上每个block的size
val blockSize = statFs.blockSize.toLong()
//获取可供程序使用的Block数量
val blockavailable = statFs.availableBlocks.toLong()
//计算标准大小使用:1024,当然使用1000也可以
return blockSize * blockavailable / 1024 / 1024
}//获得sdcard上 block的总数
//获得sdcard上每个block 的大小
//计算标准大小使用:1024,当然使用1000也可以
/**
* SDCard 总容量大小
*
* @return MB
*/
val totalSize: Long
get() {
val statFs: StatFs
val file = Environment.getExternalStorageDirectory()
statFs = StatFs(file.path)
//获得sdcard上 block的总数
val blockCount = statFs.blockCount.toLong()
//获得sdcard上每个block 的大小
val blockSize = statFs.blockSize.toLong()
//计算标准大小使用:1024,当然使用1000也可以
return blockCount * blockSize / 1024 / 1024
}
/**
* 保存bitmap到本地
*
* @param bitmap
* @return
*/
fun saveBitmap(bitmap: Bitmap?): File? {
val savePath: String
val filePic: File
savePath = if (Environment.getExternalStorageState() ==
Environment.MEDIA_MOUNTED
) {
(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
.toString()
+ File.separator)
} else {
LogUtil.d("saveBitmap: 1return")
return null
}
try {
filePic = File(savePath + "Pic_" + System.currentTimeMillis() + ".jpg")
if (!filePic.exists()) {
filePic.parentFile.mkdirs()
filePic.createNewFile()
}
val fos = FileOutputStream(filePic)
bitmap?.compress(Bitmap.CompressFormat.JPEG, 100, fos)
fos.flush()
fos.close()
} catch (e: IOException) {
e.printStackTrace()
LogUtil.d("saveBitmap: 2return")
return null
}
LogUtil.d("saveBitmap: " + filePic.absolutePath)
return filePic
}
/**
* 压缩图片
*
* @param image
* @return
*/
fun compressImage(image: Bitmap): Bitmap? {
val baos = ByteArrayOutputStream()
/** 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 */
image.compress(Bitmap.CompressFormat.JPEG, 100, baos)
/** 把压缩后的数据baos存放到ByteArrayInputStream中 */
val isBm = ByteArrayInputStream(baos.toByteArray())
return BitmapFactory.decodeStream(isBm, null, null)
}
/**
* 文件夹删除
*/
fun deleteFile(file: File) {
if (file.isDirectory) {
val files = file.listFiles()
for (i in files.indices) {
val f = files[i]
deleteFile(f)
}
file.delete() //如要保留文件夹,只删除文件,请注释这行
} else if (file.exists()) {
file.delete()
}
}
/**
* 压缩图片文件
*/
fun compressPic(context: Context?, picFile: File, listener: OnCompressListener?) {
val savePath =
(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
.toString()
+ File.separator)
Luban.with(context)
.load(picFile.path)
.ignoreBy(100)
.setTargetDir(savePath)
.filter(object : CompressionPredicate {
override fun apply(path: String): Boolean {
return !(TextUtils.isEmpty(path) || path.lowercase(Locale.getDefault())
.endsWith(".gif"))
}
})
.setCompressListener(listener).launch()
}
}
2.3 LogUtil 工具类
package com.doshare.boardroom.cameratake.utils
import android.text.TextUtils
import android.util.Log
object LogUtil {
var tagPrefix = ""
var showV = true
var showD = true
var showI = true
var showW = true
var showE = true
var showWTF = true
/**
* 得到tag(所在类.方法(L:行))
*
* @return
*/
private fun generateTag(): String {
val stackTraceElement = Thread.currentThread().stackTrace[4]
var callerClazzName = stackTraceElement.className
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1)
var tag = "%s.%s(L:%d)"
tag = String.format(
tag,
*arrayOf<Any>(
callerClazzName,
stackTraceElement.methodName,
Integer.valueOf(stackTraceElement.lineNumber)
)
)
//给tag设置前缀
tag =
if (TextUtils.isEmpty(tagPrefix)) tag else tagPrefix + ":" + tag
return tag
}
fun v(msg: String?) {
if (showV) {
val tag: String = generateTag()
Log.v(tag, msg!!)
}
}
fun v(msg: String?, tr: Throwable?) {
if (showV) {
val tag: String = generateTag()
Log.v(tag, msg, tr)
}
}
fun d(msg: String?) {
if (showD) {
val tag: String =generateTag()
Log.d(tag, msg!!)
}
}
fun d(msg: String?, tr: Throwable?) {
if (showD) {
val tag: String = generateTag()
Log.d(tag, msg, tr)
}
}
fun i(msg: String?) {
if (showI) {
val tag: String = generateTag()
Log.i(tag, msg!!)
}
}
fun i(msg: String?, tr: Throwable?) {
if (showI) {
val tag: String = generateTag()
Log.i(tag, msg, tr)
}
}
fun w(msg: String?) {
if (showW) {
val tag: String = generateTag()
Log.w(tag, msg!!)
}
}
fun w(msg: String?, tr: Throwable?) {
if (showW) {
val tag: String = generateTag()
Log.w(tag, msg, tr)
}
}
fun e(msg: String?) {
if (showE) {
val tag: String =generateTag()
Log.e(tag, msg!!)
}
}
fun e(msg: String?, tr: Throwable?) {
if (showE) {
val tag: String = generateTag()
Log.e(tag, msg, tr)
}
}
fun wtf(msg: String?) {
if (showWTF) {
val tag: String = generateTag()
Log.wtf(tag, msg)
}
}
fun wtf(msg: String?, tr: Throwable?) {
if (showWTF) {
val tag: String = generateTag()
Log.wtf(tag, msg, tr)
}
}
}
2.4 SurfaceViewCallback 工具类
package com.doshare.boardroom.cameratake
import android.app.Activity
import android.graphics.*
import android.hardware.Camera
import android.hardware.Camera.CameraInfo
import android.view.Surface
import android.view.SurfaceHolder
import com.doshare.boardroom.cameratake.listener.CameraTakeListener
import com.doshare.boardroom.cameratake.utils.FileUtil
import com.doshare.boardroom.cameratake.utils.LogUtil
import com.doshare.boardroom.view.fragment.ConfigAccessFragment
import top.zibin.luban.OnCompressListener
import java.io.ByteArrayOutputStream
import java.io.File
class SurfaceViewCallback(private val activity: Activity, listener: ConfigAccessFragment) :
SurfaceHolder.Callback {
var previewing = false
private var hasSurface = false
var mCamera: Camera? = null
var mCurrentCamIndex = 0
/** 为true时则开始捕捉照片 */
var canTake = false
/** 拍照回调接口 */
var listener: CameraTakeListener
init {
this.listener = listener
}
override fun surfaceCreated(holder: SurfaceHolder) {
if (!hasSurface) {
hasSurface = true
mCamera = openFrontFacingCameraGingerbread()
if (mCamera == null) {
listener.onFail("没有可用的摄像头")
return
}
mCamera!!.setPreviewCallback { bytes, camera ->
LogUtil.i("onPreviewFrame $canTake")
if (canTake) {
getSurfacePic(bytes, camera)
canTake = false
}
}
}
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
if (previewing) {
mCamera!!.stopPreview()
previewing = false
}
try {
mCamera!!.setPreviewDisplay(holder)
mCamera!!.startPreview()
previewing = true
setCameraDisplayOrientation(activity, mCurrentCamIndex, mCamera)
} catch (_: Exception) {
}
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
if (!previewing) return
holder.removeCallback(this)
mCamera!!.setPreviewCallback(null)
mCamera!!.stopPreview()
mCamera!!.lock()
mCamera!!.release()
mCamera = null
}
/**
* 设置照相机播放的方向
*/
private fun setCameraDisplayOrientation(activity: Activity, cameraId: Int, camera: Camera?) {
val info = CameraInfo()
Camera.getCameraInfo(cameraId, info)
val rotation = activity.windowManager.defaultDisplay.rotation
/** 度图片顺时针旋转的角度。有效值为0、90、180和270 */
/** 起始位置为0(横向) */
var degrees = 0
when (rotation) {
Surface.ROTATION_0 -> degrees = 0
Surface.ROTATION_90 -> degrees = 90
Surface.ROTATION_180 -> degrees = 180
Surface.ROTATION_270 -> degrees = 270
}
var result: Int
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360
result = (360 - result) % 360 // compensate the mirror
} else {
/** 背面 */
result = (info.orientation - degrees + 360) % 360
}
camera!!.setDisplayOrientation(result)
}
/**
* 打开摄像头面板
*/
private fun openFrontFacingCameraGingerbread(): Camera? {
var cameraCount = 0
var cam: Camera? = null
val cameraInfo = CameraInfo()
cameraCount = Camera.getNumberOfCameras()
for (camIdx in 0 until cameraCount) {
Camera.getCameraInfo(camIdx, cameraInfo)
try {
cam = Camera.open(camIdx)
mCurrentCamIndex = camIdx
} catch (e: RuntimeException) {
LogUtil.e("Camera failed to open: " + e.localizedMessage)
}
}
return cam
}
/**
* 获取照片
*/
fun getSurfacePic(data: ByteArray?, camera: Camera) {
val size = camera.parameters.previewSize
val image = YuvImage(data, ImageFormat.NV21, size.width, size.height, null)
if (image != null) {
val stream = ByteArrayOutputStream()
image.compressToJpeg(Rect(0, 0, size.width, size.height), 80, stream)
val bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size())
/** 因为图片会放生旋转,因此要对图片进行旋转到和手机在一个方向上 */
rotateMyBitmap(bmp)
}
}
/**
* 旋转图片
*/
fun rotateMyBitmap(bmp: Bitmap) {
val matrix = Matrix()
matrix.postRotate(0f)
val nbmp2 = Bitmap.createBitmap(bmp, 0, 0, bmp.width, bmp.height, matrix, true)
saveMyBitmap(FileUtil.compressImage(nbmp2))
}
/**
* 保存图片
*/
fun saveMyBitmap(mBitmap: Bitmap?) {
if (FileUtil.getAvailableSize() > 512) {
val filePic: File? = FileUtil.saveBitmap(mBitmap)
if (filePic == null) {
/** 图片保存失败 */
listener.onFail("图片保存失败")
return
}
FileUtil.compressPic(activity, filePic, object : OnCompressListener {
override fun onStart() {
// TODO 压缩开始前调用,可以在方法内启动 loading UI
}
override fun onSuccess(file: File?) {
// TODO 压缩成功后调用,返回压缩后的图片文件
FileUtil.deleteFile(filePic)
listener.onSuccess(filePic, mBitmap)
}
override fun onError(e: Throwable?) {
// TODO 当压缩过程出现问题时调用
LogUtil.e("compressPic error")
}
})
} else {
listener.onFail("存储空间小于512M,图片无法正常保存")
}
}
/**
* 获取相机当前的照片
*/
fun takePhoto() {
canTake = true
}
/**
* 释放
*/
fun destroy() {
hasSurface = false
}
}
3. fragment的java文件
package com.doshare.boardroom.view.fragment
import android.Manifest
import android.graphics.Bitmap
import android.os.Bundle
import android.view.LayoutInflater
import android.view.SurfaceHolder
import android.view.View
import android.view.ViewGroup
import androidx.core.app.ActivityCompat
import butterknife.ButterKnife
import butterknife.Unbinder
import cn.finalteam.galleryfinal.permission.EasyPermissions
import com.doshare.boardroom.R
import com.doshare.boardroom.cameratake.SurfaceViewCallback
import com.doshare.boardroom.cameratake.listener.CameraTakeListener
import com.doshare.boardroom.cameratake.utils.LogUtil
import com.doshare.boardroom.view.activity.IMainActivity
import com.doshare.boardroom.view.enums.PageEnum
import kotlinx.android.synthetic.main.fragment_config_access.*
import java.io.File
class ConfigAccessFragment : BaseFragment(),CameraTakeListener {
var unbinder: Unbinder? = null
/** 权限相关 */
private val GETPERMS = 100
private var perms = arrayOfNulls<String>(3)
var surfaceHolder: SurfaceHolder? = null
lateinit var surfaceViewCallback: SurfaceViewCallback
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_config_access, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
unbinder = activity?.let { ButterKnife.bind(it) }
perms = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
)
surfaceViewCallback = activity?.let { SurfaceViewCallback(activity!!, this) }!!
surfaceHolder = congig_access_SurfaceView.holder
surfaceHolder?.addCallback(surfaceViewCallback)
surfaceHolder?.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
checkPermission()
btn_one.setOnClickListener{
/** 点击拍照获取照片 */
this.takePhoto()
}
super.onViewCreated(view, savedInstanceState)
}
override fun onDestroy() {
super.onDestroy()
unbinder!!.unbind()
this.destroy()
}
/**
* 获取相机当前的照片
*/
fun takePhoto() {
surfaceViewCallback.takePhoto()
}
fun destroy() {
surfaceViewCallback.destroy()
}
override fun onSuccess(bitmapFile: File?, mBitmap: Bitmap?) {
iv_photo.setImageBitmap(mBitmap)
if (bitmapFile != null) {
tv_pic_dir.setText("图片路径:" + bitmapFile.path)
}
}
override fun onFail(error: String?) {
LogUtil.e(error)
}
fun checkPermission() {
//判断是否有相关权限,并申请权限
if (!EasyPermissions.hasPermissions(context, *perms)) {
activity?.let {
ActivityCompat.requestPermissions(it, perms, GETPERMS)
}
}
}
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}