Остановка Exoplayer onSwipe ViewPager
Я использую ViewPager
с одним экземпляром фрагмента, в котором я показываю Media
файлы, такие как изображения, видео, аудио.
Я реализовал ExoPlayer
для обработки Video
и Audio
файлов. И Glide
для изображений.
Из этого, чтобы избежать утечек памяти, я освобождаю объект ExoPlayer
как это в 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();
}
И в onViewCreated()
я инициализирую представление следующим образом:
@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);
}
}
Я использую FragmentStatePagerAdapeter
. Это метод getItem
:
@Override
public Fragment getItem(int position) {
return ItemViewerFragment.newInstance(mItems.get(position));
}
Я не могу обнаружить onPause
фрагмента при первом Viewpager
. При втором пролистывании video/audio
файлы перестают воспроизводиться.
В .addOnPageChangeListener
я попытался добавить .addOnPageChangeListener
:
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
try {
((ItemViewerFragment)mAdapter.getItem(mPreviousPos)).imHiddenNow();
} catch (Exception e) {
e.printStackTrace();
}
mPreviousPos = position;
}
И в ItemViewerFragment.java
:
public void imHiddenNow(){
releasePlayer();
}
Тем не менее video/audio
продолжает играть.
Вот видео ссылка на скринкаст.
Демонстрационный проект GitHub по ссылке.
Пожалуйста, обратите внимание, что решение, опубликованное ниже , не работает.
Ответы
Ответ 1
Я следовал подходу к поддержанию HashMap
фрагментированных объектов внутри PagerAdapter
- Объявите интерфейс:
interface FragmentLifecycle {
void onPauseFragment()
}
- Реализуйте интерфейс во Фрагменте.
public void onPauseFragment() {
if (simpleExoPlayer != null){
simpleExoPlayer.setPlayWhenReady(false);
}
}
-
Сохраните все объекты фрагмента в HashMap<Integer,Fragment>
с соответствующими позициями в качестве ключа. Объявите hashmap внутри PagerAdapter
. Также объявите один метод получения для доступа к объектам фрагмента из hashmap. например
@Override
public Fragment getItem(int position) {
ItemViewerFragment fragment = ItemViewerFragment.newInstance(mItems.get(position));
mFragments.put(position,fragment);
return fragment;
}
public ItemViewerFragment getMapItem (int position) {возврат mFragments.get(position); }
-
В viewPager
где вы объявили viewPager
сохраните одну переменную currentPosition
и реализуйте ViewPager.OnPageChangeListener
.
-
Внутри метода onPageSelected
,
@Override
public void onPageSelected(int position) {
if(mAdapter.getMapItem(currentPosition) != null) (mAdapter.getMapItem(currentPosition)).onPauseFragment();
currentPosition = position;
}
Ответ 2
Вот код для адаптера пейджера:
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);
}
}
Вот прослушиватель прокрутки:
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) {
}
});
Для носителя вы можете использовать для цикла Loop и одновременно добавить все фрагменты в список, а затем использовать его для эффективности:
viewPager.setOffscreenPageLimit(3);
Это позволит убедиться, что доступно только 3 экземпляра вашего фрагмента.
Для использования одного фрагмента я предлагаю вам сделать это следующим образом:
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;
}
Затем в onCreate of Fragment вы можете получить свой файл следующим образом:
File file;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
file = getArguments().getSerializable("file");
}
Теперь добавьте свои фрагменты в пейджер следующим образом:
for (int i = 0; i < totalFiles; i++) {
viewPagerAdapter.addFragment(MyFragment.newInstance(fileList.get(i));
}
Надеюсь, что это поможет.
Ответ 3
Прошло больше года с тех пор, как я использовал Exoplayer, и я как-то справился с подобной проблемой. Обратите внимание, что API немного изменились, поэтому используйте следующий код, чтобы получить представление о том, как реализовать потенциальное решение. Пожалуйста, дайте мне знать, если это не сработает, я еще раз рассмотрю API и вернусь к вам.
Приступая к решению:
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 );
}
Ответ 4
Проблема в том, что onPause
и onResume
не вызываются, когда видимость фрагмента изменяется в ViewPager. Решение состоит в том, чтобы добавить 2 события видимости: losingVisibility
и gainVisibility
.
Почему это отличное решение?
Поскольку вы храните фреймворк в управлении кешем фрагмента и жизненным циклом. Мы просто добавляем обратные вызовы, необходимые для приостановки и возобновления наших медиа в нашем фрагменте.
Шаг за шагом:
Код ниже - это просто объяснение моего кода. Шаги Step *.java, чтобы увидеть полную реализацию.
-
Создайте losingVisibility
и gainVisibility
в 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();
}
}
-
Вызовите losingVisibility
и gainVisibility
каждый раз, когда выбрана новая страница (onPageSelected
) в 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) {
}
});
-
Добавить getCachedItem
в YourPagerAdapter.java:
Третий шаг - это добавление метода для извлечения кэшированных фрагментов. Для этого мы должны кэшировать ссылку на созданный фрагмент (переопределяя instantiateItem
) и выпускать ту же ссылку (переопределяя 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);
}
}
Ответ 5
Я знаю его так поздно, но я надеюсь, что это поможет кому угодно...
viewPager сохраняет заставку.
так что у тебя есть:
prev offscreen фрагмент (начало) → видимый фрагмент (рабочий) → следующий внеэкранный фрагмент (начало)
вы можете переопределить Fragment.setUserVisibleHint()
и обрабатывать воспроизведение/паузу видео.