Background
I've been trying to get parent-to-child navigational transition implemented (specifically for recyclerview items -> detail fragment). I'm currently using sharedElementTransitions with the item container and detail container as the sharedElement along with ChangeTransform and ChangeBounds transitions. The detail fragment's enter transition is mostly working. However, I am having problems with the recyclerview item's exit transition. What I want to do is have the recyclerview dim/turn darker while the detail view expands to full screen. When I try to use a custom transition, the exit transition never animates. Instead, the detail view is immediately shown and its enter transition animates.
As you can see, the recyclerview fragment is hidden before the exit animation is played. I need the recyclerview to be shown with the dimming effect while the detail view is expanding.
Problem
How do I get the dim custom exit transition to play while the detail enter transition is also playing?
What I've tried
- setDelay - setDelay on detail enter transition. The transition is delayed but the detail fragment immediately shown (the recyclerview is blocked off by the new fragment).
- setDuration - setDuration on recyclerview exit transition. Detail fragment still blocks the recyclerview.
ItemViewModel.java
public void displayArticle(View view) {
MainActivity mainActivity = (MainActivity) view.getContext();
ArticleFragment detailsFragment = ArticleFragment.newInstance(article.getValue(), transitionName.getValue());
ArticleListingFragment itemFragment = (ArticleListingFragment) mainActivity.getSupportFragmentManager().findFragmentByTag(ArticleListingFragment.TAG);
doOpenArticleTransition(detailsFragment, itemFragment);
mainActivity.getSupportFragmentManager()
.beginTransaction()
.addSharedElement(view.findViewById(R.id.article_entry), transitionName.getValue())
.replace(R.id.main_content_container, detailsFragment)
.addToBackStack(null)
.commit();
}
private void doOpenArticleTransition(Fragment newFragment, Fragment oldFragment) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Transition openArticleTransition = TransitionInflater.from(oldFragment.getContext()).inflateTransition(R.transition.transition_article);
newFragment.setSharedElementEnterTransition(openArticleTransition);
newFragment.setEnterTransition(new Fade());
oldFragment.setExitTransition(new Dim(oldFragment.getContext()));
newFragment.setSharedElementReturnTransition(openArticleTransition);
}
}
Dim.java
public class Dim extends BaseTransition {
private static final String PROPNAME_BACKGROUND = "meridian:dim:background";
private final Context context;
public Dim(Context context) {
this.context = context;
}
public Dim(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
@Override
public void captureValues(TransitionValues transitionValues) {
Drawable background = transitionValues.view.getBackground();
transitionValues.values.put(PROPNAME_BACKGROUND, background);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
final View view;
if (startValues == null && endValues != null) {
view = endValues.view;
} else {
view = startValues.view;
}
int whiteBackground = ContextCompat.getColor(context, R.color.white);
int grayBackground = ContextCompat.getColor(context, R.color.gray700);
ValueAnimator animator = ValueAnimator.ofArgb(whiteBackground, grayBackground);
animator.setDuration(400);
animator.setStartDelay(300);
animator.addUpdateListener(animation -> view.setBackgroundColor((Integer)animation.getAnimatedValue()));
return animator;
}
}
transition_article.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
<changeBounds/>
<changeTransform/>
<transition
class="meridian.custom.anim.Elevate"/>
</transitionSet>
recyclerview_item.xml
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/article_entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/article_listing_section"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/two_line_list_entry_margin_lr"
android:layout_marginEnd="@dimen/two_line_list_entry_margin_lr"
android:layout_marginTop="@dimen/two_line_list_entry_margin_tb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/article_listing_section_title_barrier"
app:layout_constraintTop_toTopOf="parent"
style="@style/EntryOverline"
tools:text="@tools:sample/cities" />
<TextView
android:id="@+id/article_listing_date"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/two_line_list_entry_margin_tb"
android:layout_marginEnd="@dimen/two_line_list_entry_margin_lr"
android:textAlignment="viewEnd"
app:layout_constraintEnd_toStartOf="@+id/article_listing_date_image_barrier"
app:layout_constraintTop_toTopOf="parent"
style="@style/EntryOverlineGray"
tools:text="17h" />
<TextView
android:id="@+id/article_listing_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/two_line_list_entry_margin_tb"
android:layout_marginEnd="@dimen/two_line_list_entry_margin_lr"
android:layout_marginTop="@dimen/two_line_list_entry_first_line_second_line_spacing"
app:layout_constraintStart_toStartOf="@id/article_listing_section"
app:layout_constraintEnd_toStartOf="@id/article_listing_section_title_barrier"
app:layout_constraintTop_toBottomOf="@id/article_listing_section"
app:layout_constraintBottom_toBottomOf="parent"
android:minLines="3"
android:maxLines="3"
style="@style/EntrySubtitle1"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." />
<ImageView
android:id="@+id/article_listing_image"
android:layout_width="@dimen/two_line_list_entry_image_width"
android:layout_height="@dimen/two_line_list_entry_image_width"
android:layout_marginStart="@dimen/two_line_list_entry_margin_lr"
android:layout_marginEnd="@dimen/two_line_list_entry_margin_lr"
android:contentDescription="@string/content_description_image_article_entry"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/article_listing_date_image_barrier"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/item_article_entry_image_placeholder" />
<android.support.constraint.Barrier
android:id="@+id/article_listing_section_title_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="article_listing_date"/>
<android.support.constraint.Barrier
android:id="@+id/article_listing_date_image_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="article_listing_image"/>
</android.support.constraint.ConstraintLayout>
recyclerview_fragment.xml
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_article_sections"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator_layout_articles"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar" />
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_to_refresh_articles"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/articles"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:itemCount="2"
tools:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:listitem="@layout/item_article_entry" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view_article_sections"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:itemTextAppearance="@style/SectionSubtitle1"
app:menu="@menu/navigation_article_topic"
app:headerLayout="@layout/navigation_header_article_sections"/>
details_fragment.xml
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:elevation="24dp">
<include
layout="@layout/toolbar" />
<android.support.v4.widget.NestedScrollView
android:id="@+id/article_scroll_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.constraint.ConstraintLayout
android:id="@+id/article"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.article.display.ArticleFragment">
<HorizontalScrollView
android:id="@+id/article_description_scroll_container"
style="@style/HorizontalChipScroller"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:scrollbars="none"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<android.support.design.chip.ChipGroup
android:id="@+id/article_description_chip_group"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:contentDescription="@string/chip_group_description_tags"
app:singleLine="true"/>
</HorizontalScrollView>
<ImageView
android:id="@+id/article_image"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="16dp"
android:layout_marginEnd="@dimen/detail_general_margin_lr"
android:layout_marginStart="@dimen/detail_general_margin_lr"
android:layout_marginTop="@dimen/detail_general_margin_tb"
android:contentDescription="@string/content_description_image_article"
android:scaleType="centerCrop"
android:src="@drawable/item_article_entry_image_placeholder"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.45"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/article_description_scroll_container"
tools:layout_height="208dp"
tools:src="@color/colorImagePlaceholder" />
<TextView
android:id="@+id/article_section"
style="@style/DetailOverline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/detail_general_margin_lr"
android:layout_marginStart="@dimen/detail_general_margin_lr"
android:layout_marginTop="@dimen/detail_general_margin_tb"
app:layout_constraintEnd_toStartOf="@id/article_subsection"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/article_image"
tools:text="US"
tools:textColor="@color/gray900" />
<TextView
android:id="@+id/article_subsection"
style="@style/DetailOverline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/detail_general_margin_lr"
android:layout_marginTop="@dimen/detail_general_margin_tb"
app:layout_constraintStart_toEndOf="@id/article_section"
app:layout_constraintTop_toBottomOf="@id/article_image"
tools:text="Poverty"
tools:textColor="@color/gray900" />
<TextView
android:id="@+id/article_title"
style="@style/DetailH6Headline"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/detail_general_margin_tb"
android:layout_marginEnd="@dimen/detail_general_margin_lr"
android:layout_marginStart="@dimen/detail_general_margin_lr"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/article_section"
tools:text="Pope Accepts Wuerl's Resignation as Washington Archbishop, but Calls Him a Model Bishop"
tools:textColor="@color/gray900"
tools:textSize="20sp"
tools:textStyle="bold" />
<TextView
android:id="@+id/article_abstract"
style="@style/DetailBody2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/detail_general_margin_tb"
android:layout_marginEnd="@dimen/detail_general_margin_lr"
android:layout_marginStart="@dimen/detail_general_margin_lr"
android:layout_marginTop="@dimen/detail_general_margin_tb"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/article_title"
tools:text="Despite demands to oust Cardinal Wuerl over sexual abuse scandals, Pope Francis praised him as a model leader."
tools:textColor="@color/gray600" />
<TextView
android:id="@+id/article_author"
style="@style/DetailBody1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/detail_general_margin_tb"
android:layout_marginEnd="@dimen/detail_general_margin_lr"
android:layout_marginStart="@dimen/detail_general_margin_lr"
android:layout_marginTop="@dimen/detail_general_margin_tb"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/article_abstract"
tools:text="by Jason Horowitz, Elizabeth Dias and Laurie Goodstein"
tools:textColor="@color/gray900" />
<android.support.design.button.MaterialButton
android:id="@+id/article_full_content_button"
style="@style/OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/detail_general_margin_tb"
android:layout_marginEnd="@dimen/detail_general_margin_lr"
android:layout_marginStart="@dimen/outlined_button_padding_lr"
android:layout_marginTop="@dimen/detail_general_margin_tb"
android:text="@string/button_label_article_read_more"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/article_author" />
</android.support.constraint.ConstraintLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
DetailsFragment.java
@BindView(R.id.article)
ConstraintLayout container;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setupToolbar();
setTransitionName();
loadArticle();
}
private void setTransitionName() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
container.setTransitionName(this.getArguments().getString(ARG_TRANSITION));
}
}