Android仿微信录音功能,自定义控件的设计技巧

讲解一个比较通用的录音控件实现方法与设计技巧

最近由于需要做一个录音功能(/嘘 悄悄透露一下,千万别告诉红薯,就是新版本的OSC客户端噢),起初打算采用仿微信的录音方式,最后又改成了QQ的录音方式,之前的微信录音控件也就白写了[大哭]。之前有很多朋友在问我自定义控件应该怎么学习,遂正好拿出来讲讲喽,没来得及截效果图,大家就自己脑补一下微信发语音时的样子吧。

所谓自定义控件其实就是由于系统SDK无法完成需要的功能时,通过自己扩展系统组件达到完成所需功能做出的控件。

Android自定义控件有两种实现方式,一种是通过继承View类,其中的全部界面通过画布和画笔自己创建,这种控件一般多用于游戏开发中;另一种则是通过继承已有控件,或采用包含关系包含一个系统控件达到目的,这也是接下来本文所要讲到的方法。

先看代码(篇幅有限,仅保留重要方法)

/**
 * 录音专用Button,可弹出自定义的录音dialog。需要配合{@link #RecordButtonUtil}使用
 * @author kymjs(kymjs123@gmail.com)
 */
public class RecordButton extends Button {
    private static final int MIN_INTERVAL_TIME = 700; // 录音最短时间
    private static final int MAX_INTERVAL_TIME = 60000; // 录音最长时间
    private RecordButtonUtil mAudioUtil;
    private Handler mVolumeHandler; // 用于更新录音音量大小的图片
 
    public RecordButton(Context context) {
        super(context);
        mVolumeHandler = new ShowVolumeHandler(this);
        mAudioUtil = new RecordButtonUtil();
        initSavePath();
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mAudioFile == null) {
            return false;
        }
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            initlization();
            break;
        case MotionEvent.ACTION_UP:
            if (event.getY() < -50) {
                cancelRecord();
            } else {
                finishRecord();
            }
            break;
        case MotionEvent.ACTION_MOVE:
          //做一些UI提示
            break;
        }
        return true;
    }
 
    /** 初始化 dialog和录音器 */
    private void initlization() {
        mStartTime = System.currentTimeMillis();
        if (mRecordDialog == null) {
            mRecordDialog = new Dialog(getContext());
            mRecordDialog.setOnDismissListener(onDismiss);
        }
        mRecordDialog.show();
        startRecording();
    }
 
    /** 录音完成(达到最长时间或用户决定录音完成) */
    private void finishRecord() {
        stopRecording();
        mRecordDialog.dismiss();
        long intervalTime = System.currentTimeMillis() - mStartTime;
        if (intervalTime < MIN_INTERVAL_TIME) {
            AppContext.showToastShort(R.string.record_sound_short);
            File file = new File(mAudioFile);
            file.delete();
            return;
        }
        if (mFinishedListerer != null) {
            mFinishedListerer.onFinishedRecord(mAudioFile,
                    (int) ((System.currentTimeMillis() - mStartTime) / 1000));
        }
    }
    // 用户手动取消录音
    private void cancelRecord() {
        stopRecording();
        mRecordDialog.dismiss();
        File file = new File(mAudioFile);
        file.delete();
        if (mFinishedListerer != null) {
            mFinishedListerer.onCancleRecord();
        }
    }
 
    // 开始录音
    private void startRecording() {
        mAudioUtil.setAudioPath(mAudioFile);
        mAudioUtil.recordAudio();
        mThread = new ObtainDecibelThread();
        mThread.start();
 
    }
    // 停止录音
    private void stopRecording() {
        if (mThread != null) {
            mThread.exit();
            mThread = null;
        }
        if (mAudioUtil != null) {
            mAudioUtil.stopRecord();
        }
    }
 
    /******************************* inner class ****************************************/
    private class ObtainDecibelThread extends Thread {
        private volatile boolean running = true;
 
        public void exit() {
            running = false;
        }
        @Override
        public void run() {
            while (running) {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (System.currentTimeMillis() - mStartTime >= MAX_INTERVAL_TIME) {
                    // 如果超过最长录音时间
                    mVolumeHandler.sendEmptyMessage(-1);
                }
                if (mAudioUtil != null && running) {
                    // 如果用户仍在录音
                    int volumn = mAudioUtil.getVolumn();
                    if (volumn != 0)
                        mVolumeHandler.sendEmptyMessage(volumn);
                } else {
                    exit();
                }
            }
        }
    }
    private final OnDismissListener onDismiss = new OnDismissListener() {
        @Override
        public void onDismiss(DialogInterface dialog) {
            stopRecording();
        }
    };
    static class ShowVolumeHandler extends Handler {
        private final WeakReference<RecordButton> mOuterInstance;
        public ShowVolumeHandler(RecordButton outer) {
            mOuterInstance = new WeakReference<RecordButton>(outer);
        }
        @Override
        public void handleMessage(Message msg) {
            RecordButton outerButton = mOuterInstance.get();
            if (msg.what != -1) {
                // 大于0时 表示当前录音的音量
                if (outerButton.mVolumeListener != null) {
                    outerButton.mVolumeListener.onVolumeChange(mRecordDialog,
                            msg.what);
                }
            } else {
                // -1 时表示录音超时
                outerButton.finishRecord();
            }
        }
    }
 
    /** 音量改变的监听器 */
    public interface OnVolumeChangeListener {
        void onVolumeChange(Dialog dialog, int volume);
    }
    public interface OnFinishedRecordListener {
        /** 用户手动取消 */
        public void onCancleRecord();
        /** 录音完成 */
        public void onFinishedRecord(String audioPath, int recordTime);
    }
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/11fc7b986f8670ee9efc11079f5f7864.html