本文实例为大家分享了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;
}

效果图

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持Devmax。

Android录制按钮源码解析的更多相关文章

  1. HTML5 input新增type属性color颜色拾取器的实例代码

    type 属性规定 input 元素的类型。本文较详细的给大家介绍了HTML5 input新增type属性color颜色拾取器的实例代码,感兴趣的朋友跟随脚本之家小编一起看看吧

  2. amazeui模态框弹出后立马消失并刷新页面

    这篇文章主要介绍了amazeui模态框弹出后立马消失并刷新页面,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. 移动HTML5前端框架—MUI的使用

    这篇文章主要介绍了移动HTML5前端框架—MUI的使用的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  6. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  7. AmazeUI 模态窗口的实现代码

    这篇文章主要介绍了AmazeUI 模态窗口的实现代码,代码简单易懂,非常不错,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  8. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  9. ios – UIPopoverController出现在错误的位置

    所以我花了一些时间寻找答案,但到目前为止还没有找到任何答案.我正在尝试从UIInputAccessoryView上的按钮呈现弹出窗口.UIBarButtonItem我想显示popover来自定制视图,所以我可以使用图像.我创建这样的按钮:当需要显示popover时,我这样做:但我得到的是:弹出窗口看起来很好,但它应该出现在第一个按钮上时出现在第二个按钮上.然后我发现了这个问题:UIBarButto

  10. ios – 关闭UIBarButtonItem上的突出显示

    我正在尝试使用UIBarButtonItem在我的UIToolbar上添加标题.我使用简单的风格,看起来很好,但我似乎无法让它停止突出显示触摸.“突出显示时触摸”选项不适用于条形按钮项目.有没有快速简便的方法来做到这一点?

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部