欢迎Follow我的GitHub, 关注我的简书. 其余参考Android目录.

效果

Android

在应用的信息流中, 用户会分享视频, 连续展示, 这就需要处理视频滚动播放. 然而, 在列表视图(RecyclerView)中使用MediaPlayer播放视频时, 会产生一些问题, 即无法同步控制视频的播放和停止. 使用控件库可以解决这一问题.

滚动播放功能: 在页面中, 判断视频的可视比例, 最大视频项开始播放, 其余视频项关闭, 滚动中自动控制切换视频状态. 让我们来看看如何实现这一功能.

本文源码的GitHub下载地址.

使用的视频管理库.

// 视频播放库

compile 'com.github.danylovolokh:video-player-manager:0.2.0'

compile 'com.github.danylovolokh:list-visibility-utils:0.2.0'

1. 基础

依赖注入, 图片加载, 和视频播放.

compile 'com.jakewharton:butterknife:7.0.1' // 依赖注入

compile 'com.squareup.picasso:picasso:2.5.2' // 图片加载

// 视频播放库

compile 'com.github.danylovolokh:video-player-manager:0.2.0'

compile 'com.github.danylovolokh:list-visibility-utils:0.2.0'

首页跳转到Fragment, 可以选择本地视频或者网络视频两种方式.

public class MainActivity extends AppCompatActivity {

public static final int LOCAL = 0; // 本地

public static final int ONLINE = 1; // 在线

@Bind(R.id.main_t_toolbar) Toolbar mTToolbar;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButterKnife.bind(this);

mTToolbar.setTitle("列表");

setSupportActionBar(mTToolbar);

if (savedInstanceState == null) {

getSupportFragmentManager()

.beginTransaction()

.replace(R.id.main_fl_container, VideoListFragment.newInstance(LOCAL))

.commit();

}

}

@Override public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

@Override public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.enable_local_video:

if (!item.isChecked()) {

getSupportFragmentManager()

.beginTransaction()

.replace(R.id.main_fl_container, VideoListFragment.newInstance(LOCAL))

.commit();

}

break;

case R.id.enable_online_video:

if (!item.isChecked()) {

getSupportFragmentManager()

.beginTransaction()

.replace(R.id.main_fl_container, VideoListFragment.newInstance(ONLINE))

.commit();

}

break;

}

item.setChecked(!item.isChecked());

return true;

}

}

使用Fragment的工厂模式添加参数. 通过菜单选项可以切换模式.

item.setChecked(!item.isChecked());改变切换状态

2. 视频列表

设置Video列表的Adapter, 添加滚动状态监听, 实现动态切换视频.

ItemsPositionGetter判断显示百分比, 提供回调控制视频状态.

通过Fragment的设置参数, 判断播放使用本地视频还是网络视频.

@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

Bundle args = getArguments();

if (args != null) {

// 设置类型

if (args.getInt(VIDEO_TYPE_ARG) == MainActivity.LOCAL) {

initLocalVideoList();

} else {

initOnlineVideoList();

}

} else {

initLocalVideoList();

}

mRvList.setHasFixedSize(true);

mLayoutManager = new LinearLayoutManager(getActivity());

mRvList.setLayoutManager(mLayoutManager);

VideoListAdapter adapter = new VideoListAdapter(mList);

mRvList.setAdapter(adapter);

// 获取Item的位置

mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRvList);

mRvList.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override

public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {

mScrollState = scrollState;

if (scrollState == RecyclerView.SCROLL_STATE_IDLE && !mList.isEmpty()) {

mVisibilityCalculator.onScrollStateIdle(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition());

}

}

@Override

public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

if (!mList.isEmpty()) {

mVisibilityCalculator.onScroll(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition() -

mLayoutManager.findFirstVisibleItemPosition() + 1,

mScrollState);

}

}

});

}

视频列表主要是监听出现百分比, 动态切换视频.

3. 适配器

适配器和ViewHolder. 绑定视频元素, 播放监听控制覆盖层的显示与隐藏.

/**

* 视频列表的适配器

*

* Created by wangchenlong on 16/1/27.

*/

public class VideoListAdapter extends RecyclerView.Adapter {

private final List mList; // 视频项列表

// 构造器

public VideoListAdapter(List list) {

mList = list;

}

@Override

public VideoListAdapter.VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_video, parent, false);

// 必须要设置Tag, 否则无法显示

VideoListAdapter.VideoViewHolder holder = new VideoListAdapter.VideoViewHolder(view);

view.setTag(holder);

return new VideoListAdapter.VideoViewHolder(view);

}

@Override

public void onBindViewHolder(final VideoListAdapter.VideoViewHolder holder, int position) {

VideoListItem videoItem = mList.get(position);

holder.bindTo(videoItem);

}

@Override public int getItemCount() {

return mList.size();

}

public static class VideoViewHolder extends RecyclerView.ViewHolder {

@Bind(R.id.item_video_vpv_player) VideoPlayerView mVpvPlayer; // 播放控件

@Bind(R.id.item_video_iv_cover) ImageView mIvCover; // 覆盖层

@Bind(R.id.item_video_tv_title) TextView mTvTitle; // 标题

@Bind(R.id.item_video_tv_percents) TextView mTvPercents; // 百分比

private Context mContext;

private MediaPlayerWrapper.MainThreadMediaPlayerListener mPlayerListener;

public VideoViewHolder(View itemView) {

super(itemView);

ButterKnife.bind(this, itemView);

mContext = itemView.getContext().getApplicationContext();

mPlayerListener = new MediaPlayerWrapper.MainThreadMediaPlayerListener() {

@Override

public void onVideoSizeChangedMainThread(int width, int height) {

}

@Override

public void onVideoPreparedMainThread() {

// 视频播放隐藏前图

mIvCover.setVisibility(View.INVISIBLE);

}

@Override

public void onVideoCompletionMainThread() {

}

@Override

public void onErrorMainThread(int what, int extra) {

}

@Override

public void onBufferingUpdateMainThread(int percent) {

}

@Override

public void onVideoStoppedMainThread() {

// 视频暂停显示前图

mIvCover.setVisibility(View.VISIBLE);

}

};

mVpvPlayer.addMediaPlayerListener(mPlayerListener);

}

public void bindTo(VideoListItem vli) {

mTvTitle.setText(vli.getTitle());

mIvCover.setVisibility(View.VISIBLE);

Picasso.with(mContext).load(vli.getImageResource()).into(mIvCover);

}

// 返回播放器

public VideoPlayerView getVpvPlayer() {

return mVpvPlayer;

}

// 返回百分比

public TextView getTvPercents() {

return mTvPercents;

}

}

}

注意, 在onCreateViewHolder中, 在View的Tag中绑定所属的ViewHolder. 视频项类VideoListItem会在Tag中提取ViewHolder, 设置显示效果.

4. 视频项

通过ItemsPositionGetter类提供的接口, 返回显示比例, 根据显示区域的大小, 控制视频播放的启动还是停止, 实现自动切换视频状态功能.

public abstract class VideoListItem implements VideoItem, ListItem {

private final Rect mCurrentViewRect; // 当前视图的方框

private final VideoPlayerManager mVideoPlayerManager; // 视频播放管理器

private final String mTitle; // 标题

@DrawableRes private final int mImageResource; // 图片资源

// 构造器, 输入视频播放管理器

public VideoListItem(

VideoPlayerManager videoPlayerManager,

String title,

@DrawableRes int imageResource) {

mVideoPlayerManager = videoPlayerManager;

mTitle = title;

mImageResource = imageResource;

mCurrentViewRect = new Rect();

}

// 视频项的标题

public String getTitle() {

return mTitle;

}

// 视频项的背景

public int getImageResource() {

return mImageResource;

}

// 显示可视的百分比程度

@Override public int getVisibilityPercents(View view) {

int percents = 100;

view.getLocalVisibleRect(mCurrentViewRect);

int height = view.getHeight();

if (viewIsPartiallyHiddenTop()) {

percents = (height - mCurrentViewRect.top) * 100 / height;

} else if (viewIsPartiallyHiddenBottom(height)) {

percents = mCurrentViewRect.bottom * 100 / height;

}

// 设置百分比

setVisibilityPercentsText(view, percents);

return percents;

}

@Override public void setActive(View newActiveView, int newActiveViewPosition) {

VideoListAdapter.VideoViewHolder viewHolder =

(VideoListAdapter.VideoViewHolder) newActiveView.getTag();

playNewVideo(new CurrentItemMetaData(newActiveViewPosition, newActiveView),

viewHolder.getVpvPlayer(), mVideoPlayerManager);

}

@Override public void deactivate(View currentView, int position) {

stopPlayback(mVideoPlayerManager);

}

@Override public void stopPlayback(VideoPlayerManager videoPlayerManager) {

videoPlayerManager.stopAnyPlayback();

}

// 显示百分比

private void setVisibilityPercentsText(View currentView, int percents) {

VideoListAdapter.VideoViewHolder vh =

(VideoListAdapter.VideoViewHolder) currentView.getTag();

String percentsText = "可视百分比: " + String.valueOf(percents);

vh.getTvPercents().setText(percentsText);

}

// 顶部出现

private boolean viewIsPartiallyHiddenTop() {

return mCurrentViewRect.top > 0;

}

// 底部出现

private boolean viewIsPartiallyHiddenBottom(int height) {

return mCurrentViewRect.bottom > 0 && mCurrentViewRect.bottom < height;

}

}

动画效果

动画

虽然使用视频播放的管理器, 但播放功能还是需要注意一些细节. 毕竟视频播放是个比较复杂的过程, 需要考虑的很多事情.

OK, that's all! Enjoy it!

android实现朋友圈播放视频,实现类似朋友圈视频的滚动播放功能相关推荐

  1. php朋友圈九宫格怎么做,微信朋友圈九宫格视频怎么做 图片背景加九宫格视频随机播放的效果制作|微信九宫格视频...

    在如下图所示的新建项目窗口中,可以点击步骤一处的"浏览"按钮修改项目的存放位置,如果只是要输出为视频文件,这里的项目存放位置的设置用处也不大,最后输出视频文件后不保存项目即可:在步 ...

  2. 朋友圈点赞,微信发红包,视频的播放,Pc端微信加群,app端微信加群的测试点

    测试点经典例题*** 朋友圈点赞点用例的测试点: 1.是否可以点赞 2. 是否可以取消点赞 3. 多次点赞会出现什么情况 4. 多人点赞时显示是否按照时间进行排序 5. 点赞会不会提示发圈人 6. 取 ...

  3. Android 挂逼 修炼之行---微信实现本地视频发布到朋友圈功能

    一.前言 前一篇文章已经详细介绍了如何使用Xposed框架编写第一个微信插件:摇骰子和猜拳作弊器  本文继续来介绍如何使用Xposed框架编写第二个微信插件,可以将本地小视频发布到朋友圈的功能.在这之 ...

  4. android 本地视频分享到朋友圈,Android挂逼修炼之行---微信实现本地视频发布到朋友圈功能...

    原文链接:blog.csdn.net/jiangwei0910410003一.前言 本文将介绍如何使用Xposed框架编写微信插件,可以将本地小视频发布到朋友圈的功能.在这之前我们还是要有老套路,准备 ...

  5. Android”挂逼”修炼之行—微信实现本地视频发布到朋友圈功能

    一.前言 前一篇文章已经详细介绍了如何使用Xposed框架编写第一个微信插件:摇骰子和猜拳作弊器  本文继续来介绍如何使用Xposed框架编写第二个微信插件,可以将本地小视频发布到朋友圈的功能.在这之 ...

  6. Android中实现微信本地视频发布到朋友圈功能

    编码美丽 2016-11-14 17:49 一.前言 前一篇文章已经详细介绍了如何使用Xposed框架编写第一个微信插件:摇骰子和猜拳作弊 本文继续来介绍如何使用Xposed框架编写第二个微信插件,可 ...

  7. Android逆向之旅---微信封了抖音分享功能,而我要把短视频分享到朋友圈!

    一.前言 本文写的目的很简单,因为微信封了抖音短视频分享功能,但是这个对于用户来说都是不好的体验,当初3Q大战,损害的是用户利益,现在也是用户最后都是最受伤的一个.而这两次都和企鹅有关.先看看企鹅为了 ...

  8. android广告页白屏_微信官方朋友圈广告营销技巧

    微信作为一个月活跃用户高达10亿的社交平台,用户年龄从15~40岁覆盖,渗透率高达93%.一个聚集了庞大的流量平台是如何玩转广告生态的呢?又是如何快速的帮助商家曝光.引流.成交的呢?在现实中,办公.生 ...

  9. 01-android 微信实现本地视频发布到朋友圈功能

    以下内容是小女子看了转载的 原文链接:http://blog.csdn.net/jiangwei0910410003/article/details/53045634 哈哈尊重原创! 一.前言 前一篇 ...

最新文章

  1. 2.1JAVA基础复习——JAVA语言的基础组成注释和常量变量
  2. Windows Phone 7 Tips “.NET研究”(4)
  3. Windows 的这款工具,有时让我觉得 Mac 不是很香
  4. vs.net设计器里加载派生窗口类时要执行父窗口的Form_Load方法
  5. boost::multiprecision模块实现MPC后端对多精度复数的使用的测试程序
  6. 转 mysql处理高并发,防止库存超卖
  7. Applying Multicycle Exceptions in the TimeQuest Timing Analyzer--Altera Note
  8. linux核心的最新版本,求问Linux最新内核版本以及发布日期。
  9. android 开发 - 结束所有activity
  10. 独家对话谢宝友:做一款类似于 Linux 的国产操作系统 | 人物志
  11. 6.高性能MySQL --- 查询性能优化(1)
  12. Linux有待提高的七个领域
  13. Python——程序设计:商贷月供计算器!谁还没点月供了!
  14. android动图状态延迟获取到,android实现牛顿摆
  15. 排序算法之python实现(上)
  16. 室内定位技术及机场方案建议
  17. 找出大文档中的所有手机号
  18. Linux常用命令:chmod
  19. 51单片机定时器中断按键消抖(无延时)
  20. 手机浏览器UCWEB的成功史

热门文章

  1. 谢谢你,陪我走“疫”程
  2. AI赋能:智能冰箱成为家居生活新管家
  3. Excel成神之道-003-快速生成数字等差序列
  4. 龙天光明:中华道家生命美学科技“龍天护正”参展台北高阶美学跨界展
  5. 分享:2013中国开发者大会(移动和游戏)--广州站
  6. 远程包含和本地包含漏洞的原理
  7. 资源素材 -- Google发布750个Material Design Icon 图标
  8. 2005精品书籍下载地址
  9. 人工智能和人类智能的本质区别是什么(四)
  10. ai人工智能制作视频_建立一个人工智能驱动的可搜索视频档案