程序主界面

  • 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相关推荐

  1. 视频教程-FFmpeg打造Android万能音频播放器-Android

    FFmpeg打造Android万能音频播放器 从事Android移动端开发多年.主导开发过直播.电商.聊天等各种类型APP和游戏SDK:熟悉Android音视频开发.底层NDK开发等:有开源项目:ht ...

  2. Android录音笔-音频播放器,不止适用于保存的录音

    Android录音笔-音频播放器,不止适用于保存的录音 功能说明 点击录音列表项弹出播放对话框 对话框碎片的布局dialog_fragment.xml 对话框碎片MyDialog.java 对话框碎片 ...

  3. android 以音频播放器为例实现通知栏显示通知,并实现切歌、暂停、播放,并实现加载网络图片,并实现关闭第三方APP音频

    首先先给大家看下效果 接下来我们看下具体如何实施 1.首先我们创建一个音频的单例对象,这样能保证每次在播放的的音频是唯一的(类名如:MediaPlayerUtil.java) package xxx; ...

  4. android视频恢复播放器,AndroidVideoPlayer在线播放视频

    AndroidVideoPlayer在线播放视频 AndroidVideoPlayer在线播放视频,自定义SuperVideoPlayer里面封装了startPlayVideo()播放视频 loadA ...

  5. android 播放视频结束回调,Android万能音频播放器09-添加Seek功能和完成播放回调...

    1.Seek功能 播放本地音视频才有用 对AVFormatContext加锁,因为在seek的时候,不能继续播放,也就是不能继续读取frame了: pthread_mutex_lock(&se ...

  6. Android MP3音频播放器 仿唱片机播放动画,仿网易云播放动画,旋转动画,MediaPlayer AudioManager

    废话不多说上代码 private AudioManager audioManager;private SimpleDateFormat format;private SeekBar seekBar;p ...

  7. android浏览器音频播放器,javascript – 在Android浏览器中播放html5音频

    我有一个JavaScript在浏览器中播放音频,使用html5< audio>标签.它在iPhone浏览器中工作正常,但不在Android中. (使用Android 2.1测试使用htc愿 ...

  8. android音乐播放器暂停播放,Android万能音频播放器07--添加停止播放功能并释放内存...

    1.释放所分配的内存 释放顺序:释放队列->释放OpenSL->释放Audio->释放FFmpeg 1.1.释放队列 JfQueue.cpp void JfQueue::clearA ...

  9. Android Studio——简易音频播放器

    目的 设计一个具有选歌功能的音频播放器 工具及环境 使用java语言,在Android studio平台上进行开发 功能设计 界面有三个按钮选项,可以停止.播放.暂停音乐.通过选择列表的音乐,播放相应 ...

最新文章

  1. Linux查看CPU和内存使用情况详解
  2. 怎么在html中写当前时间,html页面怎么获取当前时间
  3. pose2pose 姿态迁移
  4. python的垃圾回收机制和析构函数__del__
  5. 在unity调用WebService的接口方法
  6. ssl1010-方格取数
  7. 最年轻图灵奖女性得主:谁说女的数学都比男的差
  8. java integer == int_通过实例了解Java Integer类和int的区别
  9. UIPickView 和 UIDatePicker
  10. QQ企业邮箱+Spring+Javamail+ActiveMQ(发送企业邮件)
  11. model存数据_数据库内核杂谈 存储
  12. centos7下安装pycharm
  13. 视频结构化+AI,智能安防的未来
  14. pandas 两个DataFrame带条件匹配
  15. C语言 简单实现计算器功能 ·函数指针数组实现计算器
  16. Python Django 学习 (二) 【Django 模型】
  17. linux时间同步到win7,mac与win7时间不同步怎么办_mac与win7时间不准如何解决
  18. YUV与RGB互转各种公式 (YUV与RGB的转换公式有很多种,请注意区别)
  19. android opengl版本太低,安卓模拟器opengl_安卓模拟器无法安装“系统opengl版本过低”的通用解决方法_安卓模拟器通用版_通用安卓模拟器...
  20. 最新C语言深入剖析班项目实战教程(国嵌 唐老师主讲)

热门文章

  1. Win10+GeForce 940M(CUDA8.0)+Visual Studio 2015的GPU环境配置步骤
  2. 《腾讯网UED体验设计之旅》总结:尼尔森启发式评估十原则
  3. 从零开始打造专属钉钉机器人
  4. 渲染管线中几种基础的坐标空间(对象空间、世界空间、相机空间(观察空间)、NDC空间、裁剪空间、屏幕空间)
  5. C++之数据类型转换(全)
  6. 关于日期格式的处理的小知识
  7. js内置对象 BigInt
  8. windows10如何强制杀进程
  9. Flocking算法0705
  10. 国科大科技信息检索课程笔记