有没有可能像导航组件里有一个动画一样,给喜好的碎片变化添加一个动画?安卓指南
所以我想执行类似这里的事情。
<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>
...
我想说明一下:我想使用导航组件和Jetpack偏好设置相结合。问题是,Jetpack会自动处理首选项中的动画,但我想在默认的淡入淡出之外再加一个。所以我只是想覆盖它,即:有没有办法像在hirachy中那样在xml中添加动画?
<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>
我假设,因为你问的是如何为片段的进出添加动画,所以你没有使用导航框架,因为它是免费提供的。所以,让我们假设你没有使用它,并研究一下如何实现这个目标。
首先我们需要在某个地方有一个方法来处理交换片段。让我们做一个扩展类。我在所有的例子中都使用Kotlin和Androidx。
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()
}
}
当然现在你需要将你想要的动画添加到anims目录下。
res->anim->[在此添加文件]。
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>
现在剩下的就是使用它了。你有很多选择,比如传递bundle参数,决定用hideshow代替addremove。(注意*如果你隐藏了show,动画可能无法工作,但你不会失去加载的webview或其他下载的视图,所以这真的取决于你的使用情况。
MainActivity.kt
private fun changeToMyFragment() {
if (myFragment == null) {
myFragment = MyFragment()
}
swapFragment(R.id.placeholder_framelayout, myFragment)
}
就是这样,任何时候你想改变片段,只需要使用这个方法 "swapFragment",然后把你想要的片段传进来。其他参数都是可选的,如果你需要的话,可以进行额外的控制。
我添加了日志以增加你阅读的清晰度,还有一个删除片段的功能,以防你只需要它消失。
把它想象成一个扩展,你只需把它放在任何项目中,然后按原样使用即可。你不必深入研究它,这将处理你所要求的。
祝你编码愉快