Since writing this article when hanging ladder, come back to find no pictures sorry everyone ..... Now I've changed!
2016.5.27 15 o'clock Yin at BJ
Reprint please indicate source: Dung 乧 http://blog.csdn.net/wooder111/article/details/51513582
Original translation: Click to jump
In this article, I'll show you how to implement video playback in a list. In popular applications, such as Facebook,instagram or Magisto works the same:
Facebook's:
Magisto's:
Instagram's:
This article is based on the Open source project: Videoplayermanager.
All code and work examples are present. Many of the things in this article will be skipped, so if someone really needs to understand how it works it's best to download the source code and read the article in the source of your IDE. But even without the code this article will benefit with the understanding that we are dealing with.
Two questions
To achieve what is needed, we must address two questions:
We want to manage the video playback. In Android, we have a class mediaplayer.class that works with Surfaceview and can play video. But it also has many drawbacks. We cannot use the usual videoview in the list. Videoview extension Surfaceview and Surfaceview do not have a UI synchronization buffer. All this will lead us to the situation where the video is being played and trying when you roll it up to catch the list. The sync cache exists in Textureview but no videoview is based on Textureview on Android SDK version 15. So we need to expand Textureview and work with Android MediaPlayer view. In addition, almost all methods, from (Ready, start, stop etc. ...) MediaPlayer is basically called a local method with hardware. Hardware can be tricky if it will do any work longer than 16 milliseconds (it certainly will), then we will see a list of lags. That's why they need to be called from a background thread.
We also need to know which view scrolling on the current activity on the list, you can toggle playback when the user scrolls. So basically we have to track the scrolling and determine the most compelling point of view if it changes.
Manage video Playback
Here, our goal is to provide the following features:
Suppose the movie plays on. The list and new product in the user scrolling list becomes more pronounced than the one described in the video playback. So now we have to stop the existing video playback and start the new ones.
Its main function is: Stop playing before, and start playing after the new old one stop.
Here is how it works with video sampling: When you press the video thumbnail-the current video playback stops and the other begins.
Videoplayerview
The first thing we need to achieve is a videoview based on Textureview. We cannot use Videoview in the scrolling list. because video rendering will screw up if the user scrolls through our list during playback .
I divide this task into several parts:
Create a Scalabletextureview. It is a descendant of Textureview and knows how to adjust the surface texture (the surface texture is run in replay) and provides a few options similar to the ImageView ScaleType.
publicenum ScaleType {CENTER_CROP, TOP, BOTTOM, FILL}
Create a Videoplayerview. This is the descendant of Scalabletextureview, which contains all the functions with Mediaplayer.class. This custom view encapsulates the Mediaplayer.class and provides a very similar Videoview API. It has all the methods that are directly called MediaPlayer: Setdatasource, prepare, start, stop, pause, resume, release.
Video player manager and message processing threads
The video playback manager works with the method Messageshandlerthread that is responsible for calling the MediaPlayer. We need to invoke the method similar to prepare (), start () etc in a separate thread, as they are directly connected to the device's hardware. And there are cases when we call Mediaplayer.reset () in the UI thread, but there are some problems with the player, this method is blocking the UI thread for nearly 4 minutes! This is why we are not using asynchronous Mediaplayer.prepareasync, we can use synchronous mediaplayer.prepare. We are synchronizing do everything on a separate thread.
With the start of a new playback stream. Here are a few steps to do the MediaPlayer:
- Stopped playing before. It is implemented by invoking a
MediaPlayer.stop()
method.
- Resets
MediaPlayer.reset()
the MediaPlayer by calling the method. What we need to do is because the scrolling list view may be reused and we want to have all the resources published.
MediaPlayer.release()
the MediaPlayer is freed by calling the method.
- Clears an instance of the MediaPlayer. When this point of view new playback should start the new MediaPlayer instance will be created.
- The new most visible view creates the MediaPlayer on the instance.
- Sets the
MediaPlayer.setDataSource(字符串URL)
new MediaPlayer data source by calling.
- Call
MediaPlayer.prepare()
. There is no need to use async MediaPlayer.prepareAsync()
.
- Call
MediaPlayer.start()
- Wait for the actual playback to start.
All of these actions are packaged into messages that are processed in a separate thread, such as messages that are stopped. It VideoPlayerView.stop()
is called and is eventually called MediaPlayer.stop()
. We need to customize the message because we can set the current state. We know it is stopped or has ceased otherwise the consequences are unthinkable. It can help us to control which messages are now being carried out when we can do something if we need, for example, to start new playback content.
/** * This playermessage calls {@link mediaplayer#stop ()} on the instance so is used inside {@link Videoplayerview} */ Public class Stop extends playermessage { Public Stop(Videoplayerview Videoview, Videoplayermanagercallback callback) {Super(Videoview, callback); }@Override protected void performaction(Videoplayerview Currentplayer) {currentplayer.stop (); }@Override protectedPlayermessagestateStatebefore() {returnplayermessagestate.stopping; }@Override protectedPlayermessagestateStateafter() {returnplayermessagestate.stopped; }}
If we need to start a new replay, we just call the Videoplayermanager method. And added the following set of information to Messageshandlerthread:
//Pause the queue processing andCheck Current state// ifCurrent state is "Started" ThenStop Old Playbackmplayerhandler.addmessage (NewStop (Mcurrentplayer, This)); Mplayerhandler.addmessage (NewReset (Mcurrentplayer, This)); Mplayerhandler.addmessage (NewRelease (Mcurrentplayer, This)); Mplayerhandler.addmessage (NewClearplayerinstance (Mcurrentplayer, This));//SetNewVideo Player Viewmplayerhandler.addmessage (NewSetnewviewforplayback (Newvideoplayerview, This));//StartNewPlaybackmplayerhandler.addmessages (Arrays.aslist (NewCreatenewplayerinstance (Videoplayerview, This),NewSetassetsdatasourcemessage (Videoplayerview, Assetfiledescriptor, This),//I Use local file forDemoNewPrepare (Videoplayerview, This),NewStart (Videoplayerview, This)));//Resume Queue Processing
These messages run synchronously, which is why we can pause the queue processing and post new messages at any time, for example:
The current movie is in a ready state ( MedaiPlayer.prepare()
called and MediaPlayer.start()
waiting in the queue) and the user scrolls the list so we need to start playing in a new view. In this case we:
- Pause Queue Processing
- Delete all pending information
- The post "Stop", "Reset", "release", "Clear player instances" queue. When we return from the "Ready", they will run right
- The post "creates a new media player instance", "sets the current Media Player" (which changes the MediaPlayer object on which the message was executed), "Set data source", "Prepare", "start". And this message will start the new view playback.
Well, we have to run the utility in the way we need to play: stop playing before, just start the next.
The following is the gradle of the library's dependencies:
{ compile ‘com.github.danylovolokh:video-player-manager:0.2.0‘}
Identifies the most obvious diagram in the list. List of Visibility utils
The first issue is managing video playback. The second issue is to track which views are most noticeable and to switch back to this view.
There is an entity called Listitemsvisibilitycalculator and its implementation singlelistviewitemactivecalculator do all the work.
That is, the adapter uses the ListItem interface that must be implemented in order to calculate the visibility model class for the items in the list:
/** * A General interface for list items * This interface was used by {@link Listitemsvisibilitycalculator} * * @a Uthor Danylo.volokh * / Public interface ListItem { /** * When this method was called, the implementation should provide a * visibility percents in range 0-100 * @param View the view which visibility percent should be * calculated. * Note:visibility doesn ' t has to depend on the visibility of a * full view. * It might is calculated by calculating the visibility of any * inner View * * @return percents of VI sibility * * intGetvisibilitypercents (view view);/** * When view visibility become bigger than ' current active ' view * Visibility then the new view becomes act Ive. * This method is called * / voidSetActive (View Newactiveview,intNewactiveviewposition);/** * There might be-a case-not-only-new view becomes active, * but also when no view is active. * When view should stop being active this method is called * / voidDeactivate (View CurrentView,intposition);}
The listitemsvisibilitycalculator tracks the direction of scrolling and calculates the visibility of the project in motion. The visibility of the project may depend on any view within a single item in the list. It is up to you to implement the Getvisibilitypercents () method.
There is a sample demo to apply the default implementation of this method:
/** * This method calculates visibility percentage of currentview. * This method works correctly when CurrentView is S Maller then it ' s enclosure. * @param Currentview-view which visibility should be calculated * @return currentview Visibility Perce NTS * *@Override Public int getvisibilitypercents(View CurrentView) {intPercents = -; Currentview.getlocalvisiblerect (Mcurrentviewrect);intHeight = currentview.getheight ();if(Viewispartiallyhiddentop ()) {//view is partially hidden behind the top edgePercents = (height-mcurrentviewrect.top) * -/height; }Else if(Viewispartiallyhiddenbottom (height)) {percents = Mcurrentviewrect.bottom * -/height; }returnpercents;}
Therefore, each view needs to know how to calculate its awareness percentage. Singlelistviewitemactivecalculator will vote this value from a scrolling time when such implementations should not be very heavy for each view.
The SetActive method will be called when any neighbor project has more visibility than the current active project. And when it is we should switch the rendition.
In addition, there are itemspositiongetter that can act as adapters between the Listitemsvisibilitycalculator and the ListView or Recyclerview. So listitemsvisibilitycalculator don't know if it is a ListView or Recyclerview. It's just the work of it. But it needs a bit of information provided through Itemspositiongetter:
/** * This class is a API for {@link Listitemsvisibilitycalculator} * Using This class are can access all the data from Recyclerview/* ListView * * There is The different implementations for the ListView and for * Recyclerview. * Recyclerview introduced LayoutManager that's why some of the data moved * there * * Created by Danylo.volokh on 9/20/2015. */ public interface itemspositiongetter { View getchildat ( int position); int indexofchild (view view); int getchildcount (); int getlastvisibleposition (); int getfirstvisibleposition ();}
Having that logical model is messing with the idea of separating business logic from the model. But there are some modifications which may be separated. By the way, it works fine, and now even what's going on.
Here's a simple movie that shows how it works:
The following is a utils gradle dependency relationship:
{ compile ‘com.github.danylovolokh:list-visibility-utils:0.2.0‘}
Video player manager and directory Visibility utils combination to achieve scrolling list of video playback.
Now we have everything we need to solve two libraries. Let's combine them to get the functionality we need.
Here, from the code that uses the Recyclerview fragment:
Initialize the Listitemsvisibilitycalculator and pass a reference to a list of it.
/*** Only the one (most visible) view should be active (and playing).* To calculate visibility of views we use {@link SingleListViewItemActiveCalculator}*/privatefinalnew SingleListViewItemActiveCalculator(new DefaultSingleItemCalculatorCallback(), mList);
Defaultsingleitemcalculatorcallback just calls the Listitem.setactive method when the active view changes, but you can overwrite it yourself and do whatever you need:
/*** Methods of this callback would be Called when new active item was found {@link callback#activatenewcurrentitem (ListItem, View, int)}* or when there was no act Ive item {@link callback#deactivatecurrentitem (ListItem, View, int)}-This might happen when user scrolls really fast*/
public interface callback <t extends listitem >{ void Activatenewcurrentitem (T item, view view, int position); void deactivatecurrentitem (T item, view view, int position);}
Initializes the Videoplayermanager.
/*** Here we use {@link SingleVideoPlayerManager}, which means that only one video playback is possible.*/privatefinalnew SingleVideoPlayerManager(new PlayerItemChangeListener() {@OverridepublicvoidonPlayerItemChanged(MetaData metaData) {}});
Set the visibility utils on the scrolling listener Recyclerview and by scrolling through the list of events.
@Override public void onscrollstatechanged (Recyclerview view, int scrollstate) {mscrollstate = Scrollstate;if (scrollstate = = Recyclerview.scroll_state_idle && mlist.isempty ()) { Mvideovisibilitycalculator.onscrollstateidle (Mitemspositiongetter, Mlayoutmanager.findfirstvisibleitemposition ( ), Mlayoutmanager.findlastvisibleitemposition ());}}
@OverridepublicvoidonScrolledintint dy) {if(!mList.isEmpty()){mVideoVisibilityCalculator.onScroll( mItemsPositionGetter, mLayoutManager.findFirstVisibleItemPosition(), mLayoutManager.findLastVisibleItemPosition() - 1, mScrollState);}}});
Create a itemspositiongetter.
ItemsPositionGetter mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRecyclerView);
We call for the Onresume method to start as soon as we open the screen to calculate the most obvious items.
@Override public void onresume () { Span class= "Hljs-keyword" >super . Onresume (); if (!mlist.isempty ()) {//need to call the This method from List view handler in order to has filled list Mrecyclerview.post (new Runnable () { @Override
public void Run () {Mvideovisibilitycalculator.onscrollstateidle (Mitemspositiongetter, Mlayoutmanager.findfirstvisibleitemposition (), mlayoutmanager.findlastvisibleitemposition ()); } });}}
That's it. We have a set of videos in the scrolling list that we are playing:
Basically, this is the most important part of just explaining. There is a sample application here that has more code:
Https://github.com/danylovolokh/VideoPlayerManager
See the source code for more details. Thank you!
(Updated version) Android Videoplayer in scrolling list for item video playback (ListView control and Recyclerview)