音频播放会出现很多情况,比如当我点击的是同一个音频按钮的时候,肯定就是控制音频的播放与暂停,显示时长的textview的暂停与继续。

当点击的音频按钮不是同一个的时候,

首先要重置上个点击的音频按钮相对应的音频时长的textview显示。

比如,第一次点击的是《萍聚》这首歌,时长4:01,应该立即播放此歌曲,并且时长进行倒计时4:01,4:00,3:59.....这样。

当第二次点击这首歌时,则需要暂停歌曲的播放,并暂停倒计时。

当在暂停状态点击其他的音频播放按钮的时候,

重置上次点击的音频,并重置与此对应的时间。根据前后两次点击的音频的url,判断是否需要重新获得mediaplayer实例。

比如,第一次点击的是《萍聚》,已经播放到2:21。第二次点击的是《五环之歌》,则需要把第一个的Mediaplayer释放掉,再重新得到一个五环之歌的mediaplayer。

并重置与《萍聚》相对应的textview的时长为4:01(即初始时长).

当activity切换的时候,转入后台不可见的时候,要释放掉mediaplayer。

下面请看代码。(时间比较紧,没有整理代码。肯定还有更好的解决办法。还请高手指正!)

  1.MediaPlayerUtils.java

import java.io.File;import android.app.ProgressDialog;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.SeekBar;
import android.widget.TextView;import com.ab.download.AbDownloadThread;
import com.ab.download.AbFileDownloader;
import com.ab.download.DownFile;
import com.ab.download.DownFileDao;
import com.ab.http.AbFileHttpResponseListener;
import com.ab.task.AbTaskItem;
import com.ab.task.AbTaskListener;
import com.ab.task.AbThread;
import com.ab.util.AbFileUtil;
/*** 2014-3-24下午4:19:16* * @author:hlloworld.Mr-zz* * @todo:MediaPlayer工具类,可以得到一个装载好的MediaPlayer,并且装载完毕后会自动播放一次 *                                                           1.首先会检查本地是否有指定路径(该路径有项目根目录*                                                           +voice+网络路径的文件名组成)*                                                           的音频文件 2.如果存在,直接装载播放*                                                           3.如果不存在,则异步播放网络音频*/
public class MediaPlayerUtils {private ProgressDialog pd;private MediaPlayer player = null;private Handler handler = null;private static Context context;private static MediaPlayerUtils instance = null;private UpdateSeekBarThread updateSeekBarThread;private int playPosition;private DownFile d;//断点续传的文件private AbFileDownloader loader;private MediaPlayerUtils() {}public static MediaPlayerUtils getInstance(Context context) {if (instance == null) {MediaPlayerUtils.context = context;instance = new MediaPlayerUtils();TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);telephonyManager.listen(new MyPhoneListener(),PhoneStateListener.LISTEN_CALL_STATE);}return instance;}public void initCountDownPlayer(final String voiceUrl,final Handler handler, final String time,final TextView tv) {// 如果sd卡能用,播放sd卡上的文件final File voice = new File(XingyunApplication.voiceDir+ FileUtil.getFileNameFromUrl(voiceUrl));if (CommonUtils.isSDCardExits()) {if (!voice.exists()) {// 播放网络文件
                playNetVoiceWithCountDown(voiceUrl, time,tv);// 如果sd卡中没有,先下载
                downUrl(voiceUrl, voice);
//                downUrl1(voiceUrl, voice, context);} else {try {player = player == null ? new MediaPlayer() : player;player.setOnCompletionListener(new OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {// TODO Auto-generated method stub
                            UpdateVoiceTimeThread.getInstance( time,tv).stop();}});player.setDataSource(voice.toString());player.prepare();player.start();UpdateVoiceTimeThread.getInstance( time,tv).start();Message msg = handler.obtainMessage();msg.obj = player;msg.what = Gloable.VOICE_DOWN_OK;handler.sendMessage(msg);} catch (Exception e) {// TODO Auto-generated catch block
                    e.printStackTrace();}}} else {playNetVoiceWithCountDown(voiceUrl, time,tv);}}private void playNetVoiceWithCountDown(final String voiceUrl,final String time,final TextView tv){// 如果sd卡不能用,直接播放網絡文件
                    showProgress();new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubUri uri = Uri.parse(voiceUrl);player = player == null ? MediaPlayer.create(context, uri): player;player.setOnCompletionListener(new OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {// TODO Auto-generated method stub
                                    UpdateVoiceTimeThread.getInstance( time,tv).stop();}});player.setOnPreparedListener(new OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {// TODO Auto-generated method stub
                                    dissmissProgress();player.start();UpdateVoiceTimeThread.getInstance( time,tv).start();Message msg = handler.obtainMessage();msg.obj = player;msg.what = Gloable.VOICE_DOWN_OK;handler.sendMessage(msg);}});}}).start();}/*** 显示无边框透明的progressdialog*/private void showProgress() {pd = new CustomProgressDialog(context);pd.setCanceledOnTouchOutside(false);pd.show();}/*** 隐藏无边框透明的progressdialog*/private void dissmissProgress() {if (pd == null) {return;}pd.dismiss();}public void releasePlayer() {if (player != null) {if (player.isPlaying()) {player.stop();}player.release();player = null;}if (pd != null) {pd.dismiss();pd = null;}context = null;instance = null;if (updateSeekBarThread != null) {handler.removeCallbacks(updateSeekBarThread);}}/*** 只有电话来了之后才暂停音乐的播放*/static class MyPhoneListener extends android.telephony.PhoneStateListener {@Overridepublic void onCallStateChanged(int state, String incomingNumber) {switch (state) {case TelephonyManager.CALL_STATE_RINGING:// 电话来了if (instance!=null) {instance.callIsComing();}break;case TelephonyManager.CALL_STATE_IDLE: // 通话结束if (instance!=null) {instance.callIsDown();}break;}}}/*** 来电话了*/public void callIsComing() {if (player.isPlaying()) {playPosition = player.getCurrentPosition();// 获得当前播放位置
            player.stop();}}/*** 通话结束*/public void callIsDown() {if (playPosition > 0) {player.seekTo(playPosition);playPosition = 0;}}}}

控制textview倒计时的countdownTimer

import java.text.SimpleDateFormat;
import android.media.MediaPlayer;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.sax.StartElementListener;
import android.widget.SeekBar;
import android.widget.TextView;/*** 2014-3-26下午12:28:44* * @author:crazyhelloworld.Mr-zz* @todo:*/
public class UpdateVoiceTimeThread {private static CountDownTimer cdt;private final static int TIME_CHANGE_DELAY = 1000;private final static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");private static String time;private static TextView tvv;private static UpdateVoiceTimeThread instance = null;public static long l;public static UpdateVoiceTimeThread getInstance( final String stime,final TextView tv){if (instance==null) {synchronized (UpdateVoiceTimeThread.class) {if (instance == null) {instance = new UpdateVoiceTimeThread();time = stime;tvv = tv;String smin = stime.substring(0, stime.indexOf(":"));String ssec = stime.substring(stime.indexOf(":") + 1, stime.length());int min = Integer.parseInt(smin);int sec = Integer.parseInt(ssec);l = (min * 60 + sec) * 1000;getTimer();
//                    cdt.start();
                }}}return instance;}private UpdateVoiceTimeThread(){};private static CountDownTimer getTimer(){if (cdt!=null) {cdt.cancel();cdt = null;}cdt = new CountDownTimer(l, TIME_CHANGE_DELAY) {@Overridepublic void onTick(long millisUntilFinished) {// TODO Auto-generated method stub
                tvv.setText(sdf.format(millisUntilFinished));l = l - TIME_CHANGE_DELAY;}@Overridepublic void onFinish() {
                tvv.setText(time);

            }};return cdt;}public void start() {getTimer();cdt.start();}public void pause(){cdt.cancel();tvv.setText(sdf.format(l));}public void stop(){instance = null;if (cdt!=null) {cdt.cancel();cdt = null;}tvv.setText(time);}}

adapter中声明的判断属性及handler

    /*** 根据前后两次点击的地址判断是否需要重新加载mediaplayer的标识*/private static int flag = 0;/*** 音频播放的实例*/public static MediaPlayer player = null;/*** 为了比对前后两次点击请求的音频地址是否相同。与issame结合使用,* 如果issame=true,则点击的是同一个按钮。直接播放或者暂停* 如果issame=false,再判断当前点击的与按钮的音频地址与tempvoiceurl是否一致* 如果一致,stop上一个的播放,恢复tvlength的值为初始值。* 如果不一致,直接播放*/private String tempVoiceUrl = "";/*** 当前点击的iv后面的时间textview*/private static TextView tv;/*** 当前点击的音频的长度*/private static String time;/*** 当前点击的容器(ImageView)*/private View tempView;/*** 上个音频的时长*/private String tempTime;/*** 上个音频时长的容器*/private TextView tempTv;/*** 前后两次点击的是否是一个imageview的标识*/private boolean isSame = false;private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case Gloable.VOICE_DOWN_OK:player = (MediaPlayer) msg.obj;break;default:break;}};};

adapter中音频的点击事件

holder.voice.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (!tempVoiceUrl.equals(p.getVoiceUrl())) {MediaPlayerUtils.getInstance(context).releasePlayer();flag = 0;}tv = (TextView) ((LinearLayout) v.getParent()).getChildAt(2);if (v == tempView) {isSame = true;} else {tempView = v;isSame = false;}UpdateVoiceTimeThread.getInstance(p.getVoiceTime(), tv).stop();time = p.getVoiceTime();playVoice(p.getVoiceUrl(), time);}});

adapter中按钮的点击事件调用的播放方法

private void playVoice(String url, String time) {if (flag == 0) {if (NetUtils.isNetworkAvailable(context)) {MediaPlayerUtils.getInstance(context).initCountDownPlayer(url,handler, time, tv);tempVoiceUrl = url;flag = -1;} else {Toast.makeText(context,context.getString(R.string.network_not_avaliable), 1).show();}} else if (player != null) {if (isSame) {if (!player.isPlaying()) {player.start();if (UpdateVoiceTimeThread.l == 0) {UpdateVoiceTimeThread.getInstance(time, tv).start();} else {UpdateVoiceTimeThread.getInstance(CommonUtils.getStringDate(UpdateVoiceTimeThread.l),tv).start();}} else {player.pause();UpdateVoiceTimeThread.getInstance(CommonUtils.getStringDate(UpdateVoiceTimeThread.l),tv).pause();}tempTv = tv;tempTime = time;} else {player.seekTo(0);player.start();UpdateVoiceTimeThread.getInstance(tempTime, tempTv).stop();UpdateVoiceTimeThread.getInstance(time, tv).start();}}}public static void releasePlayer() {MediaPlayerUtils.getInstance(context).releasePlayer();if (player != null) {player.release();player = null;}flag = 0;if (time != null && tv != null) {UpdateVoiceTimeThread.getInstance(time, tv).stop();}}

adapter中释放player的方法

public static void releasePlayer() {MediaPlayerUtils.getInstance(context).releasePlayer();if (player != null) {player.release();player = null;}flag = 0;if (time != null && tv != null) {UpdateVoiceTimeThread.getInstance(time, tv).stop();}}

在activity的onPause()回调方法里调用adapter的releasePlayer()方法。

记录一下,希望可以帮到一些人,更希望大神给予指点。

转载于:https://www.cnblogs.com/MrZz/p/3663775.html

【android-音视频】listview中播放音频,实现音频时长的倒计时,暂停,切换。相关推荐

  1. Android音视频开发,详说PCM音频重采样、PCM编码

    直播伴音,两种数据能否合在一起?不能叠加在一起 会有噪音 合并以后 再去编码推流 直播的例子 客户端播放器,可以开启多个播放器 对于我们重采样 很多时候就是为了统一格式,就是为了要合并这个流,去推送, ...

  2. audiotrack android,Android 音视频渲染-AudioTrack 播放

    类型 MediaPlayer:原生API中封装最全的 SoundPool:适合播放较短的音频 AudioTrack:底层的音频 API,需要自己解码,只能播放 PCM 裸数据和 WAV AudioTr ...

  3. 关于在Android音视频开发中,Google API的MediaCodeC与成熟开源编码器X264的应用对比及使用场景

    在2019年的一个大项目中,有一个功能模块让笔者感触颇深,那就是实时音视频的预览,当然这不是普通的开开直播,画面出来了就完了那么简单,如果你是一个开发者,那么你肯定知道同样大小的一张图片里,色彩丰富的 ...

  4. Excel催化剂开源第37波-音视频文件元数据提取(分辨率,时长,采样率等)

    上一篇提到图片元信息Exif的提取,当然还有一类音视频文件,也同样存储着许多宝贵的元数据,那就开源到底呗,虽然自己找寻过程也是蛮艰辛坎坷的,大家看后有收获,只求多多传播下,让前人的工作可以更有价值. ...

  5. Android音视频点/直播模块开发

    前言 随着音视频领域的火热,在很多领域(教育,游戏,娱乐,体育,跑步,餐饮,音乐等)尝试做音视频直播/点播功能,那么作为开发一个小白,如何快速学习音视频基础知识,了解音视频编解码的传输协议,编解码方式 ...

  6. Android音视频点/直播模块开发实践总结-zz

    随着音视频领域的火热,在很多领域(教育,游戏,娱乐,体育,跑步,餐饮,音乐等)尝试做音视频直播/点播功能.那么作为开发一个小白,如何快速学习音视频基础知识,了解音视频编解码的传输协议,编解码方式,以及 ...

  7. android音/视频,直播

    流媒体 采用流式传输的方式在Internet / Intranet播放的媒体格式.流媒体的数据流随时传送随 时播放,只是在开始时有些延迟.边下载边播入的流式传输方式不仅使启动延时大幅度地缩短,而且对系 ...

  8. Android音视频开发基础(六):学习MediaCodec API,完成视频H.264的解码

    前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了<Android 音视频从入门到提高 - 任务列表>.本文是Android音视 ...

  9. Android音视频开发--FFmpeg

    音视频的基础知识 视频 静止的画面叫图像(picture): 连续的图像变化每秒超过24帧(frame)画面以上时,根椐视觉暂留原理,人眼无法辨别每付单独的静态画面,看上去是平滑连续的视觉效果,这样的 ...

  10. Android音视频开发基础(七):视频采集-系统API基础

    前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了<Android 音视频从入门到提高 - 任务列表>.本文是Android音视 ...

最新文章

  1. 微信内置浏览器无法清除缓存问题
  2. 深圳大学计算机英语作业答案,2016年深圳大学大学计算机基础mooc课第四章答案...
  3. 【嵌入式】C语言高级编程-地址对齐(07)
  4. 你以为有白金卡就很牛逼?错!
  5. Python 内置模块之 asyncio(异步iO)
  6. SpringCloud Nacos + Ribbon 调用服务的 2 种方法!
  7. QT 多线程程序设计 -互斥
  8. 如何理解Cookie、Session和Token
  9. 哨兵1号(Sentinel-1)与陆地探测1号(L-SAR)对比
  10. 在Hive中使用过的函数记录(百分比、截取字符串、分组TOP N、日期转换、日期是第几周)
  11. WIFI和路由器密码破解的方法
  12. msrcr图像增强算法 matlab,图像处理之Retinex增强算法(SSR、MSR、MSRCR)
  13. hp服务器改系统启动项,hp笔记本如何进入bios修改启动项
  14. 中文期刊分类(blog版)
  15. 达梦数据库ZYJ实例安装初始化
  16. Downward paths(数论,思维)
  17. 程序员的键盘使用指南
  18. 趣店罗敏:从寒门贵子到面临退市 再到抢占预制菜新风口
  19. 年薪30万IT精英 挥别都市回乡种田务农
  20. Qt6-在线获取和安装

热门文章

  1. php7.4报错:Trying to access array offset on value of type null
  2. html页面 request,HTML DOM requestFullscreen() 方法
  3. 可恨的KYLIN OS:动不动就不支持老机
  4. VirtualBox虚拟机如何选中“启用嵌套 VT-x/AMD-V”
  5. dd: 写入‘/EMPTY‘ 出错: 设备上没有空间
  6. 不考虑知识点,考代码段更好
  7. 空间如何超越极小而存在
  8. mysql.server的路径_WindowsMysqlServer重启,log-bin路径配置
  9. innobackupex 恢复到mysql目录_innobackupex备份mysql恢复后迁移到新的mysql实例
  10. linux环境sphinx搭建,linux系统环境下搭建coreseek(sphinx+mmseg3)