Android Lollipop 中的共享元素转换故障
Shared Element Transition Glitch in Android Lollipop
我正在尝试在 ListView 的主细节情况下执行 Android 5.0 共享元素转换。我正在发送图片。
我 运行 在进入过渡时遇到了一个小故障,在主 activity 过渡出来时图像根本没有移动。加载细节 activity 后,图像从屏幕的左上角开始,并动画到它的最终位置。 return 过渡效果完美 - 图像从它的细节位置动画到母版中的正确位置。
这是它的样子:
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());
(我还将导航栏指定为共享以避免它淡出,效果很好)
我不明白为什么 return 过渡可以正常工作,但进入过渡却不行。我已经看到其他问题的解决方案是延迟进入过渡,我已经尝试这样做但问题仍然存在。这是我在详细信息 activity:
中添加到 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;
}
});
在我的详细信息 Activity 中,没有 transitionName 元素,但我在代码中设置了它。传入activity(Detail)的相关部分是:
<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
乐于post其他相关代码。有什么想法吗?
已解决
Android 开发 Google+ 页面中的用户 Chris P 为我解决了这个问题。在我的 Master 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 方法是重新创建视图本身 - 我猜该方法应该只在列表首次创建时调用。
试图在评论中留下它,但它太长了。我试过你的布局,没有任何问题。我什至不需要 postponeEnterTransition 调用。这使得调试起来有点棘手。
Transitions 的工作方式是捕捉变化前和变化后的状态,然后对差异进行动画处理。在 Activity Transitions 中,"before" 更改是从调用 Activity 中共享元素的屏幕位置和大小中提取的。然后它将这些边界应用于当前视图层次结构。然后它请求布局并捕获 "after" 状态。
在捕获"before"状态和捕获"after"状态之间存在一个敏感点。如果其中的布局因任何原因发生更改,使得在后状态中捕获的值不仅仅是 "before" 状态的重新布局,则转换将被取消。也可以在过渡开始后立即更改布局。
您是在使用延迟加载机制,还是真的在 onCreate 调用中完全加载了 Patient?您当时是否也设置了所有周围的值?我看到你有:
<ImageView ...
android:layout_below="@+id/connectionBar"
... />
所以您可能会在 connectionBar 更改后重新布局。
我正在尝试在 ListView 的主细节情况下执行 Android 5.0 共享元素转换。我正在发送图片。
我 运行 在进入过渡时遇到了一个小故障,在主 activity 过渡出来时图像根本没有移动。加载细节 activity 后,图像从屏幕的左上角开始,并动画到它的最终位置。 return 过渡效果完美 - 图像从它的细节位置动画到母版中的正确位置。
这是它的样子: 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());
(我还将导航栏指定为共享以避免它淡出,效果很好)
我不明白为什么 return 过渡可以正常工作,但进入过渡却不行。我已经看到其他问题的解决方案是延迟进入过渡,我已经尝试这样做但问题仍然存在。这是我在详细信息 activity:
中添加到 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;
}
});
在我的详细信息 Activity 中,没有 transitionName 元素,但我在代码中设置了它。传入activity(Detail)的相关部分是:
<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
乐于post其他相关代码。有什么想法吗?
已解决
Android 开发 Google+ 页面中的用户 Chris P 为我解决了这个问题。在我的 Master 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 方法是重新创建视图本身 - 我猜该方法应该只在列表首次创建时调用。
试图在评论中留下它,但它太长了。我试过你的布局,没有任何问题。我什至不需要 postponeEnterTransition 调用。这使得调试起来有点棘手。
Transitions 的工作方式是捕捉变化前和变化后的状态,然后对差异进行动画处理。在 Activity Transitions 中,"before" 更改是从调用 Activity 中共享元素的屏幕位置和大小中提取的。然后它将这些边界应用于当前视图层次结构。然后它请求布局并捕获 "after" 状态。
在捕获"before"状态和捕获"after"状态之间存在一个敏感点。如果其中的布局因任何原因发生更改,使得在后状态中捕获的值不仅仅是 "before" 状态的重新布局,则转换将被取消。也可以在过渡开始后立即更改布局。
您是在使用延迟加载机制,还是真的在 onCreate 调用中完全加载了 Patient?您当时是否也设置了所有周围的值?我看到你有:
<ImageView ...
android:layout_below="@+id/connectionBar"
... />
所以您可能会在 connectionBar 更改后重新布局。