Android ViewPager中显示图片与播放视频的填坑记录

ViewPager介绍

ViewPager的功能就是可以使视图滑动,就像Lanucher左右滑动那样。

ViewPager用于实现多页面的切换效果,该类存在于Google的兼容包android-support-v4.jar里面.

ViewPager:

1)ViewPager类直接继承了ViewGroup类,所有它是一个容器类,可以在其中添加其他的view类。

2)ViewPager类需要一个PagerAdapter适配器类给它提供数据。

3)ViewPager经常和Fragment一起使用,并且提供了专门的FragmentPagerAdapter和FragmentStatePagerAdapter类供Fragment中 的ViewPager使用。

4)在编写ViewPager的应用的使用,还需要使用两个组件类分别是PagerTitleStrip类和PagerTabStrip类,PagerTitleStrip类直接继承 自ViewGroup类,而PagerTabStrip类继承PagerTitleStrip类,所以这两个类也是容器类。但是有一点需要注意,在定义XML的layout 的时候,这两个类必须是ViewPager标签的子标签,不然会出错。

本文将详细介绍关于Android ViewPager中显示图片与播放视频填坑的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

一.需求来源与实现思路

1.最近项目需求中有用到需要在ViewPager中播放视频和显示图片的功能,视频是本地视频,最开始的实现思路是ViewPager中根据当前item位置对应的是图片还是视频去初始化PhotoView和SurfaceView,同时销毁时根据item的位置去判断移除PhotoView和SurfaceView。

2.上面那种方式确实是可以实现的,但是存在2个问题,第一,MediaPlayer的生命周期不容易控制并且存在内存泄漏问题。第二,连续三个item都是视频时,来回滑动的过程中发现会出现上个视频的最后一帧画面的bug。

3.未提升用户体验,视频播放器初始化完成前上面会覆盖有该视频的第一帧图片,但是发现存在第一帧图片与视频第一帧信息不符的情况,后面会通过代码给出解决方案。

4.图片和视频尺寸如何适配以保证不变形。

二.需要填的坑

1.对于MediaPlayer的生命周期不容易控制的本质原因是这种实现思路上我的播放器只有1个,频繁的初始化和销毁造成了问题,所以后面我更改了实现方式,一个item的视频对应一个播放器。

2.对于滑动过程中发现会出现上个视频的最后一帧画面的bug,发现是surfaceView这个控件造成的,后面通过将播放的载体更换为TextureView完美解决该问题。

3.SurfaceView与TextureView的本质异同

第一:两者都能在独立的线程中绘制和渲染,在专用的GPU线程中大大提高渲染的性能。

第二:SurfaceView专门提供了嵌入视图层级的绘制界面,开发者可以控制该界面像Size等的形式,能保证界面在屏幕上的正确位置。但也有局限:
1.由于是独立的一层View,更像是独立的一个Window,不能加上动画、平移、缩放;
2.两个SurfaceView不能相互覆盖。

第三:Texture更像是一般的View,像TextView那样能被缩放、平移,也能加上动画。TextureView只能在开启了硬件加速的Window中使用,并且消费的内存要比SurfaceView多,并伴随着1-3帧的延迟。

第四:屏幕锁屏时SurfaceView会销毁重建,TextureView不会!

三.具体实现核心代码

1.ViewPager的初始化

mAdapter = ImageBrowseFragmentPagerAdapter(supportFragmentManager, this, imgs)
 imgs_viewpager.offscreenPageLimit = 1
 imgs_viewpager.adapter = mAdapter
 imgs_viewpager.currentItem = mPosition

 //为了处理首次点击时视频播放的问题
 val message = Message.obtain()
 message.what = START_PLAY_VIDEO
 mHandler.sendMessageDelayed(message, 200)

2.Handler处理消息

private val START_PLAY_VIDEO = 0
private var DELETE_VIDEO = 1
private var DELETE_VIDEO_START_PLAY = 2
private var mHandler = Handler(Handler.Callback { msg ->
  when (msg.what) {
   //开始播放视频
   START_PLAY_VIDEO -> NotifyDispatch.dispatch(PreviewPlayVideoEvent(mPosition))
   //删除视频时刷新ui
   DELETE_VIDEO -> {
    mAdapter?.setImgs(imgs)
   }
   //解决删除视频时之后跳转到另一个item,当它是视频时不继续播放的问题
   DELETE_VIDEO_START_PLAY -> NotifyDispatch.dispatch(PreviewPlayVideoEvent(mDeletePosition))
  }
  true
 })

3.删除视频或图片的处理逻辑

 private fun deletePhotos(position: Int) {
  if (imgs!!.isEmpty()) {
   return
  }
  ThreadDispatch.right_now.execute({
   var file: File?
   file = File(imgs.get(position))
   if (file != null && file?.exists()!!) {
    val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
    val uri = Uri.fromFile(file)
    intent.data = uri
    sendBroadcast(intent)
    file?.delete()
    imgs.removeAt(position)
   }

   if (position == imgs.size) {
    mDeletePosition = position - 1
   } else {
    mDeletePosition = position
   }
   val message = Message.obtain()
   message.what = DELETE_VIDEO
   mHandler.sendMessage(message)
   NotifyDispatch.dispatch(DeletePreviewPhotoEvent(imgs))
   val message1 = Message.obtain()
   message1.what = DELETE_VIDEO_START_PLAY
   mHandler.sendMessageDelayed(message1, 200)
   if (imgs.isEmpty()) {
    finish()
   }
  })
// }
 }

4.ViewPager对应的Adapter

package com.immomo.camerax.gui.view.adapter;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.view.ViewGroup;
import com.immomo.camerax.gui.fragment.PreviewImgFragment;
import com.immomo.camerax.gui.fragment.PreviewVideoFragment;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by liuxu on 2018/3/26.
 */

public class ImageBrowseFragmentPagerAdapter extends FragmentStatePagerAdapter {
 private Context mContext;
 private List<String> datas;
 private int mCurrentSelectedPosition = -1;
 private FragmentManager mFragmentManager;
 private FragmentTransaction mFragmentTransaction;
 private ArrayList<Fragment> mFragments = new ArrayList<>();

 public ImageBrowseFragmentPagerAdapter(FragmentManager fm, Context context, List<String> datas) {
  super(fm);
  mFragmentManager = fm;
  mContext = context;
  this.datas = datas;
 }

 public void removeContext(){
  mContext = null;
 }

 @Override
 public void setPrimaryItem(ViewGroup container, int position, Object object) {
  mCurrentSelectedPosition = position;
 }

 @Override
 public void startUpdate(ViewGroup container) {
  super.startUpdate(container);
 }

 public void setImgs(List<String> imgs) {
  this.datas = imgs;
  notifyDataSetChanged();
 }

 //处理更新无效----删除条目
 @Override
 public int getItemPosition(Object object) {
  return POSITION_NONE;
 }

 public int getPrimaryItemPosition() {
  return mCurrentSelectedPosition;
 }

 public ImageBrowseFragmentPagerAdapter(FragmentManager fm) {
  super(fm);
 }

 @Override
 public boolean isViewFromObject(View view, Object object) {
  return view == ((Fragment) object).getView();
 }

 @Override
 public Fragment getItem(int position) {
  Bundle bundle = new Bundle();
  bundle.putString("url", datas.get(position));
  bundle.putInt("position", position);
  if (datas.get(position).endsWith(".jpg")) {
   PreviewImgFragment previewImgFragment = new PreviewImgFragment();
   previewImgFragment.setArguments(bundle);
   return previewImgFragment;
  } else {
   PreviewVideoFragment previewVideoFragment = new PreviewVideoFragment();
   previewVideoFragment.setArguments(bundle);
   return previewVideoFragment;
  }
 }

 @Override
 public int getCount() {
  return datas == null ? 0 : datas.size();
 }

 @Override
 public Object instantiateItem(ViewGroup container, int position) {
  return super.instantiateItem(container,position);
 }

 @Override
 public void destroyItem(ViewGroup container, int position, Object object) {
  super.destroyItem(container,position,object);
 }
}

5显示图片对应的Fragment

package com.immomo.camerax.gui.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.bumptech.glide.Glide;
import com.immomo.camerax.R;
import com.immomo.camerax.foundation.util.StatusBarUtils;
import com.immomo.camerax.gui.view.ResizablePhotoView;
/**
 * Created by liuxu on 2018/3/27.
 */
public class PreviewImgFragment extends Fragment {
 @Nullable
 @Override
 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  View view = inflater.inflate(R.layout.fragment_preview_photo, null);
  ResizablePhotoView resizablePhotoView = view.findViewById(R.id.customPhotoView);
  String url = getArguments().getString("url");
  Glide.with(getContext()).load(url).into(resizablePhotoView);
  resizablePhotoView.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    getActivity().finish();
   }
  });
  return view;
 }

 @Override
 public void onPause() {
  super.onPause();
 }

 @Override
 public void onResume() {
  super.onResume();
 }

 @Override
 public void onDetach() {
  super.onDetach();
 }

 @Override
 public void onDestroyView() {
  super.onDestroyView();
 }

 @Override
 public void onStart() {
  super.onStart();
 }

 @Override
 public void onDestroy() {
  super.onDestroy();
 }

 @Override
 public void setUserVisibleHint(boolean isVisibleToUser) {
  super.setUserVisibleHint(isVisibleToUser);
 }
}

6.图片根据宽度适配高度的自定义View

package com.immomo.camerax.gui.view;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import com.github.chrisbanes.photoview.PhotoView;

/**
 * Created by liuxu on 2018/4/7.
 */

public class ResizablePhotoView extends PhotoView {
 public ResizablePhotoView(Context context) {
  super(context);
 }

 public ResizablePhotoView(Context context, AttributeSet attr) {
  super(context, attr);
 }

 public ResizablePhotoView(Context context, AttributeSet attr, int defStyle) {
  super(context, attr, defStyle);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  Drawable d = getDrawable();
  if (d != null){
   int width = MeasureSpec.getSize(widthMeasureSpec);
   //高度根据使得图片的宽度充满屏幕计算而得
   int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());
   setMeasuredDimension(width, height);
  }else {
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }
 }
}

7.播放视频对应的Fragment

/**
 * Created by liuxu on 2018/3/27.
 */

public class PreviewVideoFragment extends Fragment {
 private ImageView mPhotoView;
 private TextureView mTextureView;
 private String mUrl;
 private int mPosition;
 private AndroidMediaPlayer mIjkVodMediaPlayer;
 private boolean mIsSelected;
 private boolean mIsFirstPrepared;
 private PreviewPlayVideoSubscriber mPreviewPlayVideoSubscriber = new PreviewPlayVideoSubscriber() {
  @Override
  public void onEventMainThread(PreviewPlayVideoEvent event) {
   super.onEventMainThread(event);
   MDLog.e("liuxu",event.getPosition()+"");
   if (event != null && event.getPosition() == mPosition) {
    //说明是当前条目
    if (mIjkVodMediaPlayer != null && !mIjkVodMediaPlayer.isPlaying()) {
     if (mTextureView != null) {
      mIjkVodMediaPlayer.setSurface(mSurface);
      mIjkVodMediaPlayer.prepareAsync();
      mPhotoView.setVisibility(View.VISIBLE);
     }
    }
    mIsSelected = true;
   } else {
    if (mIjkVodMediaPlayer != null && mIjkVodMediaPlayer.isPlaying()) {
     mIjkVodMediaPlayer.pause();
     mIjkVodMediaPlayer.stop();
    }

    if (mPhotoView != null) {
     mPhotoView.setVisibility(View.VISIBLE);
    }
    mIsSelected = false;
   }

  }
 };
 private String mWidth;
 private String mHeight;

 @Nullable
 @Override
 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  mPreviewPlayVideoSubscriber.register();
  View view = inflater.inflate(R.layout.fragment_preview_video, null);
  mPhotoView = view.findViewById(R.id.photoView);
  mTextureView = view.findViewById(R.id.surfaceView);
  mUrl = getArguments().getString("url");
  mPosition = getArguments().getInt("position");
  layoutPlayer();
  loadVideoScreenshot(getContext(), mUrl, mPhotoView, 1);
  view.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {

    release();
    getActivity().finish();
   }
  });
  initTextureMedia();
  return view;
 }

 private void initTextureMedia() {
  mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
 }

 private void play(String url) {
  try {
   mIjkVodMediaPlayer = new AndroidMediaPlayer();
   mIjkVodMediaPlayer.reset();
   mIjkVodMediaPlayer.setDataSource(url);
   //让MediaPlayer和TextureView进行视频画面的结合
   mIjkVodMediaPlayer.setSurface(mSurface);
   //设置监听
   mIjkVodMediaPlayer.setOnBufferingUpdateListener((mp, percent) -> {

   });
   mIjkVodMediaPlayer.setOnCompletionListener(mp -> {
    mp.seekTo(0);
    mp.start();
   });
   mIjkVodMediaPlayer.setOnInfoListener((mp1, what, extra) -> {
      if (what == IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
       mPhotoView.setVisibility(View.GONE);
       mIsFirstPrepared = true;
      }
      return false;
     });
   mIjkVodMediaPlayer.setOnErrorListener((mp, what, extra) -> false);
   mIjkVodMediaPlayer.setOnPreparedListener(mp -> {
    mp.start();
    if (!mIsFirstPrepared){
    }else {
     mPhotoView.setVisibility(View.GONE);
    }
   });
   mIjkVodMediaPlayer.setScreenOnWhilePlaying(true);//在视频播放的时候保持屏幕的高亮
   if (mIsSelected){
    //异步准备
    mIjkVodMediaPlayer.prepareAsync();
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 private Surface mSurface;
 private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
  @Override
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
   mSurface = new Surface(surface);
   play(mUrl);
  }

  @Override
  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

  }

  @Override
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
   if (mSurface != null){
    mSurface.release();
    mSurface = null;
   }
   if (mTextureView != null){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
     mTextureView.releasePointerCapture();
    }
   }
   release();
   return false;
  }
  @Override
  public void onSurfaceTextureUpdated(SurfaceTexture surface) {

  }
 };

 @Override
 public void onStart() {
  super.onStart();
 }
 @Override
 public void onAttach(Context context) {
  super.onAttach(context);
 }

 @Override
 public void onDetach() {
  super.onDetach();
 }

 @Override
 public void onPause() {
  MDLog.e("liuxu", "onPause" + mPosition);
  //处理锁屏时播放器停止播放
  if (mIjkVodMediaPlayer != null && mIjkVodMediaPlayer.isPlaying()){
   mIjkVodMediaPlayer.pause();
   mIjkVodMediaPlayer.stop();
  }
  super.onPause();
 }

 //屏幕打开时重新播放
 @Override
 public void onResume() {
  MDLog.e("liuxu", "onResume" + mPosition);
  super.onResume();
  if (mIsSelected && mIjkVodMediaPlayer != null && !mIjkVodMediaPlayer.isPlaying()) {
   mIjkVodMediaPlayer.prepareAsync();
  }
 }

 @Override
 public void onDestroy() {
  MDLog.e("liuxu", "onDestroy");
  release();
  if (mPreviewPlayVideoSubscriber.isRegister()) {
   mPreviewPlayVideoSubscriber.unregister();
  }
  super.onDestroy();
 }

 private void release() {
  if (mIjkVodMediaPlayer == null) {
   return;
  }
  if (mIjkVodMediaPlayer.isPlaying()) {
   mIjkVodMediaPlayer.stop();
  }
  mIjkVodMediaPlayer.release();
  mIjkVodMediaPlayer = null;
 }

 @Override
 public boolean getUserVisibleHint() {
  return super.getUserVisibleHint();
 }

 /**
  * 动态设置视频宽高信息
  */
 private void layoutPlayer() {
  //获取视频宽高比
  getPlayInfo(mUrl);
  float ratio = Float.parseFloat(mHeight) / Float.parseFloat(mWidth);
  MDLog.e("type", mPosition + "ratio" + ratio);
  int type = 0;
  //添加容错值
  if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_1_1().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()
    && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_1_1().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) {
   type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_11();
  } else if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_4_3().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()
    && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_4_3().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) {
   type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_43();
   MDLog.e("type", "43");
  } else if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_16_9().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()
    && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_16_9().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) {
   type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_169();
   MDLog.e("type", "169");
  }

  FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mTextureView.getLayoutParams();
  layoutParams.height = ScreenAdapterUtils.INSTANCE.getSurfaceHeight(type);
  mTextureView.setLayoutParams(layoutParams);
  FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mPhotoView.getLayoutParams();
  params.height = ScreenAdapterUtils.INSTANCE.getSurfaceHeight(type);
  mPhotoView.setLayoutParams(params);
  MDLog.e("params.height", params.height + "");
 }

 private void getPlayInfo(String mUri) {
  android.media.MediaMetadataRetriever mmr = new android.media.MediaMetadataRetriever();
  try {
   if (mUri != null) {
    mmr.setDataSource(mUri);
   } else {
    //mmr.setDataSource(mFD, mOffset, mLength);
   }

   //宽
   mWidth = mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
   //高
   mHeight = mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
//   mBitmap = mmr.getFrameAtTime(1 );

  } catch (Exception ex) {
  } finally {
   mmr.release();
  }

 }

 public static void loadVideoScreenshot(final Context context, String uri, ImageView imageView, long frameTimeMicros) {
  // 这里的时间是以微秒为单位
  RequestOptions requestOptions = RequestOptions.frameOf(frameTimeMicros);
  requestOptions.set(FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST);
  requestOptions.transform(new BitmapTransformation() {
   @Override
   protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
    return toTransform;
   }

   @Override
   public void updateDiskCacheKey(MessageDigest messageDigest) {
    try {
     messageDigest.update((context.getPackageName() + "RotateTransform").getBytes("utf-8"));
    } catch (Exception e) {
     e.printStackTrace();
    }
   }
  });
  Glide.with(context).load(uri).apply(requestOptions).into(imageView);
 }
}

4.结语

笔者使用这种方式实现了项目需求,但是由于本人接触音视频的相关内容比较少,全是在不断探索和学习中前进,如有不足之处请评论指正,谢谢。大家共同学习共同进步。

好了以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • android播放视频时在立体声与单声道之间切换无变化原因分析及解决

    部分客户客户使用第三方视频播放器,有立体声与单声道之间切换,发现切换后无作用 原因是由于在HAL层默认没有处理上层发的stereo 转mono的命令,所以会没有效果, 可按如下修改,添加相关处理: 1修改AudioMTKHardware.cpp 添加:static String8 keySetStereo2MonoMode = String8("EnableStereoOutput"); 2.修改status_t AudioMTKHardware::setParameters(cons

  • Android编程实现播放视频时切换全屏并隐藏状态栏的方法

    本文实例讲述了Android编程实现播放视频时切换全屏并隐藏状态栏的方法.分享给大家供大家参考,具体如下: 1. Demo示例: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (this.getResources().getConfiguration().ori

  • Android播放视频的三种方式

    在Android中,我们有三种方式来实现视频的播放: 1).使用其自带的播放器.指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型. 2).使用VideoView来播放.在布局文件中使用VideoView结合MediaController来实现对其控制. 3).使用MediaPlayer类和SurfaceView来实现,这种方式很灵活. 1.调用其自带的播放器: Uriuri = Uri.parse(Environment.getExternalStorageD

  • Android仿新浪微博/QQ空间滑动自动播放视频功能

    先来看看效果图 关键代码 1.监听滚动事件 首先要给listview添加setOnScrollListener监听,注意这个监听在recyclerView上是addOnScrollListener,也就是说下面代码同时支持recyclerView. public int firstVisible=0,visibleCount=0, totalCount=0; videoList.setOnScrollListener(new AbsListView.OnScrollListener() { @O

  • Android DragVideo实现播放视频时任意拖拽的方法

    Android DragVideo实现播放视频时任意拖拽 DragVideo A Method to Drag the Video When Playing Video 一种在播放视频时,能够拖拽的方案 为什么有这个工程 经常在爱奇艺网站上看电影,看到如果滑动掩盖了播放窗口后,就后在最下面有一个小播放界面.并且这个播放界面,是可以任意拖拽的.感觉很酷 既然web端能实现,就想了想在移动端设备上,是否也能实现这个效果,于是就有了- 效果图: ------> 实现思路:1.播放视频的view选择Te

  • android使用surfaceview+MediaPlayer播放视频

    Android中播放视频主要有两种方式: 使用其自带的播放器.指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型 使用android自带的VideoView,这种方法太简单就不介绍了 使用SurfaceView+MediaPlayer,这种方式效果比较好,这里也重点介绍 SurfaceView从android 1.0就有了,十分好用.一般来说,UI对刷新都需要在UI线程中完成,但是,surfaceview可以在非UI线程中完成刷新.这样以来就很方便了,比如在线播

  • Android多媒体教程之播放视频的四种方法

    本文主要给大家介绍的是关于Android播放视频的四种方法,分享出来供大家参考学习,下面来一起看看详细的介绍: 一.通过intent的方式,调用系统自带的播放器 Uri uri = Uri.parse("/storage/emulated/0/DCIM/Camera/20170521_200117.mp4"); //调用系统自带的播放器 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(uri,

  • Android编程实现播放视频的方法示例

    本文实例讲述了Android编程实现播放视频的方法.分享给大家供大家参考,具体如下: 播放视频文件其实并不比播放音频文件复杂,主要是使用 VideoView 类来实现的.这个 类将视频的显示和控制集于一身,使得我们仅仅借助它就可以完成一个简易的视频播放器. VideoView 的用法和 MediaPlayer 也比较类似,主要有以下常用方法: 方法名 功能描述 setVideoPath() 设置要播放的视频文件的位置. start() 开始或继续播放视频. pause() 暂停播放视频. res

  • android使用videoview播放视频

    复制代码 代码如下: public class Activity01 extends Activity{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState); setContentView(R.layout.main); final VideoView vid

  • 一个html5播放视频的video控件只支持android的默认格式mp4和3gp

    复制代码 代码如下: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body>

随机推荐