Android仿微信录音功能

提要:需求是开发类似微信发语音的功能,没有语音转文字。网上看了一些代码,不能拿来直接用,部分代码逻辑有问题,所以想把自己的代码贴出来,仅供参考。

功能:

a、设置最大录音时长和录音倒计时(为了方便测试,最大时长设置为15秒,开始倒计时设置为7秒)

b、在录音之前检查录音和存储权限

源码:

1、录音对话框管理类DialogManager:

/**
 * 功能:录音对话框管理类
 */
public class DialogManager {
  private AlertDialog.Builder builder;
  private AlertDialog dialog;
  private ImageView mIcon;
  private ImageView mVoice;
  private TextView mLabel;

  private Context context;

  /**
   * 构造方法
   *
   * @param context Activity级别的Context
   */
  public DialogManager(Context context) {
    this.context = context;
  }

  /**
   * 显示录音的对话框
   */
  public void showRecordingDialog() {
    builder = new AlertDialog.Builder(context, R.style.AudioRecorderDialogStyle);
    LayoutInflater inflater = LayoutInflater.from(context);
    View view = inflater.inflate(R.layout.audio_recorder_dialog, null);
    mIcon = view.findViewById(R.id.iv_dialog_icon);
    mVoice = view.findViewById(R.id.iv_dialog_voice);
    mLabel = view.findViewById(R.id.tv_dialog_label);

    builder.setView(view);
    dialog = builder.create();
    dialog.show();
    dialog.setCanceledOnTouchOutside(false);
  }

  /**
   * 正在播放时的状态
   */
  public void recording() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.VISIBLE);
      mLabel.setVisibility(View.VISIBLE);

      mIcon.setImageResource(R.drawable.ic_audio_recorder);
      mVoice.setImageResource(R.drawable.ic_audio_v1);
      mLabel.setText(R.string.audio_record_dialog_up_to_cancel);
    }
  }

  /**
   * 显示想取消的对话框
   */
  public void wantToCancel() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.GONE);
      mLabel.setVisibility(View.VISIBLE);

      mIcon.setImageResource(R.drawable.ic_audio_cancel);
      mLabel.setText(R.string.audio_record_dialog_release_to_cancel);
    }
  }

  /**
   * 显示时间过短的对话框
   */
  public void tooShort() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.GONE);
      mLabel.setVisibility(View.VISIBLE);

      mLabel.setText(R.string.audio_record_dialog_too_short);
    }
  }

  // 显示取消的对话框
  public void dismissDialog() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      dialog.dismiss();
      dialog = null;
    }
  }

  /**
   * 显示更新音量级别的对话框
   *
   * @param level 1-7
   */
  public void updateVoiceLevel(int level) {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.VISIBLE);
      mLabel.setVisibility(View.VISIBLE);

      int resId = context.getResources().getIdentifier("ic_audio_v" + level, "drawable", context.getPackageName());
      mVoice.setImageResource(resId);
    }
  }

  public void updateTime(int time) {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.VISIBLE);
      mLabel.setVisibility(View.VISIBLE);
      mLabel.setText(time + "s");
    }
  }
}

2、录音管理类AudioManager

 /**
 * 功能:录音管理类
 */
public class AudioManager {
  private MediaRecorder mMediaRecorder;
  private String mDir;
  private String mCurrentFilePath;

  private static AudioManager mInstance;

  private boolean isPrepared;

  private AudioManager(String dir) {
    this.mDir = dir;
  }

  //单例模式:在这里实例化AudioManager并传入录音文件地址
  public static AudioManager getInstance(String dir) {
    if (mInstance == null) {
      synchronized (AudioManager.class) {
        if (mInstance == null) {
          mInstance = new AudioManager(dir);
        }
      }
    }
    return mInstance;
  }

  /**
   * 回调准备完毕
   */
  public interface AudioStateListener {
    void wellPrepared();
  }

  public AudioStateListener mListener;

  /**
   * 回调方法
   */
  public void setOnAudioStateListener(AudioStateListener listener) {
    mListener = listener;
  }

  /**
   * 准备
   */
  public void prepareAudio() {
    try {
      isPrepared = false;
      File dir = FileUtils.createNewFile(mDir);
      String fileName = generateFileName();

      File file = new File(dir, fileName);
      mCurrentFilePath = file.getAbsolutePath();
      Logger.t("AudioManager").i("audio file name :" + mCurrentFilePath);

      mMediaRecorder = new MediaRecorder();
      //设置输出文件
      mMediaRecorder.setOutputFile(mCurrentFilePath);
      //设置MediaRecorder的音频源为麦克风
      mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
      //设置音频格式
      mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
      //设置音频的格式为AAC
      mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
      //准备录音
      mMediaRecorder.prepare();
      //开始
      mMediaRecorder.start();
      //准备结束
      isPrepared = true;
      if (mListener != null) {
        mListener.wellPrepared();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 随机生成文件的名称
   */
  private String generateFileName() {
    return UUID.randomUUID().toString() + ".m4a";
  }

  public int getVoiceLevel(int maxLevel) {
    if (isPrepared) {
      try {
        //获得最大的振幅getMaxAmplitude() 1-32767
        return maxLevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1;
      } catch (Exception e) {
      }
    }
    return 1;
  }

  /**
   * 释放资源
   */
  public void release() {
    if (mMediaRecorder != null) {
      mMediaRecorder.stop();
      mMediaRecorder.release();
      mMediaRecorder = null;
    }
  }

  public void cancel() {
    release();
    if (mCurrentFilePath != null) {
      File file = new File(mCurrentFilePath);
      FileUtils.deleteFile(file);
      mCurrentFilePath = null;
    }
  }

  public String getCurrentFilePath() {
    return mCurrentFilePath;
  }
}

3、自定义录音按钮AudioRecorderButton

/**
 * 功能:录音按钮
 */
public class AudioRecorderButton extends AppCompatButton {
  private Context mContext;
  //取消录音Y轴位移
  private static final int DISTANCE_Y_CANCEL = 80;
  //录音最大时长限制
  private static final int AUDIO_RECORDER_MAX_TIME = 15;
  //录音倒计时时间
  private static final int AUDIO_RECORDER_COUNT_DOWN = 7;
  //状态
  private static final int STATE_NORMAL = 1;// 默认的状态
  private static final int STATE_RECORDING = 2;// 正在录音
  private static final int STATE_WANT_TO_CANCEL = 3;// 希望取消
  //当前的状态
  private int mCurrentState = STATE_NORMAL;
  //已经开始录音
  private boolean isRecording = false;
  //是否触发onLongClick
  private boolean mReady;

  private DialogManager mDialogManager;
  private AudioManager mAudioManager;
  private android.media.AudioManager audioManager;

  public AudioRecorderButton(Context context) {
    this(context, null);
  }

  public AudioRecorderButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.mContext = context;
    mDialogManager = new DialogManager(context);
    audioManager = (android.media.AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

    String dir = SdUtils.getCustomFolder("Audios");//创建文件夹
    mAudioManager = AudioManager.getInstance(dir);
    mAudioManager.setOnAudioStateListener(new AudioManager.AudioStateListener() {
      @Override
      public void wellPrepared() {
        mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
      }
    });
    //按钮长按 准备录音 包括start
    setOnLongClickListener(new OnLongClickListener() {
      @Override
      public boolean onLongClick(View v) {
        //先判断有没有录音和存储权限,有则开始录音,没有就申请权限
        int hasAudioPermission = ContextCompat.checkSelfPermission(mContext, Manifest.permission.RECORD_AUDIO);
        int hasStoragePermission = ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (hasAudioPermission == PackageManager.PERMISSION_GRANTED && hasStoragePermission == PackageManager.PERMISSION_GRANTED) {
          mReady = true;
          mAudioManager.prepareAudio();
        } else {
          RxPermissions permissions = new RxPermissions((FragmentActivity) mContext);
          Disposable disposable = permissions.request(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE)
              .subscribe(new Consumer<Boolean>() {
                @Override
                public void accept(Boolean granted) {
                  if (!granted) {
                    ToastUtils.showShort("发送语音功能需要赋予录音和存储权限");
                  }
                }
              });
        }
        return true;
      }
    });
  }

  private static final int MSG_AUDIO_PREPARED = 0X110;
  private static final int MSG_VOICE_CHANGED = 0X111;
  private static final int MSG_DIALOG_DISMISS = 0X112;
  private static final int MSG_TIME_OUT = 0x113;
  private static final int UPDATE_TIME = 0x114;

  private boolean mThreadFlag = false;
  //录音时长
  private float mTime;

  //获取音量大小的Runnable
  private Runnable mGetVoiceLevelRunnable = new Runnable() {
    @Override
    public void run() {
      while (isRecording) {
        try {
          Thread.sleep(100);
          mTime += 0.1f;
          mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);
          if (mTime >= AUDIO_RECORDER_MAX_TIME) {//如果时间超过60秒,自动结束录音
            while (!mThreadFlag) {//记录已经结束了录音,不需要再次结束,以免出现问题
              mDialogManager.dismissDialog();
              mAudioManager.release();
              if (audioFinishRecorderListener != null) {
                //先回调,再Reset,不然回调中的时间是0
                audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());
                mHandler.sendEmptyMessage(MSG_TIME_OUT);
              }
              mThreadFlag = !mThreadFlag;
            }
            isRecording = false;
          } else if (mTime >= AUDIO_RECORDER_COUNT_DOWN) {
            mHandler.sendEmptyMessage(UPDATE_TIME);
          }
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  };

  private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
      switch (msg.what) {
        case MSG_AUDIO_PREPARED:
          mDialogManager.showRecordingDialog();
          isRecording = true;
          new Thread(mGetVoiceLevelRunnable).start();
          break;
        case MSG_VOICE_CHANGED:
          mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));
          break;
        case MSG_DIALOG_DISMISS:
          mDialogManager.dismissDialog();
          break;
        case MSG_TIME_OUT:
          reset();
          break;
        case UPDATE_TIME:
          int countDown = (int) (AUDIO_RECORDER_MAX_TIME - mTime);
          mDialogManager.updateTime(countDown);
          break;
      }
      return true;
    }
  });

  /**
   * 录音完成后的回调
   */
  public interface AudioFinishRecorderListener {
    /**
     * @param seconds 时长
     * @param filePath 文件
     */
    void onFinish(float seconds, String filePath);
  }

  private AudioFinishRecorderListener audioFinishRecorderListener;

  public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {
    audioFinishRecorderListener = listener;
  }

  android.media.AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = new android.media.AudioManager.OnAudioFocusChangeListener() {
    @Override
    public void onAudioFocusChange(int focusChange) {
      if (focusChange == android.media.AudioManager.AUDIOFOCUS_LOSS) {
        audioManager.abandonAudioFocus(onAudioFocusChangeListener);
      }
    }
  };

  public void myRequestAudioFocus() {
    audioManager.requestAudioFocus(onAudioFocusChangeListener, android.media.AudioManager.STREAM_MUSIC, android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    Logger.t("AudioManager").i("x :" + event.getX() + "-Y:" + event.getY());
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        mThreadFlag = false;
        isRecording = true;
        changeState(STATE_RECORDING);
        myRequestAudioFocus();
        break;
      case MotionEvent.ACTION_MOVE:
        if (isRecording) {
          //根据想x,y的坐标,判断是否想要取消
          if (event.getY() < 0 && Math.abs(event.getY()) > DISTANCE_Y_CANCEL) {
            changeState(STATE_WANT_TO_CANCEL);
          } else {
            changeState(STATE_RECORDING);
          }
        }
        break;
      case MotionEvent.ACTION_UP:
        //如果longClick 没触发
        if (!mReady) {
          reset();
          return super.onTouchEvent(event);
        }
        //触发了onLongClick 没准备好,但是已经prepared已经start
        //所以消除文件夹
        if (!isRecording || mTime < 1.0f) {
          mDialogManager.tooShort();
          mAudioManager.cancel();
          mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DISMISS, 1000);
        } else if (mCurrentState == STATE_RECORDING) {//正常录制结束
          mDialogManager.dismissDialog();
          mAudioManager.release();
          if (audioFinishRecorderListener != null) {
            audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());
          }
        } else if (mCurrentState == STATE_WANT_TO_CANCEL) {
          mDialogManager.dismissDialog();
          mAudioManager.cancel();
        }
        reset();
        audioManager.abandonAudioFocus(onAudioFocusChangeListener);
        break;
    }
    return super.onTouchEvent(event);
  }

  /**
   * 恢复状态 标志位
   */
  private void reset() {
    isRecording = false;
    mTime = 0;
    mReady = false;
    changeState(STATE_NORMAL);
  }

  /**
   * 改变状态
   */
  private void changeState(int state) {
    if (mCurrentState != state) {
      mCurrentState = state;
      switch (state) {
        case STATE_NORMAL:
          setText(R.string.audio_record_button_normal);
          break;
        case STATE_RECORDING:
          if (isRecording) {
            mDialogManager.recording();
          }
          setText(R.string.audio_record_button_recording);
          break;
        case STATE_WANT_TO_CANCEL:
          mDialogManager.wantToCancel();
          setText(R.string.audio_record_button_cancel);
          break;
      }
    }
  }
}

4、DialogStyle

<!--App Base Theme-->
<style name="AppThemeParent" parent="Theme.AppCompat.Light.NoActionBar">
  <!--不显示状态栏:22之前-->
  <item name="android:windowNoTitle">true</item>
  <item name="android:windowAnimationStyle">@style/ActivityAnimTheme</item><!--Activity动画-->
  <item name="actionOverflowMenuStyle">@style/MenuStyle</item><!--toolbar菜单样式-->
</style>

<!--Dialog式的Activity-->
<style name="ActivityDialogStyle" parent="AppThemeParent">
  <item name="android:windowBackground">@android:color/transparent</item>
  <!-- 浮于Activity之上 -->
  <item name="android:windowIsFloating">true</item>
  <!-- 边框 -->
  <item name="android:windowFrame">@null</item>
  <!-- Dialog以外的区域模糊效果 -->
  <item name="android:backgroundDimEnabled">true</item>
  <!-- 半透明 -->
  <item name="android:windowIsTranslucent">true</item>
  <!-- Dialog进入及退出动画 -->
  <item name="android:windowAnimationStyle">@style/ActivityDialogAnimation</item>
</style>

<!--Audio Recorder Dialog-->
<style name="AudioRecorderDialogStyle" parent="ActivityDialogStyle">
  <!-- Dialog以外的区域模糊效果 -->
  <item name="android:backgroundDimEnabled">false</item>
</style>

<!-- Dialog动画:渐入渐出-->
<style name="ActivityDialogAnimation" parent="@android:style/Animation.Dialog">
  <item name="android:windowEnterAnimation">@anim/fade_in</item>
  <item name="android:windowExitAnimation">@anim/fade_out</item>
</style>

5、DialogLayout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="@drawable/audio_recorder_dialog_bg"
  android:gravity="center"
  android:orientation="vertical"
  android:padding="20dp">

  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
      android:id="@+id/iv_dialog_icon"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/ic_audio_recorder" />

    <ImageView
      android:id="@+id/iv_dialog_voice"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/ic_audio_v1" />

  </LinearLayout>

  <TextView
    android:id="@+id/tv_dialog_label"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="15dp"
    android:text="@string/audio_record_dialog_up_to_cancel"
    android:textColor="@color/white"
    android:textSize="15dp" />
</LinearLayout>

6、用到的字符串

<!--AudioRecord-->
<string name="audio_record_button_normal">按住 说话</string>
<string name="audio_record_button_recording">松开 结束</string>
<string name="audio_record_button_cancel">松开手指 取消发送</string>
<string name="audio_record_dialog_up_to_cancel">手指上划,取消发送</string>
<string name="audio_record_dialog_release_to_cancel">松开手指,取消发送</string>
<string name="audio_record_dialog_too_short">录音时间过短</string>

7、使用:按钮的样式不需要写在自定义Button中,方便使用

<com.kidney.base_library.view.audioRecorder.AudioRecorderButton
  android:id="@+id/btn_audio_recorder"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@string/audio_record_button_normal" />

 AudioRecorderButton audioRecorderButton = findViewById(R.id.btn_audio_recorder);
 audioRecorderButton.setAudioFinishRecorderListener(new AudioRecorderButton.AudioFinishRecorderListener() {
   @Override
   public void onFinish(float seconds, String filePath) {
     Logger.i(seconds + "秒:" + filePath);
   }
 });

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

(0)

相关推荐

  • Android仿微信、录制音频并发送功能

    MyRecorder(仿微信,录制音频并发送功能) ①布局实现(activity_main.xml) 布局采用线性布局,上面使用的一个ListView,下面使用的是一个自定义的Button(会在下面进行介绍) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  • android仿微信聊天界面 语音录制功能

    本例为模仿微信聊天界面UI设计,文字发送以及语言录制UI. 1先看效果图: 第一:chat.xml设计 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" andro

  • Android仿微信录制语音功能

    本文实例为大家分享了Android仿微信录制语音的具体代码,供大家参考,具体内容如下 前言 我把录音分成了两部分 1.UI界面,弹窗读秒 2.一个类(包含开始.停止.创建文件名功能) 第一部分 由于6.0权限问题,点击按钮申请权限通过则弹窗,如何申请权限 弹窗布局popw_record.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http:

  • Android仿微信语音消息的录制和播放功能

    一.简述 效果: 实现功能: 长按Button时改变Button显示文字,弹出Dialog(动态更新音量),动态生成录音文件,开始录音: 监听手指动作,规定区域.录音状态下手指划出规定区域取消录音,删除生成的录音文件: 监听手指动作.当手指抬起时,判断是否开始录音,录音时长是否过短,符合条件则提示录音时长过短:正常结束时通过回调返回该次录音的文件路径和时长. 4.点击录音列表的item时,播放动画,播放对应的音频文件. 主要用到4个核心类: 自定义录音按钮(AudioRecordButton):

  • Android实现录音方法(仿微信语音、麦克风录音、发送语音、解决5.0以上BUG)

    先给大家展示下效果图,如果大家感觉不错,请参考使用方法, 效果图如下所示: 使用方法: 录音工具类:AudioRecoderUtils.java,代码如下: public class AudioRecoderUtils { //文件路径 private String filePath; //文件夹路径 private String FolderPath; private MediaRecorder mMediaRecorder; private final String TAG = "fan&q

  • Android仿微信录音功能(录音后的raw文件转mp3文件)

    现在很多时候需要用到录音,然后如果我们的App是ios和android两端的话,就要考虑录音的文件在两端都能使用,这个时候就需要适配,两端的录音文件都要是mp3文件,这样才能保证两边都能播放. 针对这个,封装了一个简单可用的录音控件. 使用方法: 1.在xml文件中添加 <ant.muxi.com.audiodemo.view.SoundTextView android:id="@+id/record_audio" android:text="按住开始录音"

  • Android仿微信录音功能

    提要:需求是开发类似微信发语音的功能,没有语音转文字.网上看了一些代码,不能拿来直接用,部分代码逻辑有问题,所以想把自己的代码贴出来,仅供参考. 功能: a.设置最大录音时长和录音倒计时(为了方便测试,最大时长设置为15秒,开始倒计时设置为7秒) b.在录音之前检查录音和存储权限 源码: 1.录音对话框管理类DialogManager: /** * 功能:录音对话框管理类 */ public class DialogManager { private AlertDialog.Builder bu

  • Android仿微信语音对讲录音功能

    自微信出现以来取得了很好的成绩,语音对讲的实现更加方便了人与人之间的交流.今天来实践一下微信的语音对讲的录音实现,这个也比较容易实现.在此,我将该按钮封装成为一个控件,并通过策略模式的方式实现录音和界面的解耦合,以方便我们在实际情况中对录音方法的不同需求(例如想要实现wav格式的编码时我们也就不能再使用MediaRecorder,而只能使用AudioRecord进行处理). 效果图: 实现思路: 1.在微信中我们可以看到实现语音对讲的是通过点按按钮来完成的,因此在这里我选择重新自己的控件使其继承

  • Android仿微信底部菜单栏效果

    前言 在市面上,大多数的APP都需要通过底部菜单栏来将程序的功能进行分类整理,通常都是分为3-5个大模块,从而正确有效地引导用户去使用我们的APP.实现底部菜单栏的方法也有很多种. 1.仿微信底部菜单栏(ViewPager+ImagerView+TextView) ......(其他方式后续会补充) 效果预览 首先来个开胃菜,看看实现效果: 先贴出项目所需的资源文件,这些可随个人自由更改颜色和文字 colors.xml <color name="bg_line_light_gray&quo

  • Android仿微信底部菜单栏功能显示未读消息数量

    底部菜单栏很重要,我看了一下很多应用软件都是用了底部菜单栏,这里使用了tabhost做了一种通用的(就是可以像微信那样显示未读消息数量的,虽然之前也做过但是layout下的xml写的太臃肿,这里去掉了很多不必要的层,个人看起来还是不错的,所以贴出来方便以后使用). 先看一下做出来之后的效果: 以后使用的时候就可以换成自己项目的图片和字体了,主框架不用变哈哈, 首先是要布局layout下xml文件 main.xml: <?xml version="1.0" encoding=&qu

  • Android仿微信语音聊天功能

    本文实例讲述了Android仿微信语音聊天功能代码.分享给大家供大家参考.具体如下: 项目效果如下: 具体代码如下: AudioManager.java package com.xuliugen.weichat; import java.io.File; import java.io.IOException; import java.util.UUID; import android.media.MediaRecorder; public class AudioManager { private

  • Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能

    如何为不同的list item呈现不同的菜单,本文实例就为大家介绍了Android仿微信或QQ滑动弹出编辑.删除菜单效果.增加下拉刷新等功能的实现,分享给大家供大家参考,具体内容如下 效果图: 1. 下载开源项目,并将其中的liberary导入到自己的项目中: 2. 使用SwipeMenuListView代替ListView,在页面中布局: <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefresh

  • Android仿微信图片点击全屏效果

    废话不多说,先看下效果: 先是微信的 再是模仿的 先说下实现原理,再一步步分析 这里总共有2个Activity一个就是主页,一个就是显示我们图片效果的页面,参数通过Intent传送,素材内容均来自网络,(感谢聪明的蘑菇) 图片都是Glide异步下的,下的,下的重要的事情说三次,然后就是用动画做放大操作然后显示出来了(并没有做下载原图的实现,反正也是一样 下载下来Set上去而且动画都不需要更简便). OK,我们来看分析下 obj,目录下分别创建了2个对象,一个用来使用来处理显示页面的图片尺寸信息以

  • Android仿微信进度弹出框的实现方法

    MainActivity: package com.ruru.dialogproject; import android.app.Activity; import android.os.Bundle; import android.view.View; public class MainActivity extends Activity implements Runnable { LoadingDialog dialog; @Override protected void onCreate(Bu

  • Android实现简单底部导航栏 Android仿微信滑动切换效果

    Android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义View配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏的设置方法类似于TabLayout的关联,View需要创建关联方法,用来关联VIewPager; 3. 通过关联方法获取ViewPager实例后,根据ViewPager页面数创建底部导航栏的图标按钮; 代码实现: 1. 新建第一个自定义View, 图标 + 文字 的底部按钮; /** * 自定义控件

随机推荐