android 视频+音频播放器Demo
程序主界面
- MainActivity.java
1.主界面,头部是两个TextView(自定义类似指针效果),底部是ViewPager。ViewPager中每个页面对应的是一个Fragment.这样就搭起了首页。
xml文件代码:
<?xml version="1.0" encoding="utf-8"?>
<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"android:background="@mipmap/base_bg"tools:context=".activity.MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="55dp"android:orientation="vertical"android:background="@mipmap/base_titlebar_bg"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="horizontal"android:gravity="center_vertical"><TextViewandroid:id="@+id/tv_audio"style="@style/mainActivity_indicator"android:text="@string/audio"/><TextViewandroid:id="@+id/music"style="@style/mainActivity_indicator"android:text="@string/music" /></LinearLayout><Viewandroid:id="@+id/indicator"android:layout_width="30dp"android:layout_height="3dp"android:background="@color/green"/></LinearLayout><android.support.v4.view.ViewPagerandroid:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="wrap_content"/>
</LinearLayout>
2.当选中音频或者视频时,TextView的文字颜色和大小都改变。
/*** 选择:音频 / 音乐* @param isAudio 是选择了音频*/public void changeSelectedIndicator(boolean isAudio){mAudio.setSelected(isAudio);mMusic.setSelected(!isAudio);float ascale = isAudio ? 1.2f : 1.0f;float mscale = isAudio ? 1.0f : 1.2f;ViewPropertyAnimator.animate(mAudio).scaleX(ascale);ViewPropertyAnimator.animate(mAudio).scaleY(ascale);ViewPropertyAnimator.animate(mMusic).scaleX(mscale);ViewPropertyAnimator.animate(mMusic).scaleY(mscale);}
3.滑动指示先与ViewPager的结合。
<span style="color:#000000;">/*** 滑动指示线* @param position* @param positionOffsetPixels*/protected void scrollIndicator(int position, int positionOffsetPixels) {int translationX = mIndicatorWidth * position + positionOffsetPixels / pageSize ;LogUtil.i("qd","translationX =="+translationX );ViewHelper.setTranslationX(mIndicator, translationX);}</span><pre name="code" class="java"><span style="color:#000000;"> mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {LogUtil.i("qd", "position===" + position + " positionOffset=" + positionOffset + " positionOffsetPixels=" + positionOffsetPixels);scrollIndicator(position, positionOffsetPixels);}@Overridepublic void onPageSelected(int position) {changeSelectedIndicator(position == 0);}@Overridepublic void onPageScrollStateChanged(int state) {}});</span>
4,给ViewPager设置FragmentStatePagerAdapter,他的构造需要FragmentManager,所以MainActivity需要继承自FragmentActivity
fragmentAdapter.addItem(new VideoFragment());fragmentAdapter.addItem(new MusicFragment());
- VideoFragment.java(视频模块)
新建了BaseFragment这样一个基类。设计思想,1.视频和音频的列表页面其实大致相同,只是显示的具体数据不同,所以可以他他们共同的东西抽取出来成为一个
public abstract class BaseFragment<T> extends Fragment implements BaseInterface
2.每个类都会涉及到,初始化view,初始化data,设置监听等。所以,把这三个方法涉及成一个Interface,由每个需要的类来进行实现。
3.每个Fragment都会执行onCreateView,只是他们的布局不同,抽取出getLayoutId(),返回每个子类具体的布局。并且在onViewCreated方法中,调用initView,initData.
这样子类只需要实现这几个方法即可。
4.如何查询手机当中的视频? 音频资源???
android将这些的信息都封装进了本地数据库中,我们只需要按照指定的格式查询数据库即可。
android 提供了异步查询数据库的一个类,AsyncQueryHandler,他使用了内容观察者模式,每次数据库发生变动时,他返回的Cursor都会变化。
<span style="font-size:18px;"> AsyncQueryHandler task = new AsyncQueryHandler(getActivity().getContentResolver()) {@Overrideprotected void onQueryComplete(int token, Object cookie, Cursor cursor) {super.onQueryComplete(token, cookie, cursor);if(cursor != null){mListView.setAdapter(getAdapter(getActivity(), cursor));}else{LogUtil.i("qd","audioFragment cursor == null");}}};
task.startQuery(0,null, getUri(), getProjection(),null,null,getOrderBy());
</span>
音频和视频的区别就在于这些参数上
<span style="font-size:18px;"> task.startQuery(0,null, getUri(), getProjection(),null,null,getOrderBy());</span>
所以,我们抽取成abstract方法,让具体的子类来实现。
音频的地址:
<span style="font-size:18px;">MediaStore.Audio.Media.EXTERNAL_CONTENT_URI</span>
视频的地址:
<span style="font-size:18px;">MediaStore.Video.Media.EXTERNAL_CONTENT_URI</span>
5.查询成功后,会返回一个cursor,其中包括了数据,将数据设置到adapter中,而且这个adapter需要继承自CursorAdapter
<span style="font-size:18px;">public class VideoAdapter extends CursorAdapter {public VideoAdapter(Context context, Cursor c) {super(context, c);}@Overridepublic View newView(Context context, Cursor cursor, ViewGroup parent) {View view = LayoutInflater.from(context).inflate(R.layout.video_list_item, null);ViewHolder holder = new ViewHolder();holder.title = (TextView) view.findViewById(R.id.title);holder.duration = (TextView) view.findViewById(R.id.duration);holder.size = (TextView) view.findViewById(R.id.size);view.setTag(holder);return view;}@Overridepublic void bindView(View view, Context context, Cursor cursor) {ViewHolder holder = (ViewHolder) view.getTag();VideoItem videoItem = VideoItem.fromCursor(cursor);holder.title.setText(videoItem.getTitle());holder.duration.setText(TimeUtil.formatLong(videoItem.getDuration()));holder.size.setText(Formatter.formatFileSize(context, videoItem.getSize()));}class ViewHolder{public TextView title;public TextView duration;public TextView size;}
}
</span>
6.最后设置当点击条目的时候,页面进行跳转。将条目的位置和查询到的所有数据全都传递过去。
- 音频播放页面
利用了android中的VideoView来进行播放视频。
主要涉及到了一下几个方法。
<span style="font-size:18px;">//1.设置播放路径
mVideoView.setVideoPath(item.getPath());
</span>
<span style="font-size:18px;">//2.视频准备好的监听
mVideoView.setOnPreparedListener(preparedListener);
</span><pre name="code" class="java"><span style="font-size:18px;">MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener(){@Overridepublic void onPrepared(MediaPlayer mp) {//3.切记需要,先启动,因为接下来需要获取他播放的位置,否在的话会报异常。mVideoView.start();if(item != null){mTitle.setText(item.getTitle());}mDuration.setText(TimeUtil.formatLong(mVideoView.getDuration()));mSeekBarVideo.setMax((int) </span><pre name="code" class="java"><span style="font-size:18px;">mVideoView.getDuration()</span>
); updateVideoCurrentPosition(); LogUtil.i("qd", "preparedListener selectPosition===" + selectPosition); loading.setVisibility(View.GONE); } }; 4.获取视频的播放时长和当前时长
<span style="font-size:18px;">mVideoView.getDuration()</span>
<span style="font-size:18px;">mVideoView.getCurrentPosition()</span>
5.设置视频播放前的监听
<span style="font-size:18px;">//缓冲视频前的准备
mVideoView.setOnInfoListener(onInfoListener);
</span><pre name="code" class="java"><span style="font-size:18px;">MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener(){@Overridepublic boolean onInfo(MediaPlayer mp, int what, int extra) {switch (what){case MediaPlayer.MEDIA_INFO_BUFFERING_START: //缓冲开始,设置加载页面可见loading.setVisibility(View.VISIBLE);break;case MediaPlayer.MEDIA_INFO_BUFFERING_END: //缓冲结束,设置加载页面不可见loading.setVisibility(View.GONE);break;}return false;}};</span>
6.设置视频缓存(针对网络视频,这个方法是用来更新第二进度条的)
<span style="font-size:18px;"> //视频缓冲 ----本地视频不存在缓冲一说
mVideoView.setOnBufferingUpdateListener(onBufferingUpdateListener);
</span><pre name="code" class="java"><span style="font-size:18px;">MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener(){@Overridepublic void onBufferingUpdate(MediaPlayer mp, int percent) {float f = (float)percent / 100;int secondaryProgress = (int) (f * mVideoView.getDuration());mSeekBarVideo.setSecondaryProgress(secondaryProgress);}};</span>
7.设置视频播放完的监听
<span style="font-size:18px;"> //视频播放完的监听mVideoView.setOnCompletionListener(completionListener);
</span><pre name="code" class="java"><span style="font-size:18px;"> MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener(){@Overridepublic void onCompletion(MediaPlayer mp) {mhandler.removeMessages(UPDATE_VIDEO_CURRENT_POSITION);}};</span>
8.当对屏幕进行手势识别的时候,用GestureDetector,在onTouch事件时,交给GestureDetector的onTouchEvent来处理。
- 万能视频播放器
主要用开源的Vitamio来实现。
1.首先,在清单文件中加入
<span style="font-size:18px;"> <activityandroid:name="io.vov.vitamio.activity.InitActivity"android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"android:launchMode="singleTop"android:theme="@android:style/Theme.NoTitleBar"android:windowSoftInputMode="stateAlwaysHidden" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</span>
2.在VideoPlayerActivity.java(视频播放页面)初始化数据时,加入
<span style="font-size:18px;"> // 初始化Vitamio SDKif (!LibsChecker.checkVitamioLibs(this))return;</span>
3.替换VideoView,成Vitamio的中的VideoView,在个类的路径
<span style="font-size:18px;">io.vov.vitamio.widget.VideoView</span>
这样就可以播放基本上所有类型的视频文件了,android默认只支持mp4格式的视频。
- 音频模块(AudioPlayerActivity.java 和 AudioPlayerService.java) 播放音乐需要服务。
Activity与Service如何交互?
1.使用AIDL
2.使用Messenger 这里主要讲解下次方式。
有点像,TCP/IP通讯,需要3此握手,才能建立连接、
第一步:
<span style="font-size:18px;">//开启服务startService(service);
//绑定服务,这样才能与其交互
bindService(service, conn, BIND_AUTO_CREATE);
</span>
第二步:
<span style="font-size:18px;"> //上面的开启服务方式,首先调用onStartCommand,然后调用onBind()@Overridepublic IBinder onBind(Intent intent) {return serviceMessenger.getBinder();}//在service中创建出一个信使,用来与Activity通信。</span><pre name="code" class="java"><span style="font-size:18px;">private Messenger serviceMessenger = new Messenger(mHandler);</span>
//创建handler,用来处理此信使获取到的信息
<span style="font-size:18px;">private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {}};</span>
第三步:
<span style="font-size:18px;">//在Activty中,当service与Activty连接上时会回调此方法。
ServiceConnection conn = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//获取service传递过来的信使Messenger serviceMessenger = new Messenger(service);//创建一条消息Message message = Message.obtain(null, AudioPlayService.WHAT_UI_INTEFACE);//携带UI类过去message.obj = AudioPlayerActivity.this;//告诉service,此UI的信使是哪个(这样,service就能拿到ui的信使,并用此信使,发送消息)message.replyTo = uiMessenger;//用service的信使,给service发送消息try {serviceMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};</span>
第四步:
<span style="font-size:18px;">//service中,处理自己的信使收到的消息
private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what){case WHAT_UI_INTEFACE://拿到,传递过来的UI对象audioUI = (AudioUI) msg.obj;//获取ui对象的信使Messenger uiMessenger = msg.replyTo;//创建消息Message message = Message.obtain(null, WHAT_PLAYSERVICE_INTERFACCE);//把service传递过去message.obj = AudioPlayService.this;//message.arg1 = flag ;//用UI类的信使,发送消息try {uiMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}break;}super.handleMessage(msg);}};
</span>
第五步:
service中拿到了Activity中的信使,所以用此信使传递消息。
<span style="font-size:18px;">//这样在Ativity中又收到了service的消息,这样即确定了Service与Activity建立了可靠的连接。
private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what){case AudioPlayService.WHAT_PLAYSERVICE_INTERFACCE://Activity 进行了三次握手,创建了连接audioPlayService = (IPlayAudio) msg.obj;if(msg.arg1 == -1){//开启音频audioPlayService.openAudio();}else{//从通知栏,点击进来额,机选刷新activity并不做其他处理updateUI(audioPlayService.getCurrentMusicItem());}break;case UPDATE_PLAY_TIME:updatePlayTime();break;case UPDATE_LYRICS:updateLyrics();break;}super.handleMessage(msg);}};</span>
这样就可以在service,activity中拿上对方的引用,来操作对方的方法,
视频 / 音频效果:
我开发中遇到的坑:
1.音频,视频的开发过程,基本不怎么报错,直接奔溃,要么是ANR,所以一定要细心,在加上Debug进行调试
2.控制台报错:client not yet。。。
Connected to the target VM, address: 'localhost:8603', transport: 'socket'
解决方案:其实还是模棱两可,碰出来的。
重新设置断点,然后在进行调试。
注意查看运行时是否是app(有时候是Activity)
3.studio开发当涉及到.so文件的正确导入方式
在gradle文件中加入下面这句话:
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
4.int类型的两个数,想出得到一个float类型的数值时,需要将除数,或者被除数转换为
float类型,否在得到的除数将== 0;
5.service生命周期
1.startService --> oncreate ---> onStartCommand --->onDestroy(必须调用stopService!!!!!此服务才能结束,否则再次打开应用的时候,可能出现ANR)
2.bindService --> onCreate ---> onBind -- > unUnBind() --> onDestroy
在activity销毁的时候,必须显示的调用unBindServce来接触绑定,销毁service
3.混合启动service startService --> bindService
---> 在activity销毁的时候,必须调用stopService()来停止服务,首先会调用onUnBind,在调用onDestroy
---> 只调用onUnbind()并不会关闭服务。 startService,必须stopService才能关闭掉
---> 如果unBindService,stopService都调用的话onUnbind ,onDestroy会按顺序调用一次。
我在开发音乐播放器的时候,在activity销毁的时候没有关闭service,下次再启动应用的时候出现白屏(也就是应用不能开启,最后会报ANR)
下载地址:
https://github.com/QDqiaodong/VideoAndAudio
android 视频+音频播放器Demo相关推荐
- 视频教程-FFmpeg打造Android万能音频播放器-Android
FFmpeg打造Android万能音频播放器 从事Android移动端开发多年.主导开发过直播.电商.聊天等各种类型APP和游戏SDK:熟悉Android音视频开发.底层NDK开发等:有开源项目:ht ...
- Android录音笔-音频播放器,不止适用于保存的录音
Android录音笔-音频播放器,不止适用于保存的录音 功能说明 点击录音列表项弹出播放对话框 对话框碎片的布局dialog_fragment.xml 对话框碎片MyDialog.java 对话框碎片 ...
- android 以音频播放器为例实现通知栏显示通知,并实现切歌、暂停、播放,并实现加载网络图片,并实现关闭第三方APP音频
首先先给大家看下效果 接下来我们看下具体如何实施 1.首先我们创建一个音频的单例对象,这样能保证每次在播放的的音频是唯一的(类名如:MediaPlayerUtil.java) package xxx; ...
- android视频恢复播放器,AndroidVideoPlayer在线播放视频
AndroidVideoPlayer在线播放视频 AndroidVideoPlayer在线播放视频,自定义SuperVideoPlayer里面封装了startPlayVideo()播放视频 loadA ...
- android 播放视频结束回调,Android万能音频播放器09-添加Seek功能和完成播放回调...
1.Seek功能 播放本地音视频才有用 对AVFormatContext加锁,因为在seek的时候,不能继续播放,也就是不能继续读取frame了: pthread_mutex_lock(&se ...
- Android MP3音频播放器 仿唱片机播放动画,仿网易云播放动画,旋转动画,MediaPlayer AudioManager
废话不多说上代码 private AudioManager audioManager;private SimpleDateFormat format;private SeekBar seekBar;p ...
- android浏览器音频播放器,javascript – 在Android浏览器中播放html5音频
我有一个JavaScript在浏览器中播放音频,使用html5< audio>标签.它在iPhone浏览器中工作正常,但不在Android中. (使用Android 2.1测试使用htc愿 ...
- android音乐播放器暂停播放,Android万能音频播放器07--添加停止播放功能并释放内存...
1.释放所分配的内存 释放顺序:释放队列->释放OpenSL->释放Audio->释放FFmpeg 1.1.释放队列 JfQueue.cpp void JfQueue::clearA ...
- Android Studio——简易音频播放器
目的 设计一个具有选歌功能的音频播放器 工具及环境 使用java语言,在Android studio平台上进行开发 功能设计 界面有三个按钮选项,可以停止.播放.暂停音乐.通过选择列表的音乐,播放相应 ...
最新文章
- Linux查看CPU和内存使用情况详解
- 怎么在html中写当前时间,html页面怎么获取当前时间
- pose2pose 姿态迁移
- python的垃圾回收机制和析构函数__del__
- 在unity调用WebService的接口方法
- ssl1010-方格取数
- 最年轻图灵奖女性得主:谁说女的数学都比男的差
- java integer == int_通过实例了解Java Integer类和int的区别
- UIPickView 和 UIDatePicker
- QQ企业邮箱+Spring+Javamail+ActiveMQ(发送企业邮件)
- model存数据_数据库内核杂谈 存储
- centos7下安装pycharm
- 视频结构化+AI,智能安防的未来
- pandas 两个DataFrame带条件匹配
- C语言 简单实现计算器功能 ·函数指针数组实现计算器
- Python Django 学习 (二) 【Django 模型】
- linux时间同步到win7,mac与win7时间不同步怎么办_mac与win7时间不准如何解决
- YUV与RGB互转各种公式 (YUV与RGB的转换公式有很多种,请注意区别)
- android opengl版本太低,安卓模拟器opengl_安卓模拟器无法安装“系统opengl版本过低”的通用解决方法_安卓模拟器通用版_通用安卓模拟器...
- 最新C语言深入剖析班项目实战教程(国嵌 唐老师主讲)