【android 安卓 模仿豆包语音条效果】

语音条view 白色的一排竖线21个,通过setEffect();方法设置音量,第一个参数是说话的时候当时音量的值,第二个参数是音量的最大值;

java 复制代码
在这里插入代码片
package com.example.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.Random;

public class LineSoundEffectView extends View {

    private Paint paint;

	// dp转px的工具,自己网上找个
    public final int ticksWidth = DensityUtils.dip2px(getContext(), 3f);
    public final int dividerWidth = DensityUtils.dip2px(getContext(), 5f);
    public final int ticksMaxHeight = DensityUtils.dip2px(getContext(), 30f);
    public final int ticksMinHeight = DensityUtils.dip2px(getContext(), 6f);
    public final int ticksNumber = 21;

    public final int fixOffset = (ticksMaxHeight - ticksMinHeight) / 2;

    // 计算总宽度
    public final int totalWidth = ticksWidth * ticksNumber + (dividerWidth * ticksNumber - 1);

    public LineSoundEffectView(Context context) {
        super(context);
        init();
    }

    public LineSoundEffectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LineSoundEffectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private void init() {
        paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setAntiAlias(true);
        paint.setDither(true);
        offsetVertical = fixOffset;
        invalidate();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    private int offsetVertical = 0;

    private int totalNum = 21;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int desiredWidth = totalWidth; // 假设我们想要设置的宽度为200px
        int desiredHeight = ticksMaxHeight; // 假设我们想要设置的高度为200px
        // 设置实际的宽度和高度
        setMeasuredDimension(desiredWidth, desiredHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < totalNum; i++) {

            int topY = offsetVertical;
            int bottomY = ticksMaxHeight - offsetVertical < ticksMinHeight ? ticksMinHeight :
                    ticksMaxHeight - offsetVertical;
            if (i < 4) {
                topY = fixOffset;
                bottomY = ticksMaxHeight - fixOffset;
                paint.setAlpha((int) ((i + 1) * 255 / 6f));
            } else if (i >= totalNum - 4) {
                topY = fixOffset;
                bottomY = ticksMaxHeight - fixOffset;
                paint.setAlpha((int) ((totalNum - i) * 255 / 5f));
            } else {
                // random 越大,音波越小
                int random = offsetVertical <= fixOffset ? i % 2 == 0 ? Math.abs(i < totalNum / 2 ? totalNum / 2 - i : i - totalNum / 2) : 0:Math.abs(i < totalNum / 2 ? totalNum / 2 - i : i - totalNum / 2)%5;
                int i1 = offsetVertical + random > fixOffset ? fixOffset : offsetVertical + random;
                topY = i1;
                bottomY = ticksMaxHeight - i1;
                paint.setAlpha(255);
            }
            canvas.drawRoundRect(new RectF((dividerWidth + ticksWidth) * i, topY, (dividerWidth + ticksWidth) * i + ticksWidth, bottomY), ticksWidth / 2, ticksWidth / 2, paint);
//            }
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {

            // 重绘视图
            invalidate();
            return true;
        }
        return super.onTouchEvent(event);
    }

    public void setEffect(int value, int maxValue) {
        offsetVertical = (int) (fixOffset * (1 - (value * 1f / maxValue)));
        invalidate();
    }
}

松手发送,上移取消代码

java 复制代码
 binding.tvChatVoice.setOnTouchListener((v, motionEvent) -> {
                    switch (motionEvent.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            downY = motionEvent.getY();
                            startVoice();
                            break;
                        case MotionEvent.ACTION_CANCEL: // 首次开权限时会走这里,录音取消
                        case MotionEvent.ACTION_UP:
                            if (mIat != null) {
                                if (isCanceledVoiceSendOp) {
                                    mIat.cancel();
                                    endVoice();
                                } else {
                                    mIat.stopListening();
                                }
                            }
                            break;

                        case MotionEvent.ACTION_MOVE: // 滑动手指
                            float moveY = motionEvent.getY();
                            LogUtils.e("move-------downY - moveY=" + (downY - moveY));
                            if (downY != 0 && downY - moveY > 100) {
                                if (!isCanceledVoiceSendOp) {
                                    
									// 震动反馈
                                    binding.tvChatVoice.setHapticFeedbackEnabled(true);
                                    binding.tvChatVoice.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                                }
                                isCanceledVoiceSendOp = true;
                                binding.llChatEffect.setBackgroundResource(R.drawable.shape_chat_voice_effect_cancel_bg);
                                binding.tvChatVoiceEffectTips.setText("松手取消");
                                binding.tvChatVoiceEffectTips.setTextColor(getResources().getColor(R.color.color_FE3750));
                            }
                            if (downY - moveY < 20) {
                                if (isCanceledVoiceSendOp) {
                                    // 震动反馈
									binding.tvChatVoice.setHapticFeedbackEnabled(true);
                                    binding.tvChatVoice.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                                }
                                isCanceledVoiceSendOp = false;
                                binding.tvChatVoiceEffectTips.setText("松手发送,上移取消");
                                binding.tvChatVoiceEffectTips.setTextColor(getResources().getColor(R.color.color_8B8B8B));
                                binding.llChatEffect.setBackgroundResource(R.drawable.shape_chat_voice_effect_bg);
                            }
                            break;

                    }
                    return true;
                }
        );
java 复制代码
/**
     * 开始语音输入
     */
    private void startVoice() {
        if (!isAttachedActivityValid()) {
            return;
        }
        binding.tvChatVoice.setHapticFeedbackEnabled(true);
        binding.tvChatVoice.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        // 取消上次请求
        cancelPreQuestionRequest();
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            requestPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO);
            return;
        }
        binding.tvChatVoiceEffectTips.setVisibility(View.VISIBLE);
        binding.tvChatVoiceEffectTips.setText("松手发送,上移取消");
        binding.tvChatVoiceEffectTips.setTextColor(getResources().getColor(R.color.color_8B8B8B));
        binding.llChatEffect.setVisibility(View.VISIBLE);
        binding.llFooter.setBackgroundResource(R.drawable.ic_chat_voice_effect_bottom_bg);
//        binding.ivChatEffect.setImageResource(R.drawable.ic_chat_effect_1);

        mVoiceTextBuffer.setLength(0);
        // 不显示听写对话框
        int ret = mIat.startListening(new RecognizerListener() {
            @Override
            public void onBeginOfSpeech() {
                // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            }

            @Override
            public void onError(SpeechError error) {
                // Tips:
                // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
//                Log.d(TAG, "onError " + error.getPlainDescription(true));
                toastCenter(error.getPlainDescription(false));
                endVoice();

            }

            @Override
            public void onEndOfSpeech() {
                // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
                // TODO 显示按住说话
                endVoice();
            }

            @Override
            public void onResult(RecognizerResult results, boolean isLast) {
                String speechText = results.getResultString();
                LogUtils.e(speechText);
                if (isLast) {
                    LogUtils.e("aiChat-------onResult 结束");
                    endVoice();
                }
                mVoiceTextBuffer.append(speechText);
                if (isLast) {
                    if (TextUtils.isEmpty(mVoiceTextBuffer.toString())) {
                        new CommonSureDialog(getActivity(), "听不清楚,请重新说一遍!", new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {

                            }
                        }).show();
                    } else {
                        // 语音搜索
                        sendChatMsg(mVoiceTextBuffer.toString(), null);
                    }
                }
            }

            @Override
            public void onVolumeChanged(int volume, byte[] data) {
                binding.lineSoundEffectView.setEffect(volume, 30);
                LogUtils.e("aiChat-------当前正在说话,音量大小 = " + volume + " 返回音频数据 = " + data.length);
            }

            @Override
            public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            }

        });
        if (ret != ErrorCode.SUCCESS) {
            toastCenter("听写启用失败,错误码ret=" + ret);
        }
    }

    /**
     * 结束语音输入
     */
    private void endVoice() {
        binding.llChatVoice.setVisibility(View.VISIBLE);
        binding.llChatEffect.setVisibility(View.GONE);
        binding.llFooter.setBackgroundResource(R.drawable.ic_chat_bottom_bg);
        binding.tvChatVoiceEffectTips.setVisibility(View.GONE);

        binding.tvChatVoiceEffectTips.setText("松手发送,上移取消");
        binding.tvChatVoiceEffectTips.setTextColor(getResources().getColor(R.color.color_8B8B8B));
        binding.llChatEffect.setBackgroundResource(R.drawable.shape_chat_voice_effect_bg);

        downY = 0;
    }
 // 语音听写对象
    private SpeechRecognizer mIat;

    // 初始化语音听写sdk
    private void initVoiceSDK() {
        // 初始化识别无UI识别对象
        // 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(getActivity(), new InitListener() {
            @Override
            public void onInit(int i) {
                LogUtils.e("aiChat-------speechInit---i=" + i);
                if (i == 0 && mIat != null) {
                    setParam();
                }
            }
        });
    }

    /**
     * 语音听写参数设置
     */
    public void setParam() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        // 设置听写引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, "plain"); // plain / json

        // 设置语言:中文
        mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
        // 设置语言区域:普通话
        mIat.setParameter(SpeechConstant.ACCENT, "mandarin"); // mandarin:普通话
        //此处用于设置dialog中不显示错误码信息
        //mIat.setParameter("view_tips_plain","false");
        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat.setParameter(SpeechConstant.VAD_BOS, "10000");

        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat.setParameter(SpeechConstant.VAD_EOS, "10000");
        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat.setParameter(SpeechConstant.ASR_PTT, "1");

        // 设置音频保存路径,保存音频格式支持pcm、wav.
        mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH,
                getActivity().getCacheDir().getAbsolutePath() + "/msc/iat.wav");
    }

xml布局 底部按住说话layout:

xml 复制代码
        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/ll_footer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/ic_chat_bottom_bg"
            android:orientation="vertical"
            android:padding="@dimen/dp_16"
            app:layout_constraintBottom_toBottomOf="parent">

            <TextView
                android:id="@+id/tv_chat_voice_effect_tips"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/dp_8"
                android:text="松开发送,上移取消"
                android:textColor="@color/color_8B8B8B"
                android:textSize="@dimen/font_size_12"
                android:translationZ="@dimen/dp_10"
                android:visibility="gone"
                app:layout_constraintBottom_toTopOf="@id/ll_chat_voice"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:visibility="visible" />

            <androidx.appcompat.widget.LinearLayoutCompat
                android:id="@+id/ll_chat_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_item_block_bg_white_r16"
                android:minHeight="@dimen/dp_46"
                app:layout_constraintBottom_toBottomOf="parent">

                <EditText
                    android:id="@+id/edt_chat_content"
                    android:layout_width="@dimen/dp_0"
                    android:layout_height="match_parent"
                    android:layout_gravity="top"
                    android:layout_weight="1"
                    android:background="@null"
                    android:hint="发消息..."
                    android:imeOptions="actionSend"
                    android:lines="1"
                    android:maxLength="200"
                    android:paddingHorizontal="@dimen/dp_16"
                    android:paddingVertical="@dimen/dp_12"
                    android:singleLine="true"
                    android:textColorHint="@color/color__999999"
                    android:textSize="@dimen/font_size_16" />
                
                <View
                    android:id="@+id/line_send_text"
                    android:layout_gravity="center_vertical"
                    android:layout_width="@dimen/line_view_1"
                    android:visibility="gone"
                    android:background="@color/color_DADADA"
                    android:layout_height="@dimen/dp_16" />

                <ImageView
                    android:id="@+id/iv_send_text"
                    android:padding="@dimen/dp_12"
                    android:visibility="gone"
                    android:src="@drawable/ic_ai_chat_send_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

                <ImageView
                    android:id="@+id/iv_chat_voice"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:padding="@dimen/dp_12"
                    android:src="@drawable/ic_chat_voice" />

            </androidx.appcompat.widget.LinearLayoutCompat>

            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/ll_chat_voice"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_item_block_bg_white_r16"
                android:visibility="gone"
                app:layout_constraintBottom_toBottomOf="parent"
                tools:visibility="gone">

                <TextView
                    android:id="@+id/tv_chat_voice"
                    android:layout_width="@dimen/dp_0"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:background="@null"
                    android:gravity="center"
                    android:paddingHorizontal="@dimen/dp_16"
                    android:paddingVertical="@dimen/dp_12"
                    android:text="按住 说话"
                    android:textColor="@color/color__333333"
                    android:textSize="@dimen/font_size_16"
                    android:textStyle="bold"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent" />

                <ImageView
                    android:id="@+id/iv_chat_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:padding="@dimen/dp_12"
                    android:src="@drawable/ic_chat_text"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/ll_chat_effect"
                    android:layout_width="@dimen/dp_0"
                    android:layout_height="@dimen/dp_0"
                    android:background="@drawable/shape_chat_voice_effect_bg"
                    android:visibility="gone"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    tools:visibility="visible">

                    <ImageView
                        android:id="@+id/iv_chat_effect"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:src="@drawable/ic_chat_effect_1"
                        android:visibility="gone"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                    <com.kshow.kxworkbench.utils.LineSoundEffectView
                        android:id="@+id/lineSoundEffectView"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />
                </androidx.constraintlayout.widget.ConstraintLayout>
            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.constraintlayout.widget.ConstraintLayout>
相关推荐
程序leo源33 分钟前
C语言:操作符详解1
android·java·c语言·c++·青少年编程·c#
sunly_6 小时前
Flutter:AnimatedPadding动态修改padding
android·flutter
诸神黄昏EX6 小时前
Android 常用命令和工具解析之GPU相关
android
顾北川_野6 小时前
Android 布局菜单或按钮图标或Menu/Item设置可见和不可见
android
练习本6 小时前
android 动画原理分析
android
别拿曾经看以后~6 小时前
原生Android调用uniapp项目中的方法
android·vue.js·uni-app
Winston Wood6 小时前
Android Binder技术概览
android·binder·进程通信
踏雪羽翼6 小时前
android 使用实现音效--Equalizer
android·音效·eqequalizer·bassboost·presetreverb
老码沉思录6 小时前
Android开发实战班 - Android开发基础之 Kotlin语言基础与特性
android·微信·kotlin
峥嵘life7 小时前
Android adb shell dumpsys audio 信息查看分析详解
android·adb