基于android平台的视频播放器设计论文
四川理工学院毕业设计
基于android平台的视频播放器设计
学 生:徐东 学 号:12021040122 专 业:电气工程及其自动化 班 级:2012.卓越 指导教师:曾晓辉、刘兴忠
四川理工学院自动化与电子信息学院
二O一六年六
徐东:基于Android平台的视频播放器设计
摘要:随着计算机科学和移动终端的不断发展,Android移动在终端已经成为了当今的主流,功能完善和性能优越的移动设备是用户优先选择的。因此开发出功能强大且界面美观的手机应用具有广阔的市场前景。
本视频播放器是基于Android平台研究开发,具有完善美观的用户界面,除了基本的播放视频外,还能查看视频信息,视频的进度条的拖拽,手势调节屏幕亮度和声音大小。
本文设计的播放器采用Vitamio的视频解码器框架,可以播放多种常见的本地视频格式,界面简洁美观。
关键词:Android,Vitamio,视频,播放器
I
四川理工学院本科毕业设计
Abstract:With the continuous development of computer science and mobile terminals, Android mobile terminal has become the mainstream of today's mainstream, functional improvement and superior performance of mobile devices is the user preferred. So it has a broad market prospect to develop the mobile phone with powerful function and beautiful interface.
The video player is based on Android platform research and development, with a perfect and beautiful user interface, in addition to the basic play video, but also to see the video information, the progress of the video of the drag, gesture control screen brightness and loudness of the sounds.
In this paper, the design of the player using Vitamio video decoder framework, you can play a variety of common video format, the interface is simple and beautiful.
Key words: Android,Vitamio,Video ,player
II
徐东:基于Android平台的视频播放器设计
目录
- 1 -
徐东:基于Android平台的视频播放器设计
- 1 -
徐东:基于Android平台的视频播放器设计
第1章 前 言
1.1设计的目的及意义
随着当今科学技术的发展, 各种各样的应用被安装到移动电话上,比如视频电话,VOD视频,移动上网冲浪,在线阅读和资料共享等。为了实现这些应用,移动电话的功能变得更聪明且智能,这也表明了移动互联网到了新时代。新时代的到来,需要一个强大的开发平台来支持,因此,智能手机操作系统技术和研究已成为最活跃的领域之一。
由于如今的Android具有开放性,现在的Android系统已经应用于手机和平板电脑上,许多的商家已经将Android移植到高清播放机和智能电视设备上,通过这样的方式消费者会有更多的机会使用以电视作为屏幕的Android系统,这样就把视频播放器显得更为重要了。
本课题研究设计了一个基于Android系统的视频播放器,以APK的形式安装在Android手机和平板中,本应用从系统的数据库读取视频信息,显示视频列表,有相应的播放界面,查看视频信息的功能,视频比例的切换,相应的控制菜单,实现视频的删除功能和上一集下一集,以及手势控制播放界面的亮度和声音大小,应用操作简单功能全面,并且可以播放几乎是有的视频格式,让用户有良好舒适的体验。
Android上面的视频播放器很多公司和个人都在开发,并且其中的一些播放器如Moboplayer、RockPlayer、RealPlayer、VPlayer等受到消费者的青睐,但是目前大多数播放器功能多样繁复,占用内存大,嵌入过多的广告使用户没有良好的用户体验,所以有必要开发出一款功能全面,操作简单,并且具有较强解码能力的视频播放器。
1.2国内外发展现状
Android是一种真正意义上的开放型的并且不依赖于设备的移动设备综合平台,他包括用户界面、操作系统、中间件和应用的主要部分。伴随开放的架构和优秀的研发环境,使Android的手持设备得到充分利用,他能提供有吸引力的移动应用,并且没有任何以前的块移动的独家产业创新的障碍。Android有丰富的图形系统,这使得它易于开发多媒体应用和网络浏览器的工具。
- 1 -
徐东:基于Android平台的视频播放器设计
自Android问世以来,全球的开发者已经做出了超过100万个应用,国内外出现了许多相当不错的视频播放器。
MoboPlayer,是由济南四叶草信息技术有限公司开发的,MoboPlayer需要在Android1.6或更高的版本上运行,MoboPlayer可以兼容更多的视频格式,并且努力的实现了任何视频格式都可以直接放在手机上播放。
VPlayer,最初由个人制作,后来组建了自己的团队。VPlayer是一款很较为流行的视频播放器,根据VPlayer的官方说明,其下载量已经超过了百万级,VPlayer的特点是支持多数格式的视频,启动后直接进入视频文件夹,就可是对视频进行播放,使用方便等。
RockPlayer,是由上海的ChangeTec.h公司开发,RockPlayer是一款比较高性能的视频播放器,能支持多数的视频格式,具有个性化的UI布局和设计,可以和其云端的服务相配合,用户之间可以分享视频。
RealPlayer,和PC上的RealPlayer是一样的,andriod上的RealPlayer也是由RealNetworks公司研究开发的,能同时播放音乐、视频、照片、但是解码能力不是很全面。
1.3 视频播放器概述
本次设计是基于Java语言通过eclipse进行编写的嵌入式播放器开发,该播放器结构简单并且占用空间小,但功能齐全。
实现目标:
1.从手机数据库获取所有视频信息。
2.播放mkv,flv,MP4,RMVB等常见格式的视频。 3.实现删除视频文件的功能 。 4.实现手势刷新功能 。
5.实现播放界面全屏播放的功能。
6.播放/暂停,上一集,下一集,手势实现音量/亮度的调节。
正如我国资深嵌入式系统专家—沈绪榜院士的预言,“未来十年将会产生头大小、具有超过一亿次运算能力的嵌入式智能芯片”,将为我们提供无限的创造空间。总之“嵌入式微控制器或者说单片机就好像是一个黑洞,会把当今很多技术和成果吸引进来。中国应当注意发展智力密集型产业”。 嵌入式播放器的迅速
- 2 -
徐东:基于Android平台的视频播放器设计
发展,使影音播放对于用户更加的方便、适用和简单,具有非常广阔的市场发展前景,也是本次设计的根本。
- 3 -
徐东:基于Android平台的视频播放器设计
第2章 Android开发技术介绍
2.1 Android的架构介绍
Android系统架构主要由5部分组成,分别是:Linux Kernel, Android Runtime, Libraries、Application Framework, Applications[12]。如图2-1所展示的为Android总体架构图,其主要是对Android的总体架构和包含的模块做具体的介绍。
图2-1 Android系统架构图
(1)应用层(applications)
Android应用层是由运行在Android设备上所有应用程序共同构成的(系统预装程序以及第三方应用程序)。Applications主要是留给开发者去开发,Android只提供了一些基本的原生应用,比如日历、电子邮件客户端、联系人、浏览器、地图、音乐播放器、视频播放器、SMS程序和其他设置等,所有应用都是基于Java语言编写而成,但是也支持通过JNI的方式实现C语言编写[1]。
(2)框架层 (Application Framework)
- 4 -
徐东:基于Android平台的视频播放器设计
Android 是开放的开发平台,能使Android开发者编制出极其丰富美观的应用程序。开发者可以灵活的利用设备硬件优势、运行后台服务、访问位置信息、向状态栏添加通知、设置闹钟等。
所有应用程序本质都是一组服务和系统,包含如下的组成部分。
视图(View)—— 提供丰富的、可扩展的视图集合,可以用于构建一个应用程序。包括列表、文本框、按钮、网格,或者是内嵌的网页浏览器。
内容提供者(Content Providers)——可使应用程序能够访问其他的应用程序(如:电话簿)的数据,或者共享自己的数据。
通知管理器(Notification Manager)——可以使所有的应用程序能在状态栏显示自定义的提示或警告。
活动管理器(Activity Manager)——用于管理应用程序的生命周期,提供了通用的导航回退功能。
资源管理器(Resource Manager)——使应用能够访问非代码资源,比如本地图形、字符串和布局文件。
(3)Libraries
Android包含了一个C/C++集合库,便于Android系统的各组件使用,开发者通过Android的应用程序框架(application frameworks)调用这些功能错误!未找到引用源。,下面列举其中的一些核心库:
SQLite——这是一个强大而轻量级的关系数据库引擎,所有的应用程序都可以引用。
FreeType——位图和矢量字体的渲染。
3D库——基于OpenGL ES 1.0APIs的实现,该库包含高度优化的3D软件光栅或使用3D硬件加速。
SGL——基本的2D图形引擎。
LibWebCore——新型的Web浏览器引擎,用于驱动Android浏览器和内嵌的web视图。
界面管理——用于管理和显示系统和多个应用程序的二维和三维图形层。 媒体库——基于PacketVideo的OpenCore ,这些媒体库支持播放和录制主流的音频和视频格式,也包括静态的图像文件,如MPEG4、H.2、MP3、JPG、PNG。
- 5 -
徐东:基于Android平台的视频播放器设计
系统C库——标准C系统库(libc)的BSD衍生,优化为基于嵌入式Linux设备。
(4)Android 运行时(Android Runtime)
Android 运行时是由两部分组成:Dalvik 虚拟机和 Android 核心类库。其中的核心类库提供了 Java 语言核心库所能使用的大部分功能,包括Java 对象库、网络通信、文件管理库等。 Dalvik 虚拟机则是提供Android 应用程序所需的运行环境,并负责动态解析执行应用、管理对象生命周期、分配空间等工作。Dalvik虚拟机依赖于Linux内核提供基本功能,如线程和底层内存管理[15],Android 运行时可使得 Android 设备在本质上与一个移动的 Linux 区分开来。
(5)硬件抽象层(HAL)
硬件抽象层(HAL,Hardware Abstraction Layer)介于Libraries和内核层中间的,是抽象出来的一层结构。HAL 存在的目的是把Linux层与Framework 内核隔离开来,它使 Android 不会过度依赖 Linux 的内核,以此达到“内核”的目的。是对 Linux 驱动的封装,对上层服务提供了统一的接口,屏蔽了底层的实现细节。
(6)Linux Kernel
Android是基于Linux2.6 提供的核心系统服务,比如:进程管理、内存管理、安全、驱动模型、网络堆栈。Linux Kernel 也作为软件和硬件之间的抽象层,它隐藏具体硬件细节而为上层提供统一的服务[6]。分层的好处就是各 层各司其职,各层提供固定的SAP ( Service Access Point)努力达到高内聚、低祸合[14]。
2.2 Android平台上开发技术介绍
2.2.1 Android四大组件
在Android系统中,开发者可是使用公共的API以提高开发软件的效率,
也就是使用别人开发的一个组件,这是Android系统的一个很有优势的特性,例如,当开发者编写的程序需要一个ListView控件并且需要滚动时,如果别人已经开发此功能的组件,并且对外发布了供他人使用的组件,此时开发者可以在自己的程序中调用该组件,而不必自己再编写一个具有此功能的组件。在需要的时候Android会启动该组件,实现你需要实现的功能。Android应用程序从
- 6 -
徐东:基于Android平台的视频播放器设计
源码逻辑上可以分为四大组件,一个应用会由四大组件或者其中几个组成,四大组件分别是Activity ,BroadcastReceiver, Service和Content Provider[8][9]错误!未找到引用源。。
1.Activity组件
顾名思义,Activity就是活动。应用程序必须至少包含一个Activity。Activity为用户操作提供了一个可视化的用户界面。例如,一个Activity可用于展示应用的一个列表供用户选择,它可以是显示一些需要说明的文字与形象的图片。一个视频播放器应用程序可以包含一个显示视频信息列表的Activity,一个播放界面的Activity。虽然他们共同组成了一个应用,但是其中的每个Activity都和其它的保持相对的,每个Activity都有生命周期,Activity的生命周期如图2-2所示。
- 7 -
徐东:基于Android平台的视频播放器设计
图2-2 生命周期
下面对图2-2中的Activity的生命周期进行简单描述
onCreate()——当Activity第一次创建的时候调用,在这里进行程序的初始化设置,包括创建视图和绑定数据到列表等,如果有记录的状态,则此时会传入一个Activity的以前状态作为参数。
onRestart()——当Activity停止后,当被再次启动之前调用。 onStart()——当Activity刚要变为被用户所见的时候被调用。
- 8 -
徐东:基于Android平台的视频播放器设计
onResume()—— 在Activity与用户开始交互之前被调用,在此时Activity位于堆栈的顶部, 并且接受用户的输入。
onPause——当系统即将要启动另外的一个Activity时调用,该方法是用来使当前的Activity暂停,该方法在短时间内完成,下一个Activity会在该方法完成后才会继续,当调用onResume()方法后,Activity回到前台。在调用onStop()方法后,当前的Activity变为用户不可见。
onStop()——当Activity即将为用户不可见时调用该方法,当一个Activity被销毁或者是另一个Activity回到运行状态时发生该方法的调用,调用onRestart()则是Activity再次回到前台和用户交互,调用onDestory()则是关闭当前的Activity。
onDestroy()——在销毁Activity前调用,该方法是Activity执行的最后的一个调用,这可能是发生在Activity的结束或者是由于系统的空间不足所以临时销毁该Activity。
2.Service组件
Service 与Activity的主要区别在于,Service是在后台活动的,它没有用户界面。而相同之处,它们都封装了完整的逻辑功能,Service组件一般是运行在后台,接受相关的指令,响应完成相应的事件,Android的Service与四大组件一样它的进程模型可以由开发者自由配置的,可以把该组件运行在同一进程中,或者不同的进程中。当希望吧Service运行在和调用的组件不同的进程中的时候,则需要利用Android所提供的RPC机制,部署一套进程之间通信的策略。
(a)Service的周期
Service的生命周期相对于Activity要简单许多。主要构成如下: onCreate():创建服务的函数。
onStart(Intent intent):开启服务的函数。 onDestory(): 销毁服务的函数。 (b)Service的两种类型
i.本地服务(Local Service):使用于应用程序的内部。
ii.远程服务(Remote Srevice):适用于Android系统内部的应用之间。
本地服务用于实现应用程序本身执行的一些耗时任务,但是并不占用应
用程序Activity的线程,而是自己单独开线程后台执行,用户体验较好。远程服
- 9 -
徐东:基于Android平台的视频播放器设计
务可以被其他的应用程序调用,因此其他的应用程序不用再写该服务,直接调用即可。
(c)Service的两种使用方式
i. 服务启动并运行,直到它自己停止或人为将其停止,在该种方式下通过调用Context.startService()启动服务,通过调用Context.stopService()结束服务它可以通过调用Service.stopSelfResult()或Service.stopSelf()来自己停止。不管调用了几次startService()方法,只需要调用一次stopService()就可以停止服务。
ii.服务可以自己定义接口,然后对自己定义出来的接口进行操作。通过调用ontext.bindService()方法建立,通过调用Context.unbindService()关闭。允许多个客户端绑定至同一个服务。如果服务此时还没能加载, bindService()方法会先加载它。
3.Content Provider (内容提供者)
Android平台内置了一套SQLite数据储存机制,并且包含一系列管理SQLite Database的相关方法。在应用程序中,通过Content providers访问数据库。每个Content provider都定义了一系列的相应方法用于访问它对应的数据库。应用程序在新建数据库时也可以定义相应的Content provider用于向其他应用程序共享数据。一般情况下,应用程序不直接调用Content provider定义的函数,而是通过Content Resolver间接调用。我们也可以自己来定Content Provider共享我们的数据,方便用户的访问[5]。这样设计的好处是一个Content Resolver可以访问任何的Content provider,统一了接口。
4.BoardcastReceiver (广播接收者)
广播接收器是用于接收广播消息通知,然后做出相应的处理,很多的广播是来于系统。例如,时间的改变、电量过低、网络的改变、用户语言的改变等。应用程序也可以发送广播,例如,通知用户,某些软件可以更新。
2.2.2 组件之间的调用
在Android中除了有定义良好的组件,怎样把各个组件完美地组合在一
起是有一定难度的。在 Android 中,Intent(意图),是连接各个组件的桥梁,是各个组件实现相互调用的重要方法。各个组件通过声明一个或多个 Intent Filter来告诉Android自己可以响应、处理哪些Intent请求。每个 Intent Filter
10 - -
徐东:基于Android平台的视频播放器设计
描述该组件意图接收何种类型的请求,以及什么类型的数据。下面将介绍Intent 和Intent Filter以及他们实现组件之间的调用过程。
1.Intent(意图)
(1) 显式意图:在代码中通过名字直接指明目标组件,通常情况下该组件名是不会被其他的应用开发者所了解的,所以这种意图典型被用作应用的内部消息。如,一个Activity启动一个相应的服务。
(2) 隐式意图:在代码中不命名目标组件。隐式意图通常用来激活其他的应用程序组件。
Android通过传递一个显式意图给一个指定目标,意图对象(Intent)中的组件的唯一的名称确定了什么组件应该来接收这个意图,而对于隐式意图则有不同的策略,当没有指定目标的情况时,Android系统需找到最适合的组件来处理这个意图或者服务来执行这个请求。
该方法是通过比较意图过滤器和意图对象,过滤器提示一个组件具备的能力和限定它能处理的意图。他们使组件接收隐式意图成为可能。当一个组件没有意图过滤器,则他只能接收显式的意图,如果该组件带有意图过滤器,那么就可以同时接收显式和隐式的意图。
2.Intent Filter
组件将意图(Intent)传递给系统核心层,然后Android系统从
AndroidManifest.xml中的 Intent Filter里面读取组件能接受的Intent,接着隐式意图通过 Intent的动作、数据以及类别和Intent Filter进行比较,如有其中的一方面不匹配,则Android都不会把该隐式 Intent 传递给目标组件。
3.Intent 传递的实现
下面介绍Activity的组件实现传递 Intent 的过程,如图2-3所示
11 - -
徐东:基于Android平台的视频播放器设计
Context StartActivity Instrumentation ExecStartActivity ActivityManagerService PackageManagerService
图2-3 Intent传递示意图
Intent的传递从Context StartActivity(Intent)开始,调用者通过传入构造好的Intent对象,然后通过执行者Instrumentation 对象来完成。执行者是整个
Activity的管理者,主要管理应用内所有的Activity的生命周期,ExecStartActivity
是它的一个隐藏的方法,其作用就是根据Intent 启动Activity的。它做的最重要的事情就是通过RPC的方式将此调用传递给ActivityManagerService。
ActivityManagerService完成相关操作分为两步,首先把意图(Intent)
vice 该服务则会拥有整个软件的所有信传递给另一个服务ActivityManagerSer息,并且它会将已知的 Intent Filter和传入的 Intent 进行比较,如果能找到,
vice,就把相关的Component的信息反馈传回ActivityManagerSer在此会完成启动Activity的很多细节的事情。
从上可知,要经过许多个服务的处理才能启动Activity。
2.2.3 Android进程和线程
Android中的组件与组件之间的调用,全是静态的概念。在Android应用中,组件的动态运行引入的新概念就是Task(任务)。Task的主要作用就是把组件之间的连接从进程中脱离出来,能以一种不同的模型进行配置,通过这种方式能有效的降低上层开发人员的理解难度,更好的帮助开发者进行开发和配置 。下面介绍Task的概念,线程、进程和消息机制。
12 - -
徐东:基于Android平台的视频播放器设计
1.Task
Task是一组以栈的模式聚集在一起的Activity组件集合[2],新加入的
Activity组件位于栈顶,而且只有当位于栈顶时才能与用户进行交互。
如果栈顶的Activity完成任务退出的时候,Task会让其退出栈,让下一个
Activity位于栈顶来与用户进行交互,如此循环知道栈中没有Activity,则Task结束。
2.进程
在应用程序安装的时候,Android系统会给每个应用程序分配一个Linux用户id,以及设置相应的权限,这样该程序的数据和资源其他的程序就不能访问了。每个apk(Android应用程序)默认运行在自己的Linux 进程当中,当程序执行时,Android会启动一个新的进程来执行,所有不同的apk在相互隔离的环境中运行。另外,可以人为的给两个应用程序分派相同的用户id,从而两个应用程序可以相互访问对方的资源和数据,并且这两个应用程序能运行在同一个进程之中。
在Android系统中,进程构造了底部的一个运行池,它不只是Task中的各个Android组件,其他三大组件Service, Content Provider, BroadcastReceiver,都是寄宿在底层某个进程中,进行运转[3]。在这里进程像是一个资源池,为了承载各个组件的运行。默认情况下,Android是将同一应用的各个组件和 Task放在同一个进程内,但是,出于效率考虑,Android应用也是允许开发者进行自定义的配置。在AndroidManifest.xml 中完成应用程序和相关进程的配置。
3.线程模块
Android在默认情况下,一个程序的各个组件(例如 Activity , BroadcastReceiver 或 Service )都是在同一个线程中执行,是由该线程的主线程负责执行, 而且主线程有一个消息队列,一直检查是否有接收到新消息,如果队列中没有消息,则主线程挂起等待。一般情况下,一个应用程序的全部组件都是运行在同一进程中的,一些复杂的的耗时任务由子线程来处理,而主线程主要是用来处理Activity 的 UI事件。因此单线程模型会因缺少上述考虑而引起应用程序的效率低下,由于所有事件都在同一个线程中处理,当处理一些耗时操作时,就会使该线程阻塞,不能及时分发事件,程序卡顿,使得用户体验差。
13 - -
徐东:基于Android平台的视频播放器设计
在Android中使用多线程的有点:与用户更好的交互、耗时长的时候可以做其他事、开发利用多处理器。
4.Android中的消息机制
在Android中存在消息循环机制,Android通过Handler、Looper来实现该机制,这是对线程而言的,每个线程都可以有消息循环和消息队列。
在Android系统 Looper 主要用于管理线程的消息循环和消息队列。通过Looper.myLooper() 方法得到该线程的Looper 对象,可以通过
Looper.getMainLooper() 方法得到该进程的主线程的Looper 对象。对于一个线程消息循环和消息队列是可有可无的,默认的线程是没有消息队列和消息循环的,我们可以调用Looper.prepare() 来创建消息队列,通过Looper.loop() 方法进入消息循环。
2.2.4 Android界面构造
对于每个应用程序而言交互界面都是非常重要的,优秀的交互界面对提升用户体验有着很好的作用,要设计出简洁美观的界面,开发者要有良好的编程能力,同时还需要平台提供优良的UI 框架。资源和布局是Android 框架的核心,完善的控件库帮助开发者快速的搭建自己需要的界面,主要包括界面布局体系、界面控件结构和资源。
1.界面布局体系
Activity的界面布局体系如下图2-3所示。
14 - -
徐东:基于Android平台的视频播放器设计
Window LinearLayout Window Manager (用户操作) RelativeLayout TextView Button ImageView
图2-3 Activity 界面布局图
如图2-3,当你做了一个如同虚线框中结构的界面,通过Activity 中的setContentView方法,放进了Activity 中,形成图示的逻辑关系。每个 Activity 都包含一个Window 对象,表示了它在屏幕上的逻辑关系,在Android中它包含了一个FrameLayout对象,显示出来的是一个带着标题的界面,开发者的自定义控件会放入 Window的界面,在Activity 中,Window 的逻辑处理是优先的。
整个控件中最顶端的是 ViewParent,它是 WindowManager 和整个控件树之间的信息翻译者,而 WindowManager 则是Android 中的一个重要服务,他能把用户的操作请求反应成指令然后传送给界面上的对应的Window。Activity会将上层的控件注册到 WindowManager 中,当用户触摸屏幕或键盘的时候,WindowManager 就会通知对应的控件,如果当空间产生一些请求的时候,也会有 ViewParent传回 WindowManager 中,从而形成通信流程。
2.界面控件结构
如图2-4,是用来描述Android的布局(UI)控件结构,在每个窗口(Window) 下,这个结构树都是完整而又标准的。 有一个子类,它类似于一个复合控件或一个容器类。所有的派生与ViewGroup的子类在这颗UI布局树中都可承担父节点的职责,而另外一些绕过ViewGroup直接从View通下来的,只能属于叶
15 - -
徐东:基于Android平台的视频播放器设计
节点的范畴内了。
我们说这是一个很标准的控件树,是由于父控件对于子控件有绝对的掌控权,每个子控件的位置和大小,都是由父控件来分配的,子类控件能够接受和能处理的事件,也是父控件派发下去的。该种结构,被许多平台和框架广泛的认可和应用,与传统的win开发和Symbian相比,虽然事件传播的途径变长了,很多事件处理的效率降低了,但是整个结构却更具有有层次性,每个子控件只需要按照父控件的指挥就好,逻辑简单,职责明确,利于开发和设计。
任何一个平台的控件,都存在一些不可避免的主题,例如,每一个控件如何标识,应该如何设定空间的大小和位置,怎样接受和处理事件,以及类似的相应问题。其界面控件树如下图2-4所示。
ViewGroup ViewGroup View View View View View
图 2-4 View 树形结构图
3.资源
资源是Android应用中不可缺少的部分。资源是开发者引入到应用程序中的一些外部元素,例如:图片、音频、视频、文本字符串、布局猪蹄等[4]。资源属于外部文件,他们作为代码的引用,并在程序编译的时候被编译到应用之中。Android 支持许多种不同的资源文件类型,例如:XML、PNG和JPEG文件。而XML文件会因为它所描述内容的不同而形式不同。布局文件通常存储子XML中,这个方法很好地简化了UI设计过程,将许多用户界面控件的静态产品和布局,以及控件属性定义移动XML中,代替了写代码 [7]。
资源是从源代码中脱离出来的,出于对效率的考虑,文件被编译成为二进
16 - -
徐东:基于Android平台的视频播放器设计
制文件、能快速加载的形式,而字符串也被压缩成为一种更高效率的储存形式。由于上诉原因,Android平台中有了不同的资源类型,最终这些资源将被编译进入APK文件中。在Android中有一个文件叫R,是一个封装类,在代码中可以通过它来引用资源。
2.2.5 开发工具 Eclipse
开发平台是一个基于语言,开放源代码的开发平台。本身只是一个框架和一组服务,开发者通过插件组件搭建需要的开发环境。重要的是,附带了一个标准的插件集,包括 开发工具( Development Kit,JDK )。
的设计思想是:一切皆插件。核心很小,其他所有功能都以插件的形式附加于核心之上。Eclipse基于内核包括:图形API(SWT/JFace),开发环境插件(JDT),插件开发环境(PDE)等。
的插件机制是轻型软件组件化架构。在富客户机平台上,使用插件来提供所有的附加功能,例如支持Java以外的其他语言。已有的分离的插件已经能够支持C/C++()和数据库开发。插件架构能够支持开发者将任意的扩展加入到现有的开发环境中,比如配置管理,而决不仅仅限于支持各种编程语言。
17 - -
徐东:基于Android平台的视频播放器设计
第3章 视频播放器需求分析
为了开发出用户所需要的软件产品,对将要开发的软件进行需求分析是软件开发中非常重要的环节,本章对视频播放器做相关的需求分析。需求分析的任务是定义系统应该做些什么事,完成什么样的工作,也是对目标应用程序提出准确、完整、具体、清晰的要求。
3.1 视频播放器概述
随着手机的叠层出新,视频播放器以成为Android智能机重要软件之一,开发出一款广大用户需求的视频播放器是很有市场前景和意义的,本次设计是基于Java语言进行嵌入式播放器开发,程序简单并且占用空间小,但功能齐全。
实现目标:
1.从手机数据库获取所有视频信息。
2.播放mkv,flv,MP4,RMVB等常见格式的视频。 3.实现删除视频文件的功能 。 4.实现手势刷新功能 。
5.实现播放界面全屏播放的功能。
6.播放/暂停,上一集,下一集,手势实现音量/亮度的调节。
3.2软件功能
3.2.1 视频播放器界面
视频播放器的主界面是用户在手机桌面点击图标后打开的界面,该界面显示视频列表,以及各视频的详细信息,包括视频的名称、大小和时长。播放器的播放界面则是用于视频播放的界面,该界面的组要功能包括声音的控制、亮度的调节、播放和暂停以及上/下选集,并且有进度条显示播放的进度。总之用户界面设计是以人为中心,使产品达到简单使用和愉悦使用的设计[11],视频播放器的界面用图例图3-1 所示。
18 - -
徐东:基于Android平台的视频播放器设计
视频详细信息 进入列表 删除视频 播放 暂停 上/下一首 用户 播放文件 退出程序 亮度调节 音量调节
图 3-1 播放器界面图例
下面对播放器的界面用图例说明。
(1) 播放功能:当软件正在运行,用户可以播放列表中选中的视频。 其事件流:①用户单击列表中的视频 。 ②播放器播放列表中点击的视频。
(2) 暂停功能:当视频正在播放且没有暂停和停止,用户可以暂停正在播放的视频。
其事件流:①用户单击“暂停”按钮。 ②播放器将暂停当前播放的视频。
(3) 播放列表:当程序正在运行,用户可以进入播放清单。 其事件流:①用户单击启动软件。 ②播放器进入播放列表。
(4) 上/下集:当播放结束后,用户可以播放上一集/下一集。 其事件流:①用户单击上一集/下一集。 ②播放器将播放上一集/下一集。
19 - -
徐东:基于Android平台的视频播放器设计
(5)删除功能:当视频列表加载成功,用户可以删除清单中的视频 其事件流:①用户长按列表中的视频条目,选择删除按钮。 ②播放器将删除指定的视频并刷新列表。
(6)音量调节:用户可以通过在右半边播放界面屏幕上滑动调节音量。 其事件流:①用户在播放界面的右半屏上下滑动。 ②上滑将调大音量,下滑将减小音量。
(7)亮度调节:用户可以通过在左半边播放界面屏幕上滑动调节亮度。 其事件流:①用户在播放界面的左半屏上下滑动。 ②上滑将调高亮度,下滑将降低亮度。
3.2.2 性能要求
1.响应速度要求
软件的响应速度要求在用户可以接受的范围,且是越快越好。
2.可扩展性
本软件时基于Android操作系统开发,是基于Android组件的开发,以
后对本软件的扩展是很方便的,具有可扩展性。
3.3视频播放流程图
视频播放流程图如图 3-2 所示。
20 - -
徐东:基于Android平台的视频播放器设计
启动程序 启动界面延时启动 本地视频列表 否 点击本地列表 是 返回 播放视频 否 点击退出 是 程序结束 退出 图3-2 视频播放流程图
21 - -
徐东:基于Android平台的视频播放器设计
第4章 视频播放器的实现
通过需求分析阶段,我们已经知道程序需要做些什么了。本章将对视频
播放器的实现做详细的介绍。
4.1 启动界面
在程序启动的时候先加载启动欢迎界面,该布局应用 LinerLayout 布局,启动界面效果图如图 4-1所示。
图4-1 播放器启动界面
(1) 布局设计
启动界面的布局为线性布局(LinerLayout),在布局中有显示图像的控件 ImageView 。布局文件 Splash.xml 内容如下:
android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" android:background=\"@drawable/app_bg3\" 22 - - 徐东:基于Android平台的视频播放器设计 android:orientation=\"vertical\" >
启动界面主要是给给用户一种更好的体验,友好的人机界面让软件更让人容易接受,主要代码如下:
package com.xudong.videoview;
public class SplashActivity extends Activity { }
在AndroidManifest.xml文件中通过设置
android:theme=\"@android:style/Theme.Black.NoTitleBar.Fullscreen\" 使启动界面无标题且显示为全屏。
private final int SPLASH_DISPLAY_LENGHT = 1000; // 延迟*秒 @Override
protected void onCreate(Bundle savedInstanceState) { }
super.onCreate(savedInstanceState); setContentView(R.layout.splash);
new Handler().postDelayed(new Runnable() {
public void run() { }
SplashActivity.this.startActivity(mainIntent); SplashActivity.this.finish();
Intent mainIntent = new Intent(SplashActivity.this, MainActivity.class);
}, SPLASH_DISPLAY_LENGHT);
4.2 列表界面
本设计的列表界面即是本程序的主界面,用户点击后会停留在这个界面,程序的各个可视化界面都有相应的布局文件,在布局文件中有各种布局方式以及各种资源文件,例如:图像、文字和颜色的引用。在程序运行的时候,可以通过调用不同的配置文件形成各种不同的可视化界面,列表界面是一个Activity ,在Android工程中,每个 Activity的启动都是先执行 Oncreate() 方法,其代码如下:
23 - -
徐东:基于Android平台的视频播放器设计
标题
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);// 使无
setContentView(R.layout.activitymain);
该段代码的主要功能是对界面的初始化操作,在代码中设置布局的方法: Context.setContentView(layoutResID),其中的参数为资源的id,该id位于工程目录中的 res/layout 下,列表的布局文件名为activitymain.xml 。
在启动界面结束后则进入列表界面如图 4-2 右图所示,列表界面主要运用相对布局(RelativeLayout)。
图 4-2 列表界面
在Android中,有一种视图叫 ListView用来展示列表的View ,特点是它拥有BaseAdapter 属性,用来将数据映射到ListView上,数据包括字符串、图片或者基本组件。Listview 的显示规则是从上到下或从左至右。Android默认的Listview每行只会显示一个Textview,本列表界面采用的是自定义的Listview,每个item 显示一个视频缩略图、一个文件名、一个视频文件的大小和视频的时长。首先,我们定义一个Videolistadapter 类继承了BaseAdapter,然后对该适
24 - -
徐东:基于Android平台的视频播放器设计
配器进行扩展使它能够显示一个视频缩略图、一个文件名、一个视频文件的大小和视频的时长。因BaseAdapter是一个抽象类,所以我们需通过里面的getView() 方法返回一个View,即为视图。该视图可以显示在 Activity上,这就是我们看到的列表界面。
给Listview 注册单击事件的,在
mlistView.setOnItemClickListener(new VideoListItemClickListener())方法中,我们可以监听用户对Listview 的单击事件,当用户单击某一条目时,程序将获取单击条目的位置(即单击第几个条目),从而获取视频文件的信息(视频的绝对路径),然后通过Intent 将数据传入播放视频的Activity达到播放视频的目的,
在主界面中加入自定义Listview控件的代码如下:
本播放器还可以设置成矩阵布局的列表(GridView),如图4-2左图所示,其功能和Listview 类似,该视图只显示视频的缩略大图和名字,通过设置两种视图的显示与隐藏达到视图切换的效果。把自定义的Gridview 加入Activity的方法如下:
android:listSelector=\"@drawable/grid_selector\" 25 - - 徐东:基于Android平台的视频播放器设计 android:numColumns=\"auto_fit\" android:padding=\"0dip\" android:stretchMode=\"columnWidth\" android:verticalSpacing=\"10dip\" /> 通过两种视图的切换以达到一种良好的用户体验,对于比较复杂的控件,例如ListView 和 GridView,通常用程序动态的处理数据[12]。 在列表界面的最下方,通过相对布局(LinearLayout)用于显示设备内存的总容量和剩余容量,使用户清楚了解存储空间的使用情况。获取容量信息的代码如下: StatFs sf = new StatFs(Environment.getExternalStorageDirectory() 数量 .getPath()); long blockSize = sf.getBlockSize();// Block 的 size long blockCount = sf.getBlockCount();// 总 Block 数量 long availCount = sf.getAvailableBlocks();// 可用的 文件系统块 4.3播放界面模块 该模块是由播放界面和相关的控制模块组成,通过相互之间的配合组成了较为完善的播放与控制系统。 4.3.1 视频的播放 如图4-3所示,是视频正在播放。要完成此功能首先,我们声明一个VideoView ,它是用于显示播放视频的控件,然后对VideoView进行初始化,部分代码如下: if (mVideoView != null) { } mVideoView = new VideoView(this); RVideoView.removeView(mVideoView); mVideoView = null; mVideoView.requestFocus();// 获取焦点,只能依附于view内部 再通过bundle.getInt(key) 方法接收从列表传过来的视频位置信息,从而获取视频绝对文件路径,并通过VideoView.setVideoPath(path)方法把路径设置到 26 - - 徐东:基于Android平台的视频播放器设计 Videoview中,然后通过mVideoView.start()方法播放获取到路径的视频文件。 图4-3 播放界面 4.3.2 控制菜单 1.亮度和音量的调节 当在播放界面左三分之一屏上下滑动的时候就会出现如图4-4所示图案,它表示的是亮度调节的图案,当手指是向上滑动的死时候屏幕亮度变亮,反知向下滑动的时候屏幕亮度变暗,同时在图案的下方会显示进度条,进度条的最左端表示最低亮度,最右边表示最高亮度。当用户操作完成后我们延时一秒隐藏提示图案,通过这样实现用户对播放界面不同的亮度需求,提升用户体验。 主要代码: mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0); 27 - - ViewGroup.LayoutParams lp = lp.width = * index / mMaxVolume; mOperationPercent.getLayoutParams(); findViewById(R.id.operation_full).getLayoutParams().width mOperationPercent.setLayoutParams(lp); 徐东:基于Android平台的视频播放器设计 图 4-4 亮度调节 视频播放器的音量调节如图4-5,它和亮度调节比较类似,当用户滑动播放界面右边的三分之一屏幕区域的时候,就会调出音量调节的提示图案,同样向上滑动提高音量,向下滑动降低音量,同时提示图案的下方也会有进度条,进度条的左右分别表示最小音量和最大音量。在用户操作完成后延时隐藏图案。 部分代码: ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams(); lp.width= (int) (findViewById(R.id.operation_full).getLayoutParams().width * lpa.screenBrightness); mOperationPercent.setLayoutParams(lp); 图 4-5 音量调节 2 .播放/暂停和进度条 如图4-6所示,当用户点击播放界面的时候就可以调出控制菜单,其中包括暂停、播放、进度条和视频的基本信息。用户在视频播放的时候,则需要播放器能播放/暂停,以及能能拖动视频播放的进度条,实现与用户的人性化交互。该应用程序使用vitamio提供的公共Api,以达到方便快捷的实现该功能。我们在主程序中通过如下方法直接添加控制台。 28 - - 徐东:基于Android平台的视频播放器设计 mVideoView.setMediaController(new MediaController(this));// 控制器 图4-6 控制台 3. 上/下选集 图4-7为选集菜单,该播放器的选集方式与其他的播放器不同,我们的设定是当播放完成的时候,弹出对话框显示上一集/下一集的选项,这时用户可以通过点击按钮实现快速的选集功能,也可以单击换回列表,在列表中选择想播放的集数。 主要代码: mVideoView.setOnCompletionListener(mCompletionListener);//监听播放完成 public void playhint( int postion) { AlertDialog.Builder builder = new Builder(this); builder.setTitle(\"提示信息\"); builder.setMessage(\"怎样操作?\"); builder.setPositiveButton(\"上一集\ public void onClick(DialogInterface dialog, int which) { } 29 - - Position--; nextplay.sendEmptyMessageDelayed(0,200); 徐东:基于Android平台的视频播放器设计 }); 图4-7 选集菜单 4.4 对话框界面 该视频播放器中应用许多自定义的对话框,下面讲解主要的一个。如图4-8所示,当用户长按视频列表中的视频条目的时候,弹出自定义对话框并使背景变暗。对话框中显示播放视频、查看视频的详细信息和视频文件的删除。播放功能和前面的方式一样;查看详细信息则又会弹出另一个自定义对话框,其中包括标题、时长、大小、文件位置、文件创建时间以及视频的分辨率等,点击确定后退出该对话框。视频的删除按钮则是通过file.delete()方法,删除指定的视频文件然后刷新视频列表。其代码见附录中的“自定义对话框”。 30 - - 徐东:基于Android平台的视频播放器设计 图4-8 自定义对话框 4.5 其他界面模块 在以上介绍的模块之外还有两个用户界面比较重要的模块,他们是菜单自动隐藏模块以及Toast模块,他们和前面介绍的组件共同完成用户界面所需要的功能。 4.5.1菜单自动隐藏模块 菜单的自动隐藏功能是非常人性化的一个设计,主要是与控制功能菜单相配合,实现菜单的定时自动隐藏功能。当用户观看视频的时候通常是全屏观看,在视频界面上除了字幕不会显示其他的用户界面,因此当用户使用完对应的菜单后,菜单按钮应该在固定的延时后自动隐藏,不再由用户手动隐藏,以此提高用户的体验。 当视频开始播放的时候,即进入了播放界面,则会创建一个时间为3秒的定时器,每当菜单被调出来的时候从0开始计时,时间到达3秒后,菜单隐藏,这样就实现了3秒定时隐藏的功能。 4.5.2 Toast 界面模块 Toast 模块是应用程序对用户的一种短暂提示,不会与用户发生交互,也不会获取焦点,所以在很多时候可以用这种方法向用户发出提示信息,比如:手机sd卡错误,视频文件删除成功等。当应用启动后,Toast 模块就完成了初始化(Toast 对象的创建,Toast 显示的时间设置),当有需要显示的提示时,则会把提示信息文本发送给Toast 模块,Toast 模块就会显示该提示信息并延时消失,完成友好的系统提示。 4.6 退出界面 如图4-9所示,为应用程序的退出界面,当用户需要退出该程序的时候点击 31 - - 徐东:基于Android平台的视频播放器设计 返回键,为了防止误触写入该提示菜单,当点击确定的时候则应用程序完全退出。该界面也是运用了自定义对话框,和前面提到的一致,其结束应用程序代码如下: PID 退出 } System.exit(0); // 常规java、c#的标准退出法,返回值为0代表正常private void exit() { android.os.Process.killProcess(android.os.Process.myPid()); // 获取 图4-9 退出提示 4.7 本章小结 本章节详细介绍了视频播放器的启动界面、列表界面、播放界面、控制菜单和其他主要用户界面模块的界面、功能和布局方式。控制菜单主要实现了视频播放的控制, Toast等其他模块让应用更丰富,用户体验更好。 32 - - 徐东:基于Android平台的视频播放器设计 第5章 视频播放器的测试 在本视频播放器设计完成之后,播放了许多的视频文件对视频播放器进 行多方面的测试,分别从视频解码、功能性测试和主观方面进行了测试。 5.1解码能力 视频的解码能力测试我们通过选择不同的视频格式和各种不同分辨率的视频,对视频播放器进行了详细的测试,最后的结果表示该视频播放器能支持现在用户常用的视频格式。 本次设计采用了Vitamio的解码框架,Vitamio 使用了 FFmpeg 做为媒体解析器和最主要的解码器,同时开发了针对不同移动平台的硬解码方案,能够完美支持 H.2/AVC、H.263、 MPEG4 等常见的视频编码,覆盖上百种多媒体格式。下表只是一些最常见的视频格式支持,除特殊标明,全部支持硬件加速: DivX/Xvid WMV (一般只有软解码) FLV TS/TP RMVB (只有软解码) MKV MOV M4V AVI MP4 3GP 对字幕的支持是非常优秀的,包括各种常见外挂字幕与很多视频格式的内嵌字幕,同多个字幕等特性的支持也非常完善。比如: SubRip(.srt) Sub Station Alpha(.ssa) / Advanced Sub Station Alpha(.ass) SAMI(.smi/.sami) MicroDVD(.sub/.txt) SubViewer2.0(.sub) 33 - - 徐东:基于Android平台的视频播放器设计 MPL2(.mpl/.txt) Matroska (.mkv) 内置字幕 5.2功能性测试 如表5-1 所示,当我们播放不同视频文件的时候对播放器各功能的测试,结果表明该视频播放器一切功能正常,并且能达到预期的要求。 表5-1 功能性测试 测试内容 播放暂停 进度条拖拽 上一集 下一集 返回 删除文件 查看详细信息 34 - - 测试结果 通过 通过 通过 通过 通过 通过 通过 徐东:基于Android平台的视频播放器设计 视图转换 下拉刷新 5.3播放器的主观效果 通过 通过 在进行了上述的测试之外,我们还进行了主观测试,即用户的体验效果。当用户点击桌面的视频播放器图标后,先出现了启动欢迎画面,并且只该视频播放器能够播放的视频文件,便于用户快速查找想要播放的文件。用户点击列表中的文件,随即进入了播放界面,跳转过程时间短,是用户能够接受的范围。在播放的时候音视频没有不同步,没有出现花屏和卡顿的现象。亮度和音量能够快速方便的调节,各个功能都能正常实现预期的效果。 该视频播放器结构简单,功能丰富,运行流畅,操作简单。能完成主流的视频文件播放以及占用内存小的优点。 5.4 本章小结 本章详细介绍了视频播放器的解码能力、功能测试和主观测试,通过大量的测试结果表明,该视频播放器拥有较强的视频解码能力,同时具有布局美观,操作简单,功能丰富,占用内存低。适用于当前市面上所有的Android 系统的手机和平板电脑上。 35 - - 徐东:基于Android平台的视频播放器设计 第6章 结束语 6.1总结 本文通过对Android 系统的研究和学习,以及从此对Android 视频播放器的开发,使我对Android的视频播放器有了更深入的了解,对软件的开发流程有了更清晰的认识。视频播放器主要由列表界面和播放界面构成,当这两个模块完成了则视频播放器的基本主框架就完成了,其他的功能都是在这两个模块的基础上扩展的。但是扩展的功能也是不可缺少的,组合在一起才能构成视频播放器的完整性,从而达到播放器的美观实用。 本次软件的开发与设计,对软件的系统框架经过了精心的设计,本次开发主要采用Eclipse开发软件和Java编程语言,首先是对开发环境的配置,包括Android SDK 和JDK的安装,在这基础上才能对应用进行编写。 视频播放器的基本功能:视频列表的浏览、播放、暂停、上下选集等功能、运行在Android系统平台上,Android系统是基于Linux的开源手机平台,是当今流行的手机操作系统,以Java语言编写的程序。 开发过程总结与体会:好的系统分析能为开发提供更好的基础,在开发之前要有相应的基础知识,有详细的系统分析,做好相应的记录。然后再开始代码的编写,这样才能有条不紊的对软件进行开发,后期是对软件的测试,多次多方法的测试,然后对软件作最大化的优化。 我在这几个月的Android系统的学习与开发中收获良多,使我对Android系统平台上的开发模式与开发流程有了更进一步的了解,也对软件产品的上市流程有了一定的了解。在这开发过程中犯过许多错误,遇到很多困难,出现许多bug,但是从中收获了更多经验教训。 6.2 问题和展望 本次视频播放器的设计取得了阶段性的成果,但是还有很多后续工作需要完成,后续的工作需要对视频播放器软件不断的完善和优化,以使视频播放器更加具有实用性,主要需要完善的包括以下几个方面: (1)添加搜索功能。在视频列表界面添加搜索工具栏,以便视频过多的时候用户可以通过搜索查找到需要的视频文件。 36 - - 徐东:基于Android平台的视频播放器设计 (2)视频列表的排序功能。用户可以根据自己的喜好定义文件排序的方式,比如:根据大小排序,根据修改时间排序,根据中英文字母排序。 (3)多选删除功能。在用户删除视频的时候提供多选菜单,以便用户能够方便快捷的删除更多的视频文件。 (4)添加中英文的切换。用户可以把菜单设置成中文或者英文的,这样方便更多的人群使用该视频播放器。 37 - - 徐东:基于Android平台的视频播放器设计 致谢 本次毕业设计是基于我在一家软件公司实习的基础上选题的,校外导师刘兴忠和校内导师曾晓辉老师师的共同细心指导下完成的, 刘兴忠导师严谨的科学态度,精益求精的工作作风,是我学习的典范。曾晓辉老师在学业上给予我精心指导,还在思想对我细致引导,在此向曾晓辉老师、刘兴忠导师表示衷心的谢意。我还要感谢我的同学和同事,有了你们的帮助和支持,我才能解决一个一个的困难和疑惑,我才能将本文顺利完成。 从论文的选题到论文的顺利完成,在这过程中有许多师长、同学、朋友给了我热心的帮助和指导,我非常感谢你们对我的帮助,感谢大学四年来所有教导过我的老师们,你们的细心教导使我学会了基本的思考方式,并为本文的撰写打下了坚实的基础,为今后的继续学习与工作做了良好的铺垫。我还要感谢含辛茹苦把我培养长大的父母,非常感谢您们! 感谢四川理工学院对我的教育和培养。 38 - - 徐东:基于Android平台的视频播放器设计 参考文献 [1] 姚昱旻,刘卫国.Android的架构与应用开发研究[J].计算机系统应用,2008, 17(11):110-113. [2] (美)Reto Meier Professional Android Application Development[M].清华大学出版社,2010, 137-208 [3] (美)W. Frank, Ableson Charlie, Collins Robi Google Android[M].揭秘人民邮电出版社. 2010 , 57-68 [4] 孙晓字. Android手机界面管理系统的设计与实现工程硕十学位论文,北京,北京邮电 大学, 2009 [5] 裴佳迪,马超,孙贵人.Android应用开发全程实录[M].人民邮电出版社.2012.30-90. [6] 马越. Android的架构与应用 硕士学位论文,北京,中国地质大学,2008 [7] Mark Murphy .Beginning Android 2[M].Berkeley: Apress, 2010.23-28. [8] 公磊,周聪.基于Android的移动终端应用程序开发与研究[J].计算机与现代化.2008,(8):85-. [9] Yi-Hua Weng, Fu-Shing Sun, Jeffry 39 - - 徐东:基于Android平台的视频播放器设计 D .Grigsby.GeoTools:An android phone application in geology[J].Computers&Geosciences, 44 (2012):24-30. [10] Sierra, K. HEAD FIRST JAVA(中文版)[M].北京:中国电力出版社2007.581-606. [11] 赵亮,张维.基于Android技术的界面设计与研究[J].电脑知识与技术,2009,5(29):8183-8185. [12] 韩超.Android经典应用程序开发[M].乌鲁木齐:人力资源出版社,2011 .81-147. [13] Google. Google API[EB].http://developer.android.com/index.html, 2007. [14] 陈璟,陈平华,李文亮.Android内核分析[J].现代计算机(专业版),2009(11):112-115. [15] 陈星,江兰帆.基于Google Android平台的移动开发研究[[J].福建电脑,2008,24(11):156-157. [16] 林城.Android 2.3应用开发实战[M].北京:机械工业出版社,2011. 2-7 40 - - 徐东:基于Android平台的视频播放器设计 附录 本次设计的核心程序代码: (1)启动时 package com.xudong.videoview; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; public class SplashActivity extends Activity { private final int SPLASH_DISPLAY_LENGHT = 1000; // 延迟*秒 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.splash); new Handler().postDelayed(new Runnable() { public void run() { Intent mainIntent = new Intent(SplashActivity.this, MainActivity.class); SplashActivity.this.startActivity(mainIntent); 41 - - 徐东:基于Android平台的视频播放器设计 } SplashActivity.this.finish(); } }, SPLASH_DISPLAY_LENGHT); } (2) protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE);// 无标题的activity setContentView(R.layout.activitymain); novideos = (TextView) findViewById(R.id.NoVideo); mlistView = (ReFlashListView) findViewById(R.id.filelistview);// 获取listview mGridView = (ReFlashGridView) findViewById(R.id.filesGridView);// 获取gridview mSDAvailable = (TextView) findViewById(R.id.sd_block); mSDAvailable.setText(showFileAvailable());// 剩余空间 42 - - 徐东:基于Android平台的视频播放器设计 System.out.println(showFileAvailable() + \"showFileAvailable\"); head = (LinearLayout) findViewById(R.id.gridhead); LayoutParams p = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER); // 获取head布局 head.addView(mGridView.getView(), p); // GridView 的刷新事件 mGridView.setonRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { onGReflash(); } }); Button bmore = (Button) findViewById(R.id.more); mlistView.setInterface(this);// 获取接口 // mGridView.setInterface(this); // 注册点击长按事件 mlistView .setOnItemLongClickListener(new 43 - - 徐东:基于Android平台的视频播放器设计 VideolistItemLongClickListener()); mlistView.setOnItemClickListener(new VideoListItemClickListener()); mGridView .setOnItemLongClickListener(new VideolistItemLongClickListener()); mGridView.setOnItemClickListener(new VideoListItemClickListener()); bmore.setOnClickListener(new mOnClickListener());// option菜单 (3)填充器,用于获取视频各种信息 package com.xudong.videoview.adapter; import java.math.BigDecimal; import java.util.List; import com.xudong.videoview.R; import com.xudong.videoview.Videoinfo; import com.xudong.videoview.MainActivity.ViewMode; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; 44 - - 徐东:基于Android平台的视频播放器设计 import android.widget.ImageView; import android.widget.TextView; public class Videolistadapter extends BaseAdapter { private static final String TAG = \"Videolistadapter\"; // public static Parcelable state; private Context context; // 上下文对象引用 private List private Videoinfo videoinfo; // Videoinfo对象引用 private int pos = -1; // 列表位置 private static ViewMode mViewMode; protected View mView; public Videolistadapter(Context context, List this.videoinfos = videoinfos; } public void setViewMode(ViewMode mode) { mViewMode = mode; } @Override 45 - - 徐东:基于Android平台的视频播放器设计 public int getCount() { return videoinfos.size(); } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Log.i(TAG, mViewMode + \"|mViewMode\"); if (mViewMode != ViewMode.GRIDVIEW) { return getListViewItem(position, convertView); } else { return getGridViewItem(position, convertView); } } 46 - - 徐东:基于Android平台的视频播放器设计 private View getListViewItem(int position, View convertView) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(context).inflate( R.layout.listview_file_item, null); holder.fileIcon = (ImageView) convertView .findViewById(R.id.file_icon);// 缩略图 holder.fileName = (TextView) convertView .findViewById(R.id.file_name);// 名字 holder.fileSize = (TextView) convertView .findViewById(R.id.file_size);// 小 大 holder.fileduration = (TextView) convertView .findViewById(R.id.file_duration);// 时长 convertView.setTag(holder); } else { 47 - - 徐东:基于Android平台的视频播放器设计 holder = (ViewHolder) convertView.getTag();// 通过getTag的方法将数据取出来 } videoinfo = videoinfos.get(position); if (position == pos) { holder.fileIcon.setImageResource(R.drawable.format_media);// 视频图标 } else { holder.fileIcon.setImageBitmap(videoinfo.getImage());// 显示图 holder.fileName.setText(videoinfo.getTitle()); // 显示标题 holder.fileSize.setText(forbytes(videoinfo.getSize()));// 显示大小 holder.fileduration.setText(formaTime(videoinfo.getDuration()));// 时长 } Log.i(TAG, \"getListViewItem\"); return convertView; } 48 - - 徐东:基于Android平台的视频播放器设计 /** */ private View getGridViewItem(int position, View convertView) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(context).inflate( R.layout.grid_view_item, null); holder.fileIcon = (ImageView) convertView .findViewById(R.id.fileicon); holder.fileName = (TextView) convertView .findViewById(R.id.filename); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag();// 通过getTag的方法将数据取出来 } videoinfo = videoinfos.get(position); if (position == pos) { holder.fileIcon.setImageResource(R.drawable 49 - - 徐东:基于Android平台的视频播放器设计 .format_media);// 视频图标 } else { holder.fileIcon.setImageBitmap(videoinfo.getImage());// 显示图 holder.fileName.setText(videoinfo.getTitle()); // 显示标题 } Log.i(TAG, \"getGridViewItem\"); return convertView; } private static final class ViewHolder { public TextView fileduration; public TextView fileSize; private ImageView fileIcon; private TextView fileName; } /* */ public static String formaTime(long time) { String min = time / (1000 * 60) + \"\"; String sec = time % (1000 * 60) + \"\"; 50 - - * 格式化时间,把毫秒转换成分:秒格式 徐东:基于Android平台的视频播放器设计 if (min.length() < 2) { min = \"0\" + time / (1000 * 60) + \"\"; } else { min = time / (1000 * 60) + \"\"; } if (sec.length() == 4) { sec = \"0\" + (time % (1000 * 60)) + \"\"; } else if (sec.length() == 3) { sec = \"00\" + (time % (1000 * 60)) + \"\"; } else if (sec.length() == 2) { sec = \"000\" + (time % (1000 * 60)) + \"\"; } else if (sec.length() == 1) { sec = \"0000\" + (time % (1000 * 60)) + \"\"; } return min + \":\" + sec.trim().substring(0, 2); } /** * 文件大小转换 * * @param bytes * @return */ 51 - - 徐东:基于Android平台的视频播放器设计 public static String forbytes(String bytes) { BigDecimal filesize = new BigDecimal(bytes); BigDecimal megabyte = new BigDecimal(1024 * 1024); float returnValue = filesize.divide(megabyte, 2, BigDecimal.ROUND_UP) .floatValue(); if (returnValue > 1) return (returnValue + \"MB\"); BigDecimal kilobyte = new BigDecimal(1024); returnValue = filesize.divide(kilobyte, 2, BigDecimal.ROUND_UP) .floatValue(); return (returnValue + \"KB\"); } } (4)文件信息 package com.xudong.videoview; import android.graphics.Bitmap; /** * video实体类 * * @author jhj 52 - - 徐东:基于Android平台的视频播放器设计 * */ public class Videoinfo {//声明要用的变量 private int id; private String title; private String album; private String artist; private String displayName; private String mimeType; private String path; private String size; private long duration; private Bitmap image; private String datetime; private String resolution; private String imagepath; private int count; public Videoinfo() { super(); } /** * @param id * @param title * @param album * @param artist * @param displayName * @param mimeType 53 - - 徐东:基于Android平台的视频播放器设计 * @param data * @param size * @param duration */ public Videoinfo(int id, String title, String album, String artist, String displayName, String path, String size, long duration, String image) { super(); this.id = id; this.title = title; this.album = album; this.artist = artist; this.displayName = displayName; this.path = path; this.size = size; this.duration = duration; } public String toString() { return \"名\" + displayName + \位置\" + path + \大小\" + size + \" 图像\" + image; } public int getId() { return id; } public void setId(int id) { this.id = id; 54 - - 徐东:基于Android平台的视频播放器设计 } public void setImage(Bitmap image) { this.image = image; } } (5)各手势代码 /** * 滑动改变声音大小 * * @param percent */ private void onVolumeSlide(float percent) { if (mVolume == -1) { mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (mVolume < 0) mVolume = 0; // 显示 mOperationBg.setImageResource(R.drawable.video_volumn_bg); mVolumeBrightnessLayout.setVisibility(View.VISIBLE); } int index = (int) (percent * mMaxVolume) 55 - - 徐东:基于Android平台的视频播放器设计 + mVolume; if (index > mMaxVolume) index = mMaxVolume; else if (index < 0) index = 0; // 变更声音 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0); // 变更进度条 ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams(); lp.width = findViewById(R.id.operation_full).getLayoutParams().width * index / mMaxVolume; mOperationPercent.setLayoutParams(lp); } /** * 滑动改变亮度 * * @param percent */ private void onBrightnessSlide(float percent) { if (mBrightness < 0) { mBrightness = 56 - - 徐东:基于Android平台的视频播放器设计 getWindow().getAttributes().screenBrightness; if (mBrightness <= 0.00f) mBrightness = 0.50f; if (mBrightness < 0.01f) mBrightness = 0.01f; // 显示 mOperationBg.setImageResource(R.drawable.video_brightness_bg); mVolumeBrightnessLayout.setVisibility(View.VISIBLE); } WindowManager.LayoutParams lpa = getWindow().getAttributes(); lpa.screenBrightness = mBrightness + percent; if (lpa.screenBrightness > 1.0f) lpa.screenBrightness = 1.0f; else if (lpa.screenBrightness < 0.01f) lpa.screenBrightness = 0.01f; getWindow().setAttributes(lpa); ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams(); lp.width = (int) (findViewById(R.id.operation_full).getLayoutParams().width * lpa.screenBrightness); mOperationPercent.setLayoutParams(lp); 57 - - 徐东:基于Android平台的视频播放器设计 } /** * 获取传入数据 */ private void getDataFromBundle() { Intent intent = getIntent(); Bundle bundle = intent.getExtras(); Position = bundle.getInt(\"position\"); } (6)播放提示的代码 /** * 播放提示 */ public void playhint( int postion) { AlertDialog.Builder builder = new Builder(this); builder.setTitle(\"提示信息\"); builder.setMessage(\"怎样操作?\"); builder.setPositiveButton(\"上一集\new OnClickListener() { public void onClick(DialogInterface dialog, int which) { Position--; if (Position==-1) { Toast.makeText(Videoplayer.this, \"没有上一集了\ .show(); 58 - - 徐东:基于Android平台的视频播放器设计 finish(); }else { nextplay.sendEmptyMessageDelayed(0,200); } } }); builder.setNeutralButton(\"下一集\new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Position++; nextplay.sendEmptyMessageDelayed(0,200); } }); builder.setNegativeButton(\"返回选集\new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub //Intent intents = new Intent(Videoplayer.this, MainActivity.class); //startActivity(intents); finish(); 59 - - 徐东:基于Android平台的视频播放器设计 } }); AlertDialog alertDialog = builder.create(); alertDialog.show(); } (7)删除文件代码 /** * 删除文件 */ public void delFile(String path) { final File file = new File(path); file.delete(); // 强制扫描我们需要更新的文件路径 MediaScannerConnection .scanFile(this, new String[] { path null, null); Log.i(tag, String.valueOf(videos) \"delete\"); Message msg = new Message(); msg.what = 3; mHandler.sendMessageDelayed(msg, 200);// 延时 刷新 if (isExist(file)) { mHandler.post(new Runnable() { @Override public void run() { - 60 - + }, 徐东:基于Android平台的视频播放器设计 // TODO Auto-generated method stub Toast.makeText(MainActivity.this, \"删除失败\ Toast.LENGTH_SHORT).show(); } }); } else { mHandler.post(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, \"删除成功\ Toast.LENGTH_SHORT).show(); } }); android.util.Log.i(tag, \"delete start s\"); } } 61 - -
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 7swz.com 版权所有 赣ICP备2024042798号-8
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务