Android录制按钮源码解析
作者:碧云天丶 发布时间:2022-06-24 19:24:44
标签:Android,录制,按钮
本文实例为大家分享了Android实现录制按钮的具体代码,供大家参考,具体内容如下
初始化
布局文件中参数
private void initParame(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RButtonY, defStyleAttr, 0);
//外圆和内部正方形之间的间距
mCircleOutMarginSize = typedArray.getDimensionPixelSize(R.styleable.RButtonY_rby_circle_out_margin, 5);
//外圆画笔的宽度
mCircleWidth = typedArray.getDimensionPixelSize(R.styleable.RButtonY_rby_circle_width, 5);
//外圆画笔的颜色
mCirclePaintColor = typedArray.getColor(R.styleable.RButtonY_rby_circle_paint_color, Color.YELLOW);
//内部正方形画笔的颜色
mRectPaintColor = typedArray.getColor(R.styleable.RButtonY_rby_rect_paint_color, Color.RED);
//内部正方形初始边长相对于外圆内切正方形边长比率 0-1
mRectRateStart = typedArray.getFloat(R.styleable.RButtonY_rby_rect_rate_start, 0.9f);
//内部正方形结束边长相对于外圆内切正方形边长比率 0-1
mRectRateFinish = typedArray.getFloat(R.styleable.RButtonY_rby_rect_rate_fnish, 0.5f);
//录制规定最短时间
mShortest = typedArray.getInteger(R.styleable.RButtonY_rby_short_time, 3);
//录制规定最长时间
mLongest = typedArray.getInteger(R.styleable.RButtonY_rby_long_time, 10);
typedArray.recycle();
}
画笔初始化
// Paint.Style.FILL设置只绘制图形内容
// Paint.Style.STROKE设置只绘制图形的边
// Paint.Style.FILL_AND_STROKE设置都绘制
private void initPaint() {
//外圆画笔
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(mCirclePaintColor);
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mCircleWidth);
//内部正方形画笔
mRectPaint = new Paint();
mRectPaint.setAntiAlias(true);
mRectPaint.setColor(mRectPaintColor);
mRectPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
内部正方形RectF初始化
private void initRect() {
mRectF = new RectF();
}
内部正方形所需动画初始化, 当开始录制或者结束录制时候,内部正方形会有一个动画效果,这个动画效果需要内部正方形边长改变才能实现.
/**
* 初始化动画
* 这里对动画进行监听, 获取正方形边长随动画改变的值,然后重绘
*/
private void initAnimator() {
mAnimator = new ValueAnimator();
/**
* onAnimationStart() - 当动画开始的时候调用.
* onAnimationEnd() - 动画结束时调用.
* onAnimationRepeat() - 动画重复时调用.
* onAnimationCancel() - 动画取消时调用.取消动画也会调用onAnimationEnd,它不会关系动画是怎么结束的。
*/
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//动画结束
isAnimRuning = false;
}
@Override
public void onAnimationStart(Animator animation) {
//动画开始
isAnimRuning = true;
}
});
//动画进度监听,获取正方形随动画变化的边长,然后重绘
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//动态获取正方形边长
mTempRectSize = (float) animation.getAnimatedValue();
invalidate();//重绘
}
});
}
确定圆形半径,圆心坐标,内部正方形边长等
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int width = getWidth();
int height = getHeight();
//圆心坐标
centerX = width / 2;
centerY = height / 2;
//半径
radius = Math.min(centerX, centerY) - mCircleOutMarginSize / 2;
//pow 平方,sqrt 开方
//正方形开始边长,圆形直径的平方除以二再开放,为正方形边长.
mRectStartSize = (int) (Math.sqrt(Math.pow(radius * 2, 2) / 2) * mRectRateStart);
//正方形结束边长
mRectEndSize = (int) (mRectStartSize * mRectRateFinish);
//mTempRectSize == 0 时, 即第一创建该View.
if (mTempRectSize == 0) {
//如果屏幕旋转,onLayout将被回调,此时并不希望mTempRectSize被重新赋值为mRectStartSize(开始状态).
//所以只有当第一次创建时,才需要为mTempRectSize赋值为mRectStartSize(开始状态)
mTempRectSize = mRectStartSize;
}
}
绘制内部正方形和外圆
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//外圆绘制
canvas.drawCircle(centerX, centerY, radius, mCirclePaint);
//正方形四点坐标
int mLeftRectTemp = (int) (centerX - mTempRectSize / 2);
int mRightRectTemp = (int) (centerX + mTempRectSize / 2);
int mTopRectTemp = (int) (centerY + mTempRectSize / 2);
int mButtonRectTemp = (int) (centerY - mTempRectSize / 2);
//绘制正方形
mRectF.set(mLeftRectTemp, mTopRectTemp, mRightRectTemp, mButtonRectTemp);
//(float) Math.sqrt(radius): 圆角半径
canvas.drawRoundRect(mRectF, (float) Math.sqrt(radius), (float) Math.sqrt(radius), mRectPaint);
}
录制开始和结束方法以及动画执行方法
/**
* 录制开始
*/
private void recordStart() {
//正方形开始动画
startAnimation(mRectStartSize, mRectEndSize);
if (rbyCb != null) {
//录制开始的回调
rbyCb.startCb(String.valueOf(mCurrent));
}
//开始计时
mHandler.sendEmptyMessage(0);
//录制标识为开始
up = true;
mTempRectSize = mRectEndSize;
}
/**
* 录制结束
*/
private void recordFinish() {
//正方形结束动画
startAnimation(mRectEndSize, mRectStartSize);
if (rbyCb != null) {
//结束时回调
rbyCb.finishCb(String.valueOf(mCurrent));
}
//录制结束,当前时间归0
mCurrent = 0;
mHandler.removeCallbacksAndMessages(null);
//录制标识为结束
up = false;
mTempRectSize = mRectStartSize;
}
/**
* 开始动画
*
* @param startValue
* @param endValue
*/
private void startAnimation(float startValue, float endValue)
mAnimator.setFloatValues(startValue, endValue);
mAnimator.setDuration(100);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.start();
}
回调接口
public interface RBYCallback {
/**
* 记录结束的回调
*
* @param current
*/
void finishCb(String current);
/**
* 每一秒 都会触发该回调
*
* @param current
*/
void eventCb(String current);
/**
* 开始记录的回调
*/
void startCb(String current);
/**
* 录制时长小于录制最短要求时间之时,用户点击按钮时候,回调该方法
*/
void lessShortTimeRecode(String current);
}
对控件点击事件进行处理
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
//如果正方形动画正在播放,就拒绝按钮点击
if (isAnimRuning) return true;
//up为false代表未开始记录,true 代表开始记录
//未开始记录时,mCurrent是等于0
if (!up && mCurrent == 0) {
recordStart();
}
//已开始记录,并且当前录制时间大于或者等于所设置的最短记录时长,则按钮可以手动结束
if (up && mCurrent >= mShortest) {
recordFinish();
}
//已开始记录,当前录制时间小于所设置的最短记录时长,并且录制时间大于1,则回调方法通知当前还不能手动结束录制
if (up && mCurrent < mShortest && mCurrent >= 1) {
if (rbyCb != null) {
rbyCb.lessShortTimeRecode(String.valueOf(mCurrent));
}
}
break;
}
return true;//消费事件
}
屏幕旋转保存与还原数据
//屏幕旋转时候保存必要的数据
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
if (mCurrent != 0) {
Bundle bundle = new Bundle();
//保存系统其他原有的状态信息
bundle.putParcelable("instance", super.onSaveInstanceState());
//保存当前的一些状态
bundle.putFloat("rect_size", mTempRectSize);//保存方形边长
bundle.putBoolean("up", up);//当前录制状态
bundle.putInt("mCurrent", mCurrent);//当前录制时间
return bundle;
} else {
return super.onSaveInstanceState();
}
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
//判断state的类型是否为bundle,若是则从bundle中取数据
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mTempRectSize = bundle.getFloat("rect_size");
up = bundle.getBoolean("up");
mCurrent = bundle.getInt("mCurrent");
//开始计时
mHandler.sendEmptyMessage(0);
super.onRestoreInstanceState(bundle.getParcelable("instance"));
return;
}
super.onRestoreInstanceState(state);
}
定时mHandler
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
mCurrent++;
if (rbyCb != null) {
rbyCb.eventCb(String.valueOf(mCurrent));
}
if (mCurrent >= mLongest) {//当前记录时间大于或等于最大记录时间,将自动结束记录
recordFinish();
} else {
mHandler.sendEmptyMessageDelayed(0, 1000);
}
}
};
页面销毁处理
//页面销毁,清空消息,防止内存泄漏
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
效果图
来源:https://blog.csdn.net/MoLiao2046/article/details/104688172


猜你喜欢
- 使用Android AudioRecord 录制PCM文件,android SDK保证在所有设备上都支持的采样频率只有44100HZ,所以如
- Android ScrollView 下嵌套 ListView 或 GridView出现问题解决办法ScrollView 下嵌套 ListV
- 本文实例讲述了C#使用Matrix执行缩放的方法。分享给大家供大家参考。具体实现方法如下:using System;using System
- 一、带时区的时间1.获取当前时间对象(带时区)import java.time.ZonedDateTime;public class dem
- 一、Sharding-JDBC简介Sharding-JDBC是Sharding-Sphere的一个产品,它有三个产品,分别是Sharding
- 定义:用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。特点: 1、它支持以不同的方式遍历一个
- 1.这是一个通过Java反射机制解析的工具类2.使用时只需创建对应的对象,并在Excel的第一行填上对应的属性名3.首先要添加相关的jar包
- Map在Java8中新增了两个replace的方法1.replace(k,v)在指定的键已经存在并且有与之相关的映射值时才会将指定的键映射到
- ELK是三款软件的简称,分别是Elasticsearch、Logstash、Kibana组成,在发展的过程中,又有新成员Beats的加入,所
- 这篇文章主要介绍了java的package和import机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习
- 阅读提示 具有mybatis基础,熟练使用mybatis-plus。概述 我们都知道,mybatis-plus是一个mybatis的增强
- 1、确定本地网络是通的:2、确定SpringBootq启动后是不报错的3、查看是不是自己在配置文件中加入了项目路径:如果加入了项目路径的话,
- 之前在项目中会用到在Java在后台把数据填入Word文档的模板来提供前台下载,为了自己能随时查看当时的实现方案及方便他人学习我写了这篇博客,
- 在进行Java打印输出,进行查看字段值的时候,觉得每次写了System.out.println之后,正式发布的时候,还得一个个的删掉,太麻烦
- package com.ppmeet; import java.io.IOException; import and
- 目录一、什么是反射:二、反射的原理:三、反射的优缺点:四、反射的用途:五、反射机制常用的类:六、反射的基本使用:1、获得Class:主要有三
- Java中的阻塞队列1. 什么是阻塞队列?阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空
- 一、@RequestMapping注解的功能从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方
- 在前面的文章中,我们分析了淘宝android客户端的一些界面实现和用户体验,今天这篇文章,主要介绍如何使用自定义控件,实现抢购倒计时的功能。
- 一、Netty简介Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现