Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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>

like image 249
kaulex Avatar asked Dec 23 '19 20:12

kaulex


1 Answers

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!

like image 145
Sam Avatar answered Oct 24 '22 12:10

Sam