web-dev-qa-db-ja.com

フラグメントを使用して共有要素の移行を開始する方法は?

新しいマテリアルデザインの仕様で説明されているように、「共有要素」を持つフラグメント間の遷移を実装しようとしています。私が見つけることができる唯一の方法は ActivityOptionsCompat.makeSceneTransitionAnimation で、これはActivityでのみ機能すると信じています。私はこれと同じ機能を探していますが、フラグメントを使用して/フラグメントを探しています。

57
unchosen

私は同じ問題を抱えていましたが、別のフラグメントから新しいフラグメントを追加することで機能していました。次のリンクは、これを始めるのに非常に役立ちます。 https://developer.Android.com/training/material/animations.html#Transitions

動作するコードは次のとおりです。あるフラグメントから別のフラグメントにImageViewをアニメーション化しています。アニメーション化するViewが両方のフラグメントで同じAndroid:transitionNameを持っていることを確認してください。他のコンテンツは本当に重要ではありません。

テストとして、これを両方のレイアウトxmlファイルにコピーできます。画像が存在することを確認してください。

<ImageView
Android:transitionName="MyTransition"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:scaleType="centerCrop"
Android:src="@drawable/test_image" />

次に、res/transitionフォルダーにchange_image_transform.xmlという名前のファイルが1つあります。

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <changeImageTransform />
</transitionSet>

これで開始できます。画像を含むフラグメントAがあり、フラグメントBを追加するとします。

これをフラグメントAで実行します。

@Override
public void onClick(View v) {
    switch(v.getId()) {
        case R.id.product_detail_image_click_area:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
                setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
                setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(Android.R.transition.explode));

                // Create new fragment to add (Fragment B)
                Fragment fragment = new ImageFragment();
                fragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
                fragment.setEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(Android.R.transition.explode));

                // Our shared element (in Fragment A)
                mProductImage   = (ImageView) mLayout.findViewById(R.id.product_detail_image);

                // Add Fragment B
                FragmentTransaction ft = getFragmentManager().beginTransaction()
                        .replace(R.id.container, fragment)
                        .addToBackStack("transaction")
                        .addSharedElement(mProductImage, "MyTransition");
                ft.commit();
            }
            else {
                // Code to run on older devices
            }
            break;
    }
}
45
ar34z

私はここに新しく、コメントすることができないので、これを回答として投稿しています。

共有要素フラグメントの遷移doは、ソースビューとターゲットビューに同じ(および一意の)transitionNameがある限り、ListViewで機能します。

リストビューアダプターを使用して、一意のtransitionNamesを必要なビューに設定する場合(たとえば、一定の+特定のアイテムID)および実行時に同じtransitionNamesをターゲットビューに設定するように詳細フラグメントを変更します( onCreateView)、トランジションは実際に動作します!

21

共有要素はフラグメントで機能しますが、留意すべき点がいくつかあります。

  1. フラグメントのsharedElementsTransitiononCreateViewを設定しようとしないでください。フラグメントのインスタンスを作成するとき、またはonCreateでそれらを定義する必要があります。

  2. Enter/exitトランジションとsharedElementTransitionの可能なアニメーションに関する公式ドキュメントに注意してください。それらは同じではありません。

  3. 試行錯誤 :)

11
Jordy

私はそれについてコメントすることができないので、これは受け入れられた答えへのコメントでなければなりません。

受け入れられた答え(WindsurferOakおよびar34zによる)は機能しますが、backStackでナビゲートするときにnullポインター例外を引き起こす「マイナー」問題を除きます。元のフラグメントではなく、ターゲットフラグメントでsetSharedElementReturnTransition()を呼び出す必要があるようです。

代わりに:

setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));

そのはず

fragment.setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));

https://github.com/tevjef/Rutgers-Course-Tracker/issues/8

3
Paul L

キーは、カスタムトランザクションを使用することです

_transaction.addSharedElement(sharedElement, "sharedImage");
_

2つのフラグメント間の共有要素遷移

この例では、2つの異なるImageViewsのいずれかをChooserFragmentからDetailFragmentに変換する必要があります。

ChooserFragmentレイアウトでは、一意のtransitionName属性が必要です。

_<ImageView
    Android:id="@+id/image_first"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:src="@drawable/ic_first"
    Android:transitionName="fistImage" />

<ImageView
    Android:id="@+id/image_second"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:src="@drawable/ic_second"
    Android:transitionName="secondImage" />
_

ChooserFragmentsクラスでは、クリックされたViewと、フラグメントの置換を処理している親ActivityにIDを渡す必要があります(IDが必要です) DetailFragment)に表示する画像リソースを確認します。情報を親アクティビティに詳細に渡す方法は、別のドキュメントで確実に説明されています。

_view.findViewById(R.id.image_first).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (mCallback != null) {
            mCallback.showDetailFragment(view, 1);
        }
    }
});

view.findViewById(R.id.image_second).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (mCallback != null) {
            mCallback.showDetailFragment(view, 2);
        }
     }
});
_

DetailFragmentでは、共有要素のImageViewにも一意のtransitionName属性が必要です。

_<ImageView
    Android:id="@+id/image_shared"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="center"
    Android:transitionName="sharedImage" />
_

DetailFragmentonCreateView()メソッドでは、どの画像リソースを表示するかを決定する必要があります(これを行わないと、共有要素は遷移後に消えます)。

_public static DetailFragment newInstance(Bundle args) {
    DetailFragment fragment = new DetailFragment();
    fragment.setArguments(args);
    return fragment;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    View view = inflater.inflate(R.layout.fragment_detail, container, false);

    ImageView sharedImage = (ImageView) view.findViewById(R.id.image_shared);

    // Check which resource should be shown.
    int type = getArguments().getInt("type");

    // Show image based on the type.
    switch (type) {
        case 1:
            sharedImage.setBackgroundResource(R.drawable.ic_first);
            break;

        case 2:
            sharedImage.setBackgroundResource(R.drawable.ic_second);
            break;
    }

    return view;
}
_

Activityはコールバックを受け取り、フラグメントの置換を処理します。

_@Override
public void showDetailFragment(View sharedElement, int type) {
    // Get the chooser fragment, which is shown in the moment.
    Fragment chooserFragment = getFragmentManager().findFragmentById(R.id.fragment_container);

    // Set up the DetailFragment and put the type as argument.
    Bundle args = new Bundle();
    args.putInt("type", type);
    Fragment fragment = DetailFragment.newInstance(args);

    // Set up the transaction.
    FragmentTransaction transaction = getFragmentManager().beginTransaction();

    // Define the shared element transition.
    fragment.setSharedElementEnterTransition(new DetailsTransition());
    fragment.setSharedElementReturnTransition(new DetailsTransition());

    // The rest of the views are just fading in/out.
    fragment.setEnterTransition(new Fade());
    chooserFragment.setExitTransition(new Fade());

    // Now use the image's view and the target transitionName to define the shared element.
    transaction.addSharedElement(sharedElement, "sharedImage");

    // Replace the fragment.
    transaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName());

    // Enable back navigation with shared element transitions.
    transaction.addToBackStack(fragment.getClass().getSimpleName());

    // Finally press play.
    transaction.commit();
}
_

忘れないでください-Transition自体。この例では、共有要素を移動および拡大縮小します。

_@TargetApi(Build.VERSION_CODES.Lollipop)
public class DetailsTransition extends TransitionSet {

    public DetailsTransition() {
        setOrdering(ORDERING_TOGETHER);
        addTransition(new ChangeBounds()).
            addTransition(new ChangeTransform()).
            addTransition(new ChangeImageTransform());
    }

}
_
0
Marius
0
Lym Zoy

SharedElementをフラグメントで検索し、GitHubで非常に便利なソースコードを見つけました。

1.まず、両方のフラグメントレイアウトのオブジェクト(ImageViewのような)にtransitionNameを定義する必要があります(クリックイベントを処理するためにフラグメントAにボタンを追加します):

フラグメントA

  <ImageView
    Android:id="@+id/fragment_a_imageView"
    Android:layout_width="128dp"
    Android:layout_height="96dp"
    Android:layout_alignParentBottom="true"
    Android:layout_centerHorizontal="true"
    Android:layout_marginBottom="80dp"
    Android:scaleType="centerCrop"
    Android:src="@drawable/gorilla"
    Android:transitionName="@string/simple_fragment_transition />

<Button
    Android:id="@+id/fragment_a_btn"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_alignParentBottom="true"
    Android:layout_centerHorizontal="true"
    Android:layout_marginBottom="24dp"
    Android:text="@string/gorilla" />

フラグメントB:

    <ImageView
    Android:id="@+id/fragment_b_image"
    Android:layout_width="match_parent"
    Android:layout_height="250dp"
    Android:scaleType="centerCrop"
    Android:src="@drawable/gorilla"
    Android:transitionName="@string/simple_fragment_transition" />
  1. 次に、移行ディレクトリの移行ファイルにこのコードを書く必要があります(このディレクトリがない場合は、Oneを作成してください:res> new> Android Resource Directory> Resource Type = transition> name = change_image_transform ):

change_image_transform.xml:

 <?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:Android="http://schemas.Android.com/apk/res/Android">
  <changeBounds/>
  <changeTransform/>
  <changeClipBounds/>
  <changeImageTransform/>
</transitionSet>
  1. 最後のステップでは、Javaでコードを完成させる必要があります。

フラグメントA:

public class FragmentA extends Fragment {

    public static final String TAG = FragmentA.class.getSimpleName();


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_a, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        final ImageView imageView = (ImageView) view.findViewById(R.id.fragment_a_imageView);
        Button button = (Button) view.findViewById(R.id.fragment_a_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getFragmentManager()
                        .beginTransaction()
                        .addSharedElement(imageView, ViewCompat.getTransitionName(imageView))
                        .addToBackStack(TAG)
                        .replace(R.id.content, new FragmentB())
                        .commit();
            }
        });
    }
}

フラグメントB:

public class FragmentB extends Fragment {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(Android.R.transition.move));

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_b, container, false);
    }
}

アクティビティに「A」フラグメントを表示することを忘れないでください。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.content, new SimpleFragmentA())
                .commit();
    }

ソース: https://github.com/mikescamell/shared-element-transitions

0
yasin