Stop Exoplayer onSwipe ViewPager

I use ViewPager with one instance of the fragment in which I display Media files such as images, videos, audio.

I implemented ExoPlayer to process Video and Audio files. And Glide for images.

From this, to avoid memory leaks, I free the ExoPlayer object like this in ItemViewerFragment.java :

  private void releasePlayer() { if (player != null) { player.release(); player = null; trackSelector = null; simpleExoPlayerView.setPlayer(null); } } @Override public void onStart() { super.onStart(); if (player == null && currentGalleryModel != null) { initializePlayer(); } } @Override public void onResume() { super.onResume(); if (player == null && currentGalleryModel != null) { initializePlayer(); } } @Override public void onPause() { super.onPause(); releasePlayer(); } @Override public void onStop() { super.onStop(); releasePlayer(); } 

And in onViewCreated() I initialize the view as follows:

  @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (currentGalleryModel.isVideo() || currentGalleryModel.isAudio()) { simpleExoPlayerView.setVisibility(View.VISIBLE); imageView.setVisibility(View.GONE); initializePlayer(); } else if (currentGalleryModel.isImage() || currentGalleryModel.isGif()) { simpleExoPlayerView.setVisibility(View.GONE); imageView.setVisibility(View.VISIBLE); Glide.with(this) .load(currentGalleryModel.getFilePath()) .placeholder(android.R.color.black) .fitCenter() .diskCacheStrategy(DiskCacheStrategy.NONE) .into(imageView); } } 

I am using FragmentStatePagerAdapeter . This is the getItem method:

 @Override public Fragment getItem(int position) { return ItemViewerFragment.newInstance(mItems.get(position)); } 

I cannot detect onPause fragment at first Viewpager . The second time you scroll through video/audio files stop playing.

In .addOnPageChangeListener I tried to add .addOnPageChangeListener :

 @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { try { ((ItemViewerFragment)mAdapter.getItem(mPreviousPos)).imHiddenNow(); } catch (Exception e) { e.printStackTrace(); } mPreviousPos = position; } 

And in ItemViewerFragment.java :

 public void imHiddenNow(){ releasePlayer(); } 

However, video/audio continues to play.

Here is a video link to the screencast.

GitHub demo project here .

Please note that the solution posted below does not work .

+11
java android android-viewpager exoplayer
source share
5 answers

I followed the approach of maintaining a HashMap fragmented objects inside the PagerAdapter

  1. Declare the interface:
 interface FragmentLifecycle { void onPauseFragment() } 
  1. Implement the interface in the Fragment.
 public void onPauseFragment() { if (simpleExoPlayer != null){ simpleExoPlayer.setPlayWhenReady(false); } } 
  1. Save all the fragment objects in the HashMap<Integer,Fragment> with the corresponding positions as the key. Declare a hashmap inside the PagerAdapter . Also declare a single retrieval method for accessing fragment objects from hashmap. eg

     @Override public Fragment getItem(int position) { ItemViewerFragment fragment = ItemViewerFragment.newInstance(mItems.get(position)); mFragments.put(position,fragment); return fragment; } 

    public ItemViewerFragment getMapItem (int position) {return mFragments.get (position); }

  2. In the viewPager where you declared viewPager save one currentPosition variable and implement ViewPager.OnPageChangeListener .

  3. Inside the onPageSelected method,

      @Override public void onPageSelected(int position) { if(mAdapter.getMapItem(currentPosition) != null) (mAdapter.getMapItem(currentPosition)).onPauseFragment(); currentPosition = position; } 
+1
source share

Here is the code for the pager adapter:

  class ViewPagerAdapter extends FragmentPagerAdapter { private final List<Fragment> mFragmentList = new ArrayList<>(); private final List<String> mFragmentTitleList = new ArrayList<>(); public ViewPagerAdapter(FragmentManager manager) { super(manager); } @Override public Fragment getItem(int position) { return mFragmentList.get(position); } @Override public int getCount() { return mFragmentList.size(); } public void addFragment(Fragment fragment, String title) { mFragmentList.add(fragment); mFragmentTitleList.add(title); } @Override public CharSequence getPageTitle(int position) { return mFragmentTitleList.get(position); } } 

Here is the scroll listener:

  viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { //Stop media here. } @Override public void onPageSelected(int position) { //Save your previous position here. } @Override public void onPageScrollStateChanged(int state) { } }); 

For media, you can use Loop for the loop and simultaneously add all the fragments to the list, and then use it for efficiency:

 viewPager.setOffscreenPageLimit(3); 

This will make sure that only 3 copies of your fragment are available.

To use one snippet, I suggest you do this as follows:

  public MyFragment() { } //This is to send a file to the fragment if you need it. public static MyFragment newInstance(File file) { MyFragment fragment = new MyFragment(); Bundle bundle = new Bundle(); bundle.putSerializable("file", file); fragment.setArguments(bundle); return fragment; } 

Then in onCreate of Fragment you can get your file as follows:

 File file; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); file = getArguments().getSerializable("file"); } 

Now add your snippets to the pager as follows:

  for (int i = 0; i < totalFiles; i++) { viewPagerAdapter.addFragment(MyFragment.newInstance(fileList.get(i)); } 

Hope this helps.

+3
source share

More than a year has passed since I used Exoplayer, and somehow I dealt with a similar problem. Please note that the APIs have changed a bit, so use the following code to get an idea of ​​how to implement a potential solution. Please let me know if this does not work, I will review the API again and get back to you.

Getting to the decision:

 private int mPlayerCurrentPosition; private int getCurrentPlayerPosition() { return mExoPlayer.getCurrentPosition(); } // call this from onPause private void releaseExoplayer() { mPlayerCurrentPosition = getPlayerCurrentPosition(); mExoPlayer.setPlayWhenReady(false); mExoPlayer.release(); // this will make the player object eligible for GC } private void resumePlaybackFromPreviousPosition(int prevPosition) { mExoPlayer.seekTo(mPlayerCurrentPosition ); } 
+1
source share

The problem is that onPause and onResume not called when fragment visibility changes in ViewPager. The solution is to add 2 visibility events: losingVisibility and gainVisibility .

Why is this a great solution?

Since you keep the framework in control of the fragment cache and life cycle. We simply add the callbacks needed to pause and resume our media in our fragment.

Step by step:

The code below is just an explanation of my code. Steps * .java steps to see the full implementation.

  1. Create losingVisibility and gainVisibility in YourFragment.java :

     public class YourFragment extends Fragment { /** * This method is only used by viewpager because the viewpager doesn't call onPause after * changing the fragment */ public void losingVisibility() { // IMPLEMENT YOUR PAUSE CODE HERE savePlayerState(); releasePlayer(); } /** * This method is only used by viewpager because the viewpager doesn't call onPause after * changing the fragment */ public void gainVisibility() { // IMPLEMENT YOUR RESUME CODE HERE loadVideo(); } } 
  2. Call losingVisibility and gainVisibility each time a new page ( onPageSelected ) is selected in YourActivity.java :

     mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { YourFragment cachedFragmentLeaving = mYourPagerAdapter.getCachedItem(mCurrentItem); if (cachedFragmentLeaving != null) { cachedFragmentLeaving.losingVisibility(); } mCurrentItem = position; YourFragment cachedFragmentEntering = mYourPagerAdapter.getCachedItem(mCurrentItem); if (cachedFragmentEntering != null) { cachedFragmentEntering.gainVisibility(); } } @Override public void onPageScrollStateChanged(int state) { } }); 
  3. Add getCachedItem to YourPagerAdapter.java :

The third step is to add a method to extract cached fragments. To do this, we must cache the link to the created fragment (overriding instantiateItem ) and release the same link (overriding destroyItem ).

  public class YourPagerAdapter extends FragmentStatePagerAdapter { private SparseArray<YourFragment> mFragmentsHolded = new SparseArray<>(); @NonNull @Override public Object instantiateItem(ViewGroup container, int position) { Object fragment = super.instantiateItem(container, position); if(fragment instanceof StepFragment) { mFragmentsHolded.append(position, (StepFragment) fragment); } return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { mFragmentsHolded.delete(position); super.destroyItem(container, position, object); } public YourFragment getCachedItem(int position) { return mFragmentsHolded.get(position, null); } } 
+1
source share

I know him so late, but I hope this helps anyone ...
viewPager saves the splash screen.
so do you have:
prev offscreen fragment (beginning) → visible fragment (working) → next offscreen fragment (beginning)
you can override Fragment.setUserVisibleHint() and handle the play / pause of the video.

0
source share

All Articles