Android 录制按钮源码解析

18 篇文章 1 订阅
15 篇文章 0 订阅

项目地址

  • 初始化

    • 布局文件中参数
      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;
    }
    
  • 效果图
    在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值