I want to add new fragment transition when BottomNavigationView change its current fragment.
I want to enter the next fragment from left to right.
I don't see this for Jetpack BottomNavigationView to add custom fragment transition animations because there are no actions available for BottomNavigationView.
As per this issue:
NavigationUI follows the material design guidelines, which specifically calls [see the 'Transitions' section] for a cross fade animation between BottomNavigationView items.
Setting your own listener is indeed the correct approach if you want to deviate from the guidelines and what NavigationUI provides.
Therefore you'll want to look at the NavigationUI source code for its onNavDestinationSelected() method and make your own version that passes in what custom animations you want, calling it from your own OnNavigationItemSelectedListener.
I have posted this question and I have get a great help from the answer of ianhanniballake ( The answer I accepted) to fulfil my expectation. For people who refer this Quection and answer in future I'm further add some code following to further reference and understanding.
Feel free to comment if you need any help after you go through the code.
Following is my Source code in MainActivity.java" OnCreate() Method
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final BottomNavigationView bottomNavigationView=findViewById(R.id.bottomNavigationView);
final NavController navController= Navigation.findNavController(this,R.id.nav_host_fragment);
selectedItem=R.id.firstFragment1;
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.firstFragment1:
if(selectedItem != R.id.firstFragment1){
selectedItem = R.id.firstFragment1;
navController.popBackStack();
}
break;
case R.id.secondFragment1:
if(selectedItem != R.id.secondFragment1) {
selectedItem= R.id.secondFragment1;
Log.d("palvision.dev", "action to first fragment");
navController.navigate(R.id.action_firstFragment_to_secondFragment2);
}
break;
}
return true;
}
});
}
Following is my source code in nav_graph.xml which is navigation graph.
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
app:startDestination="#id/firstFragment">
<fragment
android:id="#+id/firstFragment"
android:name="com.dehan.myapplicationnavtest.FirstFragment"
tools:layout="#layout/fragment_first" >
<action
android:id="#+id/action_firstFragment_to_secondFragment2"
app:destination="#+id/secondFragment"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_to_left"
app:popEnterAnim="#anim/enter_from_left"
app:popExitAnim="#anim/exit_to_right" />
</fragment>
<fragment
android:id="#+id/secondFragment"
android:name="com.dehan.myapplicationnavtest.SecondFragment"
tools:layout="#layout/fragment_second" />
Following is the code for FirstFragment.java
public class FirstFragment extends Fragment {
public FirstFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Toolbar toolbar=view.findViewById(R.id.toolbar);
NavController navController= Navigation.findNavController(getActivity(),R.id.nav_host_fragment);
AppBarConfiguration appBarConfiguration =new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupWithNavController(toolbar,navController,appBarConfiguration);
}
}
following is the code for SecondFragment.java
public class SecondFragment extends Fragment {
public SecondFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false);
}
}
following is the code for enter_from_right.xml which is located in anim folder.
all the other animation files are also placed in anim folder.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400" />
following is the code for exit_to_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="-100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400"/>
enter_from_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400"/>
exit_to_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400" />
Good day. I faced problem, when dialog animation stops working when dialog called from fragment.
First I tried DialogFragment with custom style:
public static class QRDialog extends DialogFragment {
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
dialog.setCancelable(true);
ImageView im = new ImageView(getActivity());
im.setImageResource(R.drawable.qr_code_placeholder);
im.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
im.setClickable(true);
im.setScaleType(ImageView.ScaleType.FIT_XY);
im.setAdjustViewBounds(true);
im.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dismiss();
}
});
dialog.setView(im);
return dialog.create();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getDialog().getWindow().getAttributes().windowAnimations = R.style.XSlideAnimation;
}
}
Those XSlideAnimation is simple animation style:
<style name="XSlideAnimation" >
<item name="android:windowEnterAnimation">#android:anim/slide_in_left</item>
<item name="android:windowExitAnimation">#android:anim/slide_out_right</item>
</style>
It didn't work.
Then I tried set custom animation for fragment transaction:
FragmentTransaction tr = getFragmentManager().beginTransaction();
tr.setCustomAnimations(R.anim.in, R.anim.out);
new QRDialog().show(tr, "dialog");
in.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="50%"
android:toYDelta="0"
android:duration="500"
/>
</set>
out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="50%"
android:duration="500"
/>
</set>
No success either.
Then I tried just to get new Dialog instance and set animation for it:
final Dialog dialog = new Dialog(getActivity());
ImageView im = new ImageView(getActivity());
im.setImageResource(R.drawable.qr_code_placeholder);
im.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
im.setClickable(true);
im.setScaleType(ImageView.ScaleType.FIT_XY);
im.setAdjustViewBounds(true);
dialog.setContentView(im);
im.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.getWindow().getAttributes().windowAnimations = R.style.XSlideAnimation;
dialog.setCancelable(true);
dialog.show();
And animation works perfect BUT only if I call dialog from activity. When I put exact same code in fragment, animation stop working. First approach works from activity too, but not in fragment.
How can I get working animation when creating dialog in fragment?
UPD 16-102017:
I see no answer or comments here so far, but somebody upvoted this issue once. So, just for update - in my case it's just started to work. I did literally nothing with code. Animation just appeared and staid couple days later during tests.
I set my own animations for dialog window enter but it not fires when I call .show() first time after activity created.
At the gif below you can see that first time animation is drop down and only second time it is slide up that I set.
DialogFragment:
public class DateChooseDialog extends DialogFragment {
public DateChooseDialog() {
// Required empty public constructor
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NORMAL, R.style.DialogFullScreen);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.dialog_date_choose, container);
}
#Override
public void onResume() {
WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.gravity = Gravity.BOTTOM;
params.windowAnimations = R.style.DialogSlideAnimation;
getDialog().getWindow().setAttributes(params);
super.onResume();
}
}
Animations:
<style name="DialogSlideAnimation" parent="Theme.AppCompat.Dialog">
<item name="android:windowEnterAnimation">#anim/slide_up</item>
<item name="android:windowExitAnimation">#anim/slide_down</item>
</style>
<!-- slide_up -->
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="0"
android:interpolator="#android:anim/accelerate_interpolator"
android:toYDelta="100%"
/>
<!-- slide_down -->
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="0"
android:interpolator="#android:anim/accelerate_interpolator"
android:toYDelta="100%"
/>
This is how I show dialog:
DateChooseDialog dateChooseDialog = new DateChooseDialog();
dateChooseDialog.show(getFragmentManager(), "date_choose");
Problem is all about keyboard. It resizes your whole window and while keyboard disappears with slide down animation, your whole window resizes it self through sliding down.
Please add this lines to your manifest.
<activity (Your current activity)
...
android:windowSoftInputMode="adjustPan">
</activity>
With these lines, your keyboard will overlay window and it wont resize.
Next challange is detect keyboard is opened to delay animation if neccessary.
Good luck there
I'm showing a DialogFragment when the user taps on a row in a ListView. I'd like to animate the showing of the dialog so that it grows from the center of the row. A similar effect can be seen when opening a folder from the launcher.
One idea that I've had is a combination of TranslateAnimation and ScaleAnimation. Is there another way?
Being DialogFragment a wrapper for the Dialog class, you should set a theme to your base Dialog to get the animation you want:
public class CustomDialogFragment extends DialogFragment implements OnEditorActionListener
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
// Set a theme on the dialog builder constructor!
AlertDialog.Builder builder =
new AlertDialog.Builder( getActivity(), R.style.MyCustomTheme );
builder
.setTitle( "Your title" )
.setMessage( "Your message" )
.setPositiveButton( "OK" , new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
});
return builder.create();
}
}
Then you just need to define the theme that will include your desired animation. In styles.xml add your custom theme:
<style name="MyCustomTheme" parent="#android:style/Theme.Panel">
<item name="android:windowAnimationStyle">#style/MyAnimation.Window</item>
</style>
<style name="MyAnimation.Window" parent="#android:style/Animation.Activity">
<item name="android:windowEnterAnimation">#anim/anim_in</item>
<item name="android:windowExitAnimation">#anim/anim_out</item>
</style>
Now add the animation files in the res/anim folder:
( the android:pivotY is the key )
anim_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:interpolator="#android:anim/linear_interpolator"
android:fromXScale="0.0"
android:toXScale="1.0"
android:fromYScale="0.0"
android:toYScale="1.0"
android:fillAfter="false"
android:startOffset="200"
android:duration="200"
android:pivotX = "50%"
android:pivotY = "-90%"
/>
<translate
android:fromYDelta="50%"
android:toYDelta="0"
android:startOffset="200"
android:duration="200"
/>
</set>
anim_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:interpolator="#android:anim/linear_interpolator"
android:fromXScale="1.0"
android:toXScale="0.0"
android:fromYScale="1.0"
android:toYScale="0.0"
android:fillAfter="false"
android:duration="200"
android:pivotX = "50%"
android:pivotY = "-90%"
/>
<translate
android:fromYDelta="0"
android:toYDelta="50%"
android:duration="200"
/>
</set>
Finally, the tricky thing here is to get your animation grow from the center of each row. I suppose the row is filling the screen horizontally so, on one hand the android:pivotX value will be static. On the other hand, you can't modify the android:pivotY value programmatically.
What I suggest is, you define several animations each of which having a different percentage value on the android:pivotY attribute (and several themes referencing those animations). Then, when the user taps the row, calculate the Y position in percentage of the row on the screen. Knowing the position in percentage, assign a theme to your dialog that has the appropriate android:pivotY value.
It is not a perfect solution but could do the trick for you. If you don't like the result, then I would suggest forgetting the DialogFragment and animating a simple View growing from the exact center of the row.
Check it out this code, it works for me
// Slide up animation
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromYDelta="100%"
android:interpolator="#android:anim/accelerate_interpolator"
android:toXDelta="0" />
</set>
// Slide dowm animation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromYDelta="0%p"
android:interpolator="#android:anim/accelerate_interpolator"
android:toYDelta="100%p" />
</set>
// Style
<style name="DialogAnimation">
<item name="android:windowEnterAnimation">#anim/slide_up</item>
<item name="android:windowExitAnimation">#anim/slide_down</item>
</style>
// Inside Dialog Fragment
#Override
public void onActivityCreated(Bundle arg0) {
super.onActivityCreated(arg0);
getDialog().getWindow()
.getAttributes().windowAnimations = R.style.DialogAnimation;
}
DialogFragment has a public getTheme() method that you can over ride for this exact reason. This solution uses less lines of code:
public class MyCustomDialogFragment extends DialogFragment{
...
#Override
public int getTheme() {
return R.style.MyThemeWithCustomAnimation;
}
}
To get a full-screen dialog with animation, write the following ...
Styles:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="actionModeBackground">?attr/colorPrimary</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.NoActionBar.FullScreenDialog">
<item name="android:windowAnimationStyle">#style/Animation.WindowSlideUpDown</item>
</style>
<style name="Animation.WindowSlideUpDown" parent="#android:style/Animation.Activity">
<item name="android:windowEnterAnimation">#anim/slide_up</item>
<item name="android:windowExitAnimation">#anim/slide_down</item>
</style>
res/anim/slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="#android:interpolator/accelerate_quad">
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="100%"
android:toYDelta="0%"/>
</set>
res/anim/slide_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="#android:interpolator/accelerate_quad">
<translate
android:duration="#android:integer/config_shortAnimTime"
android:fromYDelta="0%"
android:toYDelta="100%"/>
</set>
Java code:
public class MyDialog extends DialogFragment {
#Override
public int getTheme() {
return R.style.AppTheme_NoActionBar_FullScreenDialog;
}
}
private void showDialog() {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment previous = getSupportFragmentManager().findFragmentByTag(MyDialog.class.getName());
if (previous != null) {
fragmentTransaction.remove(previous);
}
fragmentTransaction.addToBackStack(null);
MyDialog dialog = new MyDialog();
dialog.show(fragmentTransaction, MyDialog.class.getName());
}
In DialogFragment, custom animation is called onCreateDialog. 'DialogAnimation' is custom animation style in previous answer.
public Dialog onCreateDialog(Bundle savedInstanceState)
{
final Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation;
return dialog;
}
Use decor view inside onStart in your dialog fragment
#Override
public void onStart() {
super.onStart();
final View decorView = getDialog()
.getWindow()
.getDecorView();
decorView.animate().translationY(-100)
.setStartDelay(300)
.setDuration(300)
.start();
}
If you want to work over APIs you have to do inside your DialogFragemnt->onStart and not inside onCreateDialog
#Override
public void onStart()
{
if (getDialog() == null)
{
return;
}
getDialog().getWindow().setWindowAnimations(
R.style.DlgAnimation);
super.onStart();
}
Note: This is just a complement to other answers.
No matter which the solutions you pick you might have the same problem as me.
I need to UNINSTALL the game from my development device before installing the new version for the animation changes to take effect.
I am not sure why but I guess it has to do with the optimized deployment on Android studio not recognizing the changes.
Have you looked at Android Developers Training on Zooming a View? Might be a good starting point.
You probably want to create a custom class extending DialogFragment to get this working.
Also, take a look at Jake Whartons NineOldAndroids for Honeycomb Animation API compatibility all the way back to API Level 1.
Add this code on values anim
<scale
android:duration="#android:integer/config_longAnimTime"
android:fromXScale="0.2"
android:fromYScale="0.2"
android:toXScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"/>
<alpha
android:fromAlpha="0.1"
android:toAlpha="1.0"
android:duration="#android:integer/config_longAnimTime"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"/>
call on styles.xml
<style name="DialogScale">
<item name="android:windowEnterAnimation">#anim/scale_in</item>
<item name="android:windowExitAnimation">#anim/scale_out</item>
</style>
On java code: set Onclick
public void onClick(View v) {
fab_onclick(R.style.DialogScale, "Scale" ,(Activity) context,getWindow().getDecorView().getRootView());
// Dialogs.fab_onclick(R.style.DialogScale, "Scale");
}
setup on method:
alertDialog.getWindow().getAttributes().windowAnimations = type;
How to start an activity after the animation has ended.
I have added android:oneshot="true" in the xml but how to start a new activity
after this animation has stopped.I have attached the entire code below.
Please let me know how to start new activity.
package com.appsolut.example.animation;
import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.widget.ImageView;
public class Animation extends Activity {
ImageView animation;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
public void onWindowFocusChanged (boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
AnimationDrawable frameAnimation =
(AnimationDrawable) animation.getBackground();
if(hasFocus) {
frameAnimation.start();
} else {
frameAnimation.stop();
}
}
public void onStart() {
{
super.onStart();
animation = (ImageView)findViewById(R.id.imageAnimation);
animation.setBackgroundResource(R.drawable.animation);
}
}
}
animation.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true" >
<item android:drawable="#drawable/img00000" android:duration="500" />
<item android:drawable="#drawable/img00001" android:duration="500" />
<item android:drawable="#drawable/img00002" android:duration="500" />
<item android:drawable="#drawable/img00003" android:duration="500" />
<item android:drawable="#drawable/img00004" android:duration="500" />
<item android:drawable="#drawable/img00005" android:duration="500" />
<item android:drawable="#drawable/img00006" android:duration="500" />
<item android:drawable="#drawable/img00007" android:duration="500" />
<item android:drawable="#drawable/img00008" android:duration="500" />
<item android:drawable="#drawable/img00009" android:duration="500" />
<item android:drawable="#drawable/img00010" android:duration="500" />
</animation-list>
Use and AnimationListener on your animation to do whatever you want on its onAnimationEnd() method.
After your animation call create a new Intent (From the Intent class).
Intent myIntent = new Intent(context, newActivity.class);
startActivity(myIntent);
Context can be getContext() or getBaseContext()
This should work. Also dont't forget to declare your new activity in the AndroidManifest or the application will crash!
Hope this helped.
Use the method hasEnded() to check if ended and then if it has then call your intent to open up the new activity.
It should work
Best Regards