android滚动列表中播放视频,GitHub - hongyang51/VideoPlayerManager: Android VideoPlayer 在滚动列表实现item视频播放(ListVie...

VideoPlayerManager

This is a project designed to help controlling Android MediaPlayer class. It makes it easier to use MediaPlayer ListView and RecyclerView.

Also it tracks the most visible item in scrolling list. When new item in the list become the most visible, this library gives and API to track it.

It consists from two libraries:

Video-Player-Manager - it gives the ability to invoke MediaPlayer methods in a background thread. It has utilities to have only one playback when multiple media files are in the list.

Before new playback starts, it stops the old playback and releases all the resources.

List-Visibility-Utils - it's a library that tracks the most visible item in the list and notifies when it changes.

NOTE: there should be the most visible item.

If there will be 3 or more items with the same visibility percent the result might be unpredictable.

Recommendation is to have few views visible on the screen. View that are big enough so that only one view is the most visible, look at the demo below.

These two libraries combined are the tool to get a Video Playback in the scrolling list: ListView, RecyclerView.

Details of implementation

68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d656475696d2d496d706c656d656e74696e67253230766964656f253230706c61796261636b253230696e253230612532307363726f6c6c65642532306c697374253230284c6973745669657725323025323625323052656379636c657256696577292d626c75652e737667

68747470733a2f2f696d672e736869656c64732e696f2f62616467652f416e64726f69642532305765656b6c792d253039496d706c656d656e74696e67253230766964656f253230706c61796261636b253230696e253230612532307363726f6c6c65642532306c6973742d677265656e2e737667

68747470733a2f2f696d672e736869656c64732e696f2f62616467652f416e64726f6964253230417273656e616c2d566964656f506c617965724d616e616765722d677265656e2e7376673f7374796c653d74727565

Problems with video list

We cannot use usual VideoView in the list. VideoView extends SurfaceView, and SurfaceView doesn't have UI synchronization buffers. All this will lead us to the situation where video that is playing is trying to catch up the list when you scroll it. Synchronization buffers are present in TextureView but there is no VideoView that is based on TextureView in Android SDK version 15. So we need a view that extends TextureView and works with Android MediaPlayer.

Almost all methods (prepare, start, stop etc...) from MediaPlayer are basically calling native methods that work with hardware. Hardware can be tricky and if will do any work longer than 16ms (And it sure will) then we will see a lagging list. That's why need to call them from background thread.

Usage

Add this snippet to your project build.gradle file:

buildscript {

repositories {

jcenter()

}

}

Usage of Video-Player-Manager

dependencies {

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

}

Put multiple VideoPlayerViews into your xml file.

In most cases you also need a images above that will be shown when playback is stopped.

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/video_player_1"

android:layout_height="0dp"

android:layout_width="match_parent"

android:layout_weight="1"

/>

android:id="@+id/video_player_2"

android:layout_height="0dp"

android:layout_width="match_parent"

android:layout_weight="1"

/>

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/video_cover_1"

android:layout_width="match_parent"

android:layout_height="0dp"

android:scaleType="centerCrop"

android:layout_weight="1"/>

android:id="@+id/video_cover_2"

android:layout_width="match_parent"

android:layout_height="0dp"

android:scaleType="centerCrop"

android:layout_weight="1"/>

Now you can use SingleVideoPlayerManager to playback only a single video at once:

//... some code

private VideoPlayerManager mVideoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {

@Override

public void onPlayerItemChanged(MetaData metaData) {

}

});

//... some code

mVideoPlayer_1 = (VideoPlayerView)root.findViewById(R.id.video_player_1);

mVideoPlayer_1.addMediaPlayerListener(new SimpleMainThreadMediaPlayerListener(){

@Override

public void onVideoPreparedMainThread() {

// We hide the cover when video is prepared. Playback is about to start

mVideoCover.setVisibility(View.INVISIBLE);

}

@Override

public void onVideoStoppedMainThread() {

// We show the cover when video is stopped

mVideoCover.setVisibility(View.VISIBLE);

}

@Override

public void onVideoCompletionMainThread() {

// We show the cover when video is completed

mVideoCover.setVisibility(View.VISIBLE);

}

});

mVideoCover = (ImageView)root.findViewById(R.id.video_cover_1);

mVideoCover.setOnClickListener(this);

mVideoPlayer_2 = (VideoPlayerView)root.findViewById(R.id.video_player_2);

mVideoPlayer_2.addMediaPlayerListener(new SimpleMainThreadMediaPlayerListener(){

@Override

public void onVideoPreparedMainThread() {

// We hide the cover when video is prepared. Playback is about to start

mVideoCover2.setVisibility(View.INVISIBLE);

}

@Override

public void onVideoStoppedMainThread() {

// We show the cover when video is stopped

mVideoCover2.setVisibility(View.VISIBLE);

}

@Override

public void onVideoCompletionMainThread() {

// We show the cover when video is completed

mVideoCover2.setVisibility(View.VISIBLE);

}

});

mVideoCover2 = (ImageView)root.findViewById(R.id.video_cover_2);

mVideoCover2.setOnClickListener(this);

// some code

@Override

public void onClick(View v) {

switch (v.getId()){

case R.id.video_cover_1:

mVideoPlayerManager.playNewVideo(null, mVideoPlayer_1, "http:\\url_to_you_video_1_source");

break;

case R.id.video_cover_2:

mVideoPlayerManager.playNewVideo(null, mVideoPlayer_2, "http:\\url_to_you_video_2_source");

break;

}

}

The Demo of Video-Player-Manager:

5aba61fc232374255c4dda862543b9fb.gif

Usage of List-Visibility-Utils

dependencies {

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

}

The models of your adapter need to implement ListItem

public interface ListItem {

int getVisibilityPercents(View view);

void setActive(View newActiveView, int newActiveViewPosition);

void deactivate(View currentView, int position);

}

This messes up a bit with separating model from the logic.

Here you need to handle the login in the model.

The ListItemsVisibilityCalculator will call according methods to:

Get view visibility

Set this item to active

Deactivate the item

// some code...

private final ListItemsVisibilityCalculator mListItemVisibilityCalculator =

new SingleListViewItemActiveCalculator(new DefaultSingleItemCalculatorCallback(), mList);

// some code...

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override

public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {

mScrollState = scrollState;

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

mListItemVisibilityCalculator.onScrollStateIdle(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition());

}

}

@Override

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

if(!mList.isEmpty()){

mListItemVisibilityCalculator.onScroll(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition() - mLayoutManager.findFirstVisibleItemPosition() + 1,

mScrollState);

}

}

});

mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRecyclerView);

@Override

public void onResume() {

super.onResume();

if(!mList.isEmpty()){

// need to call this method from list view handler in order to have filled list

mRecyclerView.post(new Runnable() {

@Override

public void run() {

mListItemVisibilityCalculator.onScrollStateIdle(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition());

}

});

}

}

The Demo of List-Visibility-Utils:

adb871742cc3ee4e18207adab9dc9e61.gif

Usage in scrolling list (ListView, RecyclerView)

Add this snippet to your module build.gradle file:

dependencies {

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

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

}

Here is the relevant code combanation of two libraries fro implementing Video Playback in scrolling list.

// Code of your acitivty

/**

* Only the one (most visible) view should be active (and playing).

* To calculate visibility of views we use {@link SingleListViewItemActiveCalculator}

*/

private final ListItemsVisibilityCalculator mVideoVisibilityCalculator =

new SingleListViewItemActiveCalculator(new DefaultSingleItemCalculatorCallback(), mList);

/**

* ItemsPositionGetter is used by {@link ListItemsVisibilityCalculator} for getting information about

* items position in the RecyclerView and LayoutManager

*/

private ItemsPositionGetter mItemsPositionGetter;

/**

* Here we use {@link SingleVideoPlayerManager}, which means that only one video playback is possible.

*/

private final VideoPlayerManager mVideoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {

@Override

public void onPlayerItemChanged(MetaData metaData) {

}

});

private int mScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// fill the list of items with an items

// some initialization code here

VideoRecyclerViewAdapter videoRecyclerViewAdapter = new VideoRecyclerViewAdapter(mVideoPlayerManager, getActivity(), mList);

mRecyclerView.setAdapter(videoRecyclerViewAdapter);

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override

public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {

mScrollState = scrollState;

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

mVideoVisibilityCalculator.onScrollStateIdle(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition());

}

}

@Override

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

if(!mList.isEmpty()){

mVideoVisibilityCalculator.onScroll(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition() - mLayoutManager.findFirstVisibleItemPosition() + 1,

mScrollState);

}

}

});

mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRecyclerView);

return rootView;

}

@Override

public void onResume() {

super.onResume();

if(!mList.isEmpty()){

// need to call this method from list view handler in order to have filled list

mRecyclerView.post(new Runnable() {

@Override

public void run() {

mVideoVisibilityCalculator.onScrollStateIdle(

mItemsPositionGetter,

mLayoutManager.findFirstVisibleItemPosition(),

mLayoutManager.findLastVisibleItemPosition());

}

});

}

}

@Override

public void onStop() {

super.onStop();

// we have to stop any playback in onStop

mVideoPlayerManager.resetMediaPlayer();

}

When visibility utils calls method "setActive" on your implementation of ListItem you have to call "playNewVideo".

Please find the working code on this Demo application.

/**

* When this item becomes active we start playback on the video in this item

*/

@Override

public void setActive(View newActiveView, int newActiveViewPosition) {

VideoViewHolder viewHolder = (VideoViewHolder) newActiveView.getTag();

playNewVideo(new CurrentItemMetaData(newActiveViewPosition, newActiveView), viewHolder.mPlayer, mVideoPlayerManager);

}

@Override

public void playNewVideo(MetaData currentItemMetaData, VideoPlayerView player, VideoPlayerManager videoPlayerManager) {

videoPlayerManager.playNewVideo(currentItemMetaData, player, mDirectUrl);

}

Demo of usage in scrolling list (ListView, RecyclerView)

073c1b8ec09a734dac73e6cf118f9a88.gif4bfffb93a6226df9c01df0fea88d0ca9.gif

License

Copyright 2015 Danylo Volokh

Licensed under the Apache License, Version 2.0 (the "License");

you may not use this file except in compliance with the License.

You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an "AS IS" BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值