Android錄制按鈕源碼解析
本文實例為大家分享了Android實現(xiàn)錄制按鈕的具體代碼,供大家參考,具體內(nèi)容如下
初始化
布局文件中參數(shù)
private void initParame(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RButtonY, defStyleAttr, 0);
//外圓和內(nèi)部正方形之間的間距
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);
//內(nèi)部正方形畫筆的顏色
mRectPaintColor = typedArray.getColor(R.styleable.RButtonY_rby_rect_paint_color, Color.RED);
//內(nèi)部正方形初始邊長相對于外圓內(nèi)切正方形邊長比率 0-1
mRectRateStart = typedArray.getFloat(R.styleable.RButtonY_rby_rect_rate_start, 0.9f);
//內(nèi)部正方形結(jié)束邊長相對于外圓內(nèi)切正方形邊長比率 0-1
mRectRateFinish = typedArray.getFloat(R.styleable.RButtonY_rby_rect_rate_fnish, 0.5f);
//錄制規(guī)定最短時間
mShortest = typedArray.getInteger(R.styleable.RButtonY_rby_short_time, 3);
//錄制規(guī)定最長時間
mLongest = typedArray.getInteger(R.styleable.RButtonY_rby_long_time, 10);
typedArray.recycle();
}
畫筆初始化
// Paint.Style.FILL設置只繪制圖形內(nèi)容
// 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);
//內(nèi)部正方形畫筆
mRectPaint = new Paint();
mRectPaint.setAntiAlias(true);
mRectPaint.setColor(mRectPaintColor);
mRectPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
內(nèi)部正方形RectF初始化
private void initRect() {
mRectF = new RectF();
}
內(nèi)部正方形所需動畫初始化, 當開始錄制或者結(jié)束錄制時候,內(nèi)部正方形會有一個動畫效果,這個動畫效果需要內(nèi)部正方形邊長改變才能實現(xiàn).
/**
* 初始化動畫
* 這里對動畫進行監(jiān)聽, 獲取正方形邊長隨動畫改變的值,然后重繪
*/
private void initAnimator() {
mAnimator = new ValueAnimator();
/**
* onAnimationStart() - 當動畫開始的時候調(diào)用.
* onAnimationEnd() - 動畫結(jié)束時調(diào)用.
* onAnimationRepeat() - 動畫重復時調(diào)用.
* onAnimationCancel() - 動畫取消時調(diào)用.取消動畫也會調(diào)用onAnimationEnd,它不會關(guān)系動畫是怎么結(jié)束的。
*/
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//動畫結(jié)束
isAnimRuning = false;
}
@Override
public void onAnimationStart(Animator animation) {
//動畫開始
isAnimRuning = true;
}
});
//動畫進度監(jiān)聽,獲取正方形隨動畫變化的邊長,然后重繪
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//動態(tài)獲取正方形邊長
mTempRectSize = (float) animation.getAnimatedValue();
invalidate();//重繪
}
});
}
確定圓形半徑,圓心坐標,內(nèi)部正方形邊長等
@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);
//正方形結(jié)束邊長
mRectEndSize = (int) (mRectStartSize * mRectRateFinish);
//mTempRectSize == 0 時, 即第一創(chuàng)建該View.
if (mTempRectSize == 0) {
//如果屏幕旋轉(zhuǎn),onLayout將被回調(diào),此時并不希望mTempRectSize被重新賦值為mRectStartSize(開始狀態(tài)).
//所以只有當?shù)谝淮蝿?chuàng)建時,才需要為mTempRectSize賦值為mRectStartSize(開始狀態(tài))
mTempRectSize = mRectStartSize;
}
}
繪制內(nèi)部正方形和外圓
@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);
}
錄制開始和結(jié)束方法以及動畫執(zhí)行方法
/**
* 錄制開始
*/
private void recordStart() {
//正方形開始動畫
startAnimation(mRectStartSize, mRectEndSize);
if (rbyCb != null) {
//錄制開始的回調(diào)
rbyCb.startCb(String.valueOf(mCurrent));
}
//開始計時
mHandler.sendEmptyMessage(0);
//錄制標識為開始
up = true;
mTempRectSize = mRectEndSize;
}
/**
* 錄制結(jié)束
*/
private void recordFinish() {
//正方形結(jié)束動畫
startAnimation(mRectEndSize, mRectStartSize);
if (rbyCb != null) {
//結(jié)束時回調(diào)
rbyCb.finishCb(String.valueOf(mCurrent));
}
//錄制結(jié)束,當前時間歸0
mCurrent = 0;
mHandler.removeCallbacksAndMessages(null);
//錄制標識為結(jié)束
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();
}
回調(diào)接口
public interface RBYCallback {
/**
* 記錄結(jié)束的回調(diào)
*
* @param current
*/
void finishCb(String current);
/**
* 每一秒 都會觸發(fā)該回調(diào)
*
* @param current
*/
void eventCb(String current);
/**
* 開始記錄的回調(diào)
*/
void startCb(String current);
/**
* 錄制時長小于錄制最短要求時間之時,用戶點擊按鈕時候,回調(diào)該方法
*/
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();
}
//已開始記錄,并且當前錄制時間大于或者等于所設置的最短記錄時長,則按鈕可以手動結(jié)束
if (up && mCurrent >= mShortest) {
recordFinish();
}
//已開始記錄,當前錄制時間小于所設置的最短記錄時長,并且錄制時間大于1,則回調(diào)方法通知當前還不能手動結(jié)束錄制
if (up && mCurrent < mShortest && mCurrent >= 1) {
if (rbyCb != null) {
rbyCb.lessShortTimeRecode(String.valueOf(mCurrent));
}
}
break;
}
return true;//消費事件
}
屏幕旋轉(zhuǎn)保存與還原數(shù)據(jù)
//屏幕旋轉(zhuǎn)時候保存必要的數(shù)據(jù)
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
if (mCurrent != 0) {
Bundle bundle = new Bundle();
//保存系統(tǒng)其他原有的狀態(tài)信息
bundle.putParcelable("instance", super.onSaveInstanceState());
//保存當前的一些狀態(tài)
bundle.putFloat("rect_size", mTempRectSize);//保存方形邊長
bundle.putBoolean("up", up);//當前錄制狀態(tài)
bundle.putInt("mCurrent", mCurrent);//當前錄制時間
return bundle;
} else {
return super.onSaveInstanceState();
}
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
//判斷state的類型是否為bundle,若是則從bundle中取數(shù)據(jù)
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) {//當前記錄時間大于或等于最大記錄時間,將自動結(jié)束記錄
recordFinish();
} else {
mHandler.sendEmptyMessageDelayed(0, 1000);
}
}
};
頁面銷毀處理
//頁面銷毀,清空消息,防止內(nèi)存泄漏
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
效果圖

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android性能優(yōu)化getResources()與Binder導致界面卡頓優(yōu)化
這篇文章主要為大家介紹了Android性能優(yōu)化getResources()與Binder導致界面卡頓優(yōu)化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02
詳解Android Handler機制和Looper Handler Message關(guān)系
Handler是Android線程之間的消息機制,主要的作用是將一個任務切換到指定的線程中去執(zhí)行,準確的說是切換到構(gòu)成Handler的looper所在的線程中去出處理。本文將詳細介紹Android Handler機制和Looper Handler Message關(guān)系。2021-06-06
使用Chrome瀏覽器調(diào)試Android App詳解
這篇文章主要介紹了使用Chrome瀏覽器調(diào)試Android App詳解,本網(wǎng)講解了使用Facebook開源Stetho實現(xiàn)在Chrome中調(diào)試Android App中,需要的朋友可以參考下2015-05-05
Android MediaPlayer音頻播放器封裝示例淺析
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內(nèi)置的媒體播放器的服務,如播放音頻,視頻等為了使用MediaPlayer,我們要調(diào)用這個類的靜態(tài)create()方法2023-04-04
Android?Studio中使用SQLite數(shù)據(jù)庫實現(xiàn)登錄和注冊功能
SQLite是一款輕型的數(shù)據(jù)庫,是遵守ACID的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),它包含在一個相對小的C庫中,下面這篇文章主要給大家介紹了關(guān)于Android?Studio中使用SQLite數(shù)據(jù)庫實現(xiàn)登錄和注冊功能的相關(guān)資料,需要的朋友可以參考下2024-06-06

