Android錄制按鈕源碼解析
本文實(shí)例為大家分享了Android實(shí)現(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ī)定最短時(shí)間 mShortest = typedArray.getInteger(R.styleable.RButtonY_rby_short_time, 3); //錄制規(guī)定最長時(shí)間 mLongest = typedArray.getInteger(R.styleable.RButtonY_rby_long_time, 10); typedArray.recycle(); }
畫筆初始化
// Paint.Style.FILL設(shè)置只繪制圖形內(nèi)容 // Paint.Style.STROKE設(shè)置只繪制圖形的邊 // Paint.Style.FILL_AND_STROKE設(shè)置都繪制 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)部正方形所需動(dòng)畫初始化, 當(dāng)開始錄制或者結(jié)束錄制時(shí)候,內(nèi)部正方形會(huì)有一個(gè)動(dòng)畫效果,這個(gè)動(dòng)畫效果需要內(nèi)部正方形邊長改變才能實(shí)現(xiàn).
/** * 初始化動(dòng)畫 * 這里對動(dòng)畫進(jìn)行監(jiān)聽, 獲取正方形邊長隨動(dòng)畫改變的值,然后重繪 */ private void initAnimator() { mAnimator = new ValueAnimator(); /** * onAnimationStart() - 當(dāng)動(dòng)畫開始的時(shí)候調(diào)用. * onAnimationEnd() - 動(dòng)畫結(jié)束時(shí)調(diào)用. * onAnimationRepeat() - 動(dòng)畫重復(fù)時(shí)調(diào)用. * onAnimationCancel() - 動(dòng)畫取消時(shí)調(diào)用.取消動(dòng)畫也會(huì)調(diào)用onAnimationEnd,它不會(huì)關(guān)系動(dòng)畫是怎么結(jié)束的。 */ mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //動(dòng)畫結(jié)束 isAnimRuning = false; } @Override public void onAnimationStart(Animator animation) { //動(dòng)畫開始 isAnimRuning = true; } }); //動(dòng)畫進(jìn)度監(jiān)聽,獲取正方形隨動(dòng)畫變化的邊長,然后重繪 mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //動(dòng)態(tài)獲取正方形邊長 mTempRectSize = (float) animation.getAnimatedValue(); invalidate();//重繪 } }); }
確定圓形半徑,圓心坐標(biāo),內(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(); //圓心坐標(biāo) 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 時(shí), 即第一創(chuàng)建該View. if (mTempRectSize == 0) { //如果屏幕旋轉(zhuǎn),onLayout將被回調(diào),此時(shí)并不希望mTempRectSize被重新賦值為mRectStartSize(開始狀態(tài)). //所以只有當(dāng)?shù)谝淮蝿?chuàng)建時(shí),才需要為mTempRectSize賦值為mRectStartSize(開始狀態(tài)) mTempRectSize = mRectStartSize; } }
繪制內(nèi)部正方形和外圓
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //外圓繪制 canvas.drawCircle(centerX, centerY, radius, mCirclePaint); //正方形四點(diǎn)坐標(biāo) 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é)束方法以及動(dòng)畫執(zhí)行方法
/** * 錄制開始 */ private void recordStart() { //正方形開始動(dòng)畫 startAnimation(mRectStartSize, mRectEndSize); if (rbyCb != null) { //錄制開始的回調(diào) rbyCb.startCb(String.valueOf(mCurrent)); } //開始計(jì)時(shí) mHandler.sendEmptyMessage(0); //錄制標(biāo)識為開始 up = true; mTempRectSize = mRectEndSize; } /** * 錄制結(jié)束 */ private void recordFinish() { //正方形結(jié)束動(dòng)畫 startAnimation(mRectEndSize, mRectStartSize); if (rbyCb != null) { //結(jié)束時(shí)回調(diào) rbyCb.finishCb(String.valueOf(mCurrent)); } //錄制結(jié)束,當(dāng)前時(shí)間歸0 mCurrent = 0; mHandler.removeCallbacksAndMessages(null); //錄制標(biāo)識為結(jié)束 up = false; mTempRectSize = mRectStartSize; } /** * 開始動(dòng)畫 * * @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); /** * 每一秒 都會(huì)觸發(fā)該回調(diào) * * @param current */ void eventCb(String current); /** * 開始記錄的回調(diào) */ void startCb(String current); /** * 錄制時(shí)長小于錄制最短要求時(shí)間之時(shí),用戶點(diǎn)擊按鈕時(shí)候,回調(diào)該方法 */ void lessShortTimeRecode(String current); }
對控件點(diǎn)擊事件進(jìn)行處理
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_UP: //如果正方形動(dòng)畫正在播放,就拒絕按鈕點(diǎn)擊 if (isAnimRuning) return true; //up為false代表未開始記錄,true 代表開始記錄 //未開始記錄時(shí),mCurrent是等于0 if (!up && mCurrent == 0) { recordStart(); } //已開始記錄,并且當(dāng)前錄制時(shí)間大于或者等于所設(shè)置的最短記錄時(shí)長,則按鈕可以手動(dòng)結(jié)束 if (up && mCurrent >= mShortest) { recordFinish(); } //已開始記錄,當(dāng)前錄制時(shí)間小于所設(shè)置的最短記錄時(shí)長,并且錄制時(shí)間大于1,則回調(diào)方法通知當(dāng)前還不能手動(dòng)結(jié)束錄制 if (up && mCurrent < mShortest && mCurrent >= 1) { if (rbyCb != null) { rbyCb.lessShortTimeRecode(String.valueOf(mCurrent)); } } break; } return true;//消費(fèi)事件 }
屏幕旋轉(zhuǎn)保存與還原數(shù)據(jù)
//屏幕旋轉(zhuǎn)時(shí)候保存必要的數(shù)據(jù) @Nullable @Override protected Parcelable onSaveInstanceState() { if (mCurrent != 0) { Bundle bundle = new Bundle(); //保存系統(tǒng)其他原有的狀態(tài)信息 bundle.putParcelable("instance", super.onSaveInstanceState()); //保存當(dāng)前的一些狀態(tài) bundle.putFloat("rect_size", mTempRectSize);//保存方形邊長 bundle.putBoolean("up", up);//當(dāng)前錄制狀態(tài) bundle.putInt("mCurrent", mCurrent);//當(dāng)前錄制時(shí)間 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"); //開始計(jì)時(shí) mHandler.sendEmptyMessage(0); super.onRestoreInstanceState(bundle.getParcelable("instance")); return; } super.onRestoreInstanceState(state); }
定時(shí)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) {//當(dāng)前記錄時(shí)間大于或等于最大記錄時(shí)間,將自動(dòng)結(jié)束記錄 recordFinish(); } else { mHandler.sendEmptyMessageDelayed(0, 1000); } } };
頁面銷毀處理
//頁面銷毀,清空消息,防止內(nèi)存泄漏 @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mHandler.removeCallbacksAndMessages(null); mHandler = null; }
效果圖
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android頂部(toolbar)搜索框?qū)崿F(xiàn)代碼
這篇文章主要介紹了android頂部(toolbar)搜索框?qū)崿F(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01AndroidStudio重新share代碼和上傳到svn新地址教程
這篇文章主要介紹了AndroidStudio重新share代碼和上傳到svn新地址教程,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04正確在Flutter中添加webview實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了正確在Flutter中添加webview實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android 日歷控件庫,可左右滑動(dòng),顯示公歷,農(nóng)歷,節(jié)假日等功能
這篇文章主要介紹了Android 日歷控件庫,可左右滑動(dòng),顯示公歷,農(nóng)歷,節(jié)假日等功能的相關(guān)資料,需要的朋友可以參考下2016-09-09Android性能優(yōu)化之利用強(qiáng)大的LeakCanary檢測內(nèi)存泄漏及解決辦法
本篇文章主要介紹了Android性能優(yōu)化之利用LeakCanary檢測內(nèi)存泄漏及解決辦法,有興趣的同學(xué)可以了解一下。2016-11-11android: targetSdkVersion升級中Only fullscreen activities can r
這篇文章主要給大家介紹了關(guān)于Android target SDK和build tool版本升級中遇到Only fullscreen activities can request orientation問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-09-09Android studio 2020中的Android SDK 下載教程
這篇文章主要介紹了Android studio 2020中的Android SDK 下載教程,本文圖文并茂給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03