Android shared element transition: hero view gets drawn in front of other - android

Please look at this video showing a shared elements activity transition.
It's a transition from a list activity to a detail activity.
[Video link no longer works]
As you can see the imageview gets drawn in front of the tabs.
What I would expect is the tabs being drawn in font on the imageview and fading out throughout the transition (so that at the end of the animation they are gone).
The only thing that seems to work is setting windowSharedElementsUseOverlay to true,
but that has other ugly effects, so that seems not to be an option.
The most commonly suggested approach is to include the tabs in the transition itself, but the problem is that the tabs are not there in the detail activity so they cannot be shared.
Code:
I start the detail activity like this:
options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs);
ActivityCompat.startActivity(activity, subActivity, options.toBundle());

I believe what you may need is to exclude, rather than include, the tab layout from the transition animation.
So in the onCreate of your list activity, include:
Transition fade = new Fade();
fade.excludeTarget(R.id.tab, true); // use appropriate id for you tab
getWindow().setExitTransition(fade);
getWindow().setEnterTransition(fade); // try getWindow().setReenterTransition(fade); instead
Definitely have a look at Alex Lockwood's answer to How do I prevent the status bar and navigation bar from animating during an activity scene animation transition? where he gives a greater and more in-depth but digestible explanation on the topic. You may also want to consider adding/implementing the solution in that post.

You should try this:
On the exiting activity, call getWindow().setExitTransition(null);
On the entering activity, call getWindow().setEnterTransition(null);
It will prevent the fade out of the exiting activity and the fade in of the entering activity, which removes the apparent blinking effect and make transition smooth.

My calling activity has a both a tablayout and a toolbar within and each time I did the transition, the image would appear on top of both tablayout and toolbar, making the transition look untidy.
I fixed the problem quite elegantly by just adding a "dummy" tablayout and a "dummy" toolbar in my called activity. The "dummy" elements are not visible so it doesn't impact the layout of my called activity but the transition effect will work properly if you add them in.
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar2"
android:transitionName="toolbar"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="60dp"
android:visibility="gone"
android:transitionName="tab"
></android.support.design.widget.TabLayout>
I then added the tablayout and the toolbar as a pair in my transition:
Pair<View, String> p4 = Pair.create(getActivity().findViewById(R.id.sliding_tabs), "tab");
Pair<View, String> p5 = Pair.create(getActivity().findViewById(R.id.toolbar), "toolbar");
Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), p1, p2, p3, p4, p5).toBundle();

Related

Shared element transition from circular ImageView to rectangular ImageView

I have two Activities: Activity A and Activity B. Activity A contains a view hierarchy with a circular ImageView in the center that looks like the following:
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/image_profile"
android:layout_width="105dp"
android:layout_height="105dp"
android:layout_weight="2"
android:transitionName="#string/transition_profile_image"/>
Activity B is a basic FrameLayout that contains a single, rectangular ImageView with the same transitonName. The goal: when clicking on the CircleImageView in Activity A, I want to transition into an expanded, fullscreen view of the image in Activity B. Going back to Activity A should reverse this transition.
I can get a transition to work using the following, basic scene transition:
profileImageView.setOnClickListener(view -> {
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this,
profileImageView, getString(R.string.transition_profile_image));
startActivity(ActivityB.getStartIntent(this, imageUrl),
options.toBundle());
});
Using this code, Activity B's rectangular ImageView overlays on top of the CircleImageView and then grows to fill the screen. It looks like the circle blinks into a rectangle. The transition of the image should instead perform some sort of transform animation from a circle to a rectangle. Anyone familiar with how to achieve this?

Android - Different transitions with multiple shared elements

I'm working with Android Lollipop Transitions, and I've sumbled upon the following problem:
I have CardView with an ImageView and, on top of it, a TextView.
When I click on the card, a new Activity is launched, and it contains both the ImageView and the TextView in different positions.
If I don't include the TextView in the Transition as a shared element, it suddenly dissapears [goes behind] the ImageView, which doesn't look, well, great.
If I include it, it doesn't scale the text nicely and suddenly changes to the final size (I am aware of this solution already, but the problem is I want to keep also the default ImageView Transition, which is a combination of a ChangeBounds Transition, a ChangeImageTransform, ... among others).
So, anybody knows how to have different transitions being thrown for different shared views when launching the new Activity?
Cheers
The way you pointed to it in your answer can be applied for this purpose but it's actually for accepting only suitable View types by a special transition (such as only ImageViews for ChangeImageTransform).
You can use addTarget(Class targetType) instead:
final Transition transition = new TransitionSet()
.addTransition(new ChangeTransform()).addTarget(TextView.class) // Only for TextViews
.addTransition(new ChangeImageTransform()).addTarget(ImageView.class) // Only for ImageViews
.addTransition(new ChangeBounds()); // For both
setSharedElementEnterTransition(transition);
This way is more simple and more logical. Also, it can be used for some other filterings (by transitionName and etc). See addTarget() overloaded types.
OK,
This is achievable extending the Transition class. Since I wanted to animate differently an ImageView and a TextView, I just wrote a TextTransform child class ofTransform, analogous to the ChangeImageTransform one which is part of the Android API 21+. The key was overriding this method (shown the case for ChangeImageTransform which looks for ImageView objects):
#Override
private void captureValues(TransitionValues transitionValues) {
View view = transitionValues.view;
if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
return;
}
(...)
}
Then you apply all transforms to the new scene, and relevant transform will be attached to their corresponding views:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<fade android:fadingMode="fade_out" />
<changeBounds />
<changeImageTransform />
<com.mypackage.transforms.TextTransform />
<fade android:fadingMode="fade_in" />
</transitionSet>
And then you set this Transition on the OnCreate method of the new Activity using setSharedElementEnterTransition(inflatedTransitionSet);

CardView animation: raise and expand on click?

I'm currently in the process of creating my first android app, and was wondering what the method would be to set a cardview to raise up and then expand into a larger rectangle, revealing a new fragment?
edit: (the new fragment would fill up the center third of the screen, no matter where the original card was located)
Authentic Motion
Tangible surfaces don’t just appear out of nowhere like a jump-cut in
a movie; they move into place helping to focus attention, establish
spatial relationships and maintain continuity. Materials respond to
touch to confirm your interaction and all changes radiate outward from
your touch point. All motion is meaningful and intimate, aiding the
user’s comprehension.
Activity + Fragment Transitions
By declaring ‘shared elements’ that are common across two screens you
can create a smooth transition between the two states.
album_grid.xml
…
<ImageView
…
android:transitionName="#string/transition_album_cover" />
album_details.xml
…
<ImageView
…
android:transitionName="#string/transition_album_cover" />
AlbumActivity.java
Intent intent = new Intent();
String transitionName = getString(R.string.transition_album_cover);
…
ActivityOptionsCompat options =
ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
albumCoverImageView, // The view which starts the transition
transitionName // The transitionName of the view we’re transitioning to
);
ActivityCompat.startActivity(activity, intent, options.toBundle());
Here we define the same transitionName in two screens. When starting
the new Activity and this transition is animated automatically. In
addition to shared elements, you can now also choreograph entering and
exiting elements.
Source: Implementing Material Design

Set NavigationDrawer's ListView margin programmatically

I am currently struggling to design my application the way i want to.
In my app i am using a NavigationDrawer and different fragments. By clicking on an item in the NavigationDrawer i swap out the fragment that is currently active.
There is one main fragment which shows a map and doesn't show a toolbar. When I switch to another fragment I want to show my toolbar and let the user interact with it.
Now when I show the toolbar I have to set the top margin of the NavigationDrawer to the size of the toolbar so it doesn't get overlapped.
When I am showing the toolbar I set the margin of the NavigationDrawer's listview like this:
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mDrawerListView.getLayoutParams();
params.setMargins(0, drawerMarginTop, 0, 0);
mDrawerListView.setLayoutParams(params);
mDrawerListView.requestLayout();
The outcome is like the complete opposite of what i expect. It seems like the margin is applied to the bottom of the view.
Screenshot:
Another thing that annoys me is that the toggle-arrow of the toolbar is not centered correctly. It's a little bit higher than it should be, so it overlaps the system bar in the top and doesn't fill the whole size of the toolbar. I tried to make this clear in the following picture:
If you need any xml or code just let me know and I will edit my question.
Thank you in advance!
EDIT 1+2:
My toolbar style:
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar_navigation"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/my_color"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
EDIT 3:
The Problem with the toggle-arrow not being centered is fixed now. Thanks to Alchete.
Unfortunately the NavigationDrawer is still buggy. I found out that if I open and close the NavigationDrawer many times it somehow changes its layout at one time and the margin is set correctly... Is there any way to force this top happen immediately?
After many times of opening and closing the drawer it looks like this: (Exactly what I want it to look like)
There must be a way to force this immediately, right?
Your alignment issue is most likely due to your toolbar height. You should be setting the toolbar height as follows:
<android.support.v7.widget.Toolbar
android:layout_height="?attr/actionBarSize">
Here's the same issue for reference: android.support.v7.widget.Toolbar icon alignment issue
I would also be using Google's IOSched app for reference on how to set these items up properly. You can find all the code on Github.
Here are their layout files. Scroll down to see their toolbar/navdrawer layouts: https://github.com/google/iosched/tree/dfaf8b83ad1b3e7c8d1af0b08d59caf4223e0b95/android/src/main/res/layout
And, also note that Google's reference design is to OVERLAP the toolbar with the navdrawer -- which is not how you have it. And, the right margin should be equivalent to the toolbar height.
See here: http://www.google.com/design/spec/patterns/navigation-drawer.html
I'm not 100% about this but it looks like you're setting the ViewGroup layout params to be of type MarginLayoutParams. Instead, set the margin on a 'normal' root ViewGroup type e.g. RelativeLayout and pass that to the View:
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT);
params.setMargins(0, drawerMarginTop, 0, 0);
mDrawerListView.setLayoutParams(params);
mDrawerListView.requestLayout();
You may want to change MATCH_PARENT to WRAP_CONTENT depending on your xml.

is it possible to do transition animations when changing views in the same activity?

Suppose I have 2 XML files and my activity will setContentView the appropriate one based on some button press from the user. Is it possible to change the transition animation for the changing of content view?
So far I see super.overridePendingTransition() which is suitable for starting new activities, however my example does not start a new activity, it just changes the layout in the current one.
Mathias Lin has explained it very well.
You can always use default stock animations supplied by Android framework.
Heres an example code:
boolean isFirstXml=evaluatingConditionFunction();
LayoutInflater inflator=getLayoutInflater();
View view=inflator.inflate(isFirstXml?R.layout.myfirstxml:R.layout.myseconxml, null, false);
view.startAnimation(AnimationUtils.loadAnimation(this, android.R.anim.slide_out_right));
setContentView(view);
Call this from any of your activity which holds your Parent View.
For custom animations you can visit developer docs. Heres the documentation link.
Yes, you can apply an animation on almost any view you like. Just via view.startAnimation(animation);
Take the outer viewgroup of your respective layout (content view) and apply the animation to it. Depending what kind of animation you want to do, it might make sense to inflate/load both layouts but hide one of them and then swap. Please specify what kind of transition you have in mind.
For example: if you do an alpha transition, you would run the alphaAnimation on the current layout, when when the animation ends (AnimationListener), you set the content view to the new layout, and fade the content back in, via another alphaAnimation.
A better solution is using ViewFlipper: it is a FrameLayout, that can do animations when changing the views.
<ViewFlipper
android:id="#+id/[your_id_here]"
android:inAnimation="..."
android:outAnimation="..."
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
<!--Your first layout -->
</RelativeLayout>
<RelativeLayout
<!--Your second layout -->
</RelativeLayout>
</ViewFlipper>
Then, switch the views with setDisplayedChild(int) or showNext() or showPrevious. If you want to have different animation for left and right movement, you have to set inAnimation and outAnimation in the code before transition.
More complete example is here.

Categories

Resources