Vertical direction, scroll one page at a time encapsulation library.

Posted by glennn.php on Wed, 11 Sep 2019 10:55:13 +0200

Links to the original text: https://p.codekk.com/detail/Android/yangchong211/YCScrollPager

YCScrollPager

Project address: yangchong211/YCScrollPager 

Introduction: Vertical direction, scroll one page at a time encapsulation library. The ViewPager approach is currently supported, and the RecyclerView approach is also supported... ViewPager only modifies the sliding rate and the transition time of scrolling pages; recyclerView is used to create the function of silk-slip switching video, and more content can be seen in demo.

More: author   Mention Bug   

Label:

 

Catalogue introduction

  • 01. Let's look at the requirements first.
  • 02. There are several implementations
    • 2.1 Use ViewPager
    • 2.2 Use Recycler View
  • 03. Specific use
    • 3.0 How to Refer to the lib
    • 3.1 ViewPager Implementation
    • 3.2 RecyclerView Implementation
  • 04. Partial optimization points
    • 4.1 ViewPager Change Sliding Rate
    • 4.2 PagerSnapHelper Notes
    • 4.3 Custom Layout Manager Notes
  • 06. Introduction to other instructions

00. Let's take a look at the effect map first.

01. Let's look at the requirements first.

  • Video playback in the project requires the effect of one page sliding in vertical direction. Sliding should be smooth and not jammed, and when the manual touch slides more than 1/2, it can be loosened to slide the next page, not more than 1/2 back to the original page.
  • Fingers drag the page to slide, so long as there is no switch to other pages, the video is playing. Switched the page, the last video was destroyed, and the page began to initialize playback.
  • When switching pages, the transition effect should be natural to avoid flash screen. Specific sliding effect, can directly refer to the tremble...

02. There are several implementations

2.1 Use ViewPager

  • Using ViewPager to Realize Up-down Switching Video Analysis with Vertical Method
    • 1. Recently, it is useful to play videos in ViewPager, that is to say, the vertical method slides up and down to switch videos. Videos are network videos. The initial idea is to initialize SurfaceView according to the current item location in ViewPager, and remove SurfaceView according to the item location when destroying.
    • 2. The above approach is indeed achievable, but there are two problems. First, the life cycle of MediaPlayer is not easy to control and there is a memory leak problem. Second, when all three item s are video in a row, a bug is found in the last frame of the last video when sliding back and forth.
    • 3. Without improving the user experience, the first frame of the video will be covered before the initialization of the video player is completed, but it is found that the first frame of the video does not match the information of the first frame of the video.
  • This is probably the way to achieve it.
    • 1. You need to customize a ViewPager that slides vertically. Note that this is particularly important.
    • 2. Slide one page at a time. It is recommended to use ViewPager + FragmentStatePager Adapter + Fragmentation, which will be described in detail later.
    • 3. Video initialization logic and destruction logic optimization, performance and sliding fluency analysis and discussion.
  • ViewPager is not recommended
    • 1. The sliding effect of ViewPager fully satisfies the scene, and supports UI bindings such as Fragment and View. As long as some modifications are made to the layout and touch event parts, the horizontal ViewPager can be changed to the vertical one.
    • 2. But not reusing is the most lethal problem. In the onLayout method, all subviews are instantiated and arranged word by word on the layout. When the number of Item s is large, it will be a huge performance waste.
    • 3. The second is the judgment of visibility. Many people think that Fragments are visible in onResume, and Fragments in ViewPager are a counterexample, especially when multiple ViewPagers are nested, there will be multiple parent Fragments and multiple children Fragments in onResume at the same time, but only one of them is visible. Unless the pre-loading mechanism of ViewPager is abandoned. When reporting important data such as page content exposure, many conditions need to be judged: onResumed, setUser VisibleHint, setOnPage ChangeListener, etc.

2.2 Use Recycler View

  • Using RecyclerView to Realize Up-down Switching Video Analysis in Branch Direction
    • 1. First, Recycler View is very simple to set up vertical sliding. At the same time, the fourth level cache of item is handled well, and the effect of sliding is better than ViewPager.
    • 2. Sliding event handling is better than viewPager. Even if you escape the drop-down refresh upload layout, it will not affect later event conflict processing. You can see demo case in detail.
  • This is probably the way to achieve it.
    • 1. Customize a LinearLayoutManager and rewrite the onScrollStateChanged method. Note that you get the sliding state.
    • 2. Sliding to switch a page at a time can be implemented by PagerSnapHelper, which is very convenient and simple.
    • 3. In the adapter corresponding to recyclerView, video operations are initialized in onCreateViewHolder and video resources are destroyed when onViewRecycled.

03. Specific use

3.0 How to Refer to the lib

  • Add dependencies directly
    implementation 'com.yc:PagerLib:1.0.1'
    

3.1 ViewPager Implementation

3.1.1 Use Vertical ViewPager as follows

  • In layout
      <com.yc.pagerlib.pager.VerticalViewPager
          android:id="@+id/vp"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>
    
  • In activity, here is Viewpager + FragmentStatePager Adapter + Fragment ``private void initViewPager (){
      List<Video> list = new ArrayList<>();
      ArrayList<Fragment> fragments = new ArrayList<>();
      //Note that the data source is replaced by yours when it is used. Here are some false data.
      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;
    }
}
```
  • Video Playing Related Operations in fragment

      @Override
      public void onStop() {
          super.onStop();
          VideoPlayerManager.instance().releaseVideoPlayer();
      }
    
      @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.2 RecyclerView Implementation

  • PagerLayoutManager is used directly in the code, as shown below
      PagerLayoutManager viewPagerLayoutManager = new PagerLayoutManager(
              this, OrientationHelper.VERTICAL);
      recyclerView.setLayoutManager(viewPagerLayoutManager);
    
  • Setting up layoutManager to initialize and destroy related listeners

      viewPagerLayoutManager.setOnViewPagerListener(new OnPagerListener() {
          @Override
          public void onInitComplete() {
              System.out.println("OnPagerListener---onInitComplete--"+"Initialization complete");
          }
    
          @Override
          public void onPageRelease(boolean isNext, int position) {
              System.out.println("OnPagerListener---onPageRelease--"+position+"-----"+isNext);
          }
    
          @Override
          public void onPageSelected(int position, boolean isBottom) {
              System.out.println("OnPagerListener---onPageSelected--"+position+"-----"+isBottom);
          }
      });
    

04. Partial optimization points

4.1 ViewPager Change Sliding Rate

  • Attributes can be modified by reflection. Note that manual try-catch is recommended when using reflection to avoid crashing due to exceptions. The code is as follows:

      /**
       * Modifying Sliding Sensitivity
       * @param flingDistance                     Sliding inertia, default is 75
       * @param minimumVelocity                   Minimum sliding value, default 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();
          }
      }
    

4.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 RCyclerView listening interface. Then set up the listener through setupCallbacks(), and if OnFlingListener is already set in RecyclerView, a status exception will be thrown. So how to reproduce this is very easy. You can see this bug many times after initialization.
  • It is recommended to capture the exception manually, and the code settings are as follows. The source code judges that if onFlingListener already exists, it will throw an exception directly if it is set up again, so here we can enhance the logical judgement, ok, then the problem will be solved.
      try {
          //The method on attachToRecyclerView source code may throw an IllegalStateException exception, which is captured manually here.
          RecyclerView.OnFlingListener onFlingListener = mRecyclerView.getOnFlingListener();
          //The source code judges that if onFlingListener already exists, it will throw an exception directly if it is set up again, so we can judge here.
          if (onFlingListener==null){
              mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
          }
      } catch (IllegalStateException e){
          e.printStackTrace();
      }
    

4.3 Custom Layout Manager Notes

  • Someone on the Internet has written a blog about how to customize LayoutManager to achieve the effect of tremolo, and I have read this article carefully myself. But I think there are a few points to note, because in order to use online app s, we must try to reduce the crash rate as much as possible...
  • The findSnapView method is called by SnapHelper, and the view obtained must add non-null judgment logic, otherwise it will easily cause crash.
  • When listening for scroll VerticallyBy, pay attention to adding judgment, that is, getChildCount() needs to return 0 if it is 0.
  • When onDetachedFromWindow calls, listener listening events can be remove d.

06. Introduction to other instructions

Introduction to Other Contents

 

About Blog Summary Links

Other recommendations

  • Blog Notes Summary [October 15 to date], including Java foundation and in-depth knowledge points, Android technology blog, Python learning notes and so on, including the usual development of the bug summary, of course, also in the spare time to collect a large number of interview questions, long-term updates, maintenance and correction, continuous improvement... Open source files are in markdown format! At the same time, life blog has been open source. Since 12 years ago, it has accumulated a total of 47 articles [nearly 200,000 words]. Please indicate the source for reprinting. Thank you!
  • Link Address: https://github.com/yangchong211/YCBlogs
  • If you feel good, you can start. Thank you. Of course, you are also welcome to make suggestions. Everything starts at a slight moment, and quantitative change causes qualitative change.

Topics: Fragment Android SurfaceView network