参考于:Android模仿微信语音聊天功能,这代码跑起来有问题,自己改动了一下,基本上没什么大问题

先贴下效果图

1、三个布局文件

activity_main.xml

dialog_manger.xml

item_layout

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 stub

if (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 stub

if (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);

}

}

// 隐藏dialog

public void dimissDialog() {

// TODO Auto-generated method stub

if (mDialog != null && mDialog.isShowing()) {

mDialog.dismiss();

mDialog = null;

}

}

public void updateVoiceLevel(int level) {

// TODO Auto-generated method stub

if (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() {

@Override

public boolean onLongClick(View v) {

// TODO Auto-generated method

mReady = 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;

}

// 获取音量大小的runnable

private Runnable mGetVoiceLevelRunnable = new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

while (isRecording) {

try {

Thread.sleep(100);

mTime += 0.1f;

mhandler.sendEmptyMessage(MSG_VOICE_CHANGE);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.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的消息

@Override

public void wellPrepared() {

// TODO Auto-generated method stub

mhandler.sendEmptyMessage(MSG_AUDIO_PREPARED);

}

/**

* 直接复写这个监听函数

*/

@Override

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

int 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事件,没有的话直接返回reset

if (!mReady) {

reset();

return super.onTouchEvent(event);

}

// 如果按的时间太短,还没准备好或者时间录制太短,就离开了,则显示这个dialog

if (!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释放一个mediarecorder

if (mListener!=null) {// 并且callbackActivity,保存录音

mListener.onFinished(mTime, mAudioManager.getCurrentFilePath());

}

} else if (mCurrentState == STATE_WANT_TO_CANCEL) {

// cancel

mAudioManager.cancel();

mDialogManager.dimissDialog();

}

reset();// 恢复标志位

break;

}

return super.onTouchEvent(event);

}

/**

* 回复标志位以及状态

*/

private void reset() {

// TODO Auto-generated method stub

isRecording = 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;

}

}

}

@Override

public 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);

// 设置文件音频的输出格式为amr

mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);

// 设置音频的编码格式为amr

mRecorder.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 block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 随机生成文件的名称

*

* @return

*/

private String generalFileName() {

//UUID.randomUUID().toString()是javaJDK提供的一个自动生成主键的方法。

return UUID.randomUUID().toString() + ".amr";

}

// 获得声音的大小

public int getVoiceLevel(int maxLevel) {

// mRecorder.getMaxAmplitude()这个是音频的振幅范围,值域是1-32767

if (isPrepared) {

try {

// 取证+1,否则去不到7

return 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 {

private LayoutInflater inflater;

private int mMinItemWith;// 设置对话框的最大宽度和最小宽度

private int mMaxItemWith;

public RecorderAdapter(Context context, List 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);

}

@Override

public 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() {

@Override

public 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 mAdapter;

private View viewanim;

private List mDatas = new ArrayList();

@Override

protected 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() {

@Override

public void onFinished(float seconds, String filePath) {

// TODO Auto-generated method stub

Recorder 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() {

@Override

public 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() {

@Override

public void onCompletion(MediaPlayer mp) {

viewanim.setBackgroundResource(R.drawable.adj);

}

});

}

});

}

@Override

protected void onPause() {

// TODO Auto-generated method stub

super.onPause();

MediaManager.pause();

}

@Override

protected void onResume() {

// TODO Auto-generated method stub

super.onResume();

MediaManager.resume();

}

@Override

protected void onDestroy() {

// TODO Auto-generated method stub

super.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;

}

}

}

android 调用微信语音识别,Android 仿微信语音识别相关推荐

  1. Android 二维码扫描(仿微信界面),根据Google zxing

    Android 二维码扫描(仿微信界面),根据Google zxing Android项目开发中经常会用到二维码扫描,例如登陆.支付等谷歌方面已经有了一个开源库(地址: https://github. ...

  2. Android 使用 CameraX 快速实现仿微信短视频录制

    Android 使用 CameraX 快速实现仿微信短视频录制(轻触拍照.长按录像) https://github.com/ldlywt/MyCameraX 微信短视频android端 https:/ ...

  3. android微信运动页面开发,微信小程序仿微信运动步数排行(交互)

    微信小程序仿微信运动步数排行(交互) 发布时间:2020-08-20 00:51:02 来源:脚本之家 阅读:101 作者:祈澈姑娘 本文介绍了微信小程序仿微信运动步数排行(交互),分享给大家,也给自 ...

  4. 微信小程序仿微信SlideView组件slide-view

    微信小程序仿微信SlideView组件. 使用 1.安装 slide-view 从小程序基础库版本 2.2.1 或以上.及开发者工具 1.02.1808300 或以上开始,小程序支持使用 npm 安装 ...

  5. Java微信运动步数排序设计_微信小程序仿微信运动步数排行(交互)

    本文介绍了微信小程序仿微信运动步数排行(交互),分享给大家,也给自己留个笔记,废话不多说了,具体如下: 效果图如下: wxml: {{item.name}} {{item.steps}} wxss: ...

  6. Android开发之使用GridView+仿微信图片上传功能(附源代码)

    前言:如果转载文章请声明转载自:https://i.cnblogs.com/EditPosts.aspx?postid=7419021  .另外针对有些网站转载本人的文章结果源码链接不对的问题,本人在 ...

  7. android 微信创建群ui,Android控件:高仿微信主UI

    高仿微信主UI 之前在Android组件:Fragment切换后保存状态 一文中讲到了Fragment切换后,是如何保存原来的状态的,最重要的就是用add方法取代现在各种教程常见的replace方法. ...

  8. android仿微信充值布局,仿微信充值金额输入框-自定义EditText

    1. 概述 在开发过程中,有时候我们需要在输入框中输入金额,并且输入金额有一定的规则限制: 1>:只能是数字: 2>:第一位只能是数字,不能是小数点,如果第一位是小数点,就默认在小数点前加 ...

  9. android 微信底部渐变,仿微信底部导航栏BottomNavigation渐变色

    ezgif.com-video-to-gif.gif 不说废话了,直接上代码: 自定义View /** * * author : Harvey * time : 2018/06/14 * desc : ...

  10. Android studio心得——用fragment仿微信APP

    前言 今天我想与大家分享一些关于如何利用fragment实现仿微信APP的经验.作为社交领域最受欢迎.功能齐全且可扩展性强的应用之一,微信APP在浏览器首页和个人中心之上还有一个重要部分:底部导航栏. ...

最新文章

  1. 在Redis中设置了过期时间的Key,需要注意哪些问题?
  2. java实现线程同步的方法_Java实现线程同步方法及原理详解
  3. 结构体名和结构体名是个指针的区别
  4. Jenkins修改插件下载地址为国内下载地址与安装中文插件
  5. jekyll网站上传服务器,jekyll 高效搭建个人博客之完整流程
  6. Spring Cloud Gateway 源码解析(4)-- filter
  7. 循环链表(代码、分析、汇编)
  8. python mysql树_python操作mysql数据库
  9. 《像计算机科学家一样思考Python(第2版)》——2.7 注释
  10. python魅力_Python逐渐失去魅力
  11. 强人Hibernate文档笔记(下)
  12. IDEA 主题颜色设置
  13. Db2性能问题:临时表空间太大,导致连不上数据库
  14. 【操作系统】30天自制操作系统--(1)虚拟机加载最小操作系统
  15. [USACO10HOL]赶小猪
  16. php strstr批量替换,也就这样,
  17. AndroidStudio注解处理器无法生成文件
  18. vue中动态获取dom元素进行操作
  19. php 的单例模式(一)
  20. TypeError: slice indices must be integers 数组索引必须是整数

热门文章

  1. Python 用“*” 输出三角形的代码写法
  2. Linux这些年经历了什么?
  3. 计算机二级c好考吗,计算机二级MS Office和C语言哪个比较好考?
  4. 34.UCASE() LCASE() 函数
  5. 【ISO/IEC9126】ISO/IEC9126中软件质量模型品质介绍总结
  6. Unity3D本地文件夹的创建、删除(Directory文件夹删除和单个文件删除)
  7. 计算机32位如何变成64位,我电脑是32位的
  8. Python 改写__str__
  9. 纸壳CMS现已支持自定义扩展字段
  10. misc1-图片隐写