web-dev-qa-db-ja.com

半径のあるCardViewでの共有要素の遷移

私はこの問題に何週間も取り組んできましたが、まだこの問題を解決することができません。

だから、私はImageViewを持つLinearLayoutを含むCardViewを持っています。

without radius

その半径がないと、共有要素の遷移はシームレスに機能します。ただし、そのCardViewに半径(app:cardCornerRadius = "25dp")を追加すると、最初に半径が削除されてからアニメーションが開始されるため、共有要素の遷移が見苦しくなります

with radius transition

最初のアプローチ:ObjectAnimator

カードの半径値をアニメーション化するObjectAnimatorを作成し、アニメーションが終了した後、遷移を開始します。

ObjectAnimator animator = ObjectAnimator
            .ofFloat(view, "radius", AppUtil.dpAsPixel(this, 25), 0);
animator.setDuration(150);
animator.addListener( // start new Activity with Transition );
animator.start();

これは機能しますが、トランジションがアニメーションの終了を待ってからトランジションを開始するため、見栄えがよくありません。必要なのは、新しいアクティビティ(TransitionSetのORDERING_TOGETHERなど)への移行中に半径がアニメーション化されていることです。

2番目のアプローチ-ChangeImageTransform

StackOverflow post を読んで、ChangeImageTransformやChangeBoundsなどの変換クラスを使用しました。

提案されたようにアプリケーションテーマを定義しました(my_transitionにはChangeImageTransformtransitionSetが含まれています)

<item name="Android:windowSharedElementEnterTransition">@transition/my_transition</item>
<item name="Android:windowSharedElementExitTransition">@transition/my_transition</item>

しかし、それは機能しません。

3番目のアプローチ-ナイーブ

私の最後の試みは、ターゲットのImageViewの半径も25dpにすることです。ターゲットのImageViewが正方形であるため、CardViewが正方形に変換されている可能性がありますが、ご想像のとおり、機能しません。

4番目のアプローチ-CardViewを使用しない

ご覧のとおり、私はペンギンの画像を使用しており、CardViewを使用して半径を作成しています。画像変換を使用して画像を丸めることはできますが、それでも共有要素遷移を作成する正しい方法ではないと思います。

そしてここに私の質問があります、最初に半径を削除せずにCardView半径で共有要素遷移を機能させる方法はありますか?

15
aldok

私はついにそれを解決することができました。興味のある人のために、ここに方法があります:

遷移を開始する前に半径を削除するのはなぜですか?ターゲットのImageViewには半径がないためです。

activity_detail.xml

<ImageView
    Android:id="@+id/iv_image_cover"
    Android:layout_width="match_parent"
    Android:layout_height="250dp"
    Android:scaleType="centerCrop"
    Android:src="@{animal.imageRes}"
    Android:transitionName="animalImage"
    tools:src="@drawable/acat"
/>

半径なしでCardViewを使用すると、目立たなくなりますが、実際にはターゲットの共有ビューになります。

  1. 半径から半径なしへの遷移を実現するには、ターゲットの共有ビューを丸めるように設定する必要があります。カードビュー(半径付き)を使用してラップするだけです。

activity_detail.xml

<Android.support.v7.widget.CardView
    Android:id="@+id/card"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:transitionName="card"
    app:cardCornerRadius="25dp"
>

    <ImageView
        Android:id="@+id/iv_image_cover"
        Android:layout_width="match_parent"
        Android:layout_height="250dp"
        Android:scaleType="centerCrop"
        Android:src="@{animal.imageRes}"
        Android:transitionName="animalImage"
        tools:src="@drawable/acat"
    />

</Android.support.v7.widget.CardView>
  1. MakeSceneTransitionを、「animalImage」ではなく「card」を使用するように変更してください。

ListActivity.class

ActivityOptionsCompat option = ActivityOptionsCompat
.makeSceneTransitionAnimation(ListActivity.this, cardView, "card");

startActivity(intent, option.toBundle());
  1. DetailActivityでは、遷移の開始時に半径アニメーションを開始できます。

DetailActivity.Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().getSharedElementEnterTransition()
        .addListener(new Transition.TransitionListener() {
            @Override
            public void onTransitionStart(Transition transition) {
                ObjectAnimator animator = ObjectAnimator
                    .ofFloat(activityDetailBinding.card, "radius", 0);
                animator.setDuration(250);
                animator.start();
            }
        });
}
  1. スムーズな移行をお楽しみください

final animation

注: レイアウト および アクティビティ の要点

15
aldok

Ovidiuの回答に基づいて、コーナーをアニメーション化できる作業トランジションがあります

https://Gist.github.com/StefanDeBruijn/d45807d386af0e066a03186fe00366e8

これは、プログラムで共有遷移セットを入力するか、xmlを介して追加できます。

<transitionSet xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto">
    <transitionSet>
        <targets>
            <target Android:targetId="@id/backdrop" />
        </targets>
        <!-- Custom transition to take care of rounded corner to square corners transition -->
        <transition
            class=".ChangeOutlineRadius"
            app:endRadius="@dimen/square_corner_radius"
            app:startRadius="@dimen/default_corner_radius" />
        <!-- Default shared element transitions -->
        <changeBounds />
        <changeTransform />
        <changeClipBounds />
        <changeImageTransform />
    </transitionSet>
</transitionSet>

Attrs.xmlに追加することを忘れないでください:

   <declare-styleable name="ChangeOutlineRadius">
        <attr name="startRadius" format="dimension" />
        <attr name="endRadius" format="dimension" />
    </declare-styleable>
2

Fragment Shared ElementTransitionsで動作させることができませんでした。 CardViewのコーナー半径は、アニメーション中に無視されます。これが機能するものです:

_fragment2.setEnterSharedElementCallback(new SharedElementCallback() {
    @Override
    public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {}

    @Override
    public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
        ImageView sharedImageView = null;

        for (View view : sharedElements) {
            if (view instanceof ImageView) {
                sharedImageView = (ImageView) view;
                break;
            }
        }

        if (sharedImageView != null) {
            sharedImageView.setClipToOutline(true);

            ObjectAnimator.ofInt(sharedImageView, new Property<ImageView, Integer>(Integer.class, "outlineRadius") {
                @Override
                public Integer get(ImageView object) {
                    return 0;
                }

                @Override
                public void set(ImageView object, final Integer value) {
                    object.setOutlineProvider(new ViewOutlineProvider() {
                        @Override
                        public void getOutline(View view, Outline outline) {
                            outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), value);
                        }
                    });
                }
            }, 150, 0).setDuration(duration).start();
        }
    }

    @Override
    public void onRejectSharedElements(List<View> rejectedSharedElements) {}

    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}
});
_

基本的に、CardViewの丸い角をアニメーション化する代わりに、ImageView自体には、代わりにアニメーション化される独自の丸い角があります。

ImageViewのコーナーはViewOutlineProviderで丸められ、ObjectAnimatorを使用して、共有要素のトランジションの再生中にコーナーの半径をアニメーション化できます。 ImageViewでsetClipToOutline(true)を呼び出すことも必要であることに注意してください。そうしないと、コーナーがクリップされません。

コールバックのonSharedElementEndメソッドは、すべての共有要素のリストとともに呼び出されます。私のサンプルコードは、共有されているImageViewの1つだけのコーナーのアニメーションを処理することに注意してください。トランジションが複数のImageViewを共有している場合は、それらも考慮する必要があります。

また、何らかの理由で、リバーストランジションが再生されるときに同じコールバックも呼び出されることに注意してください。

ある程度の努力を払えば、これを通常のトランジションに変えることができます。これは、共有要素のトランジションのセットに追加するだけで、共有要素をどう処理するかを自動的に判断します。

0
Ovidiu