1

私のアプリケーション タブレット レイアウトには、画像のグリッドを含むグリッド ビュー (RecyclerView) が左側にあり、詳細ビューが右側にあり、現在選択されているグリッド ビュー画像の縮小版とその画像を説明する追加のテキスト ビューが表示されます。クリックした画像ビュー (左側のグリッド ビュー フラグメント) を詳細ビュー フラグメントの一致する位置にスライドおよびスケーリングする共有要素アニメーション トランジションを提供する方法を見つけようとしています。各画像の選択をバックスタックに記録したくないため、私のアプリケーションは「入力」トランジションのみを必要とします。

OnItemClick(View view)次のように、ハンドラーで標準の共有要素サポート呼び出しを使用してみました。

DetailFragment detailFragment = detailFragment.newInstance(mFragmentId);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    movieDetailFragment.setSharedElementEnterTransition(
            TransitionInflater.from(this).inflateTransition(
                    R.transition.change_image_transform));
            view.setTransitionName(getString(
                    R.string.image_transition_name));
}

getSupportFragmentManager().beginTransaction()
        .replace(R.id.detail_container,
                 detailFragment,
                 DETAIL_FRAGMENT_TAG)
        .addSharedElement(view, getString(R.string.image_transition_name))
        .commit();

"view" 変数は、ユーザーがグリッド ビューでクリックした ImageView です。私の詳細フラグメントのonCreate()メソッドにもエントリが含まれています

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    mImageView.setTransitionName(
            getString(R.string.image_transition_name));
}

次の change_image_transform.xml トランジション セットも使用しています。

<transitionSet>
    <changeImageTransform/>
    <changeBounds/>
</transitionSet>

一緒に

<item name="android:windowContentTransitions">true</item>
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowSharedElementEnterTransition">
    @transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">
    @transition/change_image_transform</item>

これらの設定はすべて、シングル ペイン モードで適切に機能します。ユーザーがグリッド ビュー アクティビティ内の画像をクリックすると、(新しいアクティビティ内の) 詳細フラグメントがグリッド ビューを置き換え、アニメーションは入口と出口の両方のシナリオで完全に機能します。

ただし、2 ペイン モードでは、画像はグリッド ビューのソースから新しく作成された詳細フラグメントの宛先にスライドおよびスケーリングされません。私が抱えている問題は、ソース ImageView が永続的に表示されるグリッド ビュー フラグメントに存在するため、アニメーション フレームワークのトランジション処理に含まれないことが原因であると考えています。

私の検索では、単純なカスタム アニメーションを使用するという漠然とした推奨事項以外に、この問題に対する明確な答えは見つかりませんでした。テストとして単純な TranslateAnimation (スケーリングなし) を試しましたが、これには 2 つの問題がありました: 画像がグリッド ビュー フラグメントの境界を越えることができず、実際に画像を「移動」するという望ましくない副作用がありました (アニメーションが実行されている間、グリッド ビューに一時的に空のスポットが表示されます)。最終的に、目的の効果は、元の画像が消去されることなく、ソースから宛先への画像のスライドとスケーリングを確認することです。

提案やアドバイスをいただければ幸いです。

4

1 に答える 1

1

I decided that the simplest way to get a shared element transition to work between two fragments in 2 pane mode was to simply do the animation myself. In my solution, I do not replace the detail fragment with the details of the selected grid image; instead I just refresh the layout controls with new data from the selected grid item. To prevent the parent linear layout from resizing when the target ImageView is temporarily removed from the detail view and placed in the overlay (for animation), the ImageView parent is forced to size of contained child and then restored to its original layout width and height after the animation. Here is the function that performs the animation in response to a grid item click event:

/**
 * Sets up and runs an animation that translates and scales the
 * selected poster in the grid view to the poster ImageView in the
 * detail fragment.
 * @param srcView
 * @param destView
 */
private void runAnimation(final ImageView srcView, final ImageView destView) {
    final ViewGroup destParentView = (ViewGroup)destView.getParent();

    // Set the destination image view's parent (FrameLayout) to
    // keep the width and height of image view that will be
    // temporarily re-parented during the animation. This will
    // ensure that the enclosing LinearLayout will remain fixed
    // in appearance during the animation.
    final int parentLayoutWidth = destParentView.getLayoutParams().width;
    final int parentLayoutHeight = destParentView.getLayoutParams().height;
    destParentView.getLayoutParams().width = destParentView.getMeasuredWidth();
    destParentView.getLayoutParams().height = destParentView.getMeasuredHeight();

    // Scale the destination image to the size of
    // the source image. The animation will restore
    // the destination image scale when it is run.
    // Note that the scale needs to be set before any
    // translation so that the translation takes into
    // account the change in scale.
    float scaleX = (float)destView.getMeasuredWidth()
            / (float)srcView.getMeasuredWidth();
    float scaleY = (float) destView.getMeasuredHeight()
            / (float) srcView.getMeasuredHeight();

    destView.setScaleX(1f / scaleX);
    destView.setScaleY(1f / scaleY);

    // Now set translation on scaled image so that the
    // destination image begins at the same position as
    // the source image. The destination image translation
    // will be returned to 0 during the animation.
    int[] srcLocation = {0, 0};
    int[] dstLocation = {0, 0};
    srcView.getLocationOnScreen(srcLocation);
    destView.getLocationOnScreen(dstLocation);
    destView.setTranslationX(srcLocation[0] - dstLocation[0]);
    destView.setTranslationY(srcLocation[1] - dstLocation[1]);

    // The overlay must be created from any view whose bounds
    // encompasses both the source and destination views.
    final ViewGroup rootView =
                (ViewGroup)MainActivity.this.findViewById(R.id.main_content);
    rootView.getOverlay().add(destView);

    // Run animation and when it completes, move the destination
    // view from the overlay back to it's original parent. Also,
    // restore the parent's layout params which were changed to
    // act as a placeholder with the image was being animated.
    destView.animate()
            .scaleX(1f)
            .scaleY(1f)
            .translationX(0)
            .translationY(0)
            .setInterpolator(new DecelerateInterpolator(2))
            .setDuration(500)
            .withEndAction(new Runnable() {
                @Override
                public void run() {
                    rootView.getOverlay().remove(destView);
                    destParentView.getLayoutParams().width = parentLayoutWidth;
                    destParentView.getLayoutParams().height = parentLayoutHeight;
                    destParentView.addView(destView);
                }
            });
}
于 2015-10-11T23:14:04.377 に答える