What problem is solved by reading this passage?

Solve some technical difficulties in automatically playing video on Android slide list page. Help to better achieve similar needs. It does not involve the specific codec technology of the player, because each player may be different.

When is the best time to show a video?

It is recommended to play the video when the slide stops and not play the video when the slide is still on. At present, the apps of big factories are basically based on this idea. There are two main reasons for this:

  • Just like when we do listView or RecyclerView list page optimization at ordinary times, considering that the loading of pictures involves IO and a small part of decode operation, we will only load pictures when sliding stops or sliding speed becomes low. Reduce CPU load and memory jitter to improve page slide smoothness, so the same is true for playing video. Playing video when the list page stops sliding can greatly improve page slide smoothness.
  • Even if your player is self-developed, it is still not recommended to play the video while scrolling on the list page. Consider the following scenarios: Sliding is A video broadcast, right now, B B video to play area to play video, from A broadcast video at this time the state of the cut to stop the video broadcast video cover state, B video from video cover state cut to play (and even black loading condition), once can imagine sliding speed faster, The visual effects can be very unpleasant, that kind of stop-start effect, because the items in your list page almost all switch image states at the same time.

So where do we start the video?

Take RecyclerView as an example:

So we’re really just going to do it in this callback

The condition in this case is that the list slide stops. That’s when we’re gonna do the video

How to plan the video broadcast area reasonably?

We use one of the most popular approach: in accordance with human visual perception, choose from the top to the first page in the following table to show complete the item of video for playback Some people say that we directly use findFirstCompletelyVisibleItemPosition method is not line? In fact, this is not perfect, because the item in our list page is not just a single video play area, it also has a title, description, number of views, etc. **. Your video play area is actually only part of your entire item **

So we need to calculate the condition that the video region is fully displayed. The code is as follows:

 caseSCROLL_STATE_IDLE: // Take the first displayed item int firstPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition(); // Last displayed item int lastPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition(); // Start traversalfor(int i = firstPosition; i <= lastPosition; I++) {/ / remove this item in a list page View itemView. = recyclerView getLayoutManager () findViewByPosition (I); // Make sure to null first because sometimes the function above does return null // and then check if there is a video player component such as a list page with a video item and a photo item // so check this conditionif(itemView ! = null && itemView.findViewById(R.id.video_player) ! = null) { VideoPlayer videoPlayer = itemView.findViewById(R.id.video_player);if (isCanBePlayedByRect(videoPlayer)) {
                            safePlayVideoMethod(videoPlayer);
                            break; }}}Copy the code
private boolean isCanBePlayedByRect(VideoPlayer videoPlayer) { Rect rect = new Rect(); / / here you can take out to the axis of a video playback area the videoPlayer getLocalVisibleRect (the rect); int videoHeight = videoPlayer.getHeight(); // Final Boolean playFlag = (rect.top == 0 && rect.bottom == videoHeight)return playFlag;
    }

Copy the code

So we have a basic algorithm, but it’s still not perfect.

Are there any other conditions to determine?

Consider another complicated case, like this one:

We can see that the last two items in the list page are actually fully exposed on the screen, which meet the judgment conditions in the previous section. But visually we still feel that the two video playing areas are incomplete because they are covered by the bottom TAB.

Some users even call up virtual keys. The following figure

So our item display area is covered more. The algorithm of the previous section is no longer sufficient.

At this point, we need to go one step further and make some modifications to the previous algorithm. Someone might say, well, why don’t we just subtract the height of the TAB at the bottom? It works, but it’s not very elegant, for two reasons:

  • If the TAB height changes later, can you make sure that you know every time someone changes it?
  • In addition to the bottom TAB and the height of the virtual key ah, such a calculation is not too complicated?

Here’s another good way to circumvent the above problem:

Take the top coordinate of the bottom TAB.

We can take this coordinate after the first frame of the activity is drawn:

   if(mTabWidget ! = null && MAIN_PAGE_BOTTOM_TAB_RECT_TOP == 0) { Rect rect = new Rect(); / / note We used here is getGlobalVisibleRect not getLocal / / difference is that global is made on the axis of a full screen mTabWidget. GetGlobalVisibleRect (the rect); MAIN_PAGE_BOTTOM_TAB_RECT_TOP = rect.top; }Copy the code

So here we have the top coordinate of the view at the bottom TAB. So all we have to do is add one more term

The value of the full screen bottom of the video player component is greater than the value of the top of our TAB. Only in this way can we ensure that the view of our video is not covered

private boolean isCanBePlayedByRect(VideoPlayer videoPlayer) { Rect rect = new Rect(); videoPlayer.getLocalVisibleRect(rect); Rect gRect = new Rect(); / / the video view of global bottom coordinates are also compared with TAB top coordinates do videoPlayer. GetGlobalVisibleRect (gRect); int videoHeight = videoPlayer.getHeight(); final boolean playFlag = (rect.top == 0 && rect.bottom == videoHeight && gRect.bottom < MAIN_PAGE_BOTTOM_TAB_RECT_TOP);return playFlag;
    }

Copy the code

When do you stop playing?

Some people say we use

In fact that is not perfect, or in front of the problem, some item he is there are other elements to show, can appear already can’t see the video area, slip away, at this time, should stop playing, but because there are such as video reading several things happened to be in list but the regional scope Cause this item cannot be entered the callback.

To solve this problem, we use the following solution: When sliding the list page, determine whether the video area is completely outside the list page and can no longer be seen by people, and stop playing the video if it meets this condition

The code is as follows:

 int position = 0;
            if(dy >0) {//dy>0 means that the item is drawn from the bottom to the top of the screen so we just need to determine the top visible item position = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); }else if(dy < 0) {//dy>0 means the item is drawn from the top down so we just need to determine the bottom visible item position = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findLastVisibleItemPosition(); } View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position);if(itemView ! = null) { VideoPlayer videoPlayer = itemView.findViewById(R.id.video_player);if(videoPlayer ! = null) { Rect rect = new Rect(); Rect gRect = new Rect(); vivoSpaceVideoPlayer.getLocalVisibleRect(rect); vivoSpaceVideoPlayer.getGlobalVisibleRect(gRect); Final Boolean stopFlagForDownToUp = (dy > 0 && rect. bottom-rect. top <= STOP_FLAG_PX_VALUE); ---- TAB Final Boolean stopFlagForUptoDown1 = (dy < 0 &&grect.top > (MAIN_PAGE_BOTTOM_TAB_RECT_TOP - STOP_FLAG_PX_VALUE)); ---- no bottom TAB Final Boolean stopFlagForUptoDown2 = (dy < 0 &&rect.top! = 0);if(stopFlagForDownToUp || stopFlagForUptoDownHasParent || stopFlagForUptoDownNoParent) { videoPlayer.release(); }}}Copy the code

The algorithm here is a little bit simpler, and I’m not going to explain it all, but the only thing I’m going to say is this

Private static final int STOP_FLAG_PX_VALUE = 5; private static final int STOP_FLAG_PX_VALUE = 5;Copy the code

Why do I add one of these? Because in the case of full-screen coordinates, sometimes if you slide fast you don’t go through a certain value, so you have to have some margin. For example, our condition is that when the top coordinate of our video region (increasing from 1900 when sliding) is greater than the top coordinate of TAB (assuming that the value is 2100), we judge that it is covered and stop sliding. It doesn’t mean that the top coordinates of the video area are 1900, 1901, 1902, 1px, 1px increasing to 2100. Sometimes he will ignore, for example, some mobile phones often appear in 1997, 1998, 1999, 2102 and so on, so we need a certain margin here. Make sure the video is stopped.

How to ensure that the list page plays automatically the first time it is rendered?

When the network data comes back, we will set the value into RecyclerView and notify, then the interface will be displayed. However, if the user does not swipe at this time and one of the items on the first screen happens to be a video, how can we trigger the automatic playback of this condition?

In fact, RecyclerView has a feature that onScrolled will be executed once after the first rendering, so we just need to make a flag bit here and play it once.

  @Override
        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
            if(! mRvInitFlag) { playFirstVideoView(); mRvInitFlag =true;
                return;
            }
Copy the code
 private void playFirstVideoView() {
        VLog.d(TAG, "method in playFirstVideoView");
        if(mRecyclerView.getLayoutManager() ! = null) {// Only videos make sense, Int firstPosition = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition(); int lastPosition = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findLastVisibleItemPosition();for(int i = firstPosition; i <= lastPosition; I ++) {// if I <0 then skip it because it doesn't make senseif (i < 0) {
                    continue;
                }
                View itemView = mRecyclerView.getLayoutManager().findViewByPosition(i);
                if(itemView ! = null && itemView.findViewById(R.id.video_player) ! = null) { VideoPlayer videoPlayer = itemView.findViewById(R.id.video_player); Rect rect = new Rect(); Rect gRect = new Rect(); videoPlayer.getLocalVisibleRect(rect); videoPlayer.getGlobalVisibleRect(gRect); int videoHeight = videoPlayer.getHeight(); // As long as the first element's viewable area is intact, then play directly to itif((rect.bottom - rect.top) == videoHeight && gRect.bottom < MAIN_PAGE_BOTTOM_TAB_RECT_TOP && gRect.bottom > 0) { safePlayVideoMethod(videoPlayer); // Now that I have found it, I can play it directlybreak; }}}}Copy the code