Possibillity to add an animation on preferences in android like in Navigation Components
Asked Answered
F

1

6

Is there a possibility to add an animation to fragment changes in preferences like there is one in the navigation components? Android guide

So I want to perform something like here:

<fragment>
    <action
        android:id="@+id/action_a_to_b"
        app:destination="@id/b"
        app:enterAnim="@anim/slide_in_right"
        app:exitAnim="@anim/slide_out_right"
        app:popEnterAnim="@anim/slide_in_left"
        app:popExitAnim="@anim/slide_out_left" />

</fragment>
...

Update:

To make it clear: I want to use the Navigation Components combined with the Jetpack Prefrerences. The problem is, that jetpack handles the animations in the preferences automatically, but I want to have another one then the default fade in/out. So I just want to override it. i.e.: is there any way to add the animation in the xml like in the hirachy?

<PreferenceScreen
xmlns:app="http://schemas.android.com/apk/res-auto">

<PreferenceCategory
    app:key="help_category"
    app:title="Help">

    <Preference
        app:fragment="com.example.SyncFragment"
        app:key="feedback"
        app:summary="Report technical issues or suggest new features"
        app:title="Send feedback"/>

</PreferenceCategory>

Fizzy answered 23/12, 2019 at 20:27 Comment(3)
Yes it is possible but that can be done dynamically and both fragment should have some common view. Do you want dynamically ?Inadvertence
I just want to have the same animation for each fragment in this activity. So if I change the fragment it should inflate/and out with the specific animations. Kotlin example would be great :DFizzy
Does anyone else have a solution for this case?Fizzy
T
2

I assume because you are asking how to animate the fragment in and out, that you are not using the Navigation Framework as that gives it for free. So let's assume you are not using that and dig into how you would accomplish this.

First we need to have a method somewhere to handle swapping the fragment. Let's make an Extension class. I am using Kotlin and Androidx in all examples

FragmentExt.kt

import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import <YOUR PROJECT R FILE>

/*
 * Written by Sam Rosewall App Studio 35
*/
val Activity.activityManager: ActivityManager
    get() = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

fun FragmentActivity.swapFragment(
    fragContainerId: Int,
    newFragment: Fragment?,
    oldFragment: Fragment? = null,
    bundle: Bundle? = null,
    hideCurrentFrag: Boolean = false,
    addToBackStack:Boolean = false
) {
    if (newFragment == null || newFragment.isVisible) {
        loge("swapFragment called on already visible fragment")
        return
    }

    logv("swapFragment( ${newFragment.javaClass.simpleName} )")
    val currentFragBundle = newFragment.arguments
    if (currentFragBundle == null && bundle != null) {
        newFragment.arguments = bundle
        logv("current bundle is null, so setting new bundle passed in")
    } else if (bundle != null) {
        newFragment.arguments?.putAll(bundle)
        logv("current fragment bundle was not null, so add new bundle to it")
    }

    //Ensure no pending transactions are paused or incomplete
    val fragmentManager = supportFragmentManager
    fragmentManager.executePendingTransactions()
    val fragmentTransaction = fragmentManager.beginTransaction()

    //Make sure the requested fragment isn't already on the screen before adding it
    if (newFragment.isAdded) {
        logv("Fragment is already added")
        if (newFragment.isHidden) {
            logv("Fragment is hidden, so show it")
            fragmentTransaction.show(newFragment)
            newFragment.onResume() // since we were hiding it, we call onResume to simulate foreground on fragment

            oldFragment?.let {
                if(hideCurrentFrag) {
                    logv("hideCurrentFlag = true, hiding current fragment $it")
                    fragmentTransaction.hide(it)
                    it.onPause() // since we are hiding it, we call onPause to simulate background on fragment
                }else{
                    logv("hideCurrentFlag = false, removing current fragment $it")
                    fragmentTransaction.remove(it)
                }
            }
        }else{
            logv("Fragment is already visible")
        }
    }else if(oldFragment == null){
        if (addToBackStack) {
            fragmentTransaction.setCustomAnimations(R.anim.in_from_right_to_left, R.anim.out_to_left, R.anim.in_from_left_to_right, R.anim.out_to_right )
            fragmentTransaction.addToBackStack(null)
        }
        logv("oldFragment = null, so Replacing active contents of container with Fragment ${newFragment.javaClass.simpleName}")
        fragmentTransaction.replace(fragContainerId, newFragment)
    }else{
        logv("Fragment is not added, and there is existing fragment to remove, so adding to the screen ${newFragment.javaClass.simpleName}")
        fragmentTransaction.add(fragContainerId, newFragment)

        if(hideCurrentFrag) {
            logv("hideCurrentFlag = true, hiding current fragment $oldFragment")
            fragmentTransaction.hide(oldFragment)
            oldFragment.onPause() // since we are hiding it, we call onPause to simulate background on fragment
        }else{
            logv("hideCurrentFlag = false, removing current fragment $oldFragment")
            fragmentTransaction.setCustomAnimations(R.anim.in_from_right_to_left, R.anim.out_to_left, R.anim.in_from_left_to_right, R.anim.out_to_right )
            fragmentTransaction.remove(oldFragment)
        }
    }

    logv("committing swap fragment transaction")
    fragmentTransaction.commit()
}

fun FragmentActivity.removeFragment(@IdRes fragContainerId: Int) {
    val fragmentManager = supportFragmentManager
    fragmentManager.findFragmentById(fragContainerId)?.let {
        fragmentManager.executePendingTransactions()
        val transaction = fragmentManager.beginTransaction()
        transaction.remove(it)
        transaction.commit()
    }
}

Now of course you will need to add the animations that you are after to the anims directory.

res->anim->[add files here]

in_from_left_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:duration="500"
        android:fromXDelta="-100%"
        android:fromYDelta="0%"
        android:toXDelta="0%"
        android:toYDelta="0%" />
</set>

in_from_right_to_left.xml

    <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
    <translate
        android:duration="500"
        android:fromXDelta="100%"
        android:fromYDelta="0%"
        android:toXDelta="0%"
        android:toYDelta="0%" />
    </set>

out_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:duration="500"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:toXDelta="100%"
        android:toYDelta="0%" />
</set>

out_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:duration="500"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:toXDelta="-100%"
        android:toYDelta="0%" />
</set>

Now all that is left, is using it. You have many options such as passing in bundle arguments, deciding to hide/show instead of add/remove. (note* if you hide / show the animations may not work, but you don't lose a webview loaded or other downloaded views, so it's really up to your use case.

MainActivity.kt

   private fun changeToMyFragment() {
        if (myFragment == null) {
            myFragment = MyFragment()
        }
        swapFragment(R.id.placeholder_framelayout, myFragment)

    }

That's it, any time you want to change fragments, just use this method "swapFragment" and pass the fragment you want in. The other parameters are all optional for extra control if you need it.

I added in logs for added clarity for you to read and a remove fragment in case you just need it gone.

Think of this like an extension you just drop in any project and use as-is. You don't have to dig into it, this will handle what you are asking for.

Happy Coding!

Tennietenniel answered 15/5, 2020 at 18:16 Comment(4)
First: thank you for the detailed answer; Second: I use the Navigation Fragment but when I use android jetpack preferences then I don't know where to change the default animation or how I could add a Navigation animation to the fragment changes. Third: As Far as I see, I can't implement it with an extansion func because the jetpack frameworks handles everything automatically. Maybe I have to override it anywhere? Any further ideas?Fizzy
Sure, there are other ways to do this, but maybe try setting the default animation of the NavOptions and see if that does the trick for you. protected NavOptions getNavOptions() { NavOptions navOptions = NavOptions.Builder() .setEnterAnim(R.anim.default_enter_anim) .setExitAnim(R.anim.default_exit_anim) .setPopEnterAnim(R.anim.default_pop_enter_anim) .setPopExitAnim(R.anim.default_pop_exit_anim) .build(); return navOptions; }Tennietenniel
because theoretically any fragment that you don't supply an animation to, will use the default.Tennietenniel
If that doesn't work let me know I have another idea.Tennietenniel

© 2022 - 2024 — McMap. All rights reserved.