Android Lollipop中的共享元素转换故障

时间:2015-03-04 23:52:16

标签: android android-animation android-5.0-lollipop activity-transition shared-element-transition

我试图在ListView的Master Detail情况下进行Android 5.0共享元素转换。我发送了一张图片。

我在输入过渡时遇到了一个小故障,当主动作过渡时,Image根本不会移动。加载Detail活动后,Image将从屏幕的左上方开始,并将其设置为最终位置。返回过渡完美 - 图像从它的细节位置动画到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>

根据他们在列表中的位置,我给了两个视图相同的唯一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;
        }
    });

在我的Detail Activity中,没有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 Development Google+页面中的用户Chris P为我解决了这个问题。在onClick方法的主活动中,我有:

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

解决方案是用以下内容替换该行:

View photo = (View) listView.getChildAt(position).findViewById(R.id.photo);

我想调用适配器的getView方法是重新创建视图本身 - 我猜测只有在首次创建列表时才能调用该方法。

1 个答案:

答案 0 :(得分:2)

试图将其留在评论中,但时间太长了。我尝试了你的布局,没有任何问题。我甚至不需要postponeEnterTransition调用。这使得调试变得有点棘手。

Transitions的工作方式是在更改之前捕获事物的状态,然后在更改之后捕获,然后设置差异的动画。在“活动转换”中,“之前”更改将从调用活动中的共享元素的屏幕位置和大小中拉出。然后,它将那些边界应用于当前视图层次结构。然后它请求布局并捕获“后”状态。

捕获“之前”状态和捕获“之后”状态之间存在敏感点。如果布局因任何原因而改变,使得在after状态下捕获值不仅仅是“before”状态的重新布局,则转换将被抛弃。也可以在转换开始后立即更改布局。

您是使用延迟加载机制还是在onCreate呼叫中真正完全加载患者?您是否同时设置了所有周围的值?我知道你有:

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

因此,您可能会在connectionBar更改后获得重新布局。