之前也试过vitamio这个库,后来不知道被什么事情给耽搁了,就没继续下去。近来觉得视频还是需要学习一下的,谁让直播那么火呢,就想着写一个简单的视频播放的app先吧。好了那就开始吧,暂时取名为JPlayer,后续慢慢改进,源码也在github上(https://github.com/imchenjianneng/JPlayer),后续不断更新吧。

  首先新建工程JPlayer,然后新建个主界面吧:

<layout>
<data class="MainBinding"></data>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"> <android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp">
<!--app:layout_scrollFlags="scroll|enterAlways"--> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="12dp"
android:paddingRight="12dp"> <RadioGroup
android:id="@+id/rg_tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:orientation="horizontal"> <RadioButton
android:id="@+id/rb_local"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:gravity="center"
android:text="本地"
android:textColor="@drawable/top_tab_text_color"
android:textSize="16sp" /> <RadioButton
android:id="@+id/rb_intenet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:gravity="center"
android:text="网络"
android:textColor="@drawable/top_tab_text_color"
android:textSize="16sp" />
</RadioGroup>
</RelativeLayout>
</android.support.v7.widget.Toolbar>
</RelativeLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </android.support.design.widget.CoordinatorLayout>
</layout>

  这里简单简绍下,具体可以看源码,界面由是两个radiobutton,一个viewpager组成,比较简陋,后续再改吧,我们先实现功能要紧。真实项目一般ui都会提供好界面的。

  既然界面搭建好了,那么继续开始代码实现吧:

public class MainActivity extends BaseActivity implements ViewPager.OnPageChangeListener {

    private MainBinding binding;

    private List<Integer> bts = Arrays.asList(
R.id.rb_local,
R.id.rb_intenet); private float normalSize, normalSelected; @Override
protected void initParams() {
binding = DataBindingUtil.setContentView(getActivity(), R.layout.activity_main);
} @Override
protected void initViews() {
super.initViews(); normalSize = getResources().getDimension(R.dimen.normal_size);
normalSelected = getResources().getDimension(R.dimen.selected_size); binding.viewpager.setOffscreenPageLimit(2);
binding.viewpager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager()));
initTabPagerListener();
binding.rgTab.check(bts.get(0));
} private void initTabPagerListener() { binding.viewpager.addOnPageChangeListener(this);
binding.rgTab.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int i = bts.indexOf(checkedId);
if (i != -1) {
selectTitle(checkedId);
binding.viewpager.setCurrentItem(i);
}
}
});
} @Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override
public void onPageSelected(int position) {
selectTitle(bts.get(position)); binding.rgTab.check(bts.get(position));
} @Override
public void onPageScrollStateChanged(int state) { } private void selectTitle(int selectResId) {
binding.rbLocal.setTextSize(TypedValue.COMPLEX_UNIT_PX, normalSize);
binding.rbIntenet.setTextSize(TypedValue.COMPLEX_UNIT_PX, normalSize);
((TextView)findViewById(selectResId)).setTextSize(TypedValue.COMPLEX_UNIT_PX,
normalSelected);
} public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
LocalVideoFragment localVideoFragment = new LocalVideoFragment();
InternetVideoFragment internetVideoFragment = new InternetVideoFragment();
private List<? extends Fragment> list = Arrays.asList(
localVideoFragment,
internetVideoFragment); public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
} @Override
public int getCount() {
return list.size();
} @Override
public Fragment getItem(int position) {
return list.get(position);
}
}
}

  代码其实比较简单,主要实现了viewpager,然后LocalVideoFragment和InternetVideoFragment两个fragment,第一个是本地的界面,第二个是网络(这里暂时不实现),好了,主要的代码都在LocalVideoFragment里了。那么就接着看这个Frament的代码了。

  既然要播放视频,首先是需要实现扫描所有视频文件。先看这一段代码吧:

private class ScanVideoTask extends AsyncTask<Void, File, Void> {

        @Override
protected Void doInBackground(Void... params) {
eachAllMedias(Environment.getExternalStorageDirectory());
return null;
} @Override
protected void onProgressUpdate(File... values) {
LocalSource localSource = new LocalSource();
localSource.setName(values[0].getName());
localSource.setUrl(values[0].getAbsolutePath());
localSource.setBitmap(getVideoThumbnail(localSource, localSource.getUrl()));
localSource.setBitmap(getVideoThumbnail(localSource.getUrl()));
Log.d("LocalVideoFragment", "hhh"+localSource.getName()+":"+localSource.getUrl());
fileAdapter.appendItem(localSource);
fileAdapter.notifyDataSetChanged(); } /** 遍历所有文件夹,查找出视频文件 */
public void eachAllMedias(File f) {
if (f != null && f.exists() && f.isDirectory()) {
File[] files = f.listFiles();
if (files != null) {
for (File file : f.listFiles()) {
if (file.isDirectory()) {
eachAllMedias(file);
} else if (file.exists() && file.canRead() && isVideo(file)) {
publishProgress(file);
}
}
}
}
}
}

  扫描需要花费比较长的时间,所以这里放到一个异步task中去处理,深度优先的搜索所有的文件,扫描到了文件判断是否为视频文件,若是的话,那就直接加到recycleview中去。对于是否是视频文件,这里仅仅是判断后缀名,更合理的方式应该是解码一部分,然后判断头文件的信息的,之类就偷懒了一下,具体可以参考源码。

  既然已经扫描出了文件,那么接下来就需要这个播放列表了,因为我们要显示视频的缩略图,视频的文件名,以及播放时长和大小。用到了MediaMetadataRetriever类,通过extractMetadata方法来获取视频的相关信息,看下代码吧:

public Bitmap getVideoThumbnail(LocalSource localSource, String filePath) {
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(filePath);
bitmap = retriever.getFrameAtTime(0);
localSource.setAuthor(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_AUTHOR));
localSource.setDuration(Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)));
localSource.setDate(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));
localSource.setBitrate(Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE))); } catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
return bitmap;
}

  这通过retriever.getFrameAtTime来获取一帧的图像bitmap用来当缩略图,然后通过MediaMetadataRetriever.METADATA_KEY_DURATION获取播放时长。通过MediaMetadataRetriever.METADATA_KEY_BITRATE获取比特率,计算就可以得到所需要的文件大小。

  要上班去了,接下去的话就是重点了,怎么使用vitamio,下次继续了,想直接看代码的话可以直接github看起来,代码写得比较挫,还没有整理过,为了实现功能而写。

Android开发学习之路--基于vitamio的视频播放器(一)的更多相关文章

  1. Android开发学习之路--基于vitamio的视频播放器(二)

      终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...

  2. Android开发学习之路--Android系统架构初探

    环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究研究.这里从芯片及硬件模块-& ...

  3. Android开发学习之路-RecyclerView滑动删除和拖动排序

    Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...

  4. Android开发学习之路--Android Studio cmake编译ffmpeg

      最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...

  5. Android开发学习之路--网络编程之xml、json

    一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...

  6. Android开发学习之路--Activity之初体验

    环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...

  7. Android开发学习之路--MAC下Android Studio开发环境搭建

    自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...

  8. Android开发学习之路-记一次CSDN公开课

    今天的CSDN公开课Android事件处理重难点快速掌握中老师讲到一个概念我觉得不正确. 原话是这样的:点击事件可以通过事件监听和回调两种方法实现. 我一听到之后我的表情是这样的: 这跟我学的看的都不 ...

  9. Android开发学习之路-Android Studio开发小技巧

    上一次发过了一个介绍Studio的,这里再发一个补充下. 我们都知道,Android Studio的功能是非常强大的,也是很智能的.如果有人告诉你学Android开发要用命令行,你可以告诉他Andro ...

随机推荐

  1. 用委托(Delegate)的BeginInvoke和EndInvoke方法操作线程

    让我们首先了解下什么时候用到C#异步调用: .NET Framework 允许您C#异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的Begin ...

  2. Python中的上下文管理器和with语句

    Python2.5之后引入了上下文管理器(context manager),算是Python的黑魔法之一,它用于规定某个对象的使用范围.本文是针对于该功能的思考总结. 为什么需要上下文管理器? 首先, ...

  3. 阿里云、腾讯云开通端口 telnet不通的原因

    1.安全组是否已经开通相对应的端口: 阿里云:https://help.aliyun.com/document_detail/25471.html 腾讯云:http://bbs.qcloud.com/ ...

  4. MyBatis(2)——MyBatis 深入学习

    编写日志输出环境配置文件 在开发过程中,最重要的就是在控制台查看程序输出的日志信息,在这里我们选择使用 log4j 工具来输出: 准备工作: 将[MyBatis]文件夹下[lib]中的 log4j 开 ...

  5. [LeetCode] Is Graph Bipartite? 是二分图么?

    Given an undirected graph, return true if and only if it is bipartite. Recall that a graph is bipart ...

  6. [LeetCode] Network Delay Time 网络延迟时间

    There are N network nodes, labelled 1 to N. Given times, a list of travel times as directed edges ti ...

  7. codevs 1006 等差数列

    提交地址:http://codevs.cn/problem/1006/ 1006 等差数列  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold   题目描述 Des ...

  8. k-d tree模板练习

    1. [BZOJ]1941: [Sdoi2010]Hide and Seek 题目大意:给出n个二维平面上的点,一个点的权值是它到其他点的最长距离减最短距离,距离为曼哈顿距离,求最小权值.(n< ...

  9. VK-Cup 2017 qualification 1

    VK-Cup,cf里面只有切成俄文才能看到,题目也都是俄文的(百度翻译成英文和中文). 两人组队参赛的,赛期1天,乐多赛赛制(和时间基本无关,交上去挂了扣分).这次是第一场资格赛. 这次又和ditol ...

  10. [济南集训 2017] 求gcd之和

    题目大意: 求\(\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)\) 解题报告: 有一个结论:一个数的所有因子的欧拉函数之和等于这个数本身 运用这个我们可以开始推: \(\sum_{ ...