web-dev-qa-db-ja.com

フラグメントの共有要素の移行がViewPagerで機能しない

私のアプリには、いくつかのフラグメントで構成されるViewPagerで構成されるビューが含まれています。これらのフラグメントのいずれかでアイテムをクリックすると、共有要素(この場合は画像)が、クリックされたコンテンツに関する詳細情報を表示するフラグメントに遷移することが期待されます。

これがどのように見えるかについての非常に簡単なビデオです:

https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-114842.mp4

これは、Fragment-> Fragment transitionを使用しているだけです。

問題は、開始フラグメントをViewPager内に配置するときに発生します。これは、ViewPagerが親フラグメントの子フラグメントマネージャを使用しているためと思われます。これは、フラグメントトランザクションを処理しているアクティビティのフラグメントマネージャとは異なります。ここに何が起こるかのビデオがあります:

https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-120029.mp4

上記で説明したように、アクティビティのフラグメントマネージャーに対する子フラグメントマネージャーであるため、ここでの問題はかなり確実です。移行の方法は次のとおりです。

SimpleFragment fragment = new SimpleFragment();

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.am_list_pane, fragment, fragment.getClass().getSimpleName());

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
    TransitionSet enterTransition = new TransitionSet();
    enterTransition.addTransition(new ChangeBounds());
    enterTransition.addTransition(new ChangeClipBounds());
    enterTransition.addTransition(new ChangeImageTransform());
    enterTransition.addTransition(new ChangeTransform());

    TransitionSet returnTransition = new TransitionSet();
    returnTransition.addTransition(new ChangeBounds());
    returnTransition.addTransition(new ChangeClipBounds());
    returnTransition.addTransition(new ChangeImageTransform());
    returnTransition.addTransition(new ChangeTransform());

    fragment.setSharedElementEnterTransition(enterTransition);
    fragment.setSharedElementReturnTransition(returnTransition);

    transaction.addSharedElement(iv, iv.getTransitionName());
}

transaction.addToBackStack(fragment.getClass().getName());

transaction.commit();

これは、両方のフラグメントがアクティビティのフラグメントマネージャによって管理されている場合は正常に機能しますが、次のようにViewPagerをロードする場合は:

ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
pager.setAdapter(new Adapter(getChildFragmentManager()));

ViewPagerの子はアクティビティによって管理されておらず、もう機能しません。

これはAndroidチームによる監視ですか?これをやめる方法はありますか?ありがとう。

52
JMRboosties

おそらくあなたはすでにこれに対する答えを見つけましたが、もしあなたがそうしなかった場合、頭を数時間ひっかいてからそれを修正するために私がしたことはここにあります。

私が考える問題は、2つの要因の組み合わせです。

  • Fragments内のViewPagerは遅延してロードされます。つまり、アクティビティはViewPager内に含まれるフラグメントよりもはるかに高速に戻ります。

  • あなたが私のような場合、ViewPagerの子フラグメントはほとんど同じタイプです。つまり、それらはすべて同じトランジション名を共有します(xmlレイアウトで設定した場合)、コードで設定し、可視フラグメントで一度だけ設定しない限り

これらの問題の両方を修正するために、これが私がしたことです:

1。遅延読み込みの問題の修正:

アクティビティ(ViewPagerを含むアクティビティ)内で、super.onCreate()の後、setContentView()の前に次の行を追加します。

_@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActivityCompat.postponeEnterTransition(this); // This is the line you need to add

    setContentView(R.layout.feeds_content_list_activity);
    ...
}
_

2。同じ遷移名を持つ複数のフラグメントの問題を修正:

これを行うにはかなり多くの方法がありますが、これは私の「詳細」アクティビティ、つまりViewPageronCreate()あなたは本当にどこでもそれを行うことができます):

__viewPager.setAdapter(_sectionsPagerAdapter);
_viewPager.setCurrentItem(position);
...
...
_pagerAdapter.getItem(position).setTransitionName(getResources().getString(R.string.transition_contenet_topic));
_

ActivityViewPagerフラグメントにまだアタッチされていない可能性があるため、アクティビティからフラグメントにロードする場合は、アクティビティからフラグメントに単に遷移名を渡す方が簡単なので、注意する必要があります。リソース

実際の実装は、予想どおり簡単です。

_public void setTransitionName(String transitionName) {
    _transitionName = transitionName;
}
_

次に、フラグメントのonViewCreated()内に次の行を追加します。

_public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    ...
    if (_transitionName != null && Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.Lollipop) {
        setTransitionNameLollipop();
    }
    ...
}

@TargetApi(Build.VERSION_CODES.Lollipop)
private void setTransitionNamesLollipop() {
    _imgTopic.setTransitionName(_transitionName);
}
_

パズルの最後のピースは、フラグメントが完全にロードされたときを見つけて、ActivityCompat.startPostponedEnterTransition(getActivity());を呼び出すことです。

私の場合、UIスレッドからほとんどのものをロードしているため、フラグメントが完全にロードされていませんでした。つまり、すべてがロードされたときにこれを呼び出す方法を見つけなければなりませんでしたが、そうでない場合はこれを呼び出すことができますsetTransitionNameLollipop()を呼び出した直後。

このアプローチの問題は、非常に注意して「戻る」ためにアクティビティをexitする直前に「可視」フラグメントの遷移名をリセットしない限り、出口遷移が機能しない可能性があることです。次のように簡単に実行できます。

  1. ViewPagerでページの変更を聞く
  2. フラグメントが表示されなくなったらすぐにトランジション名を削除します
  3. 表示されるフラグメントに遷移名を設定します。
  4. finish()を呼び出す代わりに、ActivityCompat.finishAfterTransition(activity);を呼び出します

これは、私の遷移であったRecyclerViewに戻る必要がある場合、すぐに非常に複雑になる可能性があります。そのために、@ Alex Lockwoodが提供するより良い回答があります: ViewPager Fragments – Shared Element Transitions これは非常によく書かれたサンプルコードを持っています(ただし、私よりもはるかに複雑です)書きました)ここ: https://github.com/alexjlockwood/activity-transitions/tree/master/app/src/main/Java/com/alexjlockwood/activity/transitions

私の場合、彼のソリューションを実装するために行く必要はありませんでしたし、私が投稿した上記のソリューションは私のケースで機能しました。

複数の共有要素がある場合、それらに対応するためにメソッドを拡張する方法を理解できると確信しています。

36
kha

アクティビティsupportPostponeEnterTransition();

そして、フラグメントがロードされたら(おそらくEventBusなどと同期してみてください)

startPostponedEnterTransition();

このサンプルを参照してください

http://www.androiddesignpatterns.com/2015/03/activity-postponed-shared-element-transitions-part3b.html

1

最近、これで頭を壁にぶつけています。私が望んでいたのは、ニースの拡張カードタイプの共有要素トランジションを持つ別のフラグメントを起動するViewPagerのフラグメントだけでした。上記の提案はどれも役に立たなかったので、ダイアログのようなスタイルのアクティビティを起動してみることにしました。

<style name="AppTheme.CustomDialog" parent="MyTheme">
    <item name="Android:windowIsTranslucent">true</item>
    <item name="Android:windowBackground">@Android:color/transparent</item>
    <item name="Android:windowContentOverlay">@null</item>
    <item name="Android:windowNoTitle">true</item>
    <item name="Android:backgroundDimEnabled">true</item>
</style>

次に、フラグメントからアクティビティを起動します。

Intent intent = new Intent(getContext(), MyDialogActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
                        .makeSceneTransitionAnimation(getActivity(),
                                Pair.create(sharedView, "target_transition"));
ActivityCompat.startActivity(getActivity(), intent, options.toBundle());

フラグメントレイアウトでは、sharedViewにAndroid:transition_nameを配置し、アクティビティレイアウトではAndroid:transition_name = "target_transition"を使用します(2番目の引数Pair.create()と同じ)。

0
Mark