Slide Paging Video Up and Down with Dummy

Keywords: Mobile Fragment github SurfaceView network

Directory Introduction

  • 01. First look at the needs
  • 02.There are several implementations
    • 2.1 Use ViewPager
    • 2.2 Use RecyclerView
  • 03. Implement with ViewPager
    • 3.1 Custom ViewPager
    • 3.2 ViewPager and Fragment
    • 3.3 Modify Sliding Distance Page Flipping
    • 3.4 Modify sliding speed
  • 04. Implement with RecyclerView
    • 4.1 Customize LayoutManager
    • 4.2 Add Slide Monitor
    • 4.3 Monitor page scrolling
    • 4.4 attach and Detached
  • 05. Optimize Talk in detail
    • 5.1 ViewPager Change Slide Rate
    • 5.2 PagerSnapHelper Notes
    • 5.3 Customize LayoutManager Notes
    • 5.4 Logical optimization of video playback
    • 5.5 Video logic decoupling
    • 5.6 Page Flipping Carton Optimization Analysis
    • 5.7 Pull-up to turn pages quickly to black screen

01. First look at the needs

  • Video playback in the project requires the effect of sliding one page at a time in the vertical direction like dithering.Slide smoothly without jamming, and when manual touch slides more than 1/2, release to slide the next page, no more than 1/2 return to the original page.
  • Drag the page with your finger and the video is playing as long as you haven't switched to another page.Switched pages, the last video was destroyed, and the page started to initialize playback.
  • When switching pages, the transition effect should be natural and avoid flashing.Specific sliding effect, can refer directly to the jitter...

02.There are several implementations

2.1 Use ViewPager

  • Use ViewPager to switch video analysis up and down vertically
    • 1. Recent project requirements have made it useful to play videos in ViewPager by sliding up and down the vertical method to switch videos. Videos are network videos. The initial idea is to initialize SurfaceView in ViewPager according to the current item location, and remove SurfaceView according to the item location when destroyed.
    • 2. That's true, but there are two problems. First, MediaPlayer's life cycle is not easy to control and there is a memory leak problem.Second, when all three item s in a row are videos, a bug in the last frame of the previous video appears when you slide back and forth.
    • 3. The user experience is not improved. The first frame of the video will be overwritten before the initialization of the video player is completed, but there is a discrepancy between the first frame of the video and the information of the first frame of the video. The solution will be given by the code later.
  • This is the approximate implementation idea
    • 1. It is important to customize a ViewPager that slides vertically.
    • 2. Slide one page at a time, recommending the ViewPager+FragmentStatePagerAdapter+Fragment ation method, which will be described in more detail later.
    • 3. Processing logic such as video initialization, playback and destruction in fragment s.
    • 4. Since a page needs to create a fragment, note that performance and gliding smoothness need to be analyzed and explored.
  • ViewPager is not recommended
    • 1.ViewPager's own sliding effect fully satisfies the scene, and supports UI bindings such as Fragment s and Views. With some modifications to the layout and touch event sections, the horizontal ViewPager can be changed to vertical.
    • 2. But not reusing is the most fatal problem.In the onLayout method, all sub View s are instantiated and arranged word by word on the layout.When the number of Item s is large, it can be a significant performance waste.
    • 3. Secondly, the issue of visibility judgment.Many people would think that Fragments are visible when onResume is used, and Fragments in ViewPagers are an example, especially when multiple ViewPagers are nested, where multiple parent Fragments and multiple child Fragments are in the onResume state at the same time, but only one of them is visible.Unless you discard the ViewPager preload mechanism.When important data such as page content exposure is reported, there are many criteria to judge: onResumed, setUserVisibleHint, setOnPageChangeListener, etc.

2.2 Use RecyclerView

  • Use RecyclerView to switch video analysis up and down branches
    • 1. First RecyclerView sets the vertical direction of sliding to be very simple, and the fourth-level cache on the item is handled well, and the sliding effect is better than ViewPager.
    • 2. Sliding event handling is better than viewPager, even if you have a drop-down refresh pull-up layout nested in the outer layer, it does not affect post-event conflict handling. You can see demo cases in more detail.
  • This is the approximate implementation idea
    • 1. Customize a LinearLayoutManager, override the onScrollStateChanged method, and note that the sliding state is reached.
    • 2. Swipe one page at a time, using PagerSnapHelper, which is very convenient and simple.
    • 3. In the adapter corresponding to recyclerView, the video operation is initialized in onCreateViewHolder and the video resource is destroyed when onViewRecycled.
    • 4. Add custom callback interfaces to expose to developers when scrolling pages and attch, detach, defining methods such as initialization, page destruction, and so on.

03. Implement with ViewPager

3.1 Custom ViewPager

  • The code is shown below, and you can see the code in the project by omitting a lot of code.
    /**
     * <pre>
     *     @author Yang Chong
     *     blog  : https://github.com/yangchong211
     *     time  : 2019/6/20
     *     desc  : Customize ViewPager to handle boundary extremes
     *     revise:
     * </pre>
     */
    public class VerticalViewPager extends ViewPager {
    
        private boolean isVertical = false;
        private long mRecentTouchTime;
    
        public VerticalViewPager(@NonNull Context context) {
            super(context);
        }
    
        public VerticalViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        private void init() {
            setPageTransformer(true, new HorizontalVerticalPageTransformer());
            setOverScrollMode(OVER_SCROLL_NEVER);
        }
    
        public boolean isVertical() {
            return isVertical;
        }
    
        public void setVertical(boolean vertical) {
            isVertical = vertical;
            init();
        }
    
        private class HorizontalVerticalPageTransformer implements PageTransformer {
    
            private static final float MIN_SCALE = 0.25f;
    
            @Override
            public void transformPage(@NonNull View page, float position) {
                if (isVertical) {
                    if (position < -1) {
                        page.setAlpha(0);
                    } else if (position <= 1) {
                        page.setAlpha(1);
                        // Counteract the default slide transition
                        float xPosition = page.getWidth() * -position;
                        page.setTranslationX(xPosition);
                        //set Y position to swipe in from top
                        float yPosition = position * page.getHeight();
                        page.setTranslationY(yPosition);
                    } else {
                        page.setAlpha(0);
                    }
                } else {
                    int pageWidth = page.getWidth();
                    if (position < -1) { // [-Infinity,-1)
                        // This page is way off-screen to the left.
                        page.setAlpha(0);
                    } else if (position <= 0) { // [-1,0]
                        // Use the default slide transition when moving to the left page
                        page.setAlpha(1);
                        page.setTranslationX(0);
                        page.setScaleX(1);
                        page.setScaleY(1);
                    } else if (position <= 1) { // (0,1]
                        // Fade the page out.
                        page.setAlpha(1 - position);
                        // Counteract the default slide transition
                        page.setTranslationX(pageWidth * -position);
                        page.setTranslationY(0);
                        // Scale the page down (between MIN_SCALE and 1)
                        float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
                        page.setScaleX(scaleFactor);
                        page.setScaleY(scaleFactor);
                    } else { // (1,+Infinity]
                        // This page is way off-screen to the right.
                        page.setAlpha(0);
                    }
                }
            }
        }
    
        /**
         * Swap the distance between the x and y axes
         * @param event Gets the encapsulated class MotionEvent of the event type
         */
        private MotionEvent swapXY(MotionEvent event) {
            //Get Width and Height
            float width = getWidth();
            float height = getHeight();
            //Converts the moving distance of the Y axis to the moving distance of the X axis
            float swappedX = (event.getY() / height) * width;
            //Converts the moving distance of the X-axis to the moving distance of the Y-axis
            float swappedY = (event.getX() / width) * height;
            //Reset location of event
            event.setLocation(swappedX, swappedY);
            return event;
        }
    
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            mRecentTouchTime = System.currentTimeMillis();
            if (getCurrentItem() == 0 && getChildCount() == 0) {
                return false;
            }
            if (isVertical) {
                boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
                swapXY(ev);
                // return touch coordinates to original reference frame for any child views
                return intercepted;
            } else {
                return super.onInterceptTouchEvent(ev);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (getCurrentItem() == 0 && getChildCount() == 0) {
                return false;
            }
            if (isVertical) {
                return super.onTouchEvent(swapXY(ev));
            } else {
                return super.onTouchEvent(ev);
            }
        }
    }
    

3.2 ViewPager and Fragment

  • ViewPager+FragmentStatePagerAdapter+Fragment is used for processing.The main reason for choosing to use the FragmentStatePager Adapter is that it saves memory, but it takes time to create a new one after destruction.In general, it is recommended that you use the FragmentStatePager Adapter if you are presenting a very large number of entries for ViewPager.For a more in-depth analysis of the PagerAdapter, here's an article: PagerAdapter in-depth analysis and practice optimization
  • The code in the activity is as follows
    private void initViewPager() {
        List<Video> list = new ArrayList<>();
        ArrayList<Fragment> fragments = new ArrayList<>();
        for (int a = 0; a< DataProvider.VideoPlayerList.length ; a++){
            Video video = new Video(DataProvider.VideoPlayerTitle[a],
                    10,"",DataProvider.VideoPlayerList[a]);
            list.add(video);
            fragments.add(VideoFragment.newInstant(DataProvider.VideoPlayerList[a]));
        }
        vp.setOffscreenPageLimit(1);
        vp.setCurrentItem(0);
        vp.setOrientation(DirectionalViewPager.VERTICAL);
        FragmentManager supportFragmentManager = getSupportFragmentManager();
        MyPagerAdapter myPagerAdapter = new MyPagerAdapter(fragments, supportFragmentManager);
        vp.setAdapter(myPagerAdapter);
    }
    
    
    class MyPagerAdapter extends FragmentStatePagerAdapter{
    
        private ArrayList<Fragment> list;
    
        public MyPagerAdapter(ArrayList<Fragment> list , FragmentManager fm){
            super(fm);
            this.list = list;
        }
    
        @Override
        public Fragment getItem(int i) {
            return list.get(i);
        }
    
        @Override
        public int getCount() {
            return list!=null ? list.size() : 0;
        }
    }
    
  • So what about fragment s?For video players, here you can see my encapsulated library. Video lib
    public class VideoFragment extends  Fragment{
    
        public VideoPlayer videoPlayer;
        private String url;
        private int index;
    
        @Override
        public void onStop() {
            super.onStop();
            VideoPlayerManager.instance().releaseVideoPlayer();
        }
    
        public static Fragment newInstant(String url){
            VideoFragment videoFragment = new VideoFragment();
            Bundle bundle = new Bundle();
            bundle.putString("url",url);
            videoFragment.setArguments(bundle);
            return videoFragment;
        }
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Bundle arguments = getArguments();
            if (arguments != null) {
                url = arguments.getString("url");
            }
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater,
                                 @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_video, container, false);
            return view;
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            videoPlayer = view.findViewById(R.id.video_player);
        }
    
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            Log.d("Initialization operation","------"+index++);
            VideoPlayerController controller = new VideoPlayerController(getActivity());
            videoPlayer.setUp(url,null);
            videoPlayer.setPlayerType(ConstantKeys.IjkPlayerType.TYPE_IJK);
            videoPlayer.setController(controller);
            ImageUtils.loadImgByPicasso(getActivity(),"",
                    R.drawable.image_default,controller.imageView());
        }
    }
    

3.3 Modify Sliding Distance Page Flipping

  • Requirements require that you manually touch and slide more than 1/2 of the time to release to slide the next page, not more than 1/2 to return to the original page, first must be to rewrite the viewpager, can only start from source.After analysis, the logical processing of source sliding is here, where the truncator attribute represents the ratio value of the judgment!
    • This method redirects the page when slicing, for example, by sliding from the first page, and instead of sliding to the second page, it returns to the first page, which has redirection capabilities
    private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
        int targetPage;
        if (Math.abs(deltaX) > this.mFlingDistance && Math.abs(velocity) > this.mMinimumVelocity) {
            targetPage = velocity > 0 ? currentPage : currentPage + 1;
        } else {
            float truncator = currentPage >= this.mCurItem ? 0.4F : 0.6F;
            targetPage = currentPage + (int)(pageOffset + truncator);
        }
    
        if (this.mItems.size() > 0) {
            ViewPager.ItemInfo firstItem = (ViewPager.ItemInfo)this.mItems.get(0);
            ViewPager.ItemInfo lastItem = (ViewPager.ItemInfo)this.mItems.get(this.mItems.size() - 1);
            targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
        }
    
        return targetPage;
    }
    
    • The determineTargetPage method is to calculate which page to slide to next.This method call starts with the MotionEvent.ACTION_UP event, which means the parameter:
      • CurrtPage: The page currently displayed by the ViewPager
      • pageOffset: Page offset slid by user
      • velocity:Slide rate
      • DeltaX: The distance to move in the X direction
    • After debug, the problem was found with the parameters 0.4f and 0.6f.The result of analysis is: 0.6f indicates the offset that the user can flip pages by sliding, so it is not difficult to understand why to slide half screen or above.
  • Touch events can also be modified
    • Controlling the Touch event for ViewPager is essentially omnipotent, since it starts from the root.You can make logical judgments in onTouchEvent and onInterceptTouchEvent.But it's more troublesome.

3.4 Modify sliding speed

  • When using viewPager for sliding, you can use the distance your finger slides if you use it, but if you use the setCurrentItem function, you'll find that you've just flashed past and a brush screen will appear.What if you want to use the setCurrentItem function to slide the viewpager and you need an animation that slides too much?
  • Specifically, you can analyze the logic of the setCurrentItem source code and see the scrollToItem method, which is particularly important, primarily for handling the logic in the scrolling process.The main concern is also the smoothScrollTo function, in which you can see that the actual execution of the sliding is mScroll.startScroll (sx, sy, dx, dy, duration), you can see that the object mScroller slides.If you want to change its properties, you can do so through reflection.
  • The code below shows that if you touch and slide with your finger, you can speed up the sliding speed a little, although you can set the sliding duration yourself.You can control the speed of sliding by customizing the sliding time yourself.
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public void setAnimationDuration(final int during){
        try {
            // viewPager Pan Picture Event
            Field mField = ViewPager.class.getDeclaredField("mScroller");
            mField.setAccessible(true);
            // The animation effect is consistent with ViewPager
            Interpolator interpolator = new Interpolator() {
                @Override
                public float getInterpolation(float t) {
                    t -= 1.0f;
                    return t * t * t * t * t + 1.0f;
                }
            };
            Scroller mScroller = new Scroller(getContext(),interpolator){
                final int time = 2000;
                @Override
                public void startScroll(int x, int y, int dx, int dy, int duration) {
                    // Accelerate scrolling if you scroll manually
                    if (System.currentTimeMillis() - mRecentTouchTime > time) {
                        duration = during;
                    } else {
                        duration /= 2;
                    }
                    super.startScroll(x, y, dx, dy, duration);
                }
    
                @Override
                public void startScroll(int x, int y, int dx, int dy) {
                    super.startScroll(x, y, dx, dy,during);
                }
            };
            mField.set(this, mScroller);
        } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
            e.printStackTrace();
        }
    }
    

04. Implement with RecyclerView

4.1 Customize LayoutManager

  • Customize LayoutManager and inherit LinearLayoutManager, resulting in a layout policy that can be arranged horizontally or vertically.If you've been exposed to SnapHelper, you should know about the subclasses LinearSnapHelper and PagerSnapHelper. LinearSnapHelper can achieve the effect of centering the Items of a list, and PagerSnapHelper can scroll through an item at a time.
  • Override the onChildViewAttachedToWindow method, which is called in RecyclerView when Item is added.This method is equivalent to calling when a view is added to the window, that is, it executes first than the draw method and can do some initialization related operations.
    /**
     * This method must be called
     * @param recyclerView                          recyclerView
     */
    @Override
    public void onAttachedToWindow(RecyclerView recyclerView) {
        if (recyclerView == null) {
            throw new IllegalArgumentException("The attach RecycleView must not null!!");
        }
        super.onAttachedToWindow(recyclerView);
        this.mRecyclerView = recyclerView;
        if (mPagerSnapHelper==null){
            init();
        }
        mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
        mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
    }
    

4.2 Add Slide Monitor

  • When it comes to sliding a video page at a time, there must be video initialization and release capabilities.So think about where to start playing videos and where to release them?Don't worry, to monitor which page you slide to, we need to rewrite the onScrollStateChanged() function, which has three states: SCROLL_STATE_IDLE (idle), SCROLL_STATE_DRAGGING (drag), and SCROLL_STATE_SETTLING (when moving to the last position).
  • What we need is the state of RecyclerView when it stops, so we can get the Position of this View. Note that there is another problem here. When you get the Item through this position, you will get an error. This involves the caching mechanism of RecyclerView, so you can brainstorm for yourself.If you print the Log, you will find that RecyclerView.getChildCount() has always been 1 or appears to be 2.Implement an interface and pass state through it.
  • Customize listener Events
    public interface OnPagerListener {
    
        /**
         * Initialization complete
         */
        void onInitComplete();
    
        /**
         * Released monitoring
         * @param isNext                    Is Next
         * @param position                  Indexes
         */
        void onPageRelease(boolean isNext,int position);
    
        /***
         * Selected monitor and determine whether to slide to the bottom
         * @param position                  Indexes
         * @param isBottom                  Is it at the bottom
         */
        void onPageSelected(int position,boolean isBottom);
    }
    
  • Get the Item selected when RecyclerView is idle, override the onScrollStateChanged method of LinearLayoutManager
    /**
     * Slide state change
     * Slow drag-> SCROLL_STATE_DRAGGING
     * Fast Scroll - > SCROLL_STATE_SETTLING
     * Idle State - > SCROLL_STATE_IDLE
     * @param state                         state
     */
    @Override
    public void onScrollStateChanged(int state) {
        switch (state) {
            case RecyclerView.SCROLL_STATE_IDLE:
                View viewIdle = mPagerSnapHelper.findSnapView(this);
                int positionIdle = 0;
                if (viewIdle != null) {
                    positionIdle = getPosition(viewIdle);
                }
                if (mOnViewPagerListener != null && getChildCount() == 1) {
                    mOnViewPagerListener.onPageSelected(positionIdle,
                            positionIdle == getItemCount() - 1);
                }
                break;
            case RecyclerView.SCROLL_STATE_DRAGGING:
                View viewDrag = mPagerSnapHelper.findSnapView(this);
                if (viewDrag != null) {
                    int positionDrag = getPosition(viewDrag);
                }
                break;
            case RecyclerView.SCROLL_STATE_SETTLING:
                View viewSettling = mPagerSnapHelper.findSnapView(this);
                if (viewSettling != null) {
                    int positionSettling = getPosition(viewSettling);
                }
                break;
            default:
                break;
        }
    }
    

4.3 Monitor page scrolling

  • There are two methods, scrollHorizontallyBy() and scrollVerticallyBy(), to get the slide offset and determine the slide direction.
    /**
     * Listen for relative offsets in the vertical direction
     * @param dy                                y Axis Scroll Value
     * @param recycler                          recycler
     * @param state                             state Scroll state
     * @return                                  int value
     */
    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getChildCount() == 0 || dy == 0) {
            return 0;
        }
        this.mDrift = dy;
        return super.scrollVerticallyBy(dy, recycler, state);
    }
    
    
    /**
     * Listen for relative offsets in the horizontal direction
     * @param dx                                x Axis Scroll Value
     * @param recycler                          recycler
     * @param state                             state Scroll state
     * @return                                  int value
     */
    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getChildCount() == 0 || dx == 0) {
            return 0;
        }
        this.mDrift = dx;
        return super.scrollHorizontallyBy(dx, recycler, state);
    }
    

4.4 attach and Detached

  • Now that the list is checked and monitored, we'll see when the video's resources are released, the three states in the second step, to print the getChildCount() log, you'll find that getChildCount() will be 1 in SCOLL_STATE_DRAGGING, 2 in SCROLL_STATE_SETTLING, sometimes 1, sometimes 2 in SCROLL_STATE_IDLE, or RecyclerView Cache mechanism O()O of () will not be redundant here. What you need to do is know when to release the video and whether to release the previous or the next page.
    private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener =
            new RecyclerView.OnChildAttachStateChangeListener() {
        /**
         * First-time interface monitoring allows initialization
         * @param view                      view
         */
        @Override
        public void onChildViewAttachedToWindow(@NonNull View view) {
            if (mOnViewPagerListener != null && getChildCount() == 1) {
                mOnViewPagerListener.onInitComplete();
            }
        }
    
        /**
         * Call this method when a page is destroyed to do destroy operations
         * @param view                      view
         */
        @Override
        public void onChildViewDetachedFromWindow(@NonNull View view) {
            if (mDrift >= 0){
                if (mOnViewPagerListener != null) {
                    mOnViewPagerListener.onPageRelease(true , getPosition(view));
                }
            }else {
                if (mOnViewPagerListener != null) {
                    mOnViewPagerListener.onPageRelease(false , getPosition(view));
                }
            }
        }
    };
    
  • Where to add the listener listening event, as shown below.Note here that you need to remove listener listening events when the page is destroyed.
    /**
     * attach When reaching the window, the method must be called
     * @param recyclerView                          recyclerView
     */
    @Override
    public void onAttachedToWindow(RecyclerView recyclerView) {
        //Omit some code here
        mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
    }
    
    /**
     * This method is called when destroyed and the listening event needs to be removed
     * @param view                                  view
     * @param recycler                              recycler
     */
    @Override
    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
        super.onDetachedFromWindow(view, recycler);
        if (mRecyclerView!=null){
            mRecyclerView.removeOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
        }
    }
    

05. Optimize Talk in detail

5.1 ViewPager Change Slide Rate

  • You can modify attributes through reflection. Note that when using reflection, it is recommended to try-catch manually to avoid an exception causing a crash.The code is as follows:
    /**
     * Modify Slide Sensitivity
     * @param flingDistance                     Sliding inertia, default is 75
     * @param minimumVelocity                   Minimum sliding value, default is 1200
     */
    public void setScrollFling(int flingDistance , int minimumVelocity){
        try {
            Field mFlingDistance = ViewPager.class.getDeclaredField("mFlingDistance");
            mFlingDistance.setAccessible(true);
            Object o = mFlingDistance.get(this);
            Log.d("setScrollFling",o.toString());
            //Default value 75
            mFlingDistance.set(this, flingDistance);
    
            Field mMinimumVelocity = ViewPager.class.getDeclaredField("mMinimumVelocity");
            mMinimumVelocity.setAccessible(true);
            Object o1 = mMinimumVelocity.get(this);
            Log.d("setScrollFling",o1.toString());
            //Default value 1200
            mMinimumVelocity.set(this,minimumVelocity);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    

5.2 PagerSnapHelper Notes

  • Many times an exception "illegalstateexception an instance of onflinglistener already set" is thrown.
  • When you look at the SnapHelper source attachToRecyclerView(xxx) method, you can see that if the recyclerView is not null, destoryCallback() is used to cancel the previous RecyclerView's listening interface.Then set the listener through setupCallbacks(), and throw a status exception if OnFlingListener is already set in the current RecyclerView.So how to reproduce this is easy, you can see this bug several times after initialization.
  • It is recommended that you capture the exception manually, with the code settings shown below.The source code judges that if onFlingListener already exists, setting it up again will throw an exception directly, so here you can enhance your logical judgment, ok, then the problem will be solved!
    try {
        //A method on the attachToRecyclerView source may throw an IllegalStateException exception, which is captured manually here
        RecyclerView.OnFlingListener onFlingListener = mRecyclerView.getOnFlingListener();
        //The source code determines that if onFlingListener already exists, setting it again will throw an exception directly, so you can judge here
        if (onFlingListener==null){
            mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
        }
    } catch (IllegalStateException e){
        e.printStackTrace();
    }
    

5.3 Customize LayoutManager Notes

  • Someone on the web has written a blog about customizing LayoutManager to achieve the effect of dithering, and I have read this article carefully myself.But I think there are a few points to note, because to use online app s, you have to minimize the crash rate...
  • When you call the findSnapView method through SnapHelper, you must add non-empty logic to the view you get, otherwise you will easily crash.
  • When listening for scroll displacement scroll VerticallyBy, be aware to add judgment that getChildCount() needs to return 0 if it is 0.
  • When onDetachedFromWindow is called, you can leave the listener listening event out of remove.

5.4 Logical optimization of video playback

  • From foreground to background, when the video is playing or buffering, the calling method can set to pause the video.Destroy the page, release, the internal player is released, and exit in full screen, small window mode.Switch from background to foreground, call this method to restart video playback when video pauses or buffer pauses.The specific video playback code is set as follows, more details can be seen in my encapsulated video player lib:
    @Override
    protected void onStop() {
        super.onStop();
        //From foreground to background, call this method to pause a video while it is playing or buffering
        VideoPlayerManager.instance().suspendVideoPlayer();
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //Destroy page, release, internal player is released, and exit in full screen, small window mode
        VideoPlayerManager.instance().releaseVideoPlayer();
    }
    
    @Override
    public void onBackPressed() {
        //Processing return key logic; exiting full screen if full screen; exiting small window if small window
        if (VideoPlayerManager.instance().onBackPressed()){
            return;
        }else {
            //Destroy Page
            VideoPlayerManager.instance().releaseVideoPlayer();
        }
        super.onBackPressed();
    }
    
    @Override
    protected void onRestart() {
        super.onRestart();
        //Switch from background to foreground, call this method to restart video playback when video pauses or buffer pauses
        VideoPlayerManager.instance().resumeVideoPlayer();
    }
    

5.5 Video logic decoupling

  • Paging certainly involves the logic of video initialization and destruction in actual development.First of all, make sure that the video has only one playback, sliding to half of the page breaks. It is never possible to play the video on both pages. So you need to make sure that the video VideoPlayer is a single interest object, so you can guarantee uniqueness!Next, whether in recyclerView or ViewPager, when the page is in the stage of invisible destruction or view recycling, you need to destroy the video resource, encapsulate the video playback function as much as possible, and then call the method in different states of the page.
  • Of course, in the actual app, there are video playback pages, as well as a number of other features such as compliment, comments, sharing, viewing authors, and so on.These are all requests for interfaces and the ability to slide pages to pull business logic such as the next video collection data when sliding to the last page.Video playback function, because the function is more complex, so it is better to encapsulate it.Do your best to uncouple the video function!For video encapsulation libraries, you can see one I wrote earlier. Video player.

5.6 Page Flipping Carton Optimization Analysis

  • If you use recyclerView for sliding pages, you can improve the usage experience.Note: 1. Don't take time in onBindViewHolder, 2. Slide the page over the fixed height of the layout to avoid the repeated calculation of the height RecyclerView.setHasFixedSize(true), 3. For paging pull-out data note, it is recommended that you drop 10 pieces of data at a time (this can also be customized by the server agreement) instead ofSlide one page to load data from the next.

5.7 Pull-up to turn pages quickly to black screen

  • Because setting the background color of the video to black, I have seen many players initialize like this.Because the easiest solution is to add a cover and set the background of the cover.

Other Introduction

Reference Blog

01. About Blog Summary Links

02. About My Blog

Slide Page Open Source Library: https://github.com/yangchong211/YCScrollPager

Video player: https://github.com/yangchong211/YCVideoPlayer

Posted by jcleary on Fri, 06 Sep 2019 18:53:33 -0700