画像、ビデオ、オーディオなどのViewPager
ファイルを表示している単一フラグメントインスタンスでMedia
を使用しています。
ExoPlayer
&Video
ファイルを処理するためにAudio
を実装しました。そして、画像の場合はGlide
。
メモリリークを回避するために、_ItemViewerFragment.Java
_で次のようなExoPlayer
オブジェクトをリリースしています。
_ 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
を検出できません。 2回目のスワイプで、_video/audio
_ファイルの再生が停止します。
アクティビティでは、_.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
_は再生を続けます。
これが ビデオリンク スクリーンキャストへのリンクです。
HashMap
内のフラグメントオブジェクトのPagerAdapter
を維持するアプローチに従いました
interface FragmentLifecycle {
void onPauseFragment()
}
public void onPauseFragment() {
if (simpleExoPlayer != null){
simpleExoPlayer.setPlayWhenReady(false);
}
}
すべてのフラグメントオブジェクトをHashMap<Integer,Fragment>
に保存し、それぞれの位置をキーとして使用します。 PagerAdapter
内でハッシュマップを宣言します。また、ハッシュマップからフラグメントオブジェクトにアクセスするための1つのgetterメソッドを宣言します。例えば.
@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); }
viewPager
を宣言したアクティビティでは、1つの変数currentPosition
を保持し、ViewPager.OnPageChangeListener
を実装します。
onPageSelected
メソッド内では、
@Override
public void onPageSelected(int position) {
if(mAdapter.getMapItem(currentPosition) != null) (mAdapter.getMapItem(currentPosition)).onPauseFragment();
currentPosition = position;
}
ページャアダプタのコードは次のとおりです。
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) {
}
});
メディアの場合、forループを使用して、すべてのフラグメントを一度にリストに追加し、効率を上げるためにこれを使用できます。
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));
}
お役に立てれば。
Exoplayerを使ってから1年以上経ちましたが、似たような問題に取り組みました。 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 );
}
問題は、ViewPagerでフラグメントの可視性が変更されたときに、onPause
とonResume
が呼び出されないことです。解決策は、losingVisibility
とgainVisibility
の2つの可視性イベントを追加することです。
フレームワークがフラグメントキャッシュとライフサイクルを管理し続けるためです。メディアを一時停止および再開するために必要なコールバックをフラグメントに追加するだけです。
以下のコードは私のコードの説明にすぎません。 Step * .Java クラスをチェックして、完全な実装を確認してください。
YourFragment.Java でlosingVisibility
およびgainVisibility
メソッドを作成します。
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();
}
}
YourActivity.Java で新しいページが選択されるたびに(losingVisibility
)gainVisibility
とonPageSelected
を呼び出します。
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 に追加します:
3番目のステップは、キャッシュされたフラグメントを取得するメソッドを追加することです。これを行うには、作成されたフラグメントへの参照をキャッシュし(
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);
}
}
私はそれがとても遅いことを知っていますが、それが誰かに役立つことを願っています...
viewPagerはオフスクリーンフラグメントの開始を維持します..
つまり、次のようになります。
前のオフスクリーンフラグメント(開始)->表示フラグメント(動作中)->次のオフスクリーンフラグメント(開始)
解決策は、Fragment.setUserVisibleHint()
をオーバーライドして、ビデオの再生/一時停止を処理できることです。