首页 > 其他分享 >Android开发学习之路--基于vitamio的视频播放器(一)

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

时间:2023-01-15 15:08:32浏览次数:69  
标签:layout -- binding public vitamio id localSource Android android


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




Android开发学习之路--基于vitamio的视频播放器(一)_ide


<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看起来,代码写得比较挫,还没有整理过,为了实现功能而写。


标签:layout,--,binding,public,vitamio,id,localSource,Android,android
From: https://blog.51cto.com/u_15940062/6008693

相关文章

  • Android开发学习之路--React-Native之初体验
      近段时间业余在学node.js,租了个阿里云准备搭建后端,想用node.js,偶尔得知react-native可以在不同平台跑,js在iOS和android上都可以运行ok,今天就简单学习下react-native。(......
  • 从嵌入式linux到android应用开发
      时间过得很快,转眼之间已经到新公司一个月了。虽然学到了一些移动开发的知识,但是觉得离我的目标还很远,完全没能达到我想要的水平。以前产品都是自己主导的,需要完成什么,计......
  • Android开发学习之路--RxAndroid之简单原理
      学习了RxAndroid,其实也就是RxJava了,但是还是不是非常清楚到底RxAndroid有什么用呢?为什么要使用RxAndroid呢?这篇文章讲得不错,​​RxJava的原理​​。但是这里还是把整个......
  • Android开发学习之路--RxAndroid之操作符
      学习了RxAndroid的一些基本知识,上篇文章也试过了RxAndroid的map操作符,接着来学习更多的操作符的功能吧。  操作符就是为了解决对Observable对象的变换的问题,操作符用......
  • Android开发学习之路--RxAndroid之初体验
      学了一段时间android,看了部分的项目代码,然后想想老是学基础也够枯燥乏味的,那么就来学习学习新东西吧,相信很多学java的都听说过RxJava,那么android下也有RxAndroid。 ......
  • Android开发学习之路--Annotation注解简化view控件之初体验
      一般我们在写androidActivity的时候总是会在onCreate方法中加上setContentView方法来加载layout,通过findViewById来实现控件的绑定,每次写这么多代码总觉得很烦躁。近......
  • 人生顿悟之宽以待人,严以律己
        台风已经过去了,天气也渐渐地晴朗了,但是不知道为什么自己的心情却越发觉得沉重起来。    总觉得生活中少了点什么,是没有了以往的激情,还是多了几分压力。......
  • 初探linux子系统集之i2c子系统(一)
          I2c子系统在进公司来的时候就学习过了,可是那是还不是很熟悉linux中的i2c子系统,就没有细看。记得当初很想熟悉linux中的各种总线驱动,想专门写一个关于总线驱......
  • 初探linux子系统集之led子系统(三)
         世界杯结束了,德国战车夺得了大力神杯,阿根廷最终还是失败了。也许3年,5年,或者10年后,人们就不知道巴西世界杯的亚军是谁,但是总是会记得冠军是谁。就像什么考试,比......
  • Android开发学习之路--Android系统架构初探
      环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究......