diff --git a/README.md b/README.md index 6857610..224d5ee 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,60 @@ cameraHolder = CameraHolder( .setMinFaceSize(0.6f)//人脸最小占图片的百分比 ``` - + # cameraButtom + +此按钮控件用于拍照和录像,支持点击拍照,点击录像和长按录像,长按录制有动画 + +支持设置录制时长,到达录制时长后回调通知结束录制。 + +可以设置按钮仅支持拍照,仅支持录制,或都支持 + +1. 在开启长按录制时,点击录制将不可用 + +2. 开启点击录制时,拍照不可用 + +布局文件示例 + +``` + +``` + +使用示例 + +``` +page.fullCaptureBtn.setCaptureListener(object : DefaultCaptureListener(){ + //拍照 + override fun takePictures() { + cameraXFragment.takePhoto() + } + //开始录制视频 + override fun recordStart() { + page.captureVideoBtn.visibility = View.GONE + LogUtils.dTag("录制activity", "开始") + cameraXFragment.takeVideo() + //录制视频时隐藏摄像头切换 + page.switchBtn.visibility=View.GONE + } + + //录制视频到达预定的时长,可以结束了 + override fun recordShouldEnd(time: Long) { + page.captureVideoBtn.visibility = View.VISIBLE + LogUtils.dTag("录制activity", "停止") + cameraXFragment.stopTakeVideo(time) + page.switchBtn.visibility=View.VISIBLE + } +}) +``` # 示例代码在app目录下。 diff --git a/camerax_lib/src/main/AndroidManifest.xml b/camerax_lib/src/main/AndroidManifest.xml index d300f1e..6e2a752 100644 --- a/camerax_lib/src/main/AndroidManifest.xml +++ b/camerax_lib/src/main/AndroidManifest.xml @@ -11,8 +11,6 @@ - - \ No newline at end of file diff --git a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CameraButton.kt b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CameraButton.kt new file mode 100644 index 0000000..818dd9c --- /dev/null +++ b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CameraButton.kt @@ -0,0 +1,375 @@ +package com.kiylx.camerax_lib.main.buttons + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import android.os.CountDownTimer +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.kiylx.camerax_lib.R + +class CameraButton @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : View(context, attrs, defStyleAttr) { + + private var longPassRecord = false//是否使用长按录制 + private var state = STATE_IDLE //当前按钮触摸事件 + private var buttonEvent = EVENT_IDLE//录制或拍照等事件 + var buttonMode = BUTTON_STATE_ONLY_CAPTURE // 当前按钮支持的模式 + + private val progress_color = -0x11e951ea //进度条颜色 + private val outside_color = -0x11232324 //外圆背景色 + private val inside_color = -0x1 //内圆背景色 + private val inside_record_color = -0xe8bc //内圆录制时背景色 + + + private var event_Y = 0f//Touch_Event_Down时候记录的Y值 + private val mPaint: Paint by lazy { + Paint() + } + private var strokeWidth = 0f//进度条宽度 + private var outside_add_size = 0//长按外圆半径变大的Size + private var inside_reduce_size = 0//长安内圆缩小的Size + + //中心坐标 + private var center_X = 0f + private var center_Y = 0f + + private var button_radius = 0f//按钮半径 + private var button_outside_radius = 0f//外圆半径 + private var button_inside_radius = 0f//内圆半径 + private var button_size = 80 //按钮大小 + private lateinit var rectF: RectF + + private var progress = 0f//录制视频的进度 + private var duration: Long = 60 * 1000 //录制视频最大时间长度,毫秒 + private var recorded_time = 0//记录当前录制的时间 + + private var longPressRunnable + : LongPressRunnable = LongPressRunnable()//长按后处理的逻辑Runnable + private var captureListener + : CaptureListener? = null//按钮回调接口 + private lateinit var timer + : RecordCountDownTimer //计时器 + + init { + initView(context, attrs) + } + + /** + * 根据手机的分辨率从 dp 的单位 转成为 px(像素) + */ + private fun dip2px(context: Context, dpValue: Float): Int { + val scale = context.resources.displayMetrics.density + return (dpValue * scale + 0.5f).toInt() + } + + /** + * 总要支持一下XML 中布局吧 + * + * @param context CONTEXT + * @param attrs ATTRS + */ + fun initView(context: Context, attrs: AttributeSet?) { + val arr = getContext().obtainStyledAttributes(attrs, R.styleable.CameraButton) + button_size = + dip2px(context, arr.getInteger(R.styleable.CameraButton_size, button_size).toFloat()) + val tmp = arr.getInteger(R.styleable.CameraButton_maxDuration, 60) + if (tmp > 1) { + duration = tmp * 1000L + } + + buttonMode = arr.getInteger(R.styleable.CameraButton_buttonMode, BUTTON_STATE_ONLY_CAPTURE) + longPassRecord=arr.getBoolean(R.styleable.CameraButton_longPassRecord,false) + + button_radius = button_size / 2.0f + button_outside_radius = button_radius + button_inside_radius = button_radius * 0.85f + strokeWidth = (button_size / 15).toFloat() + outside_add_size = button_size / 8 + inside_reduce_size = button_size / 8 + mPaint.isAntiAlias = true + + center_X = ((button_size + outside_add_size * 2) / 2).toFloat() + center_Y = ((button_size + outside_add_size * 2) / 2).toFloat() + rectF = RectF( + center_X - (button_radius + outside_add_size - strokeWidth / 2), + center_Y - (button_radius + outside_add_size - strokeWidth / 2), + center_X + (button_radius + outside_add_size - strokeWidth / 2), + center_Y + (button_radius + outside_add_size - strokeWidth / 2) + ) + timer = RecordCountDownTimer(duration, duration / 360) //录制定时器 + arr.recycle() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + setMeasuredDimension(button_size + outside_add_size * 2, button_size + outside_add_size * 2) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + mPaint.style = Paint.Style.FILL + mPaint.color = outside_color //外圆(半透明灰色) + canvas.drawCircle(center_X, center_Y, button_outside_radius, mPaint) + mPaint.color = inside_color //内圆(白色) + canvas.drawCircle(center_X, center_Y, button_inside_radius, mPaint) + + //如果状态为录制状态,则绘制录制进度条 + if (buttonEvent == EVENT_RECORDERING) { + if (longPassRecord) { + mPaint.color = progress_color + mPaint.style = Paint.Style.STROKE + mPaint.strokeWidth = strokeWidth + canvas.drawArc(rectF, -90f, progress, false, mPaint) + } else { + mPaint.setColor(inside_record_color); //内圆(红色) + canvas.drawCircle(center_X, center_Y, button_inside_radius, mPaint); + } + } + + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> { + if (event.pointerCount > 1 || buttonEvent == EVENT_CAPTURE) { + return false + } + event_Y = event.y //记录Y值 + state = STATE_PRESS //修改当前状态为点击按下 + + //判断按钮状态是否为可录制状态 + if ((buttonMode == BUTTON_STATE_ONLY_RECORDER || buttonMode == BUTTON_STATE_BOTH) && longPassRecord) { + postDelayed( + longPressRunnable, + 500 + ) //同时延长500启动长按后处理的逻辑Runnable + } + } + MotionEvent.ACTION_MOVE -> { + if (captureListener != null && buttonEvent == EVENT_RECORDERING + && (buttonMode == BUTTON_STATE_ONLY_RECORDER || buttonMode == BUTTON_STATE_BOTH) + ) { + //记录当前Y值与按下时候Y值的差值,调用缩放回调接口 + captureListener!!.recordZoom(event_Y - event.y) + } + } + MotionEvent.ACTION_UP -> { + //根据当前按钮的状态进行相应的处理 + handlerPressByState() + state = STATE_IDLE + } + } + return true + } + + //当手指松开按钮时候处理的逻辑 + private fun handlerPressByState() { + removeCallbacks(longPressRunnable) //移除长按逻辑的Runnable + if (state == STATE_PRESS) { + if (captureListener != null) { + when (buttonMode) { + BUTTON_STATE_ONLY_RECORDER -> { + if (buttonEvent == EVENT_RECORDERING) { + timer.cancel() //停止计时器 + recordEnd() //录制结束 + } else { + //录视频 + postDelayed(longPressRunnable, 500); //同时延长500启动长按后处理的逻辑Runnable + } + } + BUTTON_STATE_ONLY_CAPTURE ,BUTTON_STATE_BOTH -> { + startCaptureAnimation(button_inside_radius) + } + } + } + } else if (state== STATE_LONG_PRESS){ + if (buttonEvent == EVENT_RECORDERING) { + timer.cancel() //停止计时器 + recordEnd() //录制结束 + } + } + } + + //录制结束 + fun recordEnd() { + if (captureListener != null) { + captureListener!!.recordShouldEnd(recorded_time.toLong()) //回调录制结束 + } + resetRecordAnim() //重制按钮状态 + } + + //重制状态 + private fun resetRecordAnim() { + state = STATE_IDLE + buttonEvent = EVENT_IDLE + progress = 0f //重制进度 + invalidate() + //还原按钮初始状态动画 + startRecordAnimation( + button_outside_radius, + button_radius, + button_inside_radius, + button_radius * 0.75f + ) + } + + //内圆动画 + private fun startCaptureAnimation(inside_start: Float) { + val inside_anim = ValueAnimator.ofFloat(inside_start, inside_start * 0.75f, inside_start) + inside_anim.addUpdateListener { animation: ValueAnimator -> + button_inside_radius = animation.animatedValue as Float + invalidate() + } + inside_anim.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + buttonEvent = EVENT_IDLE + } + + override fun onAnimationStart(animation: Animator) { + super.onAnimationStart(animation) + if (captureListener != null) { + captureListener!!.takePictures() + } + state = STATE_IDLE + buttonEvent = EVENT_CAPTURE + } + }) + inside_anim.duration = 50 + inside_anim.start() + } + + //内外圆动画 + private fun startRecordAnimation( + outside_start: Float, + outside_end: Float, + inside_start: Float, + inside_end: Float + ) { + val outside_anim = ValueAnimator.ofFloat(outside_start, outside_end) + val inside_anim = ValueAnimator.ofFloat(inside_start, inside_end) + //外圆动画监听 + outside_anim.addUpdateListener { animation: ValueAnimator -> + button_outside_radius = animation.animatedValue as Float + invalidate() + } + //内圆动画监听 + inside_anim.addUpdateListener { animation: ValueAnimator -> + button_inside_radius = animation.animatedValue as Float + invalidate() + } + val set = AnimatorSet() + //当动画结束后启动录像Runnable并且回调录像开始接口 + set.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + //设置为录制状态 + if (state == STATE_LONG_PRESS) { + captureListener?.recordStart() + buttonEvent = EVENT_RECORDERING + timer.start() + } else { + // 此处动画包括长按起始动画和还原动画 若不是长按状态应该还原状态为空闲????---CodeReview + state = STATE_IDLE + } + } + }) + set.playTogether(outside_anim, inside_anim) + set.duration = 100 + set.start() + } + + //更新进度条 + private fun updateProgress(millisUntilFinished: Long) { + recorded_time = (duration - millisUntilFinished).toInt() + progress = 360f - millisUntilFinished / duration.toFloat() * 360f + invalidate() + } + + //录制视频计时器 + private inner class RecordCountDownTimer internal constructor( + millisInFuture: Long, + countDownInterval: Long + ) : + CountDownTimer(millisInFuture, countDownInterval) { + override fun onTick(millisUntilFinished: Long) { + updateProgress(millisUntilFinished) + } + + override fun onFinish() { + recordEnd() + } + } + + //长按线程 + private inner class LongPressRunnable : Runnable { + override fun run() { + state = STATE_LONG_PRESS //如果按下后经过500毫秒则会修改当前状态为长按状态 + //启动按钮动画,外圆变大,内圆缩小 + startRecordAnimation( + button_outside_radius, + button_outside_radius + outside_add_size, + button_inside_radius, + button_inside_radius - inside_reduce_size + ) + } + } + + /*************************************************** 对外提供的API**************************************/ + /** + * 设置最长录制时间,,单位:秒 + * 当到达设定的录制时长时,触发回调通知 + */ + fun setDuration(duration: Int) { + if (duration<1) { + return + } + this.duration = duration*1000L + timer = RecordCountDownTimer(this.duration, this.duration / 360) //录制定时器 + } + + //设置回调接口 + fun setCaptureListener(captureListener: CaptureListener?) { + this.captureListener = captureListener + } + + //设置按钮功能(拍照和录像) + fun setButtonFeatures(state: Int) { + buttonMode = state + } + + //是否空闲状态 + val isIdle: Boolean + get() = if (state == STATE_IDLE) true else false + + //设置状态 + fun resetState() { + state = STATE_IDLE + } + + companion object { + // 选择拍照 拍视频 或者都有 + const val BUTTON_STATE_ONLY_CAPTURE = 1 //只能拍照 + const val BUTTON_STATE_ONLY_RECORDER = 2 //只能录像 + const val BUTTON_STATE_BOTH = 3 + const val BUTTON_STATE_BOTH_NOT = 4 + + //点击状态 + const val STATE_IDLE = 0x001 //空闲状态 + const val STATE_PRESS = 0x002 //按下状态 + const val STATE_LONG_PRESS = 0x003 //长按状态 + + //事件 + const val EVENT_IDLE = 0x001 //空闲状态 + const val EVENT_RECORDERING = 0x002 //录制状态 + const val EVENT_CAPTURE = 0x003 //拍照状态 + } +} diff --git a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureButton.java b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureButton.java deleted file mode 100644 index f1d8ce1..0000000 --- a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureButton.java +++ /dev/null @@ -1,379 +0,0 @@ -package com.kiylx.camerax_lib.main.buttons; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.os.CountDownTimer; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - -import com.kiylx.camerax_lib.R; - -/** - * 拍照按钮,拍视频动画 - * 点击拍照,长按录像 - * - */ -public class CaptureButton extends View { - // 选择拍照 拍视频 或者都有 - public static final int BUTTON_STATE_ONLY_CAPTURE = 0x101; //只能拍照 - public static final int BUTTON_STATE_ONLY_RECORDER = 0x102; //只能录像 - public static final int BUTTON_STATE_BOTH = 0x103; - - private int state; //当前按钮状态 - private int button_state = 240; //默认的大小不可用 - - public static final int STATE_IDLE = 0x001; //空闲状态 - public static final int STATE_PRESS = 0x002; //按下状态 - public static final int STATE_LONG_PRESS = 0x003; //长按状态 - public static final int STATE_RECORDERING = 0x004; //录制状态 - public static final int STATE_BAN = 0x005; //禁止状态 - - private int progress_color = 0xEE16AE16; //进度条颜色 - private int outside_color = 0xEEDCDCDC; //外圆背景色 - private int inside_color = 0xFFFFFFFF; //内圆背景色 - - private float event_Y; //Touch_Event_Down时候记录的Y值 - - private Paint mPaint; - - private float strokeWidth; //进度条宽度 - private int outside_add_size; //长按外圆半径变大的Size - private int inside_reduce_size; //长安内圆缩小的Size - - //中心坐标 - private float center_X; - private float center_Y; - - private float button_radius; //按钮半径 - private float button_outside_radius; //外圆半径 - private float button_inside_radius; //内圆半径 - private int button_size = 80; //按钮大小 - - private float progress; //录制视频的进度 - private int duration = 15; //录制视频最大时间长度,秒 - private int recorded_time; //记录当前录制的时间 - - private RectF rectF; - - private LongPressRunnable longPressRunnable; //长按后处理的逻辑Runnable - private CaptureListener captureListener; //按钮回调接口 - private RecordCountDownTimer timer; //计时器 - - public CaptureButton(Context context) { - super(context); - } - - /** - * 根据手机的分辨率从 dp 的单位 转成为 px(像素) - */ - private int dip2px(Context context, float dpValue) { - final float scale = context.getResources().getDisplayMetrics().density; - return (int) (dpValue * scale + 0.5f); - } - - /** - * 总要支持一下XML 中布局吧 - * - * @param context CONTEXT - * @param attrs ATTRS - */ - public CaptureButton(Context context, AttributeSet attrs) { - super(context, attrs); - TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.CaptureButton); - - this.button_size = dip2px(context, arr.getInteger(R.styleable.CaptureButton_size, button_size)); - this.duration = arr.getInteger(R.styleable.CaptureButton_maxDuration, duration)*1000; - - button_radius = button_size / 2.0f; - - button_outside_radius = button_radius; - button_inside_radius = button_radius * 0.85f; - - strokeWidth = button_size / 15; - outside_add_size = button_size / 8; - inside_reduce_size = button_size / 8; - - mPaint = new Paint(); - mPaint.setAntiAlias(true); - - progress = 0; - longPressRunnable = new LongPressRunnable(); - - state = STATE_IDLE; //初始化为空闲状态 - button_state = BUTTON_STATE_BOTH; //初始化按钮为可录制可拍照 - - center_X = (button_size + outside_add_size * 2) / 2; - center_Y = (button_size + outside_add_size * 2) / 2; - - rectF = new RectF( - center_X - (button_radius + outside_add_size - strokeWidth / 2), - center_Y - (button_radius + outside_add_size - strokeWidth / 2), - center_X + (button_radius + outside_add_size - strokeWidth / 2), - center_Y + (button_radius + outside_add_size - strokeWidth / 2)); - - timer = new RecordCountDownTimer(duration, duration / 360); //录制定时器 - - arr.recycle(); - } - - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(button_size + outside_add_size * 2, button_size + outside_add_size * 2); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - mPaint.setStyle(Paint.Style.FILL); - - mPaint.setColor(outside_color); //外圆(半透明灰色) - canvas.drawCircle(center_X, center_Y, button_outside_radius, mPaint); - - mPaint.setColor(inside_color); //内圆(白色) - canvas.drawCircle(center_X, center_Y, button_inside_radius, mPaint); - - //如果状态为录制状态,则绘制录制进度条 - if (state == STATE_RECORDERING) { - mPaint.setColor(progress_color); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setStrokeWidth(strokeWidth); - canvas.drawArc(rectF, -90, progress, false, mPaint); - } - } - - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - if (event.getPointerCount() > 1 || state != STATE_IDLE) - break; - event_Y = event.getY(); //记录Y值 - state = STATE_PRESS; //修改当前状态为点击按下 - - //判断按钮状态是否为可录制状态 - if ((button_state == BUTTON_STATE_ONLY_RECORDER || button_state == BUTTON_STATE_BOTH)) - postDelayed(longPressRunnable, 500); //同时延长500启动长按后处理的逻辑Runnable - break; - case MotionEvent.ACTION_MOVE: - if (captureListener != null - && state == STATE_RECORDERING - && (button_state == BUTTON_STATE_ONLY_RECORDER || button_state == BUTTON_STATE_BOTH)) { - //记录当前Y值与按下时候Y值的差值,调用缩放回调接口 - captureListener.recordZoom(event_Y - event.getY()); - } - break; - case MotionEvent.ACTION_UP: - //根据当前按钮的状态进行相应的处理 ----CodeReview---抬起瞬间应该重置状态 当前状态可能为按下和正在录制 - //state = STATE_BAN; - handlerPressByState(); - break; - } - return true; - } - - //当手指松开按钮时候处理的逻辑 - private void handlerPressByState() { - removeCallbacks(longPressRunnable); //移除长按逻辑的Runnable - //根据当前状态处理 - switch (state) { - //当前是点击按下 - case STATE_PRESS: - if (captureListener != null && (button_state == BUTTON_STATE_ONLY_CAPTURE || button_state == - BUTTON_STATE_BOTH)) { - startCaptureAnimation(button_inside_radius); - } else { - state = STATE_IDLE; - } - break; - // ---CodeReview---当内外圆动画未结束时已经是长按状态 但还没有置为STATE_RECORDERING时 应该也要结束录制 此处是一个bug - case STATE_LONG_PRESS: - //当前是长按状态 - case STATE_RECORDERING: - timer.cancel(); //停止计时器 - recordEnd(); //录制结束 - break; - } - state = STATE_IDLE; - } - - //录制结束 - public void recordEnd() { - if (captureListener != null) { - captureListener.recordEnd(recorded_time); //回调录制结束 - } - resetRecordAnim(); //重制按钮状态 - } - - //重制状态 - private void resetRecordAnim() { - state = STATE_BAN; - progress = 0; //重制进度 - invalidate(); - //还原按钮初始状态动画 - startRecordAnimation( - button_outside_radius, - button_radius, - button_inside_radius, - button_radius * 0.75f - ); - } - - //内圆动画 - private void startCaptureAnimation(float inside_start) { - ValueAnimator inside_anim = ValueAnimator.ofFloat(inside_start, inside_start * 0.75f, inside_start); - inside_anim.addUpdateListener(animation -> { - button_inside_radius = (float) animation.getAnimatedValue(); - invalidate(); - }); - inside_anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - //回调拍照接口 -// if (captureLisenter != null) { -// captureLisenter.takePictures(); -// } - // 为何拍照完成要将状态掷为禁止????此处貌似bug!!!!!!---CodeReview - //state = STATE_BAN; - //state = STATE_IDLE; - } - - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - if (captureListener != null) { - captureListener.takePictures(); - } - // 防止重复点击 状态重置 - state = STATE_BAN; - } - }); - inside_anim.setDuration(50); - inside_anim.start(); - } - - //内外圆动画 - private void startRecordAnimation(float outside_start, float outside_end, float inside_start, float inside_end) { - ValueAnimator outside_anim = ValueAnimator.ofFloat(outside_start, outside_end); - ValueAnimator inside_anim = ValueAnimator.ofFloat(inside_start, inside_end); - //外圆动画监听 - outside_anim.addUpdateListener(animation -> { - button_outside_radius = (float) animation.getAnimatedValue(); - invalidate(); - }); - //内圆动画监听 - inside_anim.addUpdateListener(animation -> { - button_inside_radius = (float) animation.getAnimatedValue(); - invalidate(); - }); - AnimatorSet set = new AnimatorSet(); - //当动画结束后启动录像Runnable并且回调录像开始接口 - set.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - //设置为录制状态 - if (state == STATE_LONG_PRESS) { - if (captureListener != null) - captureListener.recordStart(); - state = STATE_RECORDERING; - timer.start(); - } else { - // 此处动画包括长按起始动画和还原动画 若不是长按状态应该还原状态为空闲????---CodeReview - state = STATE_IDLE; - } - } - }); - set.playTogether(outside_anim, inside_anim); - set.setDuration(100); - set.start(); - } - - - //更新进度条 - private void updateProgress(long millisUntilFinished) { - recorded_time = (int) (duration - millisUntilFinished); - progress = 360f - millisUntilFinished / (float) duration * 360f; - invalidate(); - } - - //录制视频计时器 - private class RecordCountDownTimer extends CountDownTimer { - RecordCountDownTimer(long millisInFuture, long countDownInterval) { - super(millisInFuture, countDownInterval); - } - - @Override - public void onTick(long millisUntilFinished) { - updateProgress(millisUntilFinished); - } - - @Override - public void onFinish() { - //updateProgress(duration); - recordEnd(); - } - } - - //长按线程 - private class LongPressRunnable implements Runnable { - @Override - public void run() { - state = STATE_LONG_PRESS; //如果按下后经过500毫秒则会修改当前状态为长按状态 - //启动按钮动画,外圆变大,内圆缩小 - startRecordAnimation( - button_outside_radius, - button_outside_radius + outside_add_size, - button_inside_radius, - button_inside_radius - inside_reduce_size - ); - } - } - - /************************************************** - * 对外提供的API * - **************************************************/ - - //设置最长录制时间 - public void setDuration(int duration) { - this.duration = duration; - timer = new RecordCountDownTimer(duration, duration / 360); //录制定时器 - } - - //设置回调接口 - public void setCaptureListener(CaptureListener captureListener) { - this.captureListener = captureListener; - } - - //设置按钮功能(拍照和录像) - public void setButtonFeatures(int state) { - this.button_state = state; - } - - // 获取当前按钮支持状态 - public int getButtonState() { - return button_state; - } - - //是否空闲状态 - public boolean isIdle() { - return state == STATE_IDLE ? true : false; - } - - //设置状态 - public void resetState() { - state = STATE_IDLE; - } - -} diff --git a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureButton2.java b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureButton2.java deleted file mode 100644 index a3878b7..0000000 --- a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureButton2.java +++ /dev/null @@ -1,370 +0,0 @@ -package com.kiylx.camerax_lib.main.buttons; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.os.CountDownTimer; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - -import com.kiylx.camerax_lib.R; - -/** - * 拍照按钮,拍视频动画 - * 指定app:captureMode="false" 则点击按钮为录像,否则点击为拍照 - */ -public class CaptureButton2 extends View { - // 选择拍照 拍视频 或者都有 - public static final int BUTTON_STATE_ONLY_CAPTURE = 0x101; //只能拍照 - public static final int BUTTON_STATE_ONLY_RECORDER = 0x102; //只能录像 - - private int state; //当前按钮状态 - private int button_state = 240; //默认的大小不可用 - - public static final int STATE_IDLE = 0x001; //空闲状态 - public static final int STATE_PRESS = 0x002; //按下状态 - public static final int STATE_LONG_PRESS = 0x003; //长按状态 - public static final int STATE_RECORDERING = 0x004; //录制状态 - public static final int STATE_BAN = 0x005; //禁止状态 - - private int progress_color = 0xEE16AE16; //进度条颜色 - private int outside_color = 0xEEDCDCDC; //外圆背景色 - private int inside_color = 0xFFFFFFFF; //内圆背景色 - private int inside_record_color = 0xFFFF1744; //内圆录制时背景色 - - - private float event_Y; //Touch_Event_Down时候记录的Y值 - - private Paint mPaint; - - private float strokeWidth; //进度条宽度 - private int outside_add_size; //长按外圆半径变大的Size - private int inside_reduce_size; //长安内圆缩小的Size - - //中心坐标 - private float center_X; - private float center_Y; - - private float button_radius; //按钮半径 - private float button_outside_radius; //外圆半径 - private float button_inside_radius; //内圆半径 - private int button_size = 80; //按钮大小 - - private int recorded_time; //记录当前录制的时间 - - private RectF rectF; - - private LongPressRunnable longPressRunnable; //长按后处理的逻辑Runnable - private CaptureListener captureListener; //按钮回调接口 - private RecordCountDownTimer timer; //计时器 - private int duration = 60 * 1000; - - public CaptureButton2(Context context) { - super(context); - } - - /** - * 根据手机的分辨率从 dp 的单位 转成为 px(像素) - */ - private int dip2px(Context context, float dpValue) { - final float scale = context.getResources().getDisplayMetrics().density; - return (int) (dpValue * scale + 0.5f); - } - - /** - * 总要支持一下XML 中布局吧 - * - * @param context CONTEXT - * @param attrs ATTRS - */ - public CaptureButton2(Context context, AttributeSet attrs) { - super(context, attrs); - TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.CaptureButton2); - - this.button_size = dip2px(context, arr.getInteger(R.styleable.CaptureButton_size, button_size)); - boolean mode = arr.getBoolean(R.styleable.CaptureButton2_captureMode, true);//拍照还是录像模式 - - button_radius = button_size / 2.0f; - - button_outside_radius = button_radius; - button_inside_radius = button_radius * 0.85f; - - strokeWidth = button_size / 15; - outside_add_size = button_size / 8; - inside_reduce_size = button_size / 8; - - mPaint = new Paint(); - mPaint.setAntiAlias(true); - - longPressRunnable = new LongPressRunnable(); - - state = STATE_IDLE; //初始化为空闲状态 - if (mode) { - button_state = BUTTON_STATE_ONLY_CAPTURE; //初始化按钮为拍照 - } else { - button_state = BUTTON_STATE_ONLY_RECORDER; //初始化按钮为录制 - } - - center_X = (button_size + outside_add_size * 2) / 2; - center_Y = (button_size + outside_add_size * 2) / 2; - - rectF = new RectF( - center_X - (button_radius + outside_add_size - strokeWidth / 2), - center_Y - (button_radius + outside_add_size - strokeWidth / 2), - center_X + (button_radius + outside_add_size - strokeWidth / 2), - center_Y + (button_radius + outside_add_size - strokeWidth / 2)); - - timer = new RecordCountDownTimer(duration, duration / 360); //录制定时器 - - arr.recycle(); - } - - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(button_size + outside_add_size * 2, button_size + outside_add_size * 2); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - mPaint.setStyle(Paint.Style.FILL); - - mPaint.setColor(outside_color); //外圆(半透明灰色) - canvas.drawCircle(center_X, center_Y, button_outside_radius, mPaint); - - mPaint.setColor(inside_color); //内圆(白色) - canvas.drawCircle(center_X, center_Y, button_inside_radius, mPaint); - - //如果状态为录制状态,则绘制录制进度条 - if (state == STATE_RECORDERING) { - mPaint.setColor(inside_record_color); //内圆(红色) - canvas.drawCircle(center_X, center_Y, button_inside_radius, mPaint); - } - } - - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - if (event.getPointerCount() > 1 || state != STATE_IDLE) - break; - event_Y = event.getY(); //记录Y值 - state = STATE_PRESS; //修改当前状态为点击按下 - break; - case MotionEvent.ACTION_MOVE: - if (captureListener != null - && state == STATE_RECORDERING - && (button_state == BUTTON_STATE_ONLY_RECORDER)) { - //记录当前Y值与按下时候Y值的差值,调用缩放回调接口 - captureListener.recordZoom(event_Y - event.getY()); - } - break; - case MotionEvent.ACTION_UP: - //根据当前按钮的状态进行相应的处理 ----CodeReview---抬起瞬间应该重置状态 当前状态可能为按下和正在录制 - //state = STATE_BAN; - handlerPressByState(); - break; - } - return true; - } - - //当手指松开按钮时候处理的逻辑 - private void handlerPressByState() { - removeCallbacks(longPressRunnable); //移除长按逻辑的Runnable - //根据当前状态处理 - switch (state) { - //当前是点击按下 - case STATE_PRESS: - if (captureListener != null && (button_state == BUTTON_STATE_ONLY_CAPTURE)) { - //拍照 - startCaptureAnimation(button_inside_radius); - } else if (captureListener != null && (button_state == BUTTON_STATE_ONLY_RECORDER)) { - //录视频 - postDelayed(longPressRunnable, 500); //同时延长500启动长按后处理的逻辑Runnable - } else { - state = STATE_IDLE; - } - break; - // ---CodeReview---当内外圆动画未结束时已经是长按状态 但还没有置为STATE_RECORDERING时 应该也要结束录制 此处是一个bug - case STATE_LONG_PRESS: - //当前是长按状态 - case STATE_RECORDERING: - timer.cancel(); //停止计时器 - recordEnd(); //录制结束 - break; - } - state = STATE_IDLE; - } - - //长按线程 - private class LongPressRunnable implements Runnable { - @Override - public void run() { - state = STATE_LONG_PRESS; //如果按下后经过500毫秒则会修改当前状态为长按状态 - //启动按钮动画,外圆变大,内圆缩小 - startRecordAnimation( - button_outside_radius, - button_outside_radius + outside_add_size, - button_inside_radius, - button_inside_radius - inside_reduce_size - ); - } - } - - //录制结束 - public void recordEnd() { - if (captureListener != null) { - captureListener.recordEnd(recorded_time); //回调录制结束 - } - resetRecordAnim(); //重制按钮状态 - } - - //重制状态 - private void resetRecordAnim() { - state = STATE_BAN; - invalidate(); - //还原按钮初始状态动画 - startRecordAnimation( - button_outside_radius, - button_radius, - button_inside_radius, - button_radius * 0.75f - ); - } - - //内圆动画 - private void startCaptureAnimation(float inside_start) { - ValueAnimator inside_anim = ValueAnimator.ofFloat(inside_start, inside_start * 0.75f, inside_start); - inside_anim.addUpdateListener(animation -> { - button_inside_radius = (float) animation.getAnimatedValue(); - invalidate(); - }); - inside_anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - //回调拍照接口 -// if (captureLisenter != null) { -// captureLisenter.takePictures(); -// } - // 为何拍照完成要将状态掷为禁止????此处貌似bug!!!!!!---CodeReview - //state = STATE_BAN; - //state = STATE_IDLE; - } - - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - if (captureListener != null) { - captureListener.takePictures(); - } - // 防止重复点击 状态重置 - state = STATE_BAN; - } - }); - inside_anim.setDuration(50); - inside_anim.start(); - } - - //内外圆动画 - private void startRecordAnimation(float outside_start, float outside_end, float inside_start, float inside_end) { - ValueAnimator outside_anim = ValueAnimator.ofFloat(outside_start, outside_end); - ValueAnimator inside_anim = ValueAnimator.ofFloat(inside_start, inside_end); - //外圆动画监听 - outside_anim.addUpdateListener(animation -> { - button_outside_radius = (float) animation.getAnimatedValue(); - invalidate(); - }); - //内圆动画监听 - inside_anim.addUpdateListener(animation -> { - button_inside_radius = (float) animation.getAnimatedValue(); - invalidate(); - }); - AnimatorSet set = new AnimatorSet(); - //当动画结束后启动录像Runnable并且回调录像开始接口 - set.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - //设置为录制状态 - if (state == STATE_LONG_PRESS) { - if (captureListener != null) - captureListener.recordStart(); - state = STATE_RECORDERING; - timer.start(); - } else { - // 此处动画包括长按起始动画和还原动画 若不是长按状态应该还原状态为空闲????---CodeReview - state = STATE_IDLE; - } - } - }); - set.playTogether(outside_anim, inside_anim); - set.setDuration(100); - set.start(); - } - - - //录制视频计时器 - private class RecordCountDownTimer extends CountDownTimer { - RecordCountDownTimer(long millisInFuture, long countDownInterval) { - super(millisInFuture, countDownInterval); - } - - @Override - public void onTick(long millisUntilFinished) { - } - - @Override - public void onFinish() { - //updateProgress(duration); - recordEnd(); - } - } - - - /************************************************** - * 对外提供的API * - **************************************************/ - - //设置最长录制时间 - public void setDuration(int duration) { - this.duration = duration; - timer = new RecordCountDownTimer(duration, duration / 360); //录制定时器 - } - - //设置回调接口 - public void setCaptureListener(CaptureListener captureListener) { - this.captureListener = captureListener; - } - - //设置按钮功能(拍照和录像) - public void setButtonFeatures(int state) { - this.button_state = state; - } - - // 获取当前按钮支持状态 - public int getButtonState() { - return button_state; - } - - //是否空闲状态 - public boolean isIdle() { - return state == STATE_IDLE ? true : false; - } - - //设置状态 - public void resetState() { - state = STATE_IDLE; - } - -} diff --git a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureListener.kt b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureListener.kt index d3c5706..0085cd8 100644 --- a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureListener.kt +++ b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/buttons/CaptureListener.kt @@ -7,9 +7,15 @@ interface CaptureListener { fun takePictures() + /** + * 回调此方法以通知可以开始录制 + */ fun recordStart() - fun recordEnd(time: Long) + /** + * 到达设定的录制时长时,将回调此方法通知可以结束录制了 + */ + fun recordShouldEnd(time: Long) fun recordZoom(zoom: Float) @@ -29,7 +35,7 @@ open class DefaultCaptureListener :CaptureListener{ } - override fun recordEnd(time: Long) { + override fun recordShouldEnd(time: Long) { } diff --git a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/manager/CameraXManager.kt b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/manager/CameraXManager.kt index adaebb1..0c7381a 100644 --- a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/manager/CameraXManager.kt +++ b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/manager/CameraXManager.kt @@ -31,7 +31,7 @@ import com.kiylx.camerax_lib.main.manager.model.* import com.kiylx.camerax_lib.main.manager.video.VideoRecorderHolder import com.kiylx.camerax_lib.main.store.StorageConfig import com.kiylx.camerax_lib.view.CameraXPreviewViewTouchListener -import com.yeyupiaoling.cameraxapp.view.FocusImageView +import com.kiylx.camerax_lib.view.FocusImageView import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -511,7 +511,7 @@ abstract class CameraXManager( /** 更新用例的方向 */ @CallSuper open fun updateCaseRotation(rotation: Int) { - Log.e("旋转1", "$rotation") +// Log.e("旋转1", "$rotation") //横屏时,动态设置他们的方向 imageAnalyzer.targetRotation = rotation imageCapture.targetRotation = rotation diff --git a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/ui/BaseCameraXActivity.kt b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/ui/BaseCameraXActivity.kt index 5245ef0..b60d8d3 100644 --- a/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/ui/BaseCameraXActivity.kt +++ b/camerax_lib/src/main/java/com/kiylx/camerax_lib/main/ui/BaseCameraXActivity.kt @@ -14,6 +14,7 @@ import com.blankj.utilcode.util.LogUtils import com.kiylx.camerax_lib.R import com.kiylx.camerax_lib.databinding.ActivityCameraExampleBinding import com.kiylx.camerax_lib.main.buttons.CaptureListener +import com.kiylx.camerax_lib.main.buttons.DefaultCaptureListener import com.kiylx.camerax_lib.main.manager.CameraHolder import com.kiylx.camerax_lib.main.manager.KEY_CAMERA_EVENT_ACTION import com.kiylx.camerax_lib.main.manager.KEY_CAMERA_EVENT_EXTRA @@ -175,48 +176,40 @@ abstract class BaseCameraXActivity : BasicActivity(), //相机初始化完成 open fun initCameraFinished() { //拍照,拍视频的UI 操作的各种状态处理 - page.captureBtn2.setCaptureListener(object : CaptureListener { + page.fullCaptureBtn.setCaptureListener(object : DefaultCaptureListener(){ override fun takePictures() { cameraXFragment.takePhoto() } - //开始录制视频 override fun recordStart() { - - } - - //录制视频结束 - override fun recordEnd(time: Long) { - - } - - //长按拍视频的时候,在屏幕滑动可以调整焦距缩放 - override fun recordZoom(zoom: Float) { - val a = zoom + page.captureVideoBtn.visibility = View.GONE + LogUtils.dTag("录制activity", "开始") + cameraXFragment.takeVideo() + //录制视频时隐藏摄像头切换 + page.switchBtn.visibility=View.GONE } - //录制视频错误(拍照也会有错误,先不处理了吧) - override fun recordError(message: String) { - + //录制视频到达预定的时长,可以结束了 + override fun recordShouldEnd(time: Long) { + page.captureVideoBtn.visibility = View.VISIBLE + LogUtils.dTag("录制activity", "停止") + cameraXFragment.stopTakeVideo(time) + page.switchBtn.visibility=View.VISIBLE } }) - page.captureVideoBtn.setCaptureListener(object : CaptureListener { - override fun takePictures() { - - } - + page.captureVideoBtn.setCaptureListener(object : DefaultCaptureListener(){ //开始录制视频 override fun recordStart() { - page.captureBtn2.visibility = View.GONE + page.fullCaptureBtn.visibility = View.GONE LogUtils.dTag("录制activity", "开始") cameraXFragment.takeVideo() //录制视频时隐藏摄像头切换 page.switchBtn.visibility=View.GONE } - //录制视频结束 - override fun recordEnd(time: Long) { - page.captureBtn2.visibility = View.VISIBLE + //录制视频到达预定的时长,可以结束了 + override fun recordShouldEnd(time: Long) { + page.fullCaptureBtn.visibility = View.VISIBLE LogUtils.dTag("录制activity", "停止") cameraXFragment.stopTakeVideo(time) page.switchBtn.visibility=View.VISIBLE diff --git a/camerax_lib/src/main/java/com/kiylx/camerax_lib/view/FocusImageView.kt b/camerax_lib/src/main/java/com/kiylx/camerax_lib/view/FocusImageView.kt index 3acf837..eb2b9d5 100644 --- a/camerax_lib/src/main/java/com/kiylx/camerax_lib/view/FocusImageView.kt +++ b/camerax_lib/src/main/java/com/kiylx/camerax_lib/view/FocusImageView.kt @@ -1,4 +1,4 @@ -package com.yeyupiaoling.cameraxapp.view +package com.kiylx.camerax_lib.view import android.content.Context import android.graphics.Point diff --git a/camerax_lib/src/main/res/layout/activity_camera_example.xml b/camerax_lib/src/main/res/layout/activity_camera_example.xml index 1bda476..4f92b53 100644 --- a/camerax_lib/src/main/res/layout/activity_camera_example.xml +++ b/camerax_lib/src/main/res/layout/activity_camera_example.xml @@ -152,21 +152,24 @@ app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - - - - + + - + - - - - - - + + + + + + + diff --git a/camerax_lib/src/main/res/values/ids.xml b/camerax_lib/src/main/res/values/ids.xml index e8eaf96..36809af 100644 --- a/camerax_lib/src/main/res/values/ids.xml +++ b/camerax_lib/src/main/res/values/ids.xml @@ -3,6 +3,6 @@ - + \ No newline at end of file