参考与:http://www.2cto.com/kf/201505/396842.html,这网站代码跑起来有问题,自己改动了一下,基本上没什么大问题

发现问题可以评论,我会回复的

先贴下效果图

1、三个布局文件

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.example.weixin_record.MainActivity" ><ListViewandroid:id="@+id/listview"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:background="#ebebeb"android:divider="@null"android:dividerHeight="10dp" ></ListView><FrameLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content" ><!-- minHeight消除主界面上的一些间距 --><com.nickming.view.AudioRecordButtonandroid:id="@+id/recordButton"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="7dp"android:layout_marginLeft="50dp"android:layout_marginRight="50dp"android:layout_marginTop="6dp"android:background="@drawable/button_recordnormal"android:gravity="center"android:minHeight="0dp"android:padding="5dp"android:text="@string/normal"android:textColor="#727272" ></com.nickming.view.AudioRecordButton><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#ccc" /></FrameLayout></LinearLayout>

dialog_manger.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/dialog_loading_bg"android:gravity="center"android:orientation="vertical"android:padding="20dp"tools:context="com.example.weixin_record.MainActivity" ><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal" ><ImageViewandroid:id="@+id/dialog_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/recorder"android:visibility="visible" /><ImageViewandroid:id="@+id/dialog_voice"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/v1"android:visibility="visible" /></LinearLayout><TextViewandroid:id="@+id/recorder_dialogtext"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:text="@string/shouzhishanghua"android:textColor="#ffffffff" /></LinearLayout>

item_layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="60dp"android:layout_marginTop="5dp" ><ImageViewandroid:id="@+id/item_icon"android:layout_width="40dp"android:layout_height="40dp"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="5dp"android:src="@drawable/icon" /><FrameLayoutandroid:id="@+id/recorder_length"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_toLeftOf="@id/item_icon"android:background="@drawable/chatto_bg_focused" ><View android:id="@+id/show_anim01"android:layout_width="25dp"android:layout_height="25dp"android:layout_gravity="center_vertical|right"android:background="@drawable/adj"/></FrameLayout><TextView android:id="@+id/recorder_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginRight="3dp"android:layout_toLeftOf="@id/recorder_length"android:text=""android:textColor="#ff777777"/></RelativeLayout>

2.自定义的类

(1)DialogManger
package com.nickming.view;import com.example.weixin_record.R;import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/*** * @ClassName:  DialogManager   * @Description:对话框管理类* @author:  张  维* @date:   2016-5-23 下午4:56:03   **/
public class DialogManager {/*** 以下为dialog的初始化控件,包括其中的布局文件*/private Dialog mDialog;private ImageView mIcon;private ImageView mVoice;private TextView mLable;private Context mContext;public DialogManager(Context context) {mContext = context;}public void showRecordingDialog() {mDialog = new Dialog(mContext,R.style.Theme_audioDialog);// 用layoutinflater来引用布局LayoutInflater inflater = LayoutInflater.from(mContext);View view = inflater.inflate(R.layout.dialog_manager, null);mDialog.setContentView(view);mIcon = (ImageView) mDialog.findViewById(R.id.dialog_icon);mVoice = (ImageView) mDialog.findViewById(R.id.dialog_voice);mVoice.setBackgroundResource(R.drawable.play02);AnimationDrawable drawable = (AnimationDrawable) mVoice.getBackground();drawable.start();mLable = (TextView) mDialog.findViewById(R.id.recorder_dialogtext);mDialog.show();}/*** 设置正在录音时的dialog界面*/public void recording() {if (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.recorder);mLable.setText(R.string.shouzhishanghua);}}/*** 取消界面*/public void wantToCancel() {// TODO Auto-generated method stubif (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.cancel);mLable.setText(R.string.want_to_cancle);}}// 时间过短public void timeShort() {// TODO Auto-generated method stubif (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.GONE);mLable.setVisibility(View.VISIBLE);mIcon.setImageResource(R.drawable.voice_to_short);mLable.setText(R.string.timeshort);}}// 隐藏dialogpublic void dimissDialog() {// TODO Auto-generated method stubif (mDialog != null && mDialog.isShowing()) {mDialog.dismiss();mDialog = null;}}public void updateVoiceLevel(int level) {// TODO Auto-generated method stubif (mDialog != null && mDialog.isShowing()) {//先不改变它的默认状态
//          mIcon.setVisibility(View.VISIBLE);
//          mVoice.setVisibility(View.VISIBLE);
//          mLable.setVisibility(View.VISIBLE);//通过level来找到图片的id,也可以用switch来寻址,但是代码可能会比较长int resId = mContext.getResources().getIdentifier("v" + level,"drawable", mContext.getPackageName());mVoice.setImageResource(resId);}}}

(2)AudioRecordButton

package com.nickming.view;import com.example.weixin_record.R;
import com.example.weixin_record.R.string;
import com.nickming.view.AudioManager.AudioStageListener;import android.R.bool;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
/*** * @ClassName:  AudioRecordButton   * @Description:自定义的button按钮 * @author:  张  维* @date:   2016-5-23 下午2:13:20   **/
public class AudioRecordButton extends Button implements AudioStageListener {private static final int STATE_NORMAL = 1;private static final int STATE_RECORDING = 2;private static final int STATE_WANT_TO_CANCEL = 3;private static final int DISTANCE_Y_CANCEL = 50;private int mCurrentState = STATE_NORMAL;// 已经开始录音private boolean isRecording = false;private DialogManager mDialogManager;private AudioManager mAudioManager;private float mTime = 0;// 是否触发了onlongclick,准备好了private boolean mReady;/*** 先实现两个参数的构造方法,布局会默认引用这个构造方法, * 用一个 构造参数的构造方法来引用这个方法 * @param context*/public AudioRecordButton(Context context) {this(context, null);// TODO Auto-generated constructor stub}public AudioRecordButton(Context context, AttributeSet attrs) {super(context, attrs);mDialogManager = new DialogManager(getContext());// 这里没有判断储存卡是否存在,有空要判断String dir = Environment.getExternalStorageDirectory()+ "/temp";mAudioManager = AudioManager.getInstance(dir);mAudioManager.setOnAudioStageListener(this);setOnLongClickListener(new OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {// TODO Auto-generated methodmReady = true;mAudioManager.prepareAudio();return false;}});}/*** 录音完成后的回调,回调给activiy,可以获得mtime和文件的路径* @author nickming**/public interface AudioFinishRecorderListener{void onFinished(float mtime,String filePath);}private AudioFinishRecorderListener mListener;public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener){mListener=listener;}// 获取音量大小的runnableprivate Runnable mGetVoiceLevelRunnable = new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubwhile (isRecording) {try {Thread.sleep(100);mTime += 0.1f;mhandler.sendEmptyMessage(MSG_VOICE_CHANGE);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}};// 准备三个常量private static final int MSG_AUDIO_PREPARED = 0X110;private static final int MSG_VOICE_CHANGE = 0X111;private static final int MSG_DIALOG_DIMISS = 0X112;private Handler mhandler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case MSG_AUDIO_PREPARED:// 显示应该是在audio end prepare之后回调mDialogManager.showRecordingDialog();isRecording = true;new Thread(mGetVoiceLevelRunnable).start();// 需要开启一个线程来变换音量break;case MSG_VOICE_CHANGE:mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));break;case MSG_DIALOG_DIMISS:break;}};};// 在这里面发送一个handler的消息@Overridepublic void wellPrepared() {// TODO Auto-generated method stubmhandler.sendEmptyMessage(MSG_AUDIO_PREPARED);}/*** 直接复写这个监听函数*/@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubint action = event.getAction();int x = (int) event.getX();int y = (int) event.getY();switch (action) {case MotionEvent.ACTION_DOWN://表示用户开始触摸.changeState(STATE_RECORDING);break;case MotionEvent.ACTION_MOVE://表示用户在移动(手指或者其他)if (isRecording) {// 根据x,y来判断用户是否想要取消if (wantToCancel(x, y)) {changeState(STATE_WANT_TO_CANCEL);} else {changeState(STATE_RECORDING);}}break;case MotionEvent.ACTION_UP://表示用户抬起了手指 // 首先判断是否有触发onlongclick事件,没有的话直接返回resetif (!mReady) {reset();return super.onTouchEvent(event);}// 如果按的时间太短,还没准备好或者时间录制太短,就离开了,则显示这个dialogif (!isRecording || mTime < 0.6f) {mDialogManager.timeShort();//取消对话框mAudioManager.cancel();mhandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);// 持续1.3s} else if (mCurrentState == STATE_RECORDING) {//正常录制结束mDialogManager.dimissDialog();mAudioManager.release();// release释放一个mediarecorderif (mListener!=null) {// 并且callbackActivity,保存录音mListener.onFinished(mTime, mAudioManager.getCurrentFilePath());}} else if (mCurrentState == STATE_WANT_TO_CANCEL) {// cancelmAudioManager.cancel();mDialogManager.dimissDialog();}reset();// 恢复标志位break;}return super.onTouchEvent(event);}/*** 回复标志位以及状态*/private void reset() {// TODO Auto-generated method stubisRecording = false;changeState(STATE_NORMAL);mReady = false;mTime = 0;}private boolean wantToCancel(int x, int y) {// 超过按钮的宽度if (x < 0 || x > getWidth()) {// 判断是否在左边,右边,上边,下边return true;}// 超过按钮的高度if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {return true;}return false;}private void changeState(int state) {if (mCurrentState != state) {mCurrentState = state;switch (mCurrentState) {case STATE_NORMAL:setBackgroundResource(R.drawable.button_recordnormal);setText(R.string.normal);break;case STATE_RECORDING:setBackgroundResource(R.drawable.button_recording);setText(R.string.recording);if (isRecording) {mDialogManager.recording();// 复写dialog.recording();}break;case STATE_WANT_TO_CANCEL:setBackgroundResource(R.drawable.button_recording);setText(R.string.want_to_cancle);// 取消mDialogManager.wantToCancel();break;}}}@Overridepublic boolean onPreDraw() {return false;}}

(3)MediaRecorder

package com.nickming.view;import java.io.File;
import java.io.IOException;
import java.util.UUID;import android.media.MediaRecorder;
/*** * @ClassName:  AudioManager   * @Description: 录音的管理类(准备工作)* @author:  张  维* @date:   2016-5-23 下午2:10:35   **/
public class AudioManager {private MediaRecorder mRecorder;private String mDirString;private String mCurrentFilePath;private boolean isPrepared;// 是否准备好了/*** 单例化的方法 *  1 先声明一个static 类型的变量a*  2 在声明默认的构造函数 *  3 再用public synchronized static*   类名 getInstance() { if(a==null) { a=new 类();} return a; } 或者用以下的方法*//*** 单例化这个类*/private static AudioManager mInstance;private AudioManager(String dir) {mDirString=dir;}public static AudioManager getInstance(String dir) {if (mInstance == null) {synchronized (AudioManager.class) {if (mInstance == null) {mInstance = new AudioManager(dir);}}}return mInstance;}/*** 回调函数,准备完毕,准备好后,button才会开始显示录音框* * @author nickming**/public interface AudioStageListener {void wellPrepared();}public AudioStageListener mListener;public void setOnAudioStageListener(AudioStageListener listener) {mListener = listener;}// 准备方法public void prepareAudio() {try {// 一开始应该是false的isPrepared = false;File dir = new File(mDirString);//判断对象file是否存在if (!dir.exists()) {//创建此抽象路径指定的目录,包括所有必须但不存在的父目录。(及可以创建多级目录,无论是否存在父目录)dir.mkdirs();}String fileNameString = generalFileName();File file = new File(dir, fileNameString);mCurrentFilePath = file.getAbsolutePath();mRecorder = new MediaRecorder();// 设置输出文件mRecorder.setOutputFile(file.getAbsolutePath());// 设置meidaRecorder的音频源是麦克风mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置文件音频的输出格式为amrmRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);// 设置音频的编码格式为amrmRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 严格遵守google官方api给出的mediaRecorder的状态流程图mRecorder.prepare();mRecorder.start();// 准备结束isPrepared = true;// 已经准备好了,可以录制了if (mListener != null) {mListener.wellPrepared();}} catch (IllegalStateException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}/*** 随机生成文件的名称* * @return*/private String generalFileName() {//UUID.randomUUID().toString()是javaJDK提供的一个自动生成主键的方法。return UUID.randomUUID().toString() + ".amr";}// 获得声音的大小public int getVoiceLevel(int maxLevel) {// mRecorder.getMaxAmplitude()这个是音频的振幅范围,值域是1-32767if (isPrepared) {try {// 取证+1,否则去不到7return maxLevel * mRecorder.getMaxAmplitude() / 32768 + 1;} catch (Exception e) {}}return 1;}// 释放资源public void release() {// 严格按照api流程进行mRecorder.stop();mRecorder.release();mRecorder = null;}// 取消,因为prepare时产生了一个文件,所以cancel方法应该要删除这个文件,// 这是与release的方法的区别public void cancel() {release();if (mCurrentFilePath != null) {File file = new File(mCurrentFilePath);file.delete();//删除文件mCurrentFilePath = null;}}public String getCurrentFilePath() {return mCurrentFilePath;}}

3.调用的类

(1)RecorderAdapter   
package com.example.weixin_record;import java.util.List;import com.example.weixin_record.MainActivity.Recorder;import android.content.Context;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.TextView;
/*** * @ClassName:  RecorderAdapter   * @Description:list的适配器 * @author:  张  维* @date:   2016-5-23 下午4:04:02   **/
public class RecorderAdapter extends ArrayAdapter<Recorder> {private LayoutInflater inflater;private int mMinItemWith;// 设置对话框的最大宽度和最小宽度private int mMaxItemWith;public RecorderAdapter(Context context, List<Recorder> dataList) {super(context, -1, dataList);inflater = LayoutInflater.from(context);// 获取系统宽度WindowManager wManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wManager.getDefaultDisplay().getMetrics(outMetrics);mMaxItemWith = (int) (outMetrics.widthPixels * 0.7f);mMinItemWith = (int) (outMetrics.widthPixels * 0.15f);}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if (convertView == null) {convertView = inflater.inflate(R.layout.item_layout, parent, false);viewHolder=new ViewHolder();viewHolder.seconds=(TextView) convertView.findViewById(R.id.recorder_time);viewHolder.length=convertView.findViewById(R.id.recorder_length);convertView.setTag(viewHolder);}else {viewHolder=(ViewHolder) convertView.getTag();}viewHolder.seconds.setText(Math.round(getItem(position).time)+"\"");ViewGroup.LayoutParams lParams=viewHolder.length.getLayoutParams();lParams.width=(int) (mMinItemWith+mMaxItemWith/60f*getItem(position).time);viewHolder.length.setLayoutParams(lParams);return convertView;}class ViewHolder {TextView seconds;// 时间View length;// 对话框长度}}

(2)MediaManager

package com.example.weixin_record;import java.io.IOException;import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.util.Log;
/*** * @ClassName:  MediaManager   * @Description:播放的管理类* @author:  张  维* @date:   2016-5-23 下午4:04:43   **/
public class MediaManager {private static MediaPlayer mPlayer;private static boolean isPause;public static  void playSound(String filePathString,OnCompletionListener onCompletionListener) {if (mPlayer==null) {mPlayer=new MediaPlayer();//保险起见,设置报错监听mPlayer.setOnErrorListener(new OnErrorListener() {@Overridepublic boolean onError(MediaPlayer mp, int what, int extra) {Log.i("info", "");mPlayer.reset();return false;}});}else {mPlayer.reset();//就回复}try {mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mPlayer.setOnCompletionListener(onCompletionListener);mPlayer.setDataSource(filePathString);mPlayer.prepare();mPlayer.start();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (IllegalStateException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}//停止函数public static void pause(){if (mPlayer!=null&&mPlayer.isPlaying()) {mPlayer.pause();isPause=true;}}//继续public static void resume(){if (mPlayer!=null&&isPause) {mPlayer.start();isPause=false;}}public  static void release(){if (mPlayer!=null) {mPlayer.release();mPlayer=null;}}
}

(3)MainActivity

package com.example.weixin_record;import java.util.ArrayList;
import java.util.List;import com.nickming.view.AudioRecordButton;
import com.nickming.view.AudioRecordButton.AudioFinishRecorderListener;import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;public class MainActivity extends Activity {AudioRecordButton button;private ListView mlistview;private ArrayAdapter<Recorder> mAdapter;private View viewanim;private List<Recorder> mDatas = new ArrayList<MainActivity.Recorder>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mlistview = (ListView) findViewById(R.id.listview);button = (AudioRecordButton) findViewById(R.id.recordButton);button.setAudioFinishRecorderListener(new AudioFinishRecorderListener() {@Overridepublic void onFinished(float seconds, String filePath) {// TODO Auto-generated method stubRecorder recorder = new Recorder(seconds, filePath);mDatas.add(recorder);mAdapter.notifyDataSetChanged();mlistview.setSelection(mDatas.size() - 1);}});mAdapter = new RecorderAdapter(this, mDatas);mlistview.setAdapter(mAdapter);mlistview.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// 播放动画if (viewanim!=null) {//让第二个播放的时候第一个停止播放viewanim.setBackgroundResource(R.drawable.adj);viewanim=null;}viewanim = view.findViewById(R.id.show_anim01);viewanim.setBackgroundResource(R.drawable.play);AnimationDrawable drawable = (AnimationDrawable) viewanim.getBackground();drawable.start();// 播放音频Log.i("info", "position==="+position);MediaManager.playSound(mDatas.get(position).filePathString,new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {viewanim.setBackgroundResource(R.drawable.adj);}});}});}@Overrideprotected void onPause() {// TODO Auto-generated method stubsuper.onPause();MediaManager.pause();}@Overrideprotected void onResume() {// TODO Auto-generated method stubsuper.onResume();MediaManager.resume();}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();MediaManager.release();}class Recorder {float time;String filePathString;public Recorder(float time, String filePathString) {super();this.time = time;this.filePathString = filePathString;}public float getTime() {return time;}public void setTime(float time) {this.time = time;}public String getFilePathString() {return filePathString;}public void setFilePathString(String filePathString) {this.filePathString = filePathString;}}}

要完整的Demo下载地址:

http://download.csdn.net/detail/zhang106209/9528686

Android 仿微信语音识别相关推荐

  1. android 调用微信语音识别,Android 仿微信语音识别

    参考于:Android模仿微信语音聊天功能,这代码跑起来有问题,自己改动了一下,基本上没什么大问题 先贴下效果图 1.三个布局文件 activity_main.xml dialog_manger.xm ...

  2. php仿微信底部菜单,Android实现简单底部导航栏 Android仿微信滑动切换效果

    Android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义View配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏 ...

  3. android仿微信的activity平滑水平切换动画,Android实现简单底部导航栏 Android仿微信滑动切换效果...

    Android实现简单底部导航栏 Android仿微信滑动切换效果 发布时间:2020-10-09 19:48:00 来源:脚本之家 阅读:96 作者:丶白泽 Android仿微信滑动切换最终实现效果 ...

  4. Android 仿微信小视频录制

    Android 仿微信小视频录制 WechatShortVideo和WechatShortVideo文章

  5. Android自定义弹窗模仿微信,Android仿微信右上角点击加号弹出PopupWindow

    本文实例为大家分享了Android仿微信右上角点击加号弹出展示的具体代码,供大家参考,具体内容如下 一.要弹出的布局,随便设计 android:layout_width="match_par ...

  6. android格式化时间中文版,Android 仿微信聊天时间格式化显示功能

    本文给大家分享android仿微信聊天时间格式化显示功能. 在同一年的显示规则: 如果是当天显示格式为 HH:mm 例:14:45 如果是昨天,显示格式为 昨天 HH:mm 例:昨天 13:12 如果 ...

  7. android滑动菜单图标,Android实现简单底部导航栏 Android仿微信滑动切换效果

    Android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义View配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏 ...

  8. android 底部滑动效果怎么做,Android实现简单底部导航栏 Android仿微信滑动切换效果...

    android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义view配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏 ...

  9. android放微信@功能,Android仿微信语音消息的录制和播放功能

    一.简述 效果: 实现功能: 长按Button时改变Button显示文字,弹出Dialog(动态更新音量),动态生成录音文件,开始录音: 监听手指动作,规定区域.录音状态下手指划出规定区域取消录音,删 ...

最新文章

  1. 别在 Java 代码里乱打日志了,这才是正确的打日志姿势
  2. tomcat远程调试
  3. 我的 Serverless 实战 — Serverless 架构理念 ( 后端服务器发展 | Serverless 与 ServerFul | Serverless 定义 | 架构优缺点 )
  4. 个人管理 - 程序员的四个阶段
  5. Services overview
  6. Java中的低延迟FIX引擎
  7. Error inflating class xxx.view
  8. oracle ora-01476: 除数为 0,Oracle常见错误:ORA-01403 的根本原因和解决方案
  9. python灰色预测_灰色系统预测GM(1,1)模型
  10. (Java 多线程系列)java synchronized详解
  11. Boost Graph
  12. Spring或Hibernate其实都提供了透明处理Clob的方法
  13. 在虚拟主机上部署ASP.NET AJAX 1.0 Beta的程序集
  14. 80.共享内存实现进程通信
  15. 机器学习sklearn中决策树模型参数释义
  16. X.U.S.T的《自己搭建IIS找ASP程序漏洞》发表在《黑客X档案》,稿费捐给希望工程
  17. 一分钟了解QPS TPS RPS
  18. AIOT:基于智能家居谈AIOT
  19. 用 RIME 定制输入法
  20. 锁定计算机提示字母k但无用,我的键盘字母GH数字键全按不起

热门文章

  1. 对计算机辅助英语教学的建议,CALL引入我国后英语教师面临困难及建议.doc
  2. 国内机器视觉发展如何实现逆风翻盘?
  3. 数据可视化图表插件_7个最佳数据可视化WordPress插件(图表和图表)
  4. 怎样把m4a转换成mp3格式?
  5. 【AI核心技术】课程二十五:机器也可以写诗——中文诗歌生成网络初探
  6. 【VUE】vue3.0后台常用模板
  7. CSAPP实验四:性能优化实验(Perflab)
  8. 高级渗透之VBS调用WMI接口
  9. 如何清理华为云空间的照片
  10. ECC RDIMM 服务器内存条简介