Android开发学习之路--基于vitamio的视频播放器(一)
来源:互联网 发布:sql双机热备 编辑:程序博客网 时间:2024/05/16 05:06
之前也试过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的视频播放器(一)
- Android开发学习之路--基于vitamio的视频播放器(二)
- android开发视频播放器-----视频播放 Vitamio框架的使用(一)
- Android视频播放之vitamio播放视频(一)
- Android视频播放项目总结之 使用第三方Vitamio库,开发万能播放器(一)
- android 基于vitamio的本地视频播放器源码
- 利用Vitamio开发视频播放器(一)
- 安卓视频播放器的实现(基于vitamio)
- android视频播放器Vitamio的使用
- Vitamio视频播放器(一)
- Vitamio视频播放器(一)
- android视频播放器(vitamio)
- 对Android基于vitamio框架调用第三方播放器播放视频的初步认识
- android开发视频播放器------Vitamio 播放页实例
- Android视频播放项目总结之 使用第三方Vitamio库,开发万能播放器(二)
- Android视频播放项目总结之 使用第三方Vitamio库,开发万能播放器(三)
- Android视频播放项目总结之 使用第三方Vitamio库,开发万能播放器(四)
- Android vitamio视频播放器
- css负边距之详解
- Shader学习笔记3
- 直接插入排序
- IMYWebView 无缝切换 UIWebView 为 WKWebView
- 摆花
- Android开发学习之路--基于vitamio的视频播放器(一)
- 值传递和引用传递
- install alilang
- iOS 多线程(一)多线程基础
- CUDA学习--内存处理之共享内存(3)
- 2016 ACM-CCPC长春站比赛总结
- Springmvc接收前台字符串类型日期格式转换
- Android网络框架-Volley(三) 加载图片
- C#界面数据存储到制定TXT文件