登录 立即注册
安币:

安卓巴士 - 安卓开发 - Android开发 - 安卓 - 移动互联网门户

炫丽的朋友圈视频滚动播放功能

[复制链接]
来自: MrlLee 分类: Android精品源码 上传时间: 2016-4-21 17:34:41
Tag:炫丽 朋友圈 视频滚动 播放 功能
立即下载
收藏

项目介绍:

在应用的信息流中, 用户会分享视频, 连续展示, 这就需要处理视频滚动播放. 然而, 在列表视图(RecyclerView)中使用MediaPlayer播放视频时, 会产生一些问题, 即无法同步控制视频的播放和停止. 使用控件库可以解决这一问题.
滚动播放功能: 在页面中, 判断视频的可视比例, 最大视频项开始播放, 其余视频项关闭, 滚动中自动控制切换视频状态. 让我们来看看如何实现这一功能.


使用的视频管理库.
[XML] 查看源文件 复制代码
    // 视频播放库
    compile 'com.github.danylovolokh:video-player-manager:0.2.0'
    compile 'com.github.danylovolokh:list-visibility-utils:0.2.0'

效果
749674-94dde15451f4557e.png

1. 基础
依赖注入, 图片加载, 和视频播放.
[XML] 查看源文件 复制代码
   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, 可以选择本地视频或者网络视频两种方式.
[Java] 查看源文件 复制代码
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的设置参数, 判断播放使用本地视频还是网络视频.
[Java] 查看源文件 复制代码
    @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. 绑定视频元素, 播放监听控制覆盖层的显示与隐藏.
[Java] 查看源文件 复制代码
/**
 * 视频列表的适配器
 * 

* 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类提供的接口, 返回显示比例, 根据显示区域的大小, 控制视频播放的启动还是停止, 实现自动切换视频状态功能.
[Java] 查看源文件 复制代码
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;
    }
}
虽然使用视频播放的管理器, 但播放功能还是需要注意一些细节. 毕竟视频播放是个比较复杂的过程, 需要考虑的很多事情.


相关源码推荐:

我来说两句
所有评论(5)
binzhao88 2016-4-22 00:57:51
淡定,淡定,淡定……
回复
linux19920212 2016-4-22 09:27:09
强烈支持楼主ing……
回复
paqc0722 2016-4-22 12:11:23
楼主用心了,内容非常精彩。
回复
ygc10000 2016-4-22 13:01:14
楼主威武啊,安卓巴士有你更给力!
回复
ygc10000 2016-4-22 13:01:53
真心不错。
回复
number2 2016-4-22 15:25:46
楼主用心了,内容非常精彩。
回复
dielianxian 2016-4-22 16:46:27
淡定,淡定,淡定……
回复
上传代码
查看数:1586 收藏数:9 下载数:27 点赞数:1
状态:已购或VIP 售价:10(原价:10)金钱 下载权限:初级码农 
代码贡献英雄榜
用户名 下载数
联系我们
首页/微信公众账号投稿

帖子代码编辑/版权问题

QQ:435399051,769657487

如何获得代码达人称号

如何成为签约作者

联系我们
关闭
合作电话:
15618560077
Email:
805941275@qq.com
商务市场合作/投稿
问题反馈及帮助
联系我们

广告投放| 广东互联网违法和不良信息举报中心|中国互联网举报中心|下载客户端|申请友链|手机版|站点统计|安卓巴士 ( 粤ICP备15117877号 )

快速回复 返回顶部 返回列表