私はこの問題に何週間も取り組んできましたが、まだこの問題を解決することができません。
だから、私はImageViewを持つLinearLayoutを含むCardViewを持っています。
その半径がないと、共有要素の遷移はシームレスに機能します。ただし、そのCardViewに半径(app:cardCornerRadius = "25dp")を追加すると、最初に半径が削除されてからアニメーションが開始されるため、共有要素の遷移が見苦しくなります。
最初のアプローチ: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半径で共有要素遷移を機能させる方法はありますか?
私はついにそれを解決することができました。興味のある人のために、ここに方法があります:
遷移を開始する前に半径を削除するのはなぜですか?ターゲットの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を使用すると、目立たなくなりますが、実際にはターゲットの共有ビューになります。
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>
ListActivity.class
ActivityOptionsCompat option = ActivityOptionsCompat
.makeSceneTransitionAnimation(ListActivity.this, cardView, "card");
startActivity(intent, option.toBundle());
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();
}
});
}
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>
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を共有している場合は、それらも考慮する必要があります。
また、何らかの理由で、リバーストランジションが再生されるときに同じコールバックも呼び出されることに注意してください。
ある程度の努力を払えば、これを通常のトランジションに変えることができます。これは、共有要素のトランジションのセットに追加するだけで、共有要素をどう処理するかを自動的に判断します。