安卓棒棒糖中的共享元素过渡故障

4
我正在尝试在Master Detail情况下从ListView执行Android 5.0共享元素转换,发送的是一张图片。
我在进入转换中遇到了一个故障,在Master活动转换出时,图像根本不动。一旦加载了Detail活动,图像就会从屏幕左上角开始动画,并动画到其最终位置。返回转换运作完美 - 图像从它的Detail位置到Master中适当的位置进行动画。
这是它的样子:https://www.youtube.com/watch?v=AzyA8i27qWc 我相信我已经正确设置了权限并指定了转换。
<item name="android:windowContentTransitions">true</item>        
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>

<!-- specify shared element transitions -->
<item name="android:windowSharedElementEnterTransition">
    @transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">
    @transition/change_image_transform</item>

这个过渡效果的定义如下:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeImageTransform/>
    <changeBounds/>
    <changeTransform/>
</transitionSet>

我已经为列表中的两个视图分别赋予了相同的unique transitionName属性,基于它们在列表中的位置。
我正在使用makeSceneTransitionAnimation来进行动画:
    View photo = (View) adapter.getView(position, null, listView).findViewById(R.id.photo);
    View navigationBar = findViewById(android.R.id.navigationBarBackground);

    ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
            Pair.create(photo, photo.getTransitionName()),
            Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));

    Bundle extras = new Bundle();
    Patient selectedPatient = patientList.get(position);
    extras.putParcelable("patient", selectedPatient);
    extras.putInt("position", position);

    Intent intent = new Intent(PatientListActivity.this, PatientActivity_.class);
    intent.putExtras(extras);

    startActivity(intent, options.toBundle());

我还将navigationBar指定为共享,以避免其淡出,这个方法运行良好。

我不明白为什么返回过渡效果正常,但进入过渡效果不正常。我看到其他问题的解决方案是延迟进入过渡效果,我已经尝试了这样做,但问题仍然存在。以下是我在详情活动中onCreate中添加的内容:

super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_patient);

    patient = getIntent().getExtras().getParcelable("patient");
    patient = app.getPatientByName(patient.getName());

    patientPhotoView.setImageDrawable(patient.getPhoto());

    /* Set up transition functionality */
    position = getIntent().getExtras().getInt("position");
    patientPhotoView.setTransitionName("patientPhoto" + position);
    patientNameView.setTransitionName("patientName" + position);

    postponeEnterTransition();
    final ViewTreeObserver observer = getWindow().getDecorView().getViewTreeObserver();
    observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            final ViewTreeObserver observer = getWindow().getDecorView().getViewTreeObserver();
            observer.removeOnPreDrawListener(this);
            startPostponedEnterTransition();
            return true;
        }
    });

在我的详细活动中,没有 transitionName 元素,但我正在代码中进行设置。传入活动(详细信息)的相关部分如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PatientActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="200dip"
            android:orientation="vertical"
            android:id="@+id/patient_top_layout">
            <ImageView
                android:id="@+id/patientPhoto"
                android:layout_width="92dip"
                android:layout_height="92dip"
                android:layout_below="@+id/connectionBar"
                android:layout_marginRight="12dip"
                android:layout_marginTop="12dip"
                android:contentDescription="Patient photo"
                android:src="@drawable/ic_profile_default" />
         </RelativeLayout>
    </LinearLayout>
</RelativeLayout

很高兴发布其他相关的代码。有什么想法吗?

已解决

Android开发Google+页面上的用户Chris P替我解决了这个问题。在我的主Activity中的onClick方法中,我写了:

View photo = (View) adapter.getView(position, null, listView).findViewById(R.id.photo);

解决方法是将该行替换为:
View photo = (View) listView.getChildAt(position).findViewById(R.id.photo);

我猜调用适配器的getView方法会重新创建视图本身 - 我猜这个方法应该只在列表第一次创建时调用。

如果你将 @transition/change_image_transform 改成 @transition/change_image_transform,会有什么不同吗?如果你将 getWindow().getDecorView() 改成你正在使用的共享元素 ImageView 对象,会有什么不同吗? - Alex Lockwood
我不理解你提出的第一个建议更改。我还在问题中添加了change_image_transform代码。当我将getWindow().getDecorView()更改为patientPhotoView(在设置转换名称和照片后)时,没有任何区别。 - clay_to_n
抱歉,我打错字了。如果将@transition/change_image_transform更改为@android:transition/move,会有什么区别吗? - Alex Lockwood
哇,你做得很好!外向的Activity正在按照预期工作。似乎传入的Activity表现不佳。顺便说一下,android:windowContentTransitions不是正确的属性--你需要android:windowActivityTransitions。Material主题会自动将其设置为true,所以没问题。无论如何,你能发布详细Activity的布局吗? - George Mount
我刚刚创建了一个带有问题基本示例的git仓库,但我还没有解决:https://github.com/clay-to-n/shared-element-transition-glitch - clay_to_n
显示剩余4条评论
1个回答

3

尝试在评论中留下这个问题,但是太长了。我尝试了您的布局,并没有遇到任何问题。我甚至不需要调用postponeEnterTransition。这使得调试变得有点棘手。

转换的工作方式是捕获更改之前和更改之后的状态,然后对差异进行动画处理。在Activity转换中,“before”更改从调用Activity中共享元素的屏幕位置和大小中提取。然后将这些边界应用于当前视图层次结构。然后请求布局并捕获“after”状态。

在“before”状态被捕获和“after”状态被捕获之间存在一个敏感点。如果在此期间出现任何原因导致布局发生更改,以致在after状态中捕获的值不仅仅是“before”状态的重新布局,则会扰乱转换。还可能是在转换开始后立即对布局进行更改。

您是否使用延迟加载机制,或者您是否真的在onCreate调用中完全加载患者?您当时也设置了所有周围的值吗?我看到您有:

        <ImageView ...
            android:layout_below="@+id/connectionBar"
            ... />

所以当连接栏改变后,您可能会得到一个重新布局。

谢谢您的回复。我打算尝试删除代码的部分,以找出导致重新布局的原因(如果有的话)。一旦解决了这个问题,我会在这里详细说明我的发现,以帮助其他人。 - clay_to_n
我从一个空项目重新创建了我的环境,但是仍然出现故障。我肯定是漏掉了某些东西——也许是我的数组适配器有问题?如果问题是重新布局,我就不知道它会发生在哪里,因为没有其他操作。这是链接地址:https://github.com/clay-to-n/shared-element-transition-glitch - clay_to_n
解决了问题并将解决方案添加到问题中。感谢您的帮助。 - clay_to_n

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接